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

[sitecore-jss-nextjs] [sitecore-jss-react] Component level data fetching(SSR/SSG) for BYOC #1610

Merged
merged 10 commits into from
Sep 13, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Our versioning strategy is as follows:
* `[templates/nextjs]` `[sitecore-jss-nextjs]` Better error handling for component-level data fetching ([#1586](https://github.com/Sitecore/jss/pull/1586))
* `[sitecore-jss-react]` Fetch Data for FEaaS Components as part of Component SSR/SSG ([#1586](https://github.com/Sitecore/jss/pull/1590))
* `[sitecore-jss-dev-tools]` `[templates/nextjs]` `[templates/react]` Introduce "components" configuration for ComponentBuilder ([#1598](https://github.com/Sitecore/jss/pull/1598))
* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Component level data fetching(SSR) for BYOC ([#1610](https://github.com/Sitecore/jss/pull/1610))
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved

### 🧹 Chores

Expand Down
45 changes: 45 additions & 0 deletions packages/sitecore-jss-nextjs/src/components/BYOCWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
BYOCWrapper,
BYOCComponentParams,
fetchBYOCComponentServerProps,
} from '@sitecore-jss/sitecore-jss-react';
import {
GetStaticComponentProps,
GetServerSideComponentProps,
} from '../sharedTypes/component-props';
import { constants } from '@sitecore-jss/sitecore-jss';

/**
* This is a repackaged version of the React BYOCWrapper component with support for
* server rendering in Next.js (using component-level data-fetching feature of JSS).
*/

/**
* Will be called during SSG
* @param {ComponentRendering} rendering
* @returns {GetStaticPropsContext} context
*/
export const getStaticProps: GetStaticComponentProps = async (rendering) => {
if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) {
return null;
}
const params: BYOCComponentParams = rendering.params || {};
const result = await fetchBYOCComponentServerProps(params);
return result;
};

/**
* Will be called during SSR
* @param {ComponentRendering} rendering
* @returns {GetStaticPropsContext} context
*/
export const getServerSideProps: GetServerSideComponentProps = async (rendering) => {
if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) {
return null;
}
const params: BYOCComponentParams = rendering.params || {};
const result = await fetchBYOCComponentServerProps(params);
return result;
};

export default BYOCWrapper;
3 changes: 2 additions & 1 deletion packages/sitecore-jss-nextjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ export { Placeholder } from './components/Placeholder';
export { EditingComponentPlaceholder } from './components/EditingComponentPlaceholder';
export { NextImage } from './components/NextImage';
import * as FEaaSWrapper from './components/FEaaSWrapper';
import * as BYOCWrapper from './components/BYOCWrapper';
export { FEaaSWrapper };
export { BYOCWrapper };

export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder';

Expand All @@ -177,7 +179,6 @@ export {
BYOCComponent,
BYOCComponentProps,
getFEAASLibraryStylesheetLinks,
BYOCWrapper,
File,
FileField,
RichTextField,
Expand Down
31 changes: 31 additions & 0 deletions packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('BYOCComponent', () => {
ComponentName: 'Foo',
ComponentProps: JSON.stringify({ prop1: 'value1' }),
},
fetchedData: null,
};
const Foo = () => <p id="foo-content">Test</p>;
FEAAS.External.registerComponent(Foo, {
Expand All @@ -29,6 +30,34 @@ describe('BYOCComponent', () => {
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});

it('should render when props are prefetched', () => {
const fetchedData = {
prop1: 'prefetched_value1',
};
const mockProps = {
params: {
ComponentName: 'Foo',
ComponentProps: JSON.stringify({ prop1: 'value1' }),
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
},
fetchedData,
};
const Foo = () => <p id="foo-content">Test</p>;
FEAAS.External.registerComponent(Foo, {
name: 'Foo',
properties: {
prop1: {
type: 'string',
},
},
});
const wrapper = mount(<BYOCComponent {...mockProps} />);
const fooComponent = wrapper.find('feaas-external');
expect(fooComponent).to.have.lengthOf(1);
expect(fooComponent.prop('prop1')).to.equal('prefetched_value1');
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});
});

describe('Error handling', () => {
Expand All @@ -38,6 +67,7 @@ describe('Error handling', () => {
ComponentName: 'ExampleComponent',
ComponentProps: 'invalid-json',
},
fetchedData: null,
};
const wrapper = mount(<BYOCComponent {...props} />);
const errorComponent = wrapper.find('DefaultErrorComponent');
Expand All @@ -52,6 +82,7 @@ describe('Error handling', () => {
ComponentName: 'ExampleComponent',
ComponentProps: 'invalid-json',
},
fetchedData: null,
};

const wrapper = mount(<BYOCComponent {...props} />);
Expand Down
77 changes: 57 additions & 20 deletions packages/sitecore-jss-react/src/components/BYOCComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ import * as FEAAS from '@sitecore-feaas/clientside/react';

export const BYOC_COMPONENT_RENDERING_NAME = 'BYOCComponent';

/**
* FEaaS props for server rendering.
*/
type BYOCServerProps = {
/**
* Fetched data from server component props for server rendering, based on rendering params.
*/
fetchedData?: FEAAS.DataScopes;
};

/**
* Data from rendering params on Sitecore's BYOC rendering
*/
export type BYOCComponentParams = {
/**
* Name of the component to render
*/
ComponentName: string;
ComponentName?: string;
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
/**
* JSON props to pass into rendered component
*/
Expand All @@ -28,7 +38,7 @@ export type BYOCComponentParams = {
/**
* Props for BYOCComponent. Includes components list to load external components from.
*/
export type BYOCComponentProps = {
export type BYOCComponentClientProps = {
/**
* rendering params
*/
Expand All @@ -50,6 +60,8 @@ export type BYOCComponentProps = {
| React.FC<MissingComponentProps>;
};

export type BYOCComponentProps = BYOCComponentClientProps & BYOCServerProps;

type ErrorComponentProps = {
[prop: string]: unknown;
error?: Error;
Expand Down Expand Up @@ -117,26 +129,28 @@ export class BYOCComponent extends React.Component<BYOCComponentProps> {
<MissingComponent {...unRegisteredComponentProps} />
);

let componentProps: { [key: string]: unknown } = undefined;

if (props.params?.ComponentProps) {
try {
componentProps = JSON.parse(props.params.ComponentProps) ?? {};
} catch (e) {
console.warn(
`Parsing props for ${componentName} component from rendering params failed. Error: ${e}`
);
return this.props.errorComponent ? (
<this.props.errorComponent error={e as Error} />
) : (
<DefaultErrorComponent error={e as Error} />
);
}
}
const ErrorComponent = this.props.errorComponent;

let componentProps: { [key: string]: any } = props.fetchedData;

if (!componentProps) {
componentProps = props.fields ? getDataFromFields(props.fields) : {};
if (props.params?.ComponentProps) {
try {
componentProps = JSON.parse(props.params.ComponentProps) ?? {};
} catch (e) {
console.error(
`Parsing props for ${componentName} component from rendering params failed. Error: ${e}`
);
return ErrorComponent ? (
<ErrorComponent error={e as Error} />
) : (
<DefaultErrorComponent error={e as Error} />
);
}
} else {
componentProps = props.fields ? getDataFromFields(props.fields) : {};
}
}

return (
<FEAAS.ExternalComponent
componentName={componentName}
Expand All @@ -146,3 +160,26 @@ export class BYOCComponent extends React.Component<BYOCComponentProps> {
);
}
}

/**
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
* Fetches server component props required for server rendering, based on rendering params.
* @param {BYOCComponentParams} params component params
*/
export async function fetchBYOCComponentServerProps(
params: BYOCComponentParams
): Promise<BYOCComponentProps> {
let fetchedData: FEAAS.DataScopes = null;
const fetchDataOptions: FEAAS.DataOptions = params.ComponentProps
? JSON.parse(params.ComponentProps)
: {};

try {
fetchedData = await FEAAS.DataSettings.fetch(fetchDataOptions || {});
} catch (e) {
console.error('Fetch BYOC component props failed');
addy-pathania marked this conversation as resolved.
Show resolved Hide resolved
}

return {
fetchedData,
};
}
7 changes: 6 additions & 1 deletion packages/sitecore-jss-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ export {
fetchFEaaSComponentServerProps,
} from './components/FEaaSComponent';
export { FEaaSWrapper } from './components/FEaaSWrapper';
export { BYOCComponent, BYOCComponentParams, BYOCComponentProps } from './components/BYOCComponent';
export {
BYOCComponent,
BYOCComponentParams,
BYOCComponentProps,
fetchBYOCComponentServerProps,
} from './components/BYOCComponent';
export { BYOCWrapper } from './components/BYOCWrapper';
export { Link, LinkField, LinkFieldValue, LinkProps, LinkPropTypes } from './components/Link';
export { File, FileField } from './components/File';
Expand Down