From 1cdf325d767e27fcad68cc4ef51dba372668e0eb Mon Sep 17 00:00:00 2001 From: illiakovalenko <zlatoborodyi@gmail.com> Date: Mon, 13 Dec 2021 03:51:59 +0200 Subject: [PATCH 1/5] add `modifyComponentProps` property --- .../src/components/Placeholder.tsx | 23 +++++++++++++++++++ packages/sitecore-jss-nextjs/src/index.ts | 2 +- .../src/components/PlaceholderCommon.tsx | 5 +++- packages/sitecore-jss-react/src/index.ts | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 packages/sitecore-jss-nextjs/src/components/Placeholder.tsx diff --git a/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx b/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx new file mode 100644 index 0000000000..fa51f35e6e --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx @@ -0,0 +1,23 @@ +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 }; + }} + /> + ); +}; diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index 615f0f55a7..e8b49093ae 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -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, diff --git a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx index b061abb428..19d36c3d45 100644 --- a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx +++ b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx @@ -42,6 +42,8 @@ export interface PlaceholderProps { [name: string]: string; }; + modifyComponentProps?: (componentProps: { [key: string]: unknown, rendering: ComponentRendering }) => ({ [key: string]: unknown, rendering: ComponentRendering }); + /** * 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) @@ -85,6 +87,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[]; @@ -191,7 +194,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 diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index b2d7993308..5637369f02 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -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'; From 15e1375ee7daa9d242cf28d8e729e127e0de7936 Mon Sep 17 00:00:00 2001 From: illiakovalenko <zlatoborodyi@gmail.com> Date: Mon, 13 Dec 2021 13:24:09 +0200 Subject: [PATCH 2/5] Add jsdoc, unit tests --- .../src/components/Placeholder.test.tsx | 41 +++++++++++++++++++ .../src/components/PlaceholderCommon.tsx | 14 +++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx index 6e942ecff2..ce302b20c5 100644 --- a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx +++ b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx @@ -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, + }); + }); }); }); diff --git a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx index 19d36c3d45..6a61c2d296 100644 --- a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx +++ b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx @@ -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. */ @@ -41,9 +47,11 @@ export interface PlaceholderProps { params?: { [name: string]: string; }; - - modifyComponentProps?: (componentProps: { [key: string]: unknown, rendering: ComponentRendering }) => ({ [key: string]: unknown, rendering: ComponentRendering }); - + /** + * 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. + */ + 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) From d7c4abceaf67c0aa8bafbdf234d40857f3b4b460 Mon Sep 17 00:00:00 2001 From: illiakovalenko <zlatoborodyi@gmail.com> Date: Mon, 13 Dec 2021 15:33:04 +0200 Subject: [PATCH 3/5] edit jsdoc --- .../sitecore-jss-react/src/components/PlaceholderCommon.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx index 6a61c2d296..2fa357bce7 100644 --- a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx +++ b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx @@ -50,6 +50,8 @@ export interface PlaceholderProps { /** * 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; /** From 46dfbeb2134cba8c46b65c166f02bc84e30d3403 Mon Sep 17 00:00:00 2001 From: illiakovalenko <zlatoborodyi@gmail.com> Date: Mon, 13 Dec 2021 15:45:25 +0200 Subject: [PATCH 4/5] fix eslint issue --- packages/sitecore-jss-nextjs/src/components/Placeholder.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx b/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx index fa51f35e6e..42713cb1d1 100644 --- a/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Placeholder.tsx @@ -14,7 +14,9 @@ export const Placeholder = (props: PlaceholderComponentProps) => { modifyComponentProps={(initialProps) => { if (!initialProps.rendering.uid) return initialProps; - const data = componentPropsContext[initialProps.rendering.uid] as { [key: string]: unknown }; + const data = componentPropsContext[initialProps.rendering.uid] as { + [key: string]: unknown; + }; return { ...initialProps, ...data }; }} From 44ef0f9f4fa4a62b2901f1a3838d10cc2aab4f4a Mon Sep 17 00:00:00 2001 From: illiakovalenko <zlatoborodyi@gmail.com> Date: Mon, 13 Dec 2021 15:59:24 +0200 Subject: [PATCH 5/5] Edit ConnectedDemo --- .../graphql/GraphQL-ConnectedDemo.dynamic.tsx | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx index bdea9ed7cb..3ab426a1cb 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx @@ -4,7 +4,6 @@ import { Link, GetServerSideComponentProps, GetStaticComponentProps, - useComponentProps, constants, GraphQLRequestClient, withDatasourceCheck, @@ -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(); }, []); @@ -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 (