Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Next.js] Component props auto-injection by Placeholder #884

Merged
merged 5 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/sitecore-jss-nextjs/src/components/Placeholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useContext } from 'react';
import {
Placeholder as ReactPlaceholder,
PlaceholderComponentProps,
} from '@sitecore-jss/sitecore-jss-react';
import { ComponentPropsReactContext } from './ComponentPropsContext';

export const Placeholder = (props: PlaceholderComponentProps) => {
const componentPropsContext = useContext(ComponentPropsReactContext);

return (
<ReactPlaceholder
{...props}
modifyComponentProps={(initialProps) => {
if (!initialProps.rendering.uid) return initialProps;

const data = componentPropsContext[initialProps.rendering.uid] as {
[key: string]: unknown;
};

return { ...initialProps, ...data };
}}
/>
);
};
2 changes: 1 addition & 1 deletion packages/sitecore-jss-nextjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ export {

export { Link } from './components/Link';
export { RichText } from './components/RichText';
export { Placeholder } from './components/Placeholder';

export {
ComponentFactory,
Placeholder,
Image,
ImageField,
LinkField,
Expand Down
41 changes: 41 additions & 0 deletions packages/sitecore-jss-react/src/components/Placeholder.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,47 @@ describe('<Placeholder />', () => {
.indexOf(expectedMessage.value) !== -1
).to.be.true;
});

it('should apply modifyComponentProps to the final props', () => {
const component = dataSet.data.sitecore.route as any;
const phKey = 'main';
const expectedMessage = (component.placeholders.main as any[]).find((c) => c.componentName)
.fields.message;

const modifyComponentProps = (props) => {
if (props.rendering?.componentName === 'DownloadCallout') {
return {
...props,
extraData: {
x: true,
},
};
}

return props;
};

const renderedComponent = mount(
<SitecoreContext componentFactory={componentFactory}>
<Placeholder
name={phKey}
rendering={component}
modifyComponentProps={modifyComponentProps}
/>
</SitecoreContext>
);

expect(
renderedComponent
.find('.download-callout-mock')
.html()
.indexOf(expectedMessage.value) !== -1
).to.be.true;

expect(renderedComponent.find('DownloadCallout').prop('extraData')).to.deep.equal({
x: true,
});
});
});
});

Expand Down
17 changes: 15 additions & 2 deletions packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ type ErrorComponentProps = {
[prop: string]: unknown;
};

/** Provided for the component which represents rendering data */
type ComponentProps = {
[key: string]: unknown;
rendering: ComponentRendering;
};

export interface PlaceholderProps {
[key: string]: unknown;
/** Name of the placeholder to render. */
Expand All @@ -41,7 +47,13 @@ export interface PlaceholderProps {
params?: {
[name: string]: string;
};

/**
* Modify final props of component (before render) provided by rendering data.
* Can be used in case when you need to insert additional data into the component.
* @param {ComponentProps} componentProps component props to be modified
* @returns {ComponentProps} modified or initial props
*/
modifyComponentProps?: (componentProps: ComponentProps) => ComponentProps;
/**
* A component that is rendered in place of any components that are in this placeholder,
* but do not have a definition in the componentFactory (i.e. don't have a React implementation)
Expand Down Expand Up @@ -85,6 +97,7 @@ export class PlaceholderCommon<T extends PlaceholderProps> extends React.Compone
PropTypes.object as Requireable<React.ComponentClass<unknown>>,
PropTypes.func as Requireable<React.FC<unknown>>,
]),
modifyComponentProps: PropTypes.func,
};

nodeRefs: Element[];
Expand Down Expand Up @@ -191,7 +204,7 @@ export class PlaceholderCommon<T extends PlaceholderProps> extends React.Compone

return React.createElement<{ [attr: string]: unknown }>(
component as React.ComponentType,
finalProps
this.props.modifyComponentProps ? this.props.modifyComponentProps(finalProps) : finalProps
);
})
.filter((element) => element); // remove nulls
Expand Down
2 changes: 1 addition & 1 deletion packages/sitecore-jss-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export {
} from '@sitecore-jss/sitecore-jss/i18n';
export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
export { ComponentFactory } from './components/sharedTypes';
export { Placeholder } from './components/Placeholder';
export { Placeholder, PlaceholderComponentProps } from './components/Placeholder';
export { Image, ImageField } from './components/Image';
export { RichText, RichTextProps, RichTextPropTypes, RichTextField } from './components/RichText';
export { Text, TextField } from './components/Text';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Link,
GetServerSideComponentProps,
GetStaticComponentProps,
useComponentProps,
constants,
GraphQLRequestClient,
withDatasourceCheck,
Expand All @@ -27,9 +26,9 @@ type GraphQLConnectedDemoData = {
contextItem: RouteItem;
};

const GraphQLConnectedDemo = (props: StyleguideComponentProps): JSX.Element => {
const data = useComponentProps<GraphQLConnectedDemoData>(props.rendering.uid);
type GraphQLConnectedDemoProps = StyleguideComponentProps & GraphQLConnectedDemoData;

const GraphQLConnectedDemo = (props: GraphQLConnectedDemoProps): JSX.Element => {
useEffect(() => {
resetEditorChromes();
}, []);
Expand All @@ -46,41 +45,41 @@ const GraphQLConnectedDemo = (props: StyleguideComponentProps): JSX.Element => {
<code>getServerSideProps</code> execution.
</p>

{data && data.datasource && (
{props.datasource && (
<div>
<h4>Datasource Item (via Connected GraphQL)</h4>
id: {data.datasource.id}
id: {props.datasource.id}
<br />
name: {data.datasource.name}
name: {props.datasource.name}
<br />
sample1: {data.datasource.sample1?.value}
sample1: {props.datasource.sample1?.value}
<br />
sample1 (editable): <Text field={data.datasource.sample1?.jsonValue} />
sample1 (editable): <Text field={props.datasource.sample1?.jsonValue} />
<br />
sample2:
<br />
<ul>
<li>text: {data.datasource.sample2?.text}</li>
<li>url: {data.datasource.sample2?.url}</li>
<li>target: {data.datasource.sample2?.target}</li>
<li>text: {props.datasource.sample2?.text}</li>
<li>url: {props.datasource.sample2?.url}</li>
<li>target: {props.datasource.sample2?.target}</li>
<li>
editable: <Link field={data.datasource.sample2?.jsonValue} />
editable: <Link field={props.datasource.sample2?.jsonValue} />
</li>
<li>field type: {data.datasource.sample2?.definition?.type}</li>
<li>field is shared?: {data.datasource.sample2?.definition?.shared.toString()}</li>
<li>field type: {props.datasource.sample2?.definition?.type}</li>
<li>field is shared?: {props.datasource.sample2?.definition?.shared.toString()}</li>
</ul>
</div>
)}
{data && data.contextItem && (
{props.contextItem && (
<div>
<h4>Route Item (via Connected GraphQL)</h4>
id: {data.contextItem.id}
id: {props.contextItem.id}
<br />
page title: {data.contextItem.pageTitle?.value}
page title: {props.contextItem.pageTitle?.value}
<br />
children:
<ul>
{data.contextItem.children.results.map((child) => {
{props.contextItem.children.results.map((child) => {
const routeItem = child as RouteItem;

return (
Expand Down