Skip to content

Commit

Permalink
[packages/sitecore-jss-react][21.5] Combine fetched and datasource it…
Browse files Browse the repository at this point in the history
…em props in BYOC and FEAAS (#1694)
  • Loading branch information
art-alexeyenko committed Dec 14, 2023
1 parent f836ef8 commit aa5410b
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 87 deletions.
4 changes: 2 additions & 2 deletions packages/sitecore-jss-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"url": "https://github.com/sitecore/jss/issues"
},
"devDependencies": {
"@sitecore-feaas/clientside": "^0.5.5",
"@sitecore-feaas/clientside": "^0.5.6",
"@types/chai": "^4.3.4",
"@types/chai-string": "^1.4.2",
"@types/deep-equal": "^1.0.1",
Expand Down Expand Up @@ -58,7 +58,7 @@
"typescript": "~4.9.3"
},
"peerDependencies": {
"@sitecore-feaas/clientside": "^0.5.5",
"@sitecore-feaas/clientside": "^0.5.6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,99 @@ describe('BYOCComponent', () => {
expect(fooComponent.find('#foo-content')).to.have.length(1);
});

it('should use datasource fields when provided', () => {
const fields = {
prop1: {
value: 'value2',
},
};
const mockProps = {
params: {
ComponentName: 'Foo',
},
fetchedData: {},
fields,
};
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('value2');
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});

it('should prefer ComponentProps over datasource fields', () => {
const fields = {
prop1: {
value: 'value2',
},
};
const mockProps = {
params: {
ComponentName: 'Foo',
ComponentProps: JSON.stringify({ prop1: 'value1' }),
},
fetchedData: {},
fields,
};
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('value1');
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});

it('should combine ComponentProps and datasource fields', () => {
const fields = {
prop2: {
value: 'value2',
},
};
const mockProps = {
params: {
ComponentName: 'Foo',
ComponentProps: JSON.stringify({ prop1: 'value1' }),
},
fetchedData: {},
fields,
};
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('value1');
expect(fooComponent.prop('prop2')).to.equal('value2');
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});

