Skip to content

Commit

Permalink
[packages/sitecore-jss-react] Combine params and datasource props in …
Browse files Browse the repository at this point in the history
…FEAAS and BYOC
  • Loading branch information
art-alexeyenko committed Dec 13, 2023
1 parent 5bfd07a commit fb6a341
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 20 deletions.
93 changes: 93 additions & 0 deletions packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx
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
10 changes: 6 additions & 4 deletions packages/sitecore-jss-react/src/components/BYOCComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { ComponentFields } from '@sitecore-jss/sitecore-jss/layout';
import { getDataFromFields } from '../utils';
import { concatData, getDataFromFields } from '../utils';
import { MissingComponent, MissingComponentProps } from './MissingComponent';
import * as FEAAS from '@sitecore-feaas/clientside/react';

Expand Down 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,9 +150,11 @@ export class BYOCComponent extends React.Component<BYOCComponentProps> {
<DefaultErrorComponent error={e as Error} />
);
}
} else {
componentProps = props.fields ? getDataFromFields(props.fields) : {};
}
// apply props from item datasource
componentProps = props.fields
? concatData(componentProps, getDataFromFields(props.fields))
: componentProps;

// we render fallback on client to avoid problems with client-only components
return (
Expand Down
61 changes: 59 additions & 2 deletions packages/sitecore-jss-react/src/components/FEaaSComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,21 +153,78 @@ describe('<FEaaSComponent />', () => {
value: 'Welcome to Sitecore JSS',
},
};
const override = JSON.stringify({ sampleText: { value: 'Welcome to FEAAS' } });
const override = JSON.stringify({ sampleText: 'Welcome to FEAAS' });
const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: override,
},
fields,
template: '<h1 data-path="sampleText.value"></h1>',
template: '<h1 data-path="sampleText"></h1>',
};

const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
expect(wrapper.html()).to.contain('Welcome to FEAAS');
});

it('should combine override data with datasource fields', () => {
const fields: ComponentFields = {
sampleText: {
value: 'Welcome to Sitecore JSS',
},
};
const override = JSON.stringify({ otherText: 'Welcome to FEAAS' });
const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: override,
},
fields,
template: '<h1 data-path="sampleText"></h1><h1 data-path="otherText"></h1>',
};

const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
console.log('DEBUG');
console.log(wrapper.html());
expect(wrapper.html()).to.contain('Welcome to FEAAS');
expect(wrapper.html()).to.contain('Welcome to Sitecore JSS');
});

it('should prefer fetched data and combine it with override data and datasource fields', () => {
const fields: ComponentFields = {
sampleText: {
value: 'Welcome to Sitecore JSS',
},
description: {
value: 'This may be ovewritten',
},
};
const fetchedData = {
sampleText: 'Welcome to fetched data',
};
const override = JSON.stringify({ otherText: 'Welcome to FEAAS' });
const props: FEaaSComponentProps = {
params: {
...requiredParams,
ComponentDataOverride: override,
},
fields,
fetchedData,
template:
'<h1 data-path="sampleText"></h1><h1 data-path="otherText"></h1><p data-path="description"></p>',
};

const wrapper = shallow(<FEaaSComponent {...props} />);
expect(wrapper).to.have.length(1);
console.log('DEBUG');
console.log(wrapper.html());
expect(wrapper.html()).to.contain('Welcome to FEAAS');
expect(wrapper.html()).to.contain(fetchedData.sampleText);
expect(wrapper.html()).to.contain('This may be ovewritten');
});

it('should send prefetched data', () => {
const fetchedData = {
foo: 'bar',
Expand Down
27 changes: 13 additions & 14 deletions packages/sitecore-jss-react/src/components/FEaaSComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import * as FEAAS from '@sitecore-feaas/clientside/react';
import { ComponentFields, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout';
import { getDataFromFields } from '../utils';
import { concatData, getDataFromFields } from '../utils';

export const FEAAS_COMPONENT_RENDERING_NAME = 'FEaaSComponent';

Expand Down Expand Up @@ -76,28 +76,27 @@ export const FEaaSComponent = (props: FEaaSComponentProps): JSX.Element => {
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);
let data = (props.fetchedData as { [key: string]: unknown }) ?? {};
if (props.params?.ComponentDataOverride) {
// Use override data if provided
try {
data = concatData(data, JSON.parse(props.params.ComponentDataOverride));
} catch (e) {
console.error(
`ComponentDataOverride param could not be parsed and will be ignored. Error: ${e}`
);
}
}
// also apply item datasource data if present
data = props.fields ? concatData(data, getDataFromFields(props.fields)) : data;

// 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
// FEAAS should not fetch anything, since JSS does the fetching - so we pass empty array into fetch param
return (
<FEAAS.Component
data={props.fetchedData || data}
data={data}
template={props.template}
cdn={props.params?.ComponentHostName}
library={props.params?.LibraryId}
Expand Down
17 changes: 17 additions & 0 deletions packages/sitecore-jss-react/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,20 @@ export const getDataFromFields = (fields: ComponentFields): { [key: string]: unk
}, data);
return data;
};

/**
* Used to combine props data in BYOC and FEAAS components
*/
export const concatData = (
base: { [key: string]: unknown },
appendix: { [key: string]: unknown }
): { [key: string]: unknown } => {
const data = base;
appendix &&
Object.keys(appendix).forEach((key) => {
if (Object.keys(base).indexOf(key) === -1) {
data[key] = appendix[key];
}
});
return data;
};

0 comments on commit fb6a341

Please sign in to comment.