it('should render with static and fetched props when props are prefetched', () => {
const fetchedData = {
prop2: 'prefetched_value1',
Expand Down Expand Up @@ -58,7 +151,7 @@ describe('BYOCComponent', () => {
const fooComponent = wrapper.find('feaas-external');
expect(fooComponent).to.have.lengthOf(1);
expect(fooComponent.prop('prop1')).to.equal('value1');
expect(fooComponent.prop('datasources')).to.equal('{"prop2":"prefetched_value1"}');
expect(fooComponent.prop('datasources')).to.equal('{"prop2":"prefetched_value1","_":{}}');
expect(fooComponent.prop('data-external-id')).to.equal('Foo');
expect(fooComponent.find('#foo-content')).to.have.length(1);
});
Expand Down
8 changes: 4 additions & 4 deletions packages/sitecore-jss-react/src/components/BYOCComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export class BYOCComponent extends React.Component<BYOCComponentProps> {

const ErrorComponent = this.props.errorComponent;

let componentProps: { [key: string]: any } = null;
let componentProps: { [key: string]: any } = {};

if (props.params?.ComponentProps) {
try {
Expand All @@ -150,16 +150,16 @@ export class BYOCComponent extends React.Component<BYOCComponentProps> {
<DefaultErrorComponent error={e as Error} />
);
}
} else {
componentProps = props.fields ? getDataFromFields(props.fields) : {};
}
// apply props from item datasource
const dataSourcesData = { ...props.fetchedData, _: getDataFromFields(props.fields ?? {}) };

// we render fallback on client to avoid problems with client-only components
return (
<FEAAS.ExternalComponent
componentName={componentName}
clientFallback={fallbackComponent}
datasources={props.fetchedData}
datasources={dataSourcesData}
{...componentProps}
/>
);
Expand Down
90 changes: 33 additions & 57 deletions packages/sitecore-jss-react/src/components/FEaaSComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ describe('<FEaaSComponent />', () => {
};
const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.equal(
'<feaas-component class="-feaas" cdn="host123" library="library123" version="version123" component="component123" revision="staged"></feaas-component>'
expect(wrapper.html()).to.contain(
'<feaas-component class="-feaas" cdn="host123" library="library123" version="version123" component="component123" revision="staged"'
);
});

Expand All @@ -68,7 +68,7 @@ describe('<FEaaSComponent />', () => {
const wrapper = shallow(<FEaaSComponent template={template} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.equal(
`<feaas-component class="-feaas">${template}</feaas-component>`
`<feaas-component class="-feaas" data="{&quot;_&quot;:{}}">${template}</feaas-component>`
);
});

Expand All @@ -79,28 +79,28 @@ describe('<FEaaSComponent />', () => {
const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.equal(
'<feaas-component class="-feaas" cdn="host123" library="library123" version="version123" component="component123" revision="staged"></feaas-component>'
'<feaas-component class="-feaas" cdn="host123" library="library123" version="version123" component="component123" revision="staged" data="{&quot;_&quot;:{}}"></feaas-component>'
);
});

describe('data', () => {
it('should send override data', () => {
it('should send fetched data', () => {
const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: '{ "foo": "bar", "baz": 1 }',
},
fetchedData: undefined,
fetchedData: { foo: 'bar', baz: 1 },
template: '<h1 data-path="foo"></h1><h2 data-path="baz"></h2>',
};
const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.contain(
`data="${props.params?.ComponentDataOverride!.replace(/"/g, '&quot;').replace(/\s/g, '')}"`
);
const output = wrapper.html();
expect(output).to.contain('<h1 data-path="foo">bar</h1>');
expect(output).to.contain('<h2 data-path="baz">1</h2>');
});

it('should send datasource fields', () => {
const fields: ComponentFields = {
const fields = {
sampleText: {
value: 'Welcome-to-Sitecore-JSS',
},
Expand All @@ -121,76 +121,52 @@ describe('<FEaaSComponent />', () => {
},
},
};
const template = `
<h1 data-path="xm.sampleText"></h1>
<img data-path-src="xm.sampleImage.src" data-path-alt="xm.sampleImage.alt"></img>
<p data-path="xm.sampleNumber"></p>
<a data-path-href="xm.sampleLink.href" data-path-id="xm.sampleLink.id"></a>`;
const props: FEaaSComponentProps = {
params: {
...requiredParams,
},
fields,
};
const expectedData = {
sampleText: 'Welcome-to-Sitecore-JSS',
sampleImage: {
src: '/-/media/sc_logo.png',
alt: 'Sitecore-Logo',
},
sampleNumber: 1.21,
sampleLink: {
href: '/',
id: '{54C8E9B5-0B2C-5363-8FA6-D32A3A302F51}',
linktype: 'internal',
},
template,
};
const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.contain(
`data="${JSON.stringify(expectedData)
.replace(/"/g, '&quot;')
.replace(/\s/g, '')}"`
const output = wrapper.html();
expect(output).to.contain(`<h1 data-path="xm.sampleText">${fields.sampleText.value}</h1>`);
expect(output).to.contain(
`<img data-path-src="xm.sampleImage.src" data-path-alt="xm.sampleImage.alt" src="${fields.sampleImage.value.src}" alt="${fields.sampleImage.value.alt}"/>`
);
expect(output).to.contain(`<p data-path="xm.sampleNumber">${fields.sampleNumber.value}</p>`);
expect(output).to.contain(
`<a data-path-href="xm.sampleLink.href" data-path-id="xm.sampleLink.id" href="${fields.sampleLink.value.href}" id="${fields.sampleLink.value.id}"></a>`
);
});

it('should prefer override data over datasource fields', () => {
it('should combine fetched data with datasource fields', () => {
const fields: ComponentFields = {
sampleText: {
fieldText: {
value: 'Welcome to Sitecore JSS',
},
};
const fetched = { customDatasourceId: { fetchedText: 'Welcome to FEAAS' } };
const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: '{ "foo": "bar", "baz": 1 }',
},
fetchedData: fetched,
fields,
template:
'<h1 data-path="xm.fieldText"></h1><h1 data-path="customDatasourceId.fetchedText"></h1>',
};

const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.contain(
`data="${props.params?.ComponentDataOverride!.replace(/"/g, '&quot;').replace(/\s/g, '')}"`
);
});

it('should send prefetched data', () => {
const fetchedData = {
foo: 'bar',
baz: 42,
};

const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: '{ "foo": "test", "baz": 22 }',
},
fetchedData,
};

const wrapper = shallow(<FEaaSComponent {...props} />);

expect(wrapper).to.have.length(1);
const expectedData = JSON.stringify(fetchedData)
.replace(/"/g, '&quot;')
.replace(/\s/g, '');
expect(wrapper.html()).to.contain(`data="${expectedData}"`);
expect(wrapper.html()).to.contain('Welcome to FEAAS');
expect(wrapper.html()).to.contain('Welcome to Sitecore JSS');
});
});
});
21 changes: 4 additions & 17 deletions packages/sitecore-jss-react/src/components/FEaaSComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,15 @@ export const FEaaSComponent = (props: FEaaSComponentProps): JSX.Element => {
// Missing FEaaS component required props
return null;
}

let data: { [key: string]: unknown } = null;
if (props.fetchedData === null || props.fetchedData === undefined) {
if (props.params?.ComponentDataOverride) {
// Use override data if provided
try {
data = JSON.parse(props.params.ComponentDataOverride);
} catch (e) {
data = null;
}
} else if (props.fields) {
// Otherwise use datasource data (provided in fields)
data = getDataFromFields(props.fields);
}
}
// combine fetchedData from server with datasource data (if present)
const data = { ...props.fetchedData, _: getDataFromFields(props.fields ?? {}) };

// FEaaS control would still be hydrated by client
// we pass all the props as a workaround to avoid hydration error, until we convert all JSS components to server side
// this also allows component to fall back to full client-side rendering when template or src is empty
return (
<FEAAS.Component
data={props.fetchedData || data}
data={data}
template={props.template}
cdn={props.params?.ComponentHostName}
library={props.params?.LibraryId}
Expand Down Expand Up @@ -124,7 +111,7 @@ export async function fetchFEaaSComponentServerProps(
pageState && pageState !== LayoutServicePageState.Normal ? 'staged' : 'published';
const src = endpointOverride || composeComponentEndpoint(params, revisionFallback);
let template = '';
let fetchedData: FEAAS.DataScopes = null;
let fetchedData: FEAAS.DataScopes = {};
const fetchDataOptions: FEAAS.DataOptions = params.ComponentDataOverride
? JSON.parse(params.ComponentDataOverride)
: {};
Expand Down
12 changes: 6 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6433,14 +6433,14 @@ __metadata:
languageName: node
linkType: hard

"@sitecore-feaas/clientside@npm:^0.5.5":
version: 0.5.5
resolution: "@sitecore-feaas/clientside@npm:0.5.5"
"@sitecore-feaas/clientside@npm:^0.5.6":
version: 0.5.6
resolution: "@sitecore-feaas/clientside@npm:0.5.6"
dependencies:
"@sitecore/byoc": ^0.2.5
peerDependencies:
react-dom: ">=16.8.0"
checksum: 108ed61488f7f5777d3ae846f6d1c188a245440a511b7bc06200f4b236cf5e140075646efa4e3a1070ce7737fb4a2a808383e65d6570bd7c6a540dc183cc6e29
checksum: 6346d7cac5dc295ddf377fd802404229256fdfb4e2cabdf0cbd0ae8350a2fafe7e2b49abe31025a9dec848f4545e96178ec2173369a328799845ed6253c4cb90
languageName: node
linkType: hard

Expand Down Expand Up @@ -6770,7 +6770,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@sitecore-jss/sitecore-jss-react@workspace:packages/sitecore-jss-react"
dependencies:
"@sitecore-feaas/clientside": ^0.5.5
"@sitecore-feaas/clientside": ^0.5.6
"@sitecore-jss/sitecore-jss": 21.5.2-canary.4
"@types/chai": ^4.3.4
"@types/chai-string": ^1.4.2
Expand Down Expand Up @@ -6804,7 +6804,7 @@ __metadata:
ts-node: ^10.9.1
typescript: ~4.9.3
peerDependencies:
"@sitecore-feaas/clientside": ^0.5.5
"@sitecore-feaas/clientside": ^0.5.6
react: ^18.2.0
react-dom: ^18.2.0
languageName: unknown
Expand Down

0 comments on commit aa5410b

Please sign in to comment.