From 4aea79e85a2e5439c5ed194fb6758a7d8069e39c Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 11 Apr 2024 18:06:54 +0300 Subject: [PATCH 01/49] render field metadata for Text field component; introduce field metadata component - wip --- .../src/components/FieldMetadataComponent.tsx | 49 +++++++++++++++++++ .../src/components/Text.test.tsx | 24 +++++++++ .../src/components/Text.tsx | 27 ++++++++++ 3 files changed, 100 insertions(+) create mode 100644 packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx diff --git a/packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx b/packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx new file mode 100644 index 0000000000..1e23018932 --- /dev/null +++ b/packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx @@ -0,0 +1,49 @@ +import React, { FunctionComponent } from 'react'; + +export interface FieldMetadata { + contextItem?: FieldMetadataContextItem; + fieldId?: string; + fieldType?: string; + rawValue?: string; +} + +export interface FieldMetadataContextItem { + id?: string; + language?: string; + revision?: string; + version?: number; +} + +export interface FieldMetadataComponentProps { + htmlAttributes?: { + type: string; + chrometype: string; + className: string; + }; + data: string; +} + +const defaultAttributes = { + type: 'text/sitecore', + chrometype: 'field', + className: 'scpm', +}; + +export const FieldMetadataComponent: FunctionComponent = ( + props: React.PropsWithChildren +) => { + const attributes = { + ...defaultAttributes, + ...props.htmlAttributes, + }; + + return ( + + + {props.data} + + {props.children} + + + ); +}; diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index 788b2f3bbc..714211d2b8 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -179,4 +179,28 @@ describe('', () => { expect(rendered.html()).to.contain('

'); expect(rendered.html()).to.contain('value'); }); + + it('should render metadata', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + const field = { + editable: eeTextData, + }; + const rendered = mount( + +
test
+
+ ); + + console.log(rendered.html()); + }); }); diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index a45d86b0a1..95dd176fbe 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,4 +1,9 @@ import React, { ReactElement, FunctionComponent } from 'react'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadataComponent'; import PropTypes from 'prop-types'; export interface TextField { @@ -24,11 +29,14 @@ export interface TextProps { * If false, HTML-encoding of the field value is disabled and the value is rendered as-is. */ encode?: boolean; + + metadata?: FieldMetadata; } export const Text: FunctionComponent = ({ field, tag, + metadata, editable, encode, ...otherProps @@ -37,6 +45,14 @@ export const Text: FunctionComponent = ({ return null; } + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + // can't use editable value if we want to output unencoded if (!encode) { // eslint-disable-next-line no-param-reassign @@ -103,6 +119,17 @@ Text.propTypes = { editable: PropTypes.string, }), tag: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), editable: PropTypes.bool, encode: PropTypes.bool, }; From 5c4d4f962824df756f5600ea802f144f07bf3d18 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 11:39:38 +0300 Subject: [PATCH 02/49] rename FieldMetadata module, add unit test for Text component, add comments --- .../{FieldMetadataComponent.tsx => FieldMetadata.tsx} | 2 ++ packages/sitecore-jss-react/src/components/Text.test.tsx | 8 ++++++-- packages/sitecore-jss-react/src/components/Text.tsx | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) rename packages/sitecore-jss-react/src/components/{FieldMetadataComponent.tsx => FieldMetadata.tsx} (93%) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx similarity index 93% rename from packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx rename to packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 1e23018932..c88eaf1379 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadataComponent.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -1,5 +1,6 @@ import React, { FunctionComponent } from 'react'; +/** The field metadata */ export interface FieldMetadata { contextItem?: FieldMetadataContextItem; fieldId?: string; @@ -7,6 +8,7 @@ export interface FieldMetadata { rawValue?: string; } +/** The field's context item metadata */ export interface FieldMetadataContextItem { id?: string; language?: string; diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index 714211d2b8..7f5810c3a1 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -180,7 +180,7 @@ describe('', () => { expect(rendered.html()).to.contain('value'); }); - it('should render metadata', () => { + it('should render field metadata component when metadata property is present', () => { const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -192,15 +192,19 @@ describe('', () => { fieldType: 'single-line', rawValue: 'Test1', }; + const field = { editable: eeTextData, }; + const rendered = mount(
test
); - console.log(rendered.html()); + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); }); }); diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 95dd176fbe..c5f66d7e62 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -3,7 +3,7 @@ import { FieldMetadata, FieldMetadataComponent, FieldMetadataComponentProps, -} from './FieldMetadataComponent'; +} from './FieldMetadata'; import PropTypes from 'prop-types'; export interface TextField { @@ -29,7 +29,9 @@ export interface TextProps { * If false, HTML-encoding of the field value is disabled and the value is rendered as-is. */ encode?: boolean; - + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ metadata?: FieldMetadata; } @@ -45,6 +47,7 @@ export const Text: FunctionComponent = ({ return null; } + // when metadata is present, render it to be used for chrome hydration if (metadata) { const props: FieldMetadataComponentProps = { data: JSON.stringify(metadata), From b23334a09ca1bd002594c11410ae454dd0809b76 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 13:12:30 +0300 Subject: [PATCH 03/49] add field metadata component to Date, Image and File field components; include unit tests --- .../src/components/Date.test.tsx | 24 ++++++++++++++ .../src/components/Date.tsx | 30 ++++++++++++++++++ .../src/components/File.test.tsx | 25 +++++++++++++++ .../src/components/File.tsx | 31 ++++++++++++++++++- .../src/components/Image.test.tsx | 26 ++++++++++++++++ .../src/components/Image.tsx | 31 +++++++++++++++++++ 6 files changed, 166 insertions(+), 1 deletion(-) diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 98b0b4d00b..85af02168c 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -81,4 +81,28 @@ describe('', () => { expect(c.html()).equal('

11-23-2001

'); }); + + it('should render field metadata component when metadata property is present', () => { + const props = { + field: { + value: '23-11-2001', + }, + metadata: { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'date', + rawValue: 'Test1', + }, + }; + + const rendered = shallow(); + + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 3ec447496a..2f554fe193 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,5 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadata'; export interface DateFieldProps { /** The date field data. */ @@ -19,11 +24,16 @@ export interface DateFieldProps { */ editable?: boolean; render?: (date: Date | null) => React.ReactNode; + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ + metadata?: FieldMetadata; } export const DateField: React.FC = ({ field, tag, + metadata, editable, render, ...otherProps @@ -32,6 +42,15 @@ export const DateField: React.FC = ({ return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + let children: React.ReactNode; const htmlProps: { @@ -64,6 +83,17 @@ DateField.propTypes = { editable: PropTypes.string, }).isRequired, tag: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), editable: PropTypes.bool, render: PropTypes.func, }; diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index 4585efe89c..2f8c649487 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -50,4 +50,29 @@ describe('', () => { expect(rendered.html()).to.contain('id="my-file"'); expect(rendered.html()).to.contain('class="my-css"'); }); + + it('should render field metadata component when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'file', + rawValue: 'Test1', + }; + + const field = { + src: '/lorem', + title: 'ipsum', + }; + + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 510688b6b2..6b30a94279 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,5 +1,10 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadata'; export interface FileFieldValue { [propName: string]: unknown; @@ -18,9 +23,13 @@ export interface FileProps { field: FileFieldValue | FileField; /** HTML attributes that will be appended to the rendered tag. */ children?: React.ReactNode; + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ + metadata?: FieldMetadata; } -export const File: React.FC = ({ field, children, ...otherProps }) => { +export const File: React.FC = ({ field, children, metadata, ...otherProps }) => { /* File fields cannot be managed via the EE. We never output "editable." */ @@ -31,6 +40,15 @@ export const File: React.FC = ({ field, children, ...otherProps }) => return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + // handle link directly on field for forgetful devs const file = ((dynamicField as FileFieldValue).src ? field @@ -55,6 +73,17 @@ File.propTypes = { value: PropTypes.object, }), ]).isRequired, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }; File.displayName = 'File'; diff --git a/packages/sitecore-jss-react/src/components/Image.test.tsx b/packages/sitecore-jss-react/src/components/Image.test.tsx index e9598c073e..7cfb584da9 100644 --- a/packages/sitecore-jss-react/src/components/Image.test.tsx +++ b/packages/sitecore-jss-react/src/components/Image.test.tsx @@ -293,4 +293,30 @@ describe('', () => { const rendered = mount(); expect(rendered.find('img')).to.have.length(1); }); + + it('should render field metadata component when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'image', + rawValue: 'Test1', + }; + + const imgField = { + src: '/assets/img/test0.png', + width: 8, + height: 10, + }; + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.find('img')).to.have.length(0); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 62f697f247..ac88b4af78 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,6 +3,11 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadata'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -51,6 +56,11 @@ export interface ImageProps { */ editable?: boolean; + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ + metadata?: FieldMetadata; + /** * Parameters that will be attached to Sitecore media URLs */ @@ -151,6 +161,7 @@ export const getEEMarkup = ( export const Image: React.FC = ({ media, editable, + metadata, imageParams, field, mediaUrlPrefix, @@ -170,6 +181,15 @@ export const Image: React.FC = ({ return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + const imageField = dynamicMedia as ImageField; if (editable && imageField.editable) { @@ -211,6 +231,17 @@ Image.propTypes = { editable: PropTypes.string, }), ]), + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), editable: PropTypes.bool, mediaUrlPrefix: PropTypes.instanceOf(RegExp), imageParams: PropTypes.objectOf( From 889b81e7e26cb9ae982aae945a0cad5eac994940 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 13:26:09 +0300 Subject: [PATCH 04/49] add field metadata component to link and richtext field components, include unit tests --- .../src/components/Link.test.tsx | 24 ++++++++++++++ .../src/components/Link.tsx | 31 ++++++++++++++++++- .../src/components/RichText.test.tsx | 24 ++++++++++++++ .../src/components/RichText.tsx | 29 +++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/packages/sitecore-jss-react/src/components/Link.test.tsx b/packages/sitecore-jss-react/src/components/Link.test.tsx index 1b3737c82b..7e8cac5c76 100644 --- a/packages/sitecore-jss-react/src/components/Link.test.tsx +++ b/packages/sitecore-jss-react/src/components/Link.test.tsx @@ -126,4 +126,28 @@ describe('', () => { const link = c.find('a'); expect(ref.current?.id).to.equal(link.props().id); }); + + it('should render field metadata component when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + + const field = { + href: '/lorem', + text: 'ipsum', + }; + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index a4906694a6..2cd7eefc9b 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,5 +1,10 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadata'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -38,10 +43,14 @@ export type LinkProps = React.DetailedHTMLProps< * NOTE: when in Sitecore Experience Editor, this setting is ignored due to technical limitations, and the description is always rendered. */ showLinkTextWithChildrenPresent?: boolean; + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ + metadata?: FieldMetadata; }; export const Link = forwardRef( - ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { + ({ field, editable, metadata, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { const children = otherProps.children as React.ReactNode; const dynamicField: LinkField | LinkFieldValue = field; @@ -54,6 +63,15 @@ export const Link = forwardRef( return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + const resultTags: ReactElement[] = []; // EXPERIENCE EDITOR RENDERING @@ -137,6 +155,17 @@ export const LinkPropTypes = { editableLastPart: PropTypes.string, }), ]).isRequired, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), editable: PropTypes.bool, showLinkTextWithChildrenPresent: PropTypes.bool, }; diff --git a/packages/sitecore-jss-react/src/components/RichText.test.tsx b/packages/sitecore-jss-react/src/components/RichText.test.tsx index 3aebdd0e45..dddf20a31b 100644 --- a/packages/sitecore-jss-react/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.test.tsx @@ -94,4 +94,28 @@ describe('', () => { expect(rendered.html()).to.contain('

'); expect(rendered.html()).to.contain('value'); }); + + it('should render field metadata component when metadata property is present', () => { + const field = { + value: 'value', + }; + + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 6192998a7e..331cf4c0c0 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,5 +1,10 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; +import { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './FieldMetadata'; export interface RichTextField { value?: string; @@ -21,6 +26,10 @@ export interface RichTextProps { * @default true */ editable?: boolean; + /** + * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages + */ + metadata?: FieldMetadata; } export const RichText: React.FC = forwardRef( @@ -29,6 +38,15 @@ export const RichText: React.FC = forwardRef( return null; } + // when metadata is present, render it to be used for chrome hydration + if (otherProps.metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(otherProps.metadata), + }; + + return {otherProps.children}; + } + const htmlProps = { dangerouslySetInnerHTML: { __html: field.editable && editable ? field.editable : field.value, @@ -48,6 +66,17 @@ export const RichTextPropTypes = { }), tag: PropTypes.string, editable: PropTypes.bool, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }; RichText.propTypes = RichTextPropTypes; From a8591e9721cc82318371c96829198a55006c7eab Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 15:57:01 +0300 Subject: [PATCH 05/49] update FieldMetadata interfaces to prevent build errors in sitecore-jss-nextjs; component update --- .../src/components/FieldMetadata.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index c88eaf1379..7c46f0d649 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -2,18 +2,18 @@ import React, { FunctionComponent } from 'react'; /** The field metadata */ export interface FieldMetadata { - contextItem?: FieldMetadataContextItem; - fieldId?: string; - fieldType?: string; - rawValue?: string; + contextItem?: FieldMetadataContextItem | null | undefined; + fieldId?: string | null | undefined; + fieldType?: string | null | undefined; + rawValue?: string | null | undefined; } /** The field's context item metadata */ export interface FieldMetadataContextItem { - id?: string; - language?: string; - revision?: string; - version?: number; + id?: string | null | undefined; + language?: string | null | undefined; + revision?: string | null | undefined; + version?: number | null | undefined; } export interface FieldMetadataComponentProps { @@ -38,14 +38,14 @@ export const FieldMetadataComponent: FunctionComponent - - {props.data} - + {props.data} {props.children} - + ); }; From f5641e4ea70e32e8ab1cd180a75ca5c8ad7f7581 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 15:58:07 +0300 Subject: [PATCH 06/49] export fieldmetadata component and interfaces from sitecore-jss-react --- packages/sitecore-jss-react/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index f9ed101cef..6296534fa7 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -100,3 +100,8 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; +export { + FieldMetadata, + FieldMetadataComponent, + FieldMetadataComponentProps, +} from './components/FieldMetadata'; From 5d1605463eb354a3ac71b663113521c2e033f96b Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 16:07:10 +0300 Subject: [PATCH 07/49] add metadata component for nextjs link field component; include unit test --- .../src/components/Link.test.tsx | 35 +++++++++++++++++++ .../src/components/Link.tsx | 14 ++++++++ 2 files changed, 49 insertions(+) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx index 6a4acea4f4..5de9a9887c 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx @@ -360,4 +360,39 @@ describe('', () => { const rendered = mount().children(); expect(rendered).to.have.length(0); }); + + it('should render field metadata component when metadata property is present', () => { + const field = { + value: { + href: '/lorem', + text: 'ipsum', + class: 'my-link', + }, + }; + + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + + const rendered = mount( + + + + ); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.not.contain(`href="${field.value.href}"`); + expect(rendered.find(NextLink).length).to.equal(0); + expect(rendered.find(ReactLink).length).to.equal(0); + }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 4d8a1eb3c6..96854ebafb 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -8,6 +8,10 @@ import { LinkProps as ReactLinkProps, LinkPropTypes, } from '@sitecore-jss/sitecore-jss-react'; +import { + FieldMetadataComponent, + FieldMetadataComponentProps, +} from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { /** @@ -22,6 +26,7 @@ export const Link = forwardRef( const { field, editable, + metadata, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent, @@ -35,6 +40,15 @@ export const Link = forwardRef( return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {children}; + } + const value = ((field as LinkFieldValue).href ? field : (field as LinkField).value) as LinkFieldValue; From f8cfc3d47d0ce175d7d6c68a98e9fc7529d43863 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 17:27:11 +0300 Subject: [PATCH 08/49] add field metadata component to nextimage component; small fix in link field component --- .../src/components/Link.tsx | 2 -- .../src/components/NextImage.test.tsx | 23 +++++++++++++++++++ .../src/components/NextImage.tsx | 13 ++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 96854ebafb..f466642719 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,8 +7,6 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, -} from '@sitecore-jss/sitecore-jss-react'; -import { FieldMetadataComponent, FieldMetadataComponentProps, } from '@sitecore-jss/sitecore-jss-react'; diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx index e3c4107916..050e09b823 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx @@ -283,4 +283,27 @@ describe('', () => { ); }); }); + + it('should render field metadata component when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'image', + rawValue: 'Test1', + }; + + const field = { value: { src: '/assets/img/test0.png', alt: 'my image' } }; + + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.find('img')).to.have.length(0); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index 2d1e8d8ad9..e584eec24a 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -1,12 +1,13 @@ import { mediaApi } from '@sitecore-jss/sitecore-jss/media'; import PropTypes from 'prop-types'; import React from 'react'; - import { getEEMarkup, ImageProps, ImageField, ImageFieldValue, + FieldMetadataComponent, + FieldMetadataComponentProps, } from '@sitecore-jss/sitecore-jss-react'; import Image, { ImageProps as NextImageProperties } from 'next/image'; @@ -16,6 +17,7 @@ export const NextImage: React.FC = ({ editable, imageParams, field, + metadata, mediaUrlPrefix, fill, priority, @@ -36,6 +38,15 @@ export const NextImage: React.FC = ({ return null; } + // when metadata is present, render it to be used for chrome hydration + if (metadata) { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {otherProps.children}; + } + const imageField = dynamicMedia as ImageField; // we likely have an experience editor value, should be a string From dcab12fd296f5e06732f710879fce2b29fdc2895 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 18:01:17 +0300 Subject: [PATCH 09/49] unit tests for FieldMetadata --- .../src/components/FieldMetadata.test.tsx | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx new file mode 100644 index 0000000000..8420fe72b7 --- /dev/null +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx @@ -0,0 +1,84 @@ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import { expect } from 'chai'; +import { mount, render } from 'enzyme'; + +import { FieldMetadataComponent, FieldMetadataComponentProps } from './FieldMetadata'; +import { describe } from 'node:test'; + +describe('', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + const stringifiedData = JSON.stringify(testMetadata); + + it('Should render provided metadata', () => { + const props: FieldMetadataComponentProps = { + data: stringifiedData, + }; + + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.include(stringifiedData); + }); + + it('Should render with provided children', () => { + const props: FieldMetadataComponentProps = { + data: stringifiedData, + }; + + const rendered = mount( + +
nested
+
+ ); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.find('div')).to.have.length(1); + expect(rendered.html()).to.include('nested'); + }); + + it('Should render with default attributes', () => { + const props: FieldMetadataComponentProps = { + data: stringifiedData, + }; + + const rendered = mount(); + + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain('type="text/sitecore"'); + expect(rendered.html()).to.contain('chrometype="field"'); + expect(rendered.html()).to.contain('class="scpm"'); + }); + + it('Should render with provided attributes', () => { + const props: FieldMetadataComponentProps = { + data: stringifiedData, + htmlAttributes: { + chrometype: 'foo', + type: 'bar', + className: 'far', + }, + }; + + const rendered = mount(); + + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain('type="bar"'); + expect(rendered.html()).to.contain('chrometype="foo"'); + expect(rendered.html()).to.contain('class="far"'); + }); +}); From 64431fb42ab64ae6d00d02b55988ce5012329c18 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 12 Apr 2024 18:03:43 +0300 Subject: [PATCH 10/49] update unit test --- packages/sitecore-jss-react/src/components/Date.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 85af02168c..724d398801 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-unused-expressions */ import { expect } from 'chai'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import React from 'react'; import { DateField } from './Date'; @@ -100,8 +100,9 @@ describe('', () => { }, }; - const rendered = shallow(); + const rendered = mount(); + expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); }); From 6a050257f1f5f3c739b3c0b936757498dc46b200 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Mon, 15 Apr 2024 10:03:05 +0300 Subject: [PATCH 11/49] introduce getFieldMetadataMarkup function and used in the field components; add unit test --- .../src/components/Link.tsx | 9 ++---- .../src/components/NextImage.tsx | 9 ++---- .../src/components/Date.tsx | 12 ++----- .../src/components/FieldMetadata.test.tsx | 32 ++++++++++++++++++- .../src/components/FieldMetadata.tsx | 8 +++++ .../src/components/File.tsx | 12 ++----- .../src/components/Image.tsx | 12 ++----- .../src/components/Link.tsx | 12 ++----- .../src/components/RichText.tsx | 12 ++----- .../src/components/Text.tsx | 12 ++----- packages/sitecore-jss-react/src/index.ts | 1 + 11 files changed, 56 insertions(+), 75 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index f466642719..83e52c4be9 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,8 +7,7 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - FieldMetadataComponent, - FieldMetadataComponentProps, + getFieldMetadataMarkup, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -40,11 +39,7 @@ export const Link = forwardRef( // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {children}; + return getFieldMetadataMarkup(metadata, children); } const value = ((field as LinkFieldValue).href diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index e584eec24a..87436461a3 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -6,8 +6,7 @@ import { ImageProps, ImageField, ImageFieldValue, - FieldMetadataComponent, - FieldMetadataComponentProps, + getFieldMetadataMarkup, } from '@sitecore-jss/sitecore-jss-react'; import Image, { ImageProps as NextImageProperties } from 'next/image'; @@ -40,11 +39,7 @@ export const NextImage: React.FC = ({ // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, otherProps.children); } const imageField = dynamicMedia as ImageField; diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 2f554fe193..ce8fb9d3f5 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,10 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; export interface DateFieldProps { /** The date field data. */ @@ -44,11 +40,7 @@ export const DateField: React.FC = ({ // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, otherProps.children); } let children: React.ReactNode; diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx index 8420fe72b7..915a35e961 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx @@ -3,8 +3,13 @@ import React from 'react'; import { expect } from 'chai'; import { mount, render } from 'enzyme'; -import { FieldMetadataComponent, FieldMetadataComponentProps } from './FieldMetadata'; +import { + FieldMetadataComponent, + FieldMetadataComponentProps, + getFieldMetadataMarkup, +} from './FieldMetadata'; import { describe } from 'node:test'; +import { json } from 'stream/consumers'; describe('', () => { const testMetadata = { @@ -82,3 +87,28 @@ describe('', () => { expect(rendered.html()).to.contain('class="far"'); }); }); + +describe('getFieldMetadataMarkup', () => { + it('Should render component with provided metadata and children ', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'single-line', + rawValue: 'Test1', + }; + + const div =
nested
; + + const rendered = mount(getFieldMetadataMarkup(testMetadata, div)); + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.include(JSON.stringify(testMetadata)); + expect(rendered.html()).to.include('
nested
'); + }); +}); diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 7c46f0d649..da5d168aed 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -49,3 +49,11 @@ export const FieldMetadataComponent: FunctionComponent ); }; + +export const getFieldMetadataMarkup = (metadata: FieldMetadata, children: any) => { + const props: FieldMetadataComponentProps = { + data: JSON.stringify(metadata), + }; + + return {children}; +}; diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 6b30a94279..afedc99202 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,10 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; export interface FileFieldValue { [propName: string]: unknown; @@ -42,11 +38,7 @@ export const File: React.FC = ({ field, children, metadata, ...otherP // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, otherProps.children); } // handle link directly on field for forgetful devs diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index ac88b4af78..7fd0620fd6 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,11 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -183,11 +179,7 @@ export const Image: React.FC = ({ // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, otherProps.children); } const imageField = dynamicMedia as ImageField; diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 2cd7eefc9b..a651864b9c 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,10 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -65,11 +61,7 @@ export const Link = forwardRef( // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, children); } const resultTags: ReactElement[] = []; diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 331cf4c0c0..f722883a87 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,10 +1,6 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; export interface RichTextField { value?: string; @@ -40,11 +36,7 @@ export const RichText: React.FC = forwardRef( // when metadata is present, render it to be used for chrome hydration if (otherProps.metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(otherProps.metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(otherProps.metadata, otherProps.children); } const htmlProps = { diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index c5f66d7e62..e7d5f9e783 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,9 +1,5 @@ import React, { ReactElement, FunctionComponent } from 'react'; -import { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, -} from './FieldMetadata'; +import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; import PropTypes from 'prop-types'; export interface TextField { @@ -49,11 +45,7 @@ export const Text: FunctionComponent = ({ // when metadata is present, render it to be used for chrome hydration if (metadata) { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; - - return {otherProps.children}; + return getFieldMetadataMarkup(metadata, otherProps.children); } // can't use editable value if we want to output unencoded diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index 6296534fa7..ac97c6fcaf 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -104,4 +104,5 @@ export { FieldMetadata, FieldMetadataComponent, FieldMetadataComponentProps, + getFieldMetadataMarkup, } from './components/FieldMetadata'; From 2e3362ca71ec6527350ff1b9f12b55cad39da050 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Mon, 15 Apr 2024 12:28:54 +0300 Subject: [PATCH 12/49] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ea8fa8f23..8c9b2a6ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ Our versioning strategy is as follows: ## Unreleased +### 🎉 New Features & Improvements +* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Introduce FieldMetadata component and functionality to render it when metadata field property is provided in the field's layout data. In such case FieldMetadaComponent should be rendered instead of the actual field component to enable chrome's hydration when editing in pages. Ability to render metadata has been added to the field rendering components for react and nextjs. ([#1773](https://github.com/Sitecore/jss/pull/1773)) + ## 21.7.1 ### 🐛 Bug Fixes From 11416904b800398534a50c4333148d1c6ca3cb95 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Tue, 16 Apr 2024 14:14:36 +0300 Subject: [PATCH 13/49] react - use higher order component to wrap metadata around field components --- .../src/components/Date.test.tsx | 20 +- .../src/components/Date.tsx | 93 ++++----- .../src/components/FieldMetadata.tsx | 71 ++++--- .../src/components/File.test.tsx | 5 +- .../src/components/File.tsx | 74 ++++--- .../src/components/Image.test.tsx | 3 +- .../src/components/Image.tsx | 115 +++++------ .../src/components/Link.test.tsx | 3 +- .../src/components/Link.tsx | 181 +++++++++--------- .../src/components/RichText.test.tsx | 12 +- .../src/components/RichText.tsx | 41 ++-- .../src/components/Text.test.tsx | 4 +- .../src/components/Text.tsx | 142 +++++++------- packages/sitecore-jss-react/src/index.ts | 7 +- 14 files changed, 361 insertions(+), 410 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 724d398801..4a9095bf75 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -86,17 +86,17 @@ describe('', () => { const props = { field: { value: '23-11-2001', - }, - metadata: { - contextItem: { - id: '{09A07660-6834-476C-B93B-584248D3003B}', - language: 'en', - revision: 'a0b36ce0a7db49418edf90eb9621e145', - version: 1, + metadata: { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'date', + rawValue: 'Test1', }, - fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - fieldType: 'date', - rawValue: 'Test1', }, }; diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index ce8fb9d3f5..8e74f7f422 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; export interface DateFieldProps { /** The date field data. */ @@ -8,6 +8,7 @@ export interface DateFieldProps { field: { value?: string; editable?: string; + metadata?: FieldMetadata; }; /** * The HTML element that will wrap the contents of the field. @@ -20,72 +21,58 @@ export interface DateFieldProps { */ editable?: boolean; render?: (date: Date | null) => React.ReactNode; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; } -export const DateField: React.FC = ({ - field, - tag, - metadata, - editable, - render, - ...otherProps -}) => { - if (!field || (!field.editable && !field.value)) { - return null; - } - - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, otherProps.children); - } - - let children: React.ReactNode; +export const DateField: React.FC = withFieldMetadataWrapper( + ({ field, tag, editable, render, ...otherProps }) => { + if (!field || (!field.editable && !field.value)) { + return null; + } - const htmlProps: { - [htmlAttr: string]: unknown; - children?: React.ReactNode; - } = { - ...otherProps, - }; + let children: React.ReactNode; - if (field.editable && editable) { - htmlProps.dangerouslySetInnerHTML = { - __html: field.editable, + const htmlProps: { + [htmlAttr: string]: unknown; + children?: React.ReactNode; + } = { + ...otherProps, }; - } else if (render) { - children = render(field.value ? new Date(field.value) : null); - } else { - children = field.value; - } - if (tag || (field.editable && editable)) { - return React.createElement(tag || 'span', htmlProps, children); - } else { - return {children}; + if (field.editable && editable) { + htmlProps.dangerouslySetInnerHTML = { + __html: field.editable, + }; + } else if (render) { + children = render(field.value ? new Date(field.value) : null); + } else { + children = field.value; + } + + if (tag || (field.editable && editable)) { + return React.createElement(tag || 'span', htmlProps, children); + } else { + return {children}; + } } -}; +); DateField.propTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }).isRequired, tag: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), editable: PropTypes.bool, render: PropTypes.func, }; diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index da5d168aed..d00953fb6f 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent } from 'react'; +import React, { ComponentType, forwardRef } from 'react'; /** The field metadata */ export interface FieldMetadata { @@ -16,44 +16,53 @@ export interface FieldMetadataContextItem { version?: number | null | undefined; } -export interface FieldMetadataComponentProps { - htmlAttributes?: { - type: string; - chrometype: string; - className: string; - }; - data: string; +export interface FieldMetadataWrapperProps { + metadata: any; + children: React.ReactNode; } -const defaultAttributes = { - type: 'text/sitecore', - chrometype: 'field', - className: 'scpm', -}; - -export const FieldMetadataComponent: FunctionComponent = ( - props: React.PropsWithChildren -) => { - const attributes = { - ...defaultAttributes, - ...props.htmlAttributes, +export const FieldMetadataWrapper = (props: FieldMetadataWrapperProps): JSX.Element => { + const data = JSON.stringify(props.metadata); + const defaultAttributes = { + type: 'text/sitecore', + chrometype: 'field', + className: 'scpm', }; - const codeOpenAttributes = { ...attributes, kind: 'open' }; - const codeCloseAttributes = { ...attributes, kind: 'close' }; + const codeOpenAttributes = { ...defaultAttributes, kind: 'open' }; + const codeCloseAttributes = { ...defaultAttributes, kind: 'close' }; return ( - - {props.data} + <> + {data} {props.children} - + ); }; -export const getFieldMetadataMarkup = (metadata: FieldMetadata, children: any) => { - const props: FieldMetadataComponentProps = { - data: JSON.stringify(metadata), - }; +/** + * Wraps the field component with metadata markup intended to be used for chromes hydartion + * @param {ComponentType} FieldComponent the field component + */ +export function withFieldMetadataWrapper>( + FieldComponent: ComponentType +) { + // eslint-disable-next-line react/display-name + return forwardRef(({ ...props }: FieldComponentProps, ref: any) => { + const metadata = (props as any)?.field?.metadata; - return {children}; -}; + if (!props?.field) { + return null; + } + + if (!metadata || !props.editable) { + return ; + } + + return ( + + + + ); + }); +} diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index 2f8c649487..6247e00c86 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -67,10 +67,11 @@ describe('', () => { const field = { src: '/lorem', title: 'ipsum', + metadata: testMetadata, }; - const rendered = mount(); - + const rendered = mount(); + console.log(rendered.html()); expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index afedc99202..ff4e8815ff 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,12 +1,13 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; export interface FileFieldValue { [propName: string]: unknown; src?: string; title?: string; displayName?: string; + metadata?: FieldMetadata; } export interface FileField { @@ -19,42 +20,35 @@ export interface FileProps { field: FileFieldValue | FileField; /** HTML attributes that will be appended to the rendered
tag. */ children?: React.ReactNode; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; } -export const File: React.FC = ({ field, children, metadata, ...otherProps }) => { - /* +export const File: React.FC = withFieldMetadataWrapper( + ({ field, children, ...otherProps }) => { + /* File fields cannot be managed via the EE. We never output "editable." - */ + */ - const dynamicField: FileField | FileFieldValue = field; + const dynamicField: FileField | FileFieldValue = field; - if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { - return null; - } + if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { + return null; + } - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, otherProps.children); - } + // handle link directly on field for forgetful devs + const file = ((dynamicField as FileFieldValue).src + ? field + : dynamicField.value) as FileFieldValue; + if (!file) { + return null; + } - // handle link directly on field for forgetful devs - const file = ((dynamicField as FileFieldValue).src - ? field - : dynamicField.value) as FileFieldValue; - if (!file) { - return null; + const linkText = !children ? file.title || file.displayName : null; + const anchorAttrs = { + href: file.src, + }; + return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); } - - const linkText = !children ? file.title || file.displayName : null; - const anchorAttrs = { - href: file.src, - }; - return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); -}; +); File.propTypes = { field: PropTypes.oneOfType([ @@ -63,19 +57,19 @@ File.propTypes = { }), PropTypes.shape({ value: PropTypes.object, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }), ]).isRequired, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), }; File.displayName = 'File'; diff --git a/packages/sitecore-jss-react/src/components/Image.test.tsx b/packages/sitecore-jss-react/src/components/Image.test.tsx index 7cfb584da9..6a31c5322d 100644 --- a/packages/sitecore-jss-react/src/components/Image.test.tsx +++ b/packages/sitecore-jss-react/src/components/Image.test.tsx @@ -311,8 +311,9 @@ describe('', () => { src: '/assets/img/test0.png', width: 8, height: 10, + metadata: testMetadata, }; - const rendered = mount(); + const rendered = mount(); expect(rendered.find('code')).to.have.length(2); expect(rendered.find('img')).to.have.length(0); diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 7fd0620fd6..8d7ac47bd0 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -14,6 +14,7 @@ export interface ImageFieldValue { export interface ImageField { value?: ImageFieldValue; editable?: string; + metadata?: FieldMetadata; } export interface ImageSizeParameters { @@ -52,11 +53,6 @@ export interface ImageProps { */ editable?: boolean; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; - /** * Parameters that will be attached to Sitecore media URLs */ @@ -154,55 +150,44 @@ export const getEEMarkup = ( return getEditableWrapper(editableMarkup); }; -export const Image: React.FC = ({ - media, - editable, - metadata, - imageParams, - field, - mediaUrlPrefix, - ...otherProps -}) => { - // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) - if (field && !media) { - media = field; - } - - const dynamicMedia = media as ImageField | ImageFieldValue; - - if ( - !media || - (!dynamicMedia.editable && !dynamicMedia.value && !(dynamicMedia as ImageFieldValue).src) - ) { - return null; - } - - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, otherProps.children); +export const Image: React.FC = withFieldMetadataWrapper( + ({ media, editable, imageParams, field, mediaUrlPrefix, ...otherProps }) => { + // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) + if (field && !media) { + media = field; + } + + const dynamicMedia = media as ImageField | ImageFieldValue; + + if ( + !media || + (!dynamicMedia.editable && !dynamicMedia.value && !(dynamicMedia as ImageFieldValue).src) + ) { + return null; + } + + const imageField = dynamicMedia as ImageField; + + if (editable && imageField.editable) { + return getEEMarkup(imageField, imageParams, mediaUrlPrefix, otherProps); + } + + // some wise-guy/gal is passing in a 'raw' image object value + const img = (dynamicMedia as ImageFieldValue).src + ? media + : (dynamicMedia.value as ImageFieldValue); + if (!img) { + return null; + } + + const attrs = getImageAttrs({ ...img, ...otherProps }, imageParams, mediaUrlPrefix); + if (attrs) { + return ; + } + + return null; // we can't handle the truth } - - const imageField = dynamicMedia as ImageField; - - if (editable && imageField.editable) { - return getEEMarkup(imageField, imageParams, mediaUrlPrefix, otherProps); - } - - // some wise-guy/gal is passing in a 'raw' image object value - const img = (dynamicMedia as ImageFieldValue).src - ? media - : (dynamicMedia.value as ImageFieldValue); - if (!img) { - return null; - } - - const attrs = getImageAttrs({ ...img, ...otherProps }, imageParams, mediaUrlPrefix); - if (attrs) { - return ; - } - - return null; // we can't handle the truth -}; +); Image.propTypes = { media: PropTypes.oneOfType([ @@ -212,6 +197,17 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }), ]), field: PropTypes.oneOfType([ @@ -223,17 +219,6 @@ Image.propTypes = { editable: PropTypes.string, }), ]), - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), editable: PropTypes.bool, mediaUrlPrefix: PropTypes.instanceOf(RegExp), imageParams: PropTypes.objectOf( diff --git a/packages/sitecore-jss-react/src/components/Link.test.tsx b/packages/sitecore-jss-react/src/components/Link.test.tsx index 7e8cac5c76..437b96158e 100644 --- a/packages/sitecore-jss-react/src/components/Link.test.tsx +++ b/packages/sitecore-jss-react/src/components/Link.test.tsx @@ -143,8 +143,9 @@ describe('', () => { const field = { href: '/lorem', text: 'ipsum', + metadata: testMetadata, }; - const rendered = mount(); + const rendered = mount(); expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index a651864b9c..bb9ede8629 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -19,6 +19,7 @@ export interface LinkField { value: LinkFieldValue; editableFirstPart?: string; editableLastPart?: string; + metadata?: FieldMetadata; } export type LinkProps = React.DetailedHTMLProps< @@ -39,101 +40,95 @@ export type LinkProps = React.DetailedHTMLProps< * NOTE: when in Sitecore Experience Editor, this setting is ignored due to technical limitations, and the description is always rendered. */ showLinkTextWithChildrenPresent?: boolean; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; }; -export const Link = forwardRef( - ({ field, editable, metadata, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { - const children = otherProps.children as React.ReactNode; - const dynamicField: LinkField | LinkFieldValue = field; - - if ( - !field || - (!dynamicField.editableFirstPart && - !dynamicField.value && - !(dynamicField as LinkFieldValue).href) - ) { - return null; - } +export const Link = withFieldMetadataWrapper( + // eslint-disable-next-line react/display-name + forwardRef( + ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { + const children = otherProps.children as React.ReactNode; + const dynamicField: LinkField | LinkFieldValue = field; + + if ( + !field || + (!dynamicField.editableFirstPart && + !dynamicField.value && + !(dynamicField as LinkFieldValue).href) + ) { + return null; + } - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, children); - } + const resultTags: ReactElement[] = []; + + // EXPERIENCE EDITOR RENDERING + if (editable && dynamicField.editableFirstPart) { + const markup = (dynamicField.editableFirstPart as string) + dynamicField.editableLastPart; + + // in an ideal world, we'd pre-render React children here and inject them between editableFirstPart and editableLastPart. + // However, we cannot combine arbitrary unparsed HTML (innerHTML) based components with actual vDOM components (the children) + // because the innerHTML is not parsed - it'd make a discontinuous vDOM. So, we'll go for the next best compromise of rendering the link field and children separately + // as siblings. Should be "good enough" for most cases - and write your own helper if it isn't. Or bring xEditor out of 2006. + + const htmlProps = { + className: 'sc-link-wrapper', + dangerouslySetInnerHTML: { + __html: markup, + }, + ...otherProps, + key: 'editable', + }; + + // Exclude children, since 'dangerouslySetInnerHTML' and 'children' can't be set together + // and children will be added as a sibling + delete htmlProps.children; + + resultTags.push(); + + // don't render normal link tag when editing, if no children exist + // this preserves normal-ish behavior if not using a link body (no hacks required) + if (!children) { + return resultTags[0]; + } + } - const resultTags: ReactElement[] = []; + // handle link directly on field for forgetful devs + const link = (dynamicField as LinkFieldValue).href + ? (field as LinkFieldValue) + : (dynamicField as LinkField).value; - // EXPERIENCE EDITOR RENDERING - if (editable && dynamicField.editableFirstPart) { - const markup = (dynamicField.editableFirstPart as string) + dynamicField.editableLastPart; + if (!link) { + return null; + } - // in an ideal world, we'd pre-render React children here and inject them between editableFirstPart and editableLastPart. - // However, we cannot combine arbitrary unparsed HTML (innerHTML) based components with actual vDOM components (the children) - // because the innerHTML is not parsed - it'd make a discontinuous vDOM. So, we'll go for the next best compromise of rendering the link field and children separately - // as siblings. Should be "good enough" for most cases - and write your own helper if it isn't. Or bring xEditor out of 2006. + const anchor = link.linktype !== 'anchor' && link.anchor ? `#${link.anchor}` : ''; + const querystring = link.querystring ? `?${link.querystring}` : ''; - const htmlProps = { - className: 'sc-link-wrapper', - dangerouslySetInnerHTML: { - __html: markup, - }, - ...otherProps, - key: 'editable', + const anchorAttrs: { [attr: string]: unknown } = { + href: `${link.href}${querystring}${anchor}`, + className: link.class, + title: link.title, + target: link.target, }; - // Exclude children, since 'dangerouslySetInnerHTML' and 'children' can't be set together - // and children will be added as a sibling - delete htmlProps.children; - - resultTags.push(); - - // don't render normal link tag when editing, if no children exist - // this preserves normal-ish behavior if not using a link body (no hacks required) - if (!children) { - return resultTags[0]; + if (anchorAttrs.target === '_blank' && !anchorAttrs.rel) { + // information disclosure attack prevention keeps target blank site from getting ref to window.opener + anchorAttrs.rel = 'noopener noreferrer'; } - } - - // handle link directly on field for forgetful devs - const link = (dynamicField as LinkFieldValue).href - ? (field as LinkFieldValue) - : (dynamicField as LinkField).value; - - if (!link) { - return null; - } - const anchor = link.linktype !== 'anchor' && link.anchor ? `#${link.anchor}` : ''; - const querystring = link.querystring ? `?${link.querystring}` : ''; + const linkText = showLinkTextWithChildrenPresent || !children ? link.text || link.href : null; - const anchorAttrs: { [attr: string]: unknown } = { - href: `${link.href}${querystring}${anchor}`, - className: link.class, - title: link.title, - target: link.target, - }; + resultTags.push( + React.createElement( + 'a', + { ...anchorAttrs, ...otherProps, key: 'link', ref }, + linkText, + children + ) + ); - if (anchorAttrs.target === '_blank' && !anchorAttrs.rel) { - // information disclosure attack prevention keeps target blank site from getting ref to window.opener - anchorAttrs.rel = 'noopener noreferrer'; + return {resultTags}; } - - const linkText = showLinkTextWithChildrenPresent || !children ? link.text || link.href : null; - - resultTags.push( - React.createElement( - 'a', - { ...anchorAttrs, ...otherProps, key: 'link', ref }, - linkText, - children - ) - ); - - return {resultTags}; - } + ) ); export const LinkPropTypes = { @@ -145,19 +140,19 @@ export const LinkPropTypes = { value: PropTypes.object, editableFirstPart: PropTypes.string, editableLastPart: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }), ]).isRequired, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), editable: PropTypes.bool, showLinkTextWithChildrenPresent: PropTypes.bool, }; diff --git a/packages/sitecore-jss-react/src/components/RichText.test.tsx b/packages/sitecore-jss-react/src/components/RichText.test.tsx index dddf20a31b..42d64ba2ef 100644 --- a/packages/sitecore-jss-react/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.test.tsx @@ -96,10 +96,6 @@ describe('', () => { }); it('should render field metadata component when metadata property is present', () => { - const field = { - value: 'value', - }; - const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -112,10 +108,16 @@ describe('', () => { rawValue: 'Test1', }; - const rendered = mount(); + const field = { + value: 'value', + metadata: testMetadata, + }; + + const rendered = mount(); expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index f722883a87..8ed6c3cdd4 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,10 +1,11 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; export interface RichTextField { value?: string; editable?: string; + metadata?: FieldMetadata; } export interface RichTextProps { @@ -22,23 +23,15 @@ export interface RichTextProps { * @default true */ editable?: boolean; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; } -export const RichText: React.FC = forwardRef( - ({ field, tag, editable, ...otherProps }, ref) => { +export const RichText: React.FC = withFieldMetadataWrapper( + // eslint-disable-next-line react/display-name + forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { return null; } - // when metadata is present, render it to be used for chrome hydration - if (otherProps.metadata) { - return getFieldMetadataMarkup(otherProps.metadata, otherProps.children); - } - const htmlProps = { dangerouslySetInnerHTML: { __html: field.editable && editable ? field.editable : field.value, @@ -48,27 +41,27 @@ export const RichText: React.FC = forwardRef( }; return React.createElement(tag || 'div', htmlProps); - } + }) ); export const RichTextPropTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }), tag: PropTypes.string, editable: PropTypes.bool, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), }; RichText.propTypes = RichTextPropTypes; diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index 7f5810c3a1..653aaecdc0 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -195,10 +195,11 @@ describe('', () => { const field = { editable: eeTextData, + metadata: testMetadata, }; const rendered = mount( - +
test
); @@ -206,5 +207,6 @@ describe('', () => { expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index e7d5f9e783..25cb55256f 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,10 +1,11 @@ import React, { ReactElement, FunctionComponent } from 'react'; -import { FieldMetadata, getFieldMetadataMarkup } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; import PropTypes from 'prop-types'; export interface TextField { value?: string | number; editable?: string; + metadata?: FieldMetadata; } export interface TextProps { @@ -25,110 +26,95 @@ export interface TextProps { * If false, HTML-encoding of the field value is disabled and the value is rendered as-is. */ encode?: boolean; - /** - * The field metadata; when present it should be exposed for chrome hydration process when rendering in Pages - */ - metadata?: FieldMetadata; } -export const Text: FunctionComponent = ({ - field, - tag, - metadata, - editable, - encode, - ...otherProps -}) => { - if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { - return null; - } - - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, otherProps.children); - } +export const Text: FunctionComponent = withFieldMetadataWrapper( + ({ field, tag, editable, encode, ...otherProps }) => { + if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { + return null; + } - // can't use editable value if we want to output unencoded - if (!encode) { - // eslint-disable-next-line no-param-reassign - editable = false; - } + // can't use editable value if we want to output unencoded + if (!encode) { + // eslint-disable-next-line no-param-reassign + editable = false; + } - const isEditable = field.editable && editable; + const isEditable = field.editable && editable; - let output: string | number | (ReactElement | string)[] = isEditable - ? field.editable || '' - : field.value === undefined - ? '' - : field.value; + let output: string | number | (ReactElement | string)[] = isEditable + ? field.editable || '' + : field.value === undefined + ? '' + : field.value; - // when string value isn't formatted, we should format line breaks - if (!field.editable && typeof output === 'string') { - const splitted = String(output).split('\n'); + // when string value isn't formatted, we should format line breaks + if (!field.editable && typeof output === 'string') { + const splitted = String(output).split('\n'); - if (splitted.length) { - const formatted: (ReactElement | string)[] = []; + if (splitted.length) { + const formatted: (ReactElement | string)[] = []; - splitted.forEach((str, i) => { - const isLast = i === splitted.length - 1; + splitted.forEach((str, i) => { + const isLast = i === splitted.length - 1; - formatted.push(str); + formatted.push(str); - if (!isLast) { - formatted.push(
); - } - }); + if (!isLast) { + formatted.push(
); + } + }); - output = formatted; + output = formatted; + } } - } - const setDangerously = isEditable || !encode; + const setDangerously = isEditable || !encode; - let children = null; - const htmlProps: { - [htmlAttributes: string]: unknown; - children?: React.ReactNode; - } = { - ...otherProps, - }; - - if (setDangerously) { - htmlProps.dangerouslySetInnerHTML = { - __html: output, + let children = null; + const htmlProps: { + [htmlAttributes: string]: unknown; + children?: React.ReactNode; + } = { + ...otherProps, }; - } else { - children = output; - } - if (tag || setDangerously) { - return React.createElement(tag || 'span', htmlProps, children); - } else { - return {children}; + if (setDangerously) { + htmlProps.dangerouslySetInnerHTML = { + __html: output, + }; + } else { + children = output; + } + + if (tag || setDangerously) { + return React.createElement(tag || 'span', htmlProps, children); + } else { + return {children}; + } } -}; +); Text.propTypes = { field: PropTypes.shape({ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), editable: PropTypes.string, - }), - tag: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, }), + tag: PropTypes.string, editable: PropTypes.bool, encode: PropTypes.bool, }; - Text.defaultProps = { editable: true, encode: true, diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index ac97c6fcaf..d834c1b68a 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -100,9 +100,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { - FieldMetadata, - FieldMetadataComponent, - FieldMetadataComponentProps, - getFieldMetadataMarkup, -} from './components/FieldMetadata'; +export { FieldMetadata, withFieldMetadataWrapper } from './components/FieldMetadata'; From 08df912705555934c256b2c87316f1998db20a63 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Tue, 16 Apr 2024 15:33:50 +0300 Subject: [PATCH 14/49] update nextjs components to use metadata wrapper hoc; aadjust unit tests --- .../src/components/Link.test.tsx | 30 ++-- .../src/components/Link.tsx | 17 +- .../src/components/NextImage.test.tsx | 15 +- .../src/components/NextImage.tsx | 150 ++++++++---------- 4 files changed, 102 insertions(+), 110 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx index 5de9a9887c..ded92731f6 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx @@ -355,21 +355,14 @@ describe('', () => { expect(rendered).to.have.length(0); }); - it('should render nothing with missing editable and value', () => { + it('should render nothing with missing field', () => { const field = {}; - const rendered = mount().children(); - expect(rendered).to.have.length(0); + const rendered = mount(); + expect(rendered.children()).to.have.length(1); + expect(rendered.html()).to.equal(''); }); it('should render field metadata component when metadata property is present', () => { - const field = { - value: { - href: '/lorem', - text: 'ipsum', - class: 'my-link', - }, - }; - const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -382,17 +375,24 @@ describe('', () => { rawValue: 'Test1', }; + const field = { + value: { + href: '/lorem', + text: 'ipsum', + class: 'my-link', + }, + metadata: testMetadata, + }; + const rendered = mount( - + ); expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.not.contain(`href="${field.value.href}"`); - expect(rendered.find(NextLink).length).to.equal(0); - expect(rendered.find(ReactLink).length).to.equal(0); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 83e52c4be9..becc7d516a 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,7 +7,7 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - getFieldMetadataMarkup, + withFieldMetadataWrapper, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -18,12 +18,12 @@ export type LinkProps = ReactLinkProps & { internalLinkMatcher?: RegExp; }; -export const Link = forwardRef( - (props: LinkProps, ref): JSX.Element | null => { +export const Link = withFieldMetadataWrapper( + // eslint-disable-next-line react/display-name + forwardRef((props: LinkProps, ref): JSX.Element | null => { const { field, editable, - metadata, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent, @@ -37,11 +37,6 @@ export const Link = forwardRef( return null; } - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, children); - } - const value = ((field as LinkFieldValue).href ? field : (field as LinkField).value) as LinkFieldValue; @@ -75,8 +70,10 @@ export const Link = forwardRef( const reactLinkProps = { ...props }; delete reactLinkProps.internalLinkMatcher; + // we've already rendered the metadata wrapper - so set metadata to null to prevent duplicate wrapping + reactLinkProps.field.metadata = null; return ; - } + }) ); Link.defaultProps = { diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx index 050e09b823..ba1e13d9ed 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx @@ -252,7 +252,10 @@ describe('', () => { describe('error cases', () => { const src = '/assets/img/test0.png'; it('should throw an error if src is present', () => { - expect(() => mount()).to.throw( + const field = { + src: '/assets/img/test0.png', + }; + expect(() => mount()).to.throw( 'Detected src prop. If you wish to use src, use next/image directly.' ); }); @@ -297,13 +300,17 @@ describe('', () => { rawValue: 'Test1', }; - const field = { value: { src: '/assets/img/test0.png', alt: 'my image' } }; + const field = { + value: { src: '/assets/img/test0.png', alt: 'my image' }, + metadata: testMetadata, + }; - const rendered = mount(); + const rendered = mount(); expect(rendered.find('code')).to.have.length(2); - expect(rendered.find('img')).to.have.length(0); + expect(rendered.find('img')).to.have.length(1); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index 87436461a3..b13935853f 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -6,93 +6,81 @@ import { ImageProps, ImageField, ImageFieldValue, - getFieldMetadataMarkup, + withFieldMetadataWrapper, } from '@sitecore-jss/sitecore-jss-react'; import Image, { ImageProps as NextImageProperties } from 'next/image'; type NextImageProps = Omit & Partial; -export const NextImage: React.FC = ({ - editable, - imageParams, - field, - metadata, - mediaUrlPrefix, - fill, - priority, - ...otherProps -}) => { - // next handles src and we use a custom loader, - // throw error if these are present - if (otherProps.src) { - throw new Error('Detected src prop. If you wish to use src, use next/image directly.'); +export const NextImage: React.FC = withFieldMetadataWrapper( + ({ editable, imageParams, field, mediaUrlPrefix, fill, priority, ...otherProps }) => { + // next handles src and we use a custom loader, + // throw error if these are present + if (otherProps.src) { + throw new Error('Detected src prop. If you wish to use src, use next/image directly.'); + } + + const dynamicMedia = field as ImageField | ImageFieldValue; + + if ( + !field || + (!dynamicMedia.editable && !dynamicMedia.value && !(dynamicMedia as ImageFieldValue).src) + ) { + return null; + } + + const imageField = dynamicMedia as ImageField; + + // we likely have an experience editor value, should be a string + if (editable && imageField.editable) { + return getEEMarkup( + imageField, + imageParams as { [paramName: string]: string | number }, + mediaUrlPrefix as RegExp, + otherProps as { src: string } + ); + } + + // some wise-guy/gal is passing in a 'raw' image object value + const img: ImageFieldValue = (dynamicMedia as ImageFieldValue).src + ? (field as ImageFieldValue) + : (dynamicMedia.value as ImageFieldValue); + if (!img) { + return null; + } + + const attrs = { + ...img, + ...otherProps, + fill, + priority, + src: mediaApi.updateImageUrl( + img.src as string, + imageParams as { [paramName: string]: string | number }, + mediaUrlPrefix as RegExp + ), + }; + + const imageProps = { + ...attrs, + // force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter + // this is required for Sitecore media API resizing to work properly + src: mediaApi.replaceMediaUrlPrefix(attrs.src, mediaUrlPrefix as RegExp), + }; + + // Exclude `width`, `height` in case image is responsive, `fill` is used + if (imageProps.fill) { + delete imageProps.width; + delete imageProps.height; + } + + if (attrs) { + return ; + } + + return null; // we can't handle the truth } - - const dynamicMedia = field as ImageField | ImageFieldValue; - - if ( - !field || - (!dynamicMedia.editable && !dynamicMedia.value && !(dynamicMedia as ImageFieldValue).src) - ) { - return null; - } - - // when metadata is present, render it to be used for chrome hydration - if (metadata) { - return getFieldMetadataMarkup(metadata, otherProps.children); - } - - const imageField = dynamicMedia as ImageField; - - // we likely have an experience editor value, should be a string - if (editable && imageField.editable) { - return getEEMarkup( - imageField, - imageParams as { [paramName: string]: string | number }, - mediaUrlPrefix as RegExp, - otherProps as { src: string } - ); - } - - // some wise-guy/gal is passing in a 'raw' image object value - const img: ImageFieldValue = (dynamicMedia as ImageFieldValue).src - ? (field as ImageFieldValue) - : (dynamicMedia.value as ImageFieldValue); - if (!img) { - return null; - } - - const attrs = { - ...img, - ...otherProps, - fill, - priority, - src: mediaApi.updateImageUrl( - img.src as string, - imageParams as { [paramName: string]: string | number }, - mediaUrlPrefix as RegExp - ), - }; - - const imageProps = { - ...attrs, - // force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter - // this is required for Sitecore media API resizing to work properly - src: mediaApi.replaceMediaUrlPrefix(attrs.src, mediaUrlPrefix as RegExp), - }; - - // Exclude `width`, `height` in case image is responsive, `fill` is used - if (imageProps.fill) { - delete imageProps.width; - delete imageProps.height; - } - - if (attrs) { - return ; - } - - return null; // we can't handle the truth -}; +); NextImage.propTypes = { field: PropTypes.oneOfType([ From 752506f8a326a8214543e302496e0c92aac9e7e9 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Tue, 16 Apr 2024 16:16:00 +0300 Subject: [PATCH 15/49] adjust unit tests and fix File component --- .../src/components/Date.test.tsx | 31 ++++++++++--------- .../src/components/File.test.tsx | 11 ++++--- .../src/components/File.tsx | 2 +- .../src/components/Link.test.tsx | 5 +-- .../src/components/Text.test.tsx | 6 ++-- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 4a9095bf75..03c1aad816 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -5,14 +5,14 @@ import React from 'react'; import { DateField } from './Date'; describe('', () => { - it('should return null if no editable or value', () => { + it('should render nothing is field is missing', () => { const p = { field: {}, }; const c = shallow(); - - expect(c.type()).to.be.null; + console.log(c.type()); + expect(c.html()).to.equal(''); }); it('should render value', () => { @@ -83,20 +83,22 @@ describe('', () => { }); it('should render field metadata component when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'date', + rawValue: 'Test1', + }; + const props = { field: { value: '23-11-2001', - metadata: { - contextItem: { - id: '{09A07660-6834-476C-B93B-584248D3003B}', - language: 'en', - revision: 'a0b36ce0a7db49418edf90eb9621e145', - version: 1, - }, - fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - fieldType: 'date', - rawValue: 'Test1', - }, + metadata: testMetadata, }, }; @@ -105,5 +107,6 @@ describe('', () => { expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index 6247e00c86..a73adee5b0 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -14,8 +14,8 @@ describe('', () => { const field = { editable: 'lorem', }; - const rendered = mount().children(); - expect(rendered).to.have.length(0); + const rendered = mount(); + expect(rendered.html()).to.equal(''); }); it('should render with src directly on provided field', () => { @@ -51,7 +51,7 @@ describe('', () => { expect(rendered.html()).to.contain('class="my-css"'); }); - it('should render field metadata component when metadata property is present', () => { + it('should render field metadata when metadata property is present', () => { const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -70,10 +70,11 @@ describe('', () => { metadata: testMetadata, }; - const rendered = mount(); + const rendered = mount(); console.log(rendered.html()); - expect(rendered.find('code')).to.have.length(2); + // expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index ff4e8815ff..d1b03126d6 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -7,11 +7,11 @@ export interface FileFieldValue { src?: string; title?: string; displayName?: string; - metadata?: FieldMetadata; } export interface FileField { value: FileFieldValue; + metadata?: FieldMetadata; } export interface FileProps { diff --git a/packages/sitecore-jss-react/src/components/Link.test.tsx b/packages/sitecore-jss-react/src/components/Link.test.tsx index 437b96158e..ad131fe2ab 100644 --- a/packages/sitecore-jss-react/src/components/Link.test.tsx +++ b/packages/sitecore-jss-react/src/components/Link.test.tsx @@ -14,8 +14,8 @@ describe('', () => { it('should render nothing with missing editable and value', () => { const field = {}; - const rendered = mount().children(); - expect(rendered).to.have.length(0); + const rendered = mount(); + expect(rendered.html()).to.equal(''); }); it('should render editable with an editable value', () => { @@ -150,5 +150,6 @@ describe('', () => { expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index 653aaecdc0..ad91e10bbb 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -14,20 +14,20 @@ describe('', () => { expect(rendered.html()).to.be.null; }); - it('should render nothing with empty value', () => { + it('should render nothing with missing field', () => { const field = { value: '', }; const rendered = mount(); expect(rendered).to.have.length(1); - expect(rendered.html()).to.be.null; + expect(rendered.html()).to.equal(''); }); it('should render nothing with missing editable and value', () => { const field = {}; const rendered = mount(); expect(rendered).to.have.length(1); - expect(rendered.html()).to.be.null; + expect(rendered.html()).to.equal(''); }); it('should render editable with editable value', () => { From 03b9de9edf7a2fbf97d897ce41d017f76efacead Mon Sep 17 00:00:00 2001 From: yavorsk Date: Tue, 16 Apr 2024 17:50:03 +0300 Subject: [PATCH 16/49] adjust image field tests; include check for media property in metadata wrapper --- packages/sitecore-jss-react/src/components/FieldMetadata.tsx | 2 +- packages/sitecore-jss-react/src/components/Image.test.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index d00953fb6f..8e2d7d69ac 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -51,7 +51,7 @@ export function withFieldMetadataWrapper { const metadata = (props as any)?.field?.metadata; - if (!props?.field) { + if (!props?.field && !props?.media) { return null; } diff --git a/packages/sitecore-jss-react/src/components/Image.test.tsx b/packages/sitecore-jss-react/src/components/Image.test.tsx index 6a31c5322d..928adc8208 100644 --- a/packages/sitecore-jss-react/src/components/Image.test.tsx +++ b/packages/sitecore-jss-react/src/components/Image.test.tsx @@ -316,8 +316,9 @@ describe('', () => { const rendered = mount(); expect(rendered.find('code')).to.have.length(2); - expect(rendered.find('img')).to.have.length(0); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain('src="/assets/img/test0.png"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); From ecc5d826850f862305818cd2753489f423804b22 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Tue, 16 Apr 2024 20:56:42 +0300 Subject: [PATCH 17/49] some types updates --- .../src/components/FieldMetadata.tsx | 20 +++++++++---------- .../src/components/Image.tsx | 11 ++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 8e2d7d69ac..9c7550d238 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -2,26 +2,26 @@ import React, { ComponentType, forwardRef } from 'react'; /** The field metadata */ export interface FieldMetadata { - contextItem?: FieldMetadataContextItem | null | undefined; - fieldId?: string | null | undefined; - fieldType?: string | null | undefined; - rawValue?: string | null | undefined; + contextItem?: FieldMetadataContextItem; + fieldId?: string; + fieldType?: string; + rawValue?: string; } /** The field's context item metadata */ export interface FieldMetadataContextItem { - id?: string | null | undefined; - language?: string | null | undefined; - revision?: string | null | undefined; - version?: number | null | undefined; + id?: string; + language?: string; + revision?: string; + version?: number; } -export interface FieldMetadataWrapperProps { +interface FieldMetadataWrapperProps { metadata: any; children: React.ReactNode; } -export const FieldMetadataWrapper = (props: FieldMetadataWrapperProps): JSX.Element => { +const FieldMetadataWrapper = (props: FieldMetadataWrapperProps): JSX.Element => { const data = JSON.stringify(props.metadata); const defaultAttributes = { type: 'text/sitecore', diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 8d7ac47bd0..2a29a3cce8 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -217,6 +217,17 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, + metadata: PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, + }), }), ]), editable: PropTypes.bool, From 4d23b396dc33f836e2ece48c97915ab21483a6c4 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 09:49:21 +0300 Subject: [PATCH 18/49] some unit tests adjustments and metadata wrapper component update --- .../src/components/Link.test.tsx | 4 +- .../src/components/FieldMetadata.test.tsx | 129 ++++++++---------- .../src/components/FieldMetadata.tsx | 6 +- .../src/components/File.test.tsx | 124 ++++++++--------- .../src/components/Image.tsx | 11 -- .../src/components/Link.test.tsx | 4 +- .../src/components/Text.test.tsx | 3 +- 7 files changed, 126 insertions(+), 155 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx index ded92731f6..de095e9714 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx @@ -351,8 +351,8 @@ describe('', () => { it('should render nothing with missing field', () => { const field = (null as unknown) as LinkField; - const rendered = mount().children(); - expect(rendered).to.have.length(0); + const rendered = mount(); + expect(rendered.html()).to.equal(''); }); it('should render nothing with missing field', () => { diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx index 915a35e961..20935c71e0 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx @@ -1,17 +1,11 @@ /* eslint-disable no-unused-expressions */ import React from 'react'; import { expect } from 'chai'; -import { mount, render } from 'enzyme'; - -import { - FieldMetadataComponent, - FieldMetadataComponentProps, - getFieldMetadataMarkup, -} from './FieldMetadata'; +import { mount } from 'enzyme'; +import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; import { describe } from 'node:test'; -import { json } from 'stream/consumers'; -describe('', () => { +describe('withFieldMetadataWrapper', () => { const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -25,90 +19,83 @@ describe('', () => { }; const stringifiedData = JSON.stringify(testMetadata); - it('Should render provided metadata', () => { - const props: FieldMetadataComponentProps = { - data: stringifiedData, - }; - - const rendered = mount(); - - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.include(stringifiedData); - }); + const TestComponent: React.FC = (props: any) => { + return ( +
+

foo

+

bar

+
+ ); + }; - it('Should render with provided children', () => { - const props: FieldMetadataComponentProps = { - data: stringifiedData, + it('Should return component if field is empty', () => { + const props = { + editable: true, }; - const rendered = mount( - -
nested
-
- ); + const WrappedComponent = withFieldMetadataWrapper((props) => { + return ; + }); - expect(rendered.find('code')).to.have.length(2); + const rendered = mount(); + + expect(rendered.find('code')).to.have.length(0); expect(rendered.find('div')).to.have.length(1); - expect(rendered.html()).to.include('nested'); + expect(rendered.html()).to.contain('bar'); }); - it('Should render with default attributes', () => { - const props: FieldMetadataComponentProps = { - data: stringifiedData, + it('Should render unwrapped component if metadata field is not provided', () => { + const props = { + field: {}, }; - const rendered = mount(); + const WrappedComponent = withFieldMetadataWrapper((props) => { + return ; + }); + + const rendered = mount(); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain('type="text/sitecore"'); - expect(rendered.html()).to.contain('chrometype="field"'); - expect(rendered.html()).to.contain('class="scpm"'); + expect(rendered.find('code')).to.have.length(0); + expect(rendered.find('div')).to.have.length(1); + expect(rendered.html()).to.contain('bar'); }); - it('Should render with provided attributes', () => { - const props: FieldMetadataComponentProps = { - data: stringifiedData, - htmlAttributes: { - chrometype: 'foo', - type: 'bar', - className: 'far', + it('Should render unwrapped component if metadata is provided but field is not editable', () => { + const props = { + field: { + metadata: testMetadata, }, + editable: false, }; - const rendered = mount(); + const WrappedComponent = withFieldMetadataWrapper((props) => { + return ; + }); + + const rendered = mount(); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain('type="bar"'); - expect(rendered.html()).to.contain('chrometype="foo"'); - expect(rendered.html()).to.contain('class="far"'); + expect(rendered.find('code')).to.have.length(0); + expect(rendered.find('div')).to.have.length(1); + expect(rendered.html()).to.contain('bar'); }); -}); -describe('getFieldMetadataMarkup', () => { - it('Should render component with provided metadata and children ', () => { - const testMetadata = { - contextItem: { - id: '{09A07660-6834-476C-B93B-584248D3003B}', - language: 'en', - revision: 'a0b36ce0a7db49418edf90eb9621e145', - version: 1, + it('Should wrap field with provided metadata', () => { + const props = { + field: { + metadata: testMetadata, }, - fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - fieldType: 'single-line', - rawValue: 'Test1', + editable: true, }; - const div =
nested
; + const WrappedComponent = withFieldMetadataWrapper((props) => { + return ; + }); + + const rendered = mount(); - const rendered = mount(getFieldMetadataMarkup(testMetadata, div)); expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.include(JSON.stringify(testMetadata)); - expect(rendered.html()).to.include('
nested
'); + expect(rendered.find('div')).to.have.length(1); + expect(rendered.html()).to.contain('bar'); + expect(rendered.html()).to.contain(stringifiedData); }); }); diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 9c7550d238..a24f4f4976 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -51,11 +51,7 @@ export function withFieldMetadataWrapper { const metadata = (props as any)?.field?.metadata; - if (!props?.field && !props?.media) { - return null; - } - - if (!metadata || !props.editable) { + if (!props?.field || !metadata || !props?.editable) { return ; } diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index a73adee5b0..8cff73419e 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -6,75 +6,75 @@ import { File, FileField } from './File'; describe('', () => { it('should render nothing with missing field', () => { const field = null as FileField; - const rendered = mount().children(); - expect(rendered).to.have.length(0); - }); - - it('should render nothing with missing value', () => { - const field = { - editable: 'lorem', - }; const rendered = mount(); expect(rendered.html()).to.equal(''); }); - it('should render with src directly on provided field', () => { - const field = { - src: '/lorem', - title: 'ipsum', - }; - const rendered = mount().find('a'); - expect(rendered.html()).to.contain(field.src); - expect(rendered.html()).to.contain(field.title); - }); + // it('should render nothing with missing value', () => { + // const field = { + // editable: 'lorem', + // }; + // const rendered = mount(); + // expect(rendered.html()).to.equal(''); + // }); - it('should render display name if no title', () => { - const field = { - value: { - src: '/lorem', - displayName: 'ipsum', - }, - }; - const rendered = mount().find('a'); - expect(rendered.html()).to.contain(field.value.displayName); - }); + // it('should render with src directly on provided field', () => { + // const field = { + // src: '/lorem', + // title: 'ipsum', + // }; + // const rendered = mount().find('a'); + // expect(rendered.html()).to.contain(field.src); + // expect(rendered.html()).to.contain(field.title); + // }); - it('should render other attributes with other props provided', () => { - const field = { - value: { - src: '/lorem', - title: 'ipsum', - }, - }; - const rendered = mount().find('a'); - expect(rendered.html()).to.contain('id="my-file"'); - expect(rendered.html()).to.contain('class="my-css"'); - }); + // it('should render display name if no title', () => { + // const field = { + // value: { + // src: '/lorem', + // displayName: 'ipsum', + // }, + // }; + // const rendered = mount().find('a'); + // expect(rendered.html()).to.contain(field.value.displayName); + // }); - it('should render field metadata when metadata property is present', () => { - const testMetadata = { - contextItem: { - id: '{09A07660-6834-476C-B93B-584248D3003B}', - language: 'en', - revision: 'a0b36ce0a7db49418edf90eb9621e145', - version: 1, - }, - fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - fieldType: 'file', - rawValue: 'Test1', - }; + // it('should render other attributes with other props provided', () => { + // const field = { + // value: { + // src: '/lorem', + // title: 'ipsum', + // }, + // }; + // const rendered = mount().find('a'); + // expect(rendered.html()).to.contain('id="my-file"'); + // expect(rendered.html()).to.contain('class="my-css"'); + // }); - const field = { - src: '/lorem', - title: 'ipsum', - metadata: testMetadata, - }; + // it('should render field metadata when metadata property is present', () => { + // const testMetadata = { + // contextItem: { + // id: '{09A07660-6834-476C-B93B-584248D3003B}', + // language: 'en', + // revision: 'a0b36ce0a7db49418edf90eb9621e145', + // version: 1, + // }, + // fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + // fieldType: 'file', + // rawValue: 'Test1', + // }; - const rendered = mount(); - console.log(rendered.html()); - // expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); - }); + // const field = { + // src: '/lorem', + // title: 'ipsum', + // metadata: testMetadata, + // }; + + // const rendered = mount(); + // console.log(rendered.html()); + // // expect(rendered.find('code')).to.have.length(2); + // expect(rendered.html()).to.contain('kind="open"'); + // expect(rendered.html()).to.contain('kind="close"'); + // expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + // }); }); diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 2a29a3cce8..b3510a07fe 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -197,17 +197,6 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), }), ]), field: PropTypes.oneOfType([ diff --git a/packages/sitecore-jss-react/src/components/Link.test.tsx b/packages/sitecore-jss-react/src/components/Link.test.tsx index ad131fe2ab..1f7c91d230 100644 --- a/packages/sitecore-jss-react/src/components/Link.test.tsx +++ b/packages/sitecore-jss-react/src/components/Link.test.tsx @@ -8,8 +8,8 @@ import { generalLinkField as eeLinkData } from '../test-data/ee-data'; describe('', () => { it('should render nothing with missing field', () => { const field = (null as unknown) as LinkField; - const rendered = mount().children(); - expect(rendered).to.have.length(0); + const rendered = mount(); + expect(rendered.html()).to.equal(''); }); it('should render nothing with missing editable and value', () => { diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index ad91e10bbb..3f75896a7d 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -10,8 +10,7 @@ describe('', () => { it('should render nothing with missing field', () => { const field: TextField = null; const rendered = mount(); - expect(rendered).to.have.length(1); - expect(rendered.html()).to.be.null; + expect(rendered.html()).to.equal(''); }); it('should render nothing with missing field', () => { From 0b05351bac86c55de52d08cba4153be8e1f9cacb Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 10:06:35 +0300 Subject: [PATCH 19/49] some FieldMetadata related renamings --- .../src/components/Link.tsx | 4 +- .../src/components/NextImage.tsx | 4 +- .../src/components/Date.tsx | 4 +- .../src/components/FieldMetadata.test.tsx | 10 ++--- .../src/components/FieldMetadata.tsx | 10 ++--- .../src/components/File.tsx | 42 +++++++++---------- .../src/components/Image.tsx | 4 +- .../src/components/Link.tsx | 4 +- .../src/components/RichText.tsx | 4 +- .../src/components/Text.tsx | 4 +- packages/sitecore-jss-react/src/index.ts | 2 +- 11 files changed, 45 insertions(+), 47 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index becc7d516a..9ce63157d7 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,7 +7,7 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - withFieldMetadataWrapper, + withMetadata, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -18,7 +18,7 @@ export type LinkProps = ReactLinkProps & { internalLinkMatcher?: RegExp; }; -export const Link = withFieldMetadataWrapper( +export const Link = withMetadata( // eslint-disable-next-line react/display-name forwardRef((props: LinkProps, ref): JSX.Element | null => { const { diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index b13935853f..052d88ec7a 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -6,13 +6,13 @@ import { ImageProps, ImageField, ImageFieldValue, - withFieldMetadataWrapper, + withMetadata, } from '@sitecore-jss/sitecore-jss-react'; import Image, { ImageProps as NextImageProperties } from 'next/image'; type NextImageProps = Omit & Partial; -export const NextImage: React.FC = withFieldMetadataWrapper( +export const NextImage: React.FC = withMetadata( ({ editable, imageParams, field, mediaUrlPrefix, fill, priority, ...otherProps }) => { // next handles src and we use a custom loader, // throw error if these are present diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 8e74f7f422..03f71d2382 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; export interface DateFieldProps { /** The date field data. */ @@ -23,7 +23,7 @@ export interface DateFieldProps { render?: (date: Date | null) => React.ReactNode; } -export const DateField: React.FC = withFieldMetadataWrapper( +export const DateField: React.FC = withMetadata( ({ field, tag, editable, render, ...otherProps }) => { if (!field || (!field.editable && !field.value)) { return null; diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx index 20935c71e0..b1d7905e21 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { expect } from 'chai'; import { mount } from 'enzyme'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; import { describe } from 'node:test'; describe('withFieldMetadataWrapper', () => { @@ -33,7 +33,7 @@ describe('withFieldMetadataWrapper', () => { editable: true, }; - const WrappedComponent = withFieldMetadataWrapper((props) => { + const WrappedComponent = withMetadata((props) => { return ; }); @@ -49,7 +49,7 @@ describe('withFieldMetadataWrapper', () => { field: {}, }; - const WrappedComponent = withFieldMetadataWrapper((props) => { + const WrappedComponent = withMetadata((props) => { return ; }); @@ -68,7 +68,7 @@ describe('withFieldMetadataWrapper', () => { editable: false, }; - const WrappedComponent = withFieldMetadataWrapper((props) => { + const WrappedComponent = withMetadata((props) => { return ; }); @@ -87,7 +87,7 @@ describe('withFieldMetadataWrapper', () => { editable: true, }; - const WrappedComponent = withFieldMetadataWrapper((props) => { + const WrappedComponent = withMetadata((props) => { return ; }); diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index a24f4f4976..835e0cbce6 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -16,12 +16,12 @@ export interface FieldMetadataContextItem { version?: number; } -interface FieldMetadataWrapperProps { +interface MetadataWrapperProps { metadata: any; children: React.ReactNode; } -const FieldMetadataWrapper = (props: FieldMetadataWrapperProps): JSX.Element => { +const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { const data = JSON.stringify(props.metadata); const defaultAttributes = { type: 'text/sitecore', @@ -44,7 +44,7 @@ const FieldMetadataWrapper = (props: FieldMetadataWrapperProps): JSX.Element => * Wraps the field component with metadata markup intended to be used for chromes hydartion * @param {ComponentType} FieldComponent the field component */ -export function withFieldMetadataWrapper>( +export function withMetadata>( FieldComponent: ComponentType ) { // eslint-disable-next-line react/display-name @@ -56,9 +56,9 @@ export function withFieldMetadataWrapper + - + ); }); } diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index d1b03126d6..703ecd398c 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; export interface FileFieldValue { [propName: string]: unknown; @@ -22,33 +22,31 @@ export interface FileProps { children?: React.ReactNode; } -export const File: React.FC = withFieldMetadataWrapper( - ({ field, children, ...otherProps }) => { - /* +export const File: React.FC = withMetadata(({ field, children, ...otherProps }) => { + /* File fields cannot be managed via the EE. We never output "editable." */ - const dynamicField: FileField | FileFieldValue = field; + const dynamicField: FileField | FileFieldValue = field; - if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { - return null; - } - - // handle link directly on field for forgetful devs - const file = ((dynamicField as FileFieldValue).src - ? field - : dynamicField.value) as FileFieldValue; - if (!file) { - return null; - } + if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { + return null; + } - const linkText = !children ? file.title || file.displayName : null; - const anchorAttrs = { - href: file.src, - }; - return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); + // handle link directly on field for forgetful devs + const file = ((dynamicField as FileFieldValue).src + ? field + : dynamicField.value) as FileFieldValue; + if (!file) { + return null; } -); + + const linkText = !children ? file.title || file.displayName : null; + const anchorAttrs = { + href: file.src, + }; + return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); +}); File.propTypes = { field: PropTypes.oneOfType([ diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index b3510a07fe..1fc884bb4f 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -150,7 +150,7 @@ export const getEEMarkup = ( return getEditableWrapper(editableMarkup); }; -export const Image: React.FC = withFieldMetadataWrapper( +export const Image: React.FC = withMetadata( ({ media, editable, imageParams, field, mediaUrlPrefix, ...otherProps }) => { // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) if (field && !media) { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index bb9ede8629..607fea2b89 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -42,7 +42,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link = withFieldMetadataWrapper( +export const Link = withMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 8ed6c3cdd4..8cb03f2c7c 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,6 +1,6 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; export interface RichTextField { value?: string; @@ -25,7 +25,7 @@ export interface RichTextProps { editable?: boolean; } -export const RichText: React.FC = withFieldMetadataWrapper( +export const RichText: React.FC = withMetadata( // eslint-disable-next-line react/display-name forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 25cb55256f..4a1f3bba44 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,5 +1,5 @@ import React, { ReactElement, FunctionComponent } from 'react'; -import { FieldMetadata, withFieldMetadataWrapper } from './FieldMetadata'; +import { FieldMetadata, withMetadata } from './FieldMetadata'; import PropTypes from 'prop-types'; export interface TextField { @@ -28,7 +28,7 @@ export interface TextProps { encode?: boolean; } -export const Text: FunctionComponent = withFieldMetadataWrapper( +export const Text: FunctionComponent = withMetadata( ({ field, tag, editable, encode, ...otherProps }) => { if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { return null; diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index d834c1b68a..ae54b3103b 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -100,4 +100,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { FieldMetadata, withFieldMetadataWrapper } from './components/FieldMetadata'; +export { FieldMetadata, withMetadata } from './components/FieldMetadata'; From 46481cd2ce8b026e77ab972c9995a456a137344e Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 10:24:20 +0300 Subject: [PATCH 20/49] add unit test for RichText nextjs component --- .../src/components/RichText.test.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx index de0a4db97c..ff709aeb43 100644 --- a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx @@ -379,4 +379,52 @@ describe('RichText', () => { expect(router.prefetch).callCount(0); }); + + it('should render field metadata component when metadata property is present', () => { + const app = document.createElement('main'); + + document.body.appendChild(app); + + const router = Router(); + + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'image', + rawValue: 'Test1', + }; + + const props = { + field: { + value: ` +
`, + metadata: testMetadata, + }, + }; + + const rendered = mount( + + + , + { attachTo: app } + ); + + // const rendered = mount(); + + expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('
'); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + }); }); From cd24bb98c41a3f887fd05001d234d264e7dc445f Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 11:01:28 +0300 Subject: [PATCH 21/49] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c9b2a6ce3..970ce26430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Our versioning strategy is as follows: ## Unreleased ### 🎉 New Features & Improvements -* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Introduce FieldMetadata component and functionality to render it when metadata field property is provided in the field's layout data. In such case FieldMetadaComponent should be rendered instead of the actual field component to enable chrome's hydration when editing in pages. Ability to render metadata has been added to the field rendering components for react and nextjs. ([#1773](https://github.com/Sitecore/jss/pull/1773)) +* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Introduce FieldMetadata component and functionality to render it when metadata field property is provided in the field's layout data. In such case the field component is wrapped with metadata markup to enable chromes hydration when editing in pages. Ability to render metadata has been added to the field rendering components for react and nextjs. ([#1774](https://github.com/Sitecore/jss/pull/1774)) ## 21.7.1 From 0a5c3d5dcbbe65aa7c0256a5cbd9ae46d12e2d6a Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 11:21:08 +0300 Subject: [PATCH 22/49] update changelog pull request --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 970ce26430..94aeb474f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Our versioning strategy is as follows: ## Unreleased ### 🎉 New Features & Improvements -* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Introduce FieldMetadata component and functionality to render it when metadata field property is provided in the field's layout data. In such case the field component is wrapped with metadata markup to enable chromes hydration when editing in pages. Ability to render metadata has been added to the field rendering components for react and nextjs. ([#1774](https://github.com/Sitecore/jss/pull/1774)) +* `[sitecore-jss-react]` `[sitecore-jss-nextjs]` Introduce FieldMetadata component and functionality to render it when metadata field property is provided in the field's layout data. In such case the field component is wrapped with metadata markup to enable chromes hydration when editing in pages. Ability to render metadata has been added to the field rendering components for react and nextjs. ([#1773](https://github.com/Sitecore/jss/pull/1773)) ## 21.7.1 From 77ccdcd72a70b291a6e474404ec1326fefcdd10c Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 15:28:57 +0300 Subject: [PATCH 23/49] some type updates --- .../src/components/FieldMetadata.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 835e0cbce6..132c63a3ee 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -17,7 +17,7 @@ export interface FieldMetadataContextItem { } interface MetadataWrapperProps { - metadata: any; + metadata: FieldMetadata; children: React.ReactNode; } @@ -44,21 +44,23 @@ const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { * Wraps the field component with metadata markup intended to be used for chromes hydartion * @param {ComponentType} FieldComponent the field component */ -export function withMetadata>( +export function withMetadata>( FieldComponent: ComponentType ) { // eslint-disable-next-line react/display-name - return forwardRef(({ ...props }: FieldComponentProps, ref: any) => { - const metadata = (props as any)?.field?.metadata; + return forwardRef( + ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = (props as any)?.field?.metadata; - if (!props?.field || !metadata || !props?.editable) { - return ; - } + if (!props?.field || !metadata || !props?.editable) { + return ; + } - return ( - - - - ); - }); + return ( + + + + ); + } + ); } From 6c74f77213c230db4a98871e82ee40120b71bcc7 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 17:18:40 +0300 Subject: [PATCH 24/49] reenable file tests --- .../src/components/File.test.tsx | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index 8cff73419e..170649732a 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -10,71 +10,71 @@ describe('', () => { expect(rendered.html()).to.equal(''); }); - // it('should render nothing with missing value', () => { - // const field = { - // editable: 'lorem', - // }; - // const rendered = mount(); - // expect(rendered.html()).to.equal(''); - // }); + it('should render nothing with missing value', () => { + const field = { + editable: 'lorem', + }; + const rendered = mount(); + expect(rendered.html()).to.equal(''); + }); - // it('should render with src directly on provided field', () => { - // const field = { - // src: '/lorem', - // title: 'ipsum', - // }; - // const rendered = mount().find('a'); - // expect(rendered.html()).to.contain(field.src); - // expect(rendered.html()).to.contain(field.title); - // }); + it('should render with src directly on provided field', () => { + const field = { + src: '/lorem', + title: 'ipsum', + }; + const rendered = mount().find('a'); + expect(rendered.html()).to.contain(field.src); + expect(rendered.html()).to.contain(field.title); + }); - // it('should render display name if no title', () => { - // const field = { - // value: { - // src: '/lorem', - // displayName: 'ipsum', - // }, - // }; - // const rendered = mount().find('a'); - // expect(rendered.html()).to.contain(field.value.displayName); - // }); + it('should render display name if no title', () => { + const field = { + value: { + src: '/lorem', + displayName: 'ipsum', + }, + }; + const rendered = mount().find('a'); + expect(rendered.html()).to.contain(field.value.displayName); + }); - // it('should render other attributes with other props provided', () => { - // const field = { - // value: { - // src: '/lorem', - // title: 'ipsum', - // }, - // }; - // const rendered = mount().find('a'); - // expect(rendered.html()).to.contain('id="my-file"'); - // expect(rendered.html()).to.contain('class="my-css"'); - // }); + it('should render other attributes with other props provided', () => { + const field = { + value: { + src: '/lorem', + title: 'ipsum', + }, + }; + const rendered = mount().find('a'); + expect(rendered.html()).to.contain('id="my-file"'); + expect(rendered.html()).to.contain('class="my-css"'); + }); - // it('should render field metadata when metadata property is present', () => { - // const testMetadata = { - // contextItem: { - // id: '{09A07660-6834-476C-B93B-584248D3003B}', - // language: 'en', - // revision: 'a0b36ce0a7db49418edf90eb9621e145', - // version: 1, - // }, - // fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - // fieldType: 'file', - // rawValue: 'Test1', - // }; + it('should render field metadata when metadata property is present', () => { + const testMetadata = { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'file', + rawValue: 'Test1', + }; - // const field = { - // src: '/lorem', - // title: 'ipsum', - // metadata: testMetadata, - // }; + const field = { + src: '/lorem', + title: 'ipsum', + metadata: testMetadata, + }; - // const rendered = mount(); - // console.log(rendered.html()); - // // expect(rendered.find('code')).to.have.length(2); - // expect(rendered.html()).to.contain('kind="open"'); - // expect(rendered.html()).to.contain('kind="close"'); - // expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); - // }); + const rendered = mount(); + console.log(rendered.html()); + // expect(rendered.find('code')).to.have.length(2); + expect(rendered.html()).to.contain('kind="open"'); + expect(rendered.html()).to.contain('kind="close"'); + expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + }); }); From d5dcd8d7c30a7cf09500388f7c0fe72b5568c82a Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:21:00 +0300 Subject: [PATCH 25/49] update function description Co-authored-by: Illia Kovalenko <23364749+illiakovalenko@users.noreply.github.com> --- packages/sitecore-jss-react/src/components/FieldMetadata.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 835e0cbce6..936ecc63ea 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -41,7 +41,7 @@ const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { }; /** - * Wraps the field component with metadata markup intended to be used for chromes hydartion + * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages * @param {ComponentType} FieldComponent the field component */ export function withMetadata>( From 69fe14be33c3c0774c51a34f9153aaea297457bd Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:30:15 +0300 Subject: [PATCH 26/49] minor variable renaming Co-authored-by: Illia Kovalenko <23364749+illiakovalenko@users.noreply.github.com> --- packages/sitecore-jss-react/src/components/FieldMetadata.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 936ecc63ea..fa735f41c0 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -23,7 +23,7 @@ interface MetadataWrapperProps { const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { const data = JSON.stringify(props.metadata); - const defaultAttributes = { + const attributes = { type: 'text/sitecore', chrometype: 'field', className: 'scpm', From fcb38b99ab664181a429633828be01186566ad63 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 17:34:24 +0300 Subject: [PATCH 27/49] remove unnecessary commented line --- packages/sitecore-jss-nextjs/src/components/RichText.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx index ff709aeb43..9612b0d1de 100644 --- a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx @@ -419,8 +419,6 @@ describe('RichText', () => { { attachTo: app } ); - // const rendered = mount(); - expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('
'); expect(rendered.html()).to.contain('kind="open"'); From 8c47f7623cfde4ff0e7b857bd62dd03a421efdac Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 20:45:17 +0300 Subject: [PATCH 28/49] remove unnecessary undefined check --- packages/sitecore-jss-react/src/components/FieldMetadata.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 132c63a3ee..99987a2aae 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -50,9 +50,9 @@ export function withMetadata // eslint-disable-next-line react/display-name return forwardRef( ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = (props as any)?.field?.metadata; + const metadata = (props as any).field?.metadata; - if (!props?.field || !metadata || !props?.editable) { + if (props?.field || !metadata || !props?.editable) { return ; } From 19333b238fd2be5ae7c50f0896e078a96db59d0d Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 21:38:05 +0300 Subject: [PATCH 29/49] move FieldMetada interfaces to base package; extract metadata proptypes --- .../src/components/Date.tsx | 15 ++------ .../src/components/FieldMetadata.tsx | 34 +++++++++---------- .../src/components/File.tsx | 15 ++------ .../src/components/Image.tsx | 15 ++------ .../src/components/Link.tsx | 15 ++------ .../src/components/RichText.tsx | 15 ++------ .../src/components/Text.tsx | 15 ++------ packages/sitecore-jss-react/src/index.ts | 3 +- packages/sitecore-jss/src/utils/editing.ts | 16 +++++++++ packages/sitecore-jss/src/utils/index.ts | 2 ++ 10 files changed, 54 insertions(+), 91 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 03f71d2382..58f5a29106 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface DateFieldProps { /** The date field data. */ @@ -60,17 +61,7 @@ DateField.propTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }).isRequired, tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx index 99987a2aae..c2ee1864ef 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -1,20 +1,6 @@ import React, { ComponentType, forwardRef } from 'react'; - -/** The field metadata */ -export interface FieldMetadata { - contextItem?: FieldMetadataContextItem; - fieldId?: string; - fieldType?: string; - rawValue?: string; -} - -/** The field's context item metadata */ -export interface FieldMetadataContextItem { - id?: string; - language?: string; - revision?: string; - version?: number; -} +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import PropTypes from 'prop-types'; interface MetadataWrapperProps { metadata: FieldMetadata; @@ -44,7 +30,7 @@ const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { * Wraps the field component with metadata markup intended to be used for chromes hydartion * @param {ComponentType} FieldComponent the field component */ -export function withMetadata>( +export function withMetadata>( FieldComponent: ComponentType ) { // eslint-disable-next-line react/display-name @@ -52,7 +38,7 @@ export function withMetadata ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { const metadata = (props as any).field?.metadata; - if (props?.field || !metadata || !props?.editable) { + if (!metadata || !props?.editable) { return ; } @@ -64,3 +50,15 @@ export function withMetadata } ); } + +export const FieldMetadataPropTypes = PropTypes.shape({ + contextItem: PropTypes.shape({ + id: PropTypes.string, + language: PropTypes.string, + revision: PropTypes.string, + version: PropTypes.number, + }), + fieldId: PropTypes.string, + fieldType: PropTypes.string, + rawValue: PropTypes.string, +}); diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 703ecd398c..4506535e49 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface FileFieldValue { [propName: string]: unknown; @@ -55,17 +56,7 @@ File.propTypes = { }), PropTypes.shape({ value: PropTypes.object, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }), ]).isRequired, }; diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 1fc884bb4f..f5618cfc64 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,7 +3,8 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -206,17 +207,7 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }), ]), editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 607fea2b89..4e2e051a50 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,6 +1,7 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -140,17 +141,7 @@ export const LinkPropTypes = { value: PropTypes.object, editableFirstPart: PropTypes.string, editableLastPart: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }), ]).isRequired, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 8cb03f2c7c..fab0c7810f 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,6 +1,7 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface RichTextField { value?: string; @@ -48,17 +49,7 @@ export const RichTextPropTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }), tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 4a1f3bba44..7b9a9277d2 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,5 +1,6 @@ import React, { ReactElement, FunctionComponent } from 'react'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; import PropTypes from 'prop-types'; export interface TextField { @@ -99,17 +100,7 @@ Text.propTypes = { field: PropTypes.shape({ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), editable: PropTypes.string, - metadata: PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, - }), + metadata: FieldMetadataPropTypes, }), tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index ae54b3103b..8571cf41aa 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -1,4 +1,5 @@ export { constants, enableDebug, ClientError } from '@sitecore-jss/sitecore-jss'; +export { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export { isEditorActive, resetEditorChromes, @@ -100,4 +101,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { FieldMetadata, withMetadata } from './components/FieldMetadata'; +export { withMetadata } from './components/FieldMetadata'; diff --git a/packages/sitecore-jss/src/utils/editing.ts b/packages/sitecore-jss/src/utils/editing.ts index b9798c1673..ba6a92caad 100644 --- a/packages/sitecore-jss/src/utils/editing.ts +++ b/packages/sitecore-jss/src/utils/editing.ts @@ -19,6 +19,22 @@ export interface Metadata { packages: { [key: string]: string }; } +/** The field metadata */ +export interface FieldMetadata { + contextItem?: FieldMetadataContextItem; + fieldId?: string; + fieldType?: string; + rawValue?: string; +} + +/** The field's context item metadata */ +export interface FieldMetadataContextItem { + id?: string; + language?: string; + revision?: string; + version?: number; +} + /** * Static utility class for Sitecore Experience Editor */ diff --git a/packages/sitecore-jss/src/utils/index.ts b/packages/sitecore-jss/src/utils/index.ts index cde295e130..bb6cb2b12c 100644 --- a/packages/sitecore-jss/src/utils/index.ts +++ b/packages/sitecore-jss/src/utils/index.ts @@ -8,6 +8,8 @@ export { resetEditorChromes, handleEditorAnchors, Metadata, + FieldMetadata, + FieldMetadataContextItem, } from './editing'; export { DefaultEditFrameButton, From 04afea6337e4d0a6bafcb00bc045306dda0f525c Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 21:43:34 +0300 Subject: [PATCH 30/49] move FieldMetadata under enchancments --- packages/sitecore-jss-react/src/components/Date.tsx | 4 ++-- packages/sitecore-jss-react/src/components/File.tsx | 4 ++-- packages/sitecore-jss-react/src/components/Image.tsx | 4 ++-- packages/sitecore-jss-react/src/components/Link.tsx | 4 ++-- .../sitecore-jss-react/src/components/RichText.tsx | 4 ++-- packages/sitecore-jss-react/src/components/Text.tsx | 4 ++-- .../withFieldMetadata.test.tsx} | 10 +++++----- .../withFieldMetadata.tsx} | 2 +- packages/sitecore-jss-react/src/index.ts | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) rename packages/sitecore-jss-react/src/{components/FieldMetadata.test.tsx => enhancers/withFieldMetadata.test.tsx} (88%) rename packages/sitecore-jss-react/src/{components/FieldMetadata.tsx => enhancers/withFieldMetadata.tsx} (95%) diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 58f5a29106..0aeae6dac6 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface DateFieldProps { @@ -24,7 +24,7 @@ export interface DateFieldProps { render?: (date: Date | null) => React.ReactNode; } -export const DateField: React.FC = withMetadata( +export const DateField: React.FC = withFieldMetadata( ({ field, tag, editable, render, ...otherProps }) => { if (!field || (!field.editable && !field.value)) { return null; diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 4506535e49..26399aec7a 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface FileFieldValue { @@ -23,7 +23,7 @@ export interface FileProps { children?: React.ReactNode; } -export const File: React.FC = withMetadata(({ field, children, ...otherProps }) => { +export const File: React.FC = withFieldMetadata(({ field, children, ...otherProps }) => { /* File fields cannot be managed via the EE. We never output "editable." */ diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index f5618cfc64..b9b09d5de6 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface ImageFieldValue { @@ -151,7 +151,7 @@ export const getEEMarkup = ( return getEditableWrapper(editableMarkup); }; -export const Image: React.FC = withMetadata( +export const Image: React.FC = withFieldMetadata( ({ media, editable, imageParams, field, mediaUrlPrefix, ...otherProps }) => { // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) if (field && !media) { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 4e2e051a50..4ec7dc2950 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,6 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface LinkFieldValue { @@ -43,7 +43,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link = withMetadata( +export const Link = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index fab0c7810f..01b07c3ab8 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,6 +1,6 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; export interface RichTextField { @@ -26,7 +26,7 @@ export interface RichTextProps { editable?: boolean; } -export const RichText: React.FC = withMetadata( +export const RichText: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 7b9a9277d2..86294e30bc 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,5 +1,5 @@ import React, { ReactElement, FunctionComponent } from 'react'; -import { withMetadata, FieldMetadataPropTypes } from './FieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; import PropTypes from 'prop-types'; @@ -29,7 +29,7 @@ export interface TextProps { encode?: boolean; } -export const Text: FunctionComponent = withMetadata( +export const Text: FunctionComponent = withFieldMetadata( ({ field, tag, editable, encode, ...otherProps }) => { if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { return null; diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx similarity index 88% rename from packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx rename to packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx index b1d7905e21..3ec2e6a4d9 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { expect } from 'chai'; import { mount } from 'enzyme'; -import { FieldMetadata, withMetadata } from './FieldMetadata'; +import { FieldMetadata, withFieldMetadata } from './withFieldMetadata'; import { describe } from 'node:test'; describe('withFieldMetadataWrapper', () => { @@ -33,7 +33,7 @@ describe('withFieldMetadataWrapper', () => { editable: true, }; - const WrappedComponent = withMetadata((props) => { + const WrappedComponent = withFieldMetadata((props) => { return ; }); @@ -49,7 +49,7 @@ describe('withFieldMetadataWrapper', () => { field: {}, }; - const WrappedComponent = withMetadata((props) => { + const WrappedComponent = withFieldMetadata((props) => { return ; }); @@ -68,7 +68,7 @@ describe('withFieldMetadataWrapper', () => { editable: false, }; - const WrappedComponent = withMetadata((props) => { + const WrappedComponent = withFieldMetadata((props) => { return ; }); @@ -87,7 +87,7 @@ describe('withFieldMetadataWrapper', () => { editable: true, }; - const WrappedComponent = withMetadata((props) => { + const WrappedComponent = withFieldMetadata((props) => { return ; }); diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx similarity index 95% rename from packages/sitecore-jss-react/src/components/FieldMetadata.tsx rename to packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index c2ee1864ef..cf6ab8ec12 100644 --- a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -30,7 +30,7 @@ const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { * Wraps the field component with metadata markup intended to be used for chromes hydartion * @param {ComponentType} FieldComponent the field component */ -export function withMetadata>( +export function withFieldMetadata>( FieldComponent: ComponentType ) { // eslint-disable-next-line react/display-name diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index 8571cf41aa..4a8836b4e6 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -101,4 +101,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { withMetadata } from './components/FieldMetadata'; +export { withFieldMetadata as withMetadata } from './enhancers/withFieldMetadata'; From 581527e4a82e809bcb5a00b6c01cbbf670c7d7cb Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 17 Apr 2024 21:54:23 +0300 Subject: [PATCH 31/49] added some descriptions --- packages/sitecore-jss/src/utils/editing.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/sitecore-jss/src/utils/editing.ts b/packages/sitecore-jss/src/utils/editing.ts index ba6a92caad..86e75b0af7 100644 --- a/packages/sitecore-jss/src/utils/editing.ts +++ b/packages/sitecore-jss/src/utils/editing.ts @@ -19,7 +19,10 @@ export interface Metadata { packages: { [key: string]: string }; } -/** The field metadata */ +/** The field metadata; should be returned by layout service; + * when present, metadata markup is rendered as a wrapper around every field component + * and is used for chromes hydration during editing in pages + */ export interface FieldMetadata { contextItem?: FieldMetadataContextItem; fieldId?: string; @@ -27,7 +30,7 @@ export interface FieldMetadata { rawValue?: string; } -/** The field's context item metadata */ +/** The field's context item metadata, a property of the FIeldMetadata interface */ export interface FieldMetadataContextItem { id?: string; language?: string; From 32deed22bc87871edf072b8798491cff6bf80ee4 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 18 Apr 2024 11:31:00 +0300 Subject: [PATCH 32/49] move and rename FieldMetadata to layout submodule of base package --- .../src/components/Date.tsx | 4 ++-- .../src/components/File.tsx | 4 ++-- .../src/components/Image.tsx | 4 ++-- .../src/components/Link.tsx | 4 ++-- .../src/components/RichText.tsx | 4 ++-- .../src/components/Text.tsx | 4 ++-- .../src/enhancers/withFieldMetadata.tsx | 4 ++-- packages/sitecore-jss-react/src/index.ts | 2 +- packages/sitecore-jss/src/layout/index.ts | 2 ++ packages/sitecore-jss/src/layout/models.ts | 19 +++++++++++++++++++ packages/sitecore-jss/src/utils/editing.ts | 19 ------------------- packages/sitecore-jss/src/utils/index.ts | 2 -- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 0aeae6dac6..9fbbe3271d 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface DateFieldProps { /** The date field data. */ @@ -9,7 +9,7 @@ export interface DateFieldProps { field: { value?: string; editable?: string; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; }; /** * The HTML element that will wrap the contents of the field. diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 26399aec7a..5324e55b08 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface FileFieldValue { [propName: string]: unknown; @@ -12,7 +12,7 @@ export interface FileFieldValue { export interface FileField { value: FileFieldValue; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; } export interface FileProps { diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index b9b09d5de6..13ef5ba361 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -15,7 +15,7 @@ export interface ImageFieldValue { export interface ImageField { value?: ImageFieldValue; editable?: string; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; } export interface ImageSizeParameters { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 4ec7dc2950..e3d4fa3ba8 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,7 +1,7 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -20,7 +20,7 @@ export interface LinkField { value: LinkFieldValue; editableFirstPart?: string; editableLastPart?: string; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; } export type LinkProps = React.DetailedHTMLProps< diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 01b07c3ab8..71212a35ca 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,12 +1,12 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface RichTextField { value?: string; editable?: string; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; } export interface RichTextProps { diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 86294e30bc..477e37aaed 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,12 +1,12 @@ import React, { ReactElement, FunctionComponent } from 'react'; import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; import PropTypes from 'prop-types'; export interface TextField { value?: string | number; editable?: string; - metadata?: FieldMetadata; + metadata?: FieldMetadataValue; } export interface TextProps { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index aeb036bf21..cb2c0fe814 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -1,9 +1,9 @@ import React, { ComponentType, forwardRef } from 'react'; -import { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; import PropTypes from 'prop-types'; interface MetadataWrapperProps { - metadata: FieldMetadata; + metadata: FieldMetadataValue; children: React.ReactNode; } diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index 4a8836b4e6..3b636a8782 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -1,5 +1,5 @@ export { constants, enableDebug, ClientError } from '@sitecore-jss/sitecore-jss'; -export { FieldMetadata } from '@sitecore-jss/sitecore-jss/utils'; +export { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export { isEditorActive, resetEditorChromes, diff --git a/packages/sitecore-jss/src/layout/index.ts b/packages/sitecore-jss/src/layout/index.ts index 8262ececf2..0afbfc1c41 100644 --- a/packages/sitecore-jss/src/layout/index.ts +++ b/packages/sitecore-jss/src/layout/index.ts @@ -16,6 +16,8 @@ export { RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, + FieldMetadataValue, + FieldMetadataContextItem, } from './models'; export { getFieldValue, getChildPlaceholder } from './utils'; diff --git a/packages/sitecore-jss/src/layout/models.ts b/packages/sitecore-jss/src/layout/models.ts index 87657e0613..a1c192c688 100644 --- a/packages/sitecore-jss/src/layout/models.ts +++ b/packages/sitecore-jss/src/layout/models.ts @@ -155,3 +155,22 @@ export interface PlaceholderData { path: string; elements: Array; } + +/** The field metadata; should be returned by layout service; + * when present, metadata markup is rendered as a wrapper around every field component + * and is used for chromes hydration during editing in pages + */ +export interface FieldMetadataValue { + contextItem?: FieldMetadataContextItem; + fieldId?: string; + fieldType?: string; + rawValue?: string; +} + +/** The field's context item metadata, a property of the FIeldMetadata interface */ +export interface FieldMetadataContextItem { + id?: string; + language?: string; + revision?: string; + version?: number; +} diff --git a/packages/sitecore-jss/src/utils/editing.ts b/packages/sitecore-jss/src/utils/editing.ts index 86e75b0af7..b9798c1673 100644 --- a/packages/sitecore-jss/src/utils/editing.ts +++ b/packages/sitecore-jss/src/utils/editing.ts @@ -19,25 +19,6 @@ export interface Metadata { packages: { [key: string]: string }; } -/** The field metadata; should be returned by layout service; - * when present, metadata markup is rendered as a wrapper around every field component - * and is used for chromes hydration during editing in pages - */ -export interface FieldMetadata { - contextItem?: FieldMetadataContextItem; - fieldId?: string; - fieldType?: string; - rawValue?: string; -} - -/** The field's context item metadata, a property of the FIeldMetadata interface */ -export interface FieldMetadataContextItem { - id?: string; - language?: string; - revision?: string; - version?: number; -} - /** * Static utility class for Sitecore Experience Editor */ diff --git a/packages/sitecore-jss/src/utils/index.ts b/packages/sitecore-jss/src/utils/index.ts index bb6cb2b12c..cde295e130 100644 --- a/packages/sitecore-jss/src/utils/index.ts +++ b/packages/sitecore-jss/src/utils/index.ts @@ -8,8 +8,6 @@ export { resetEditorChromes, handleEditorAnchors, Metadata, - FieldMetadata, - FieldMetadataContextItem, } from './editing'; export { DefaultEditFrameButton, From a0115fd819cccd0492a918996d51e34e1c030d43 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 18 Apr 2024 11:32:59 +0300 Subject: [PATCH 33/49] rename FieldMetadata component --- .../sitecore-jss-react/src/enhancers/withFieldMetadata.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index cb2c0fe814..1f4907fd18 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -7,7 +7,7 @@ interface MetadataWrapperProps { children: React.ReactNode; } -const MetadataWrapper = (props: MetadataWrapperProps): JSX.Element => { +const FieldMetadata = (props: MetadataWrapperProps): JSX.Element => { const data = JSON.stringify(props.metadata); const attributes = { type: 'text/sitecore', @@ -43,9 +43,9 @@ export function withFieldMetadata + - + ); } ); From 80956005492b11ded845da26f23435caedea144f Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 18 Apr 2024 11:41:09 +0300 Subject: [PATCH 34/49] add tsdoc description for fieldmetadata component --- .../src/enhancers/withFieldMetadata.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index 1f4907fd18..a1efa40293 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -2,12 +2,17 @@ import React, { ComponentType, forwardRef } from 'react'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; import PropTypes from 'prop-types'; -interface MetadataWrapperProps { +interface FieldMetadataProps { metadata: FieldMetadataValue; children: React.ReactNode; } -const FieldMetadata = (props: MetadataWrapperProps): JSX.Element => { +/** + * The component which renders field metadata markup + * @param {FieldMetadataProps} props the props of the component + * @returns metadata markup wrapped around children + */ +const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { const data = JSON.stringify(props.metadata); const attributes = { type: 'text/sitecore', From d749ad996c60a5a79efcd6a4601500d36e6b16bd Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 18 Apr 2024 12:33:11 +0300 Subject: [PATCH 35/49] conditionally forwardRef in fieldMetadata --- .../src/components/Link.tsx | 3 +- .../src/components/Link.tsx | 3 +- .../src/components/RichText.tsx | 3 +- .../src/enhancers/withFieldMetadata.tsx | 44 +++++++++++++------ 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 9ce63157d7..e98720f4e0 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -73,7 +73,8 @@ export const Link = withMetadata( // we've already rendered the metadata wrapper - so set metadata to null to prevent duplicate wrapping reactLinkProps.field.metadata = null; return ; - }) + }), + true ); Link.defaultProps = { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index e3d4fa3ba8..92e5340652 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -129,7 +129,8 @@ export const Link = withFieldMetadata( return {resultTags}; } - ) + ), + true ); export const LinkPropTypes = { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 71212a35ca..e7c67e0359 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -42,7 +42,8 @@ export const RichText: React.FC = withFieldMetadata( }; return React.createElement(tag || 'div', htmlProps); - }) + }), + true ); export const RichTextPropTypes = { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index a1efa40293..c54e780d73 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -34,26 +34,44 @@ const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { /** * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages * @param {ComponentType} FieldComponent the field component + * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ export function withFieldMetadata>( - FieldComponent: ComponentType + FieldComponent: ComponentType, + isForwardRef = false ) { - // eslint-disable-next-line react/display-name - return forwardRef( - ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = (props as any).field?.metadata; + if (isForwardRef) { + // eslint-disable-next-line react/display-name + return forwardRef( + ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = (props as any).field?.metadata; + + if (!metadata || !props?.editable) { + return ; + } - if (!metadata || !props?.editable) { - return ; + return ( + + + + ); } + ); + } + // eslint-disable-next-line react/display-name + return ({ ...props }: FieldComponentProps) => { + const metadata = (props as any).field?.metadata; - return ( - - - - ); + if (!metadata || !props?.editable) { + return ; } - ); + + return ( + + + + ); + }; } export const FieldMetadataPropTypes = PropTypes.shape({ From dde6878cdcf3c28c1e39abea70eba1745cd31d74 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Thu, 18 Apr 2024 13:42:13 +0300 Subject: [PATCH 36/49] two separate withFieldMetadata functions based on if used with forwardRef --- .../src/components/Link.tsx | 7 ++- .../src/components/NextImage.tsx | 4 +- .../src/components/Link.tsx | 10 ++-- .../src/components/RichText.tsx | 10 ++-- .../src/enhancers/withFieldMetadata.tsx | 47 ++++++++++--------- packages/sitecore-jss-react/src/index.ts | 2 +- 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index e98720f4e0..94b5c934ce 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,7 +7,7 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - withMetadata, + withFieldMetadataForwardRef, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -18,7 +18,7 @@ export type LinkProps = ReactLinkProps & { internalLinkMatcher?: RegExp; }; -export const Link = withMetadata( +export const Link = withFieldMetadataForwardRef( // eslint-disable-next-line react/display-name forwardRef((props: LinkProps, ref): JSX.Element | null => { const { @@ -73,8 +73,7 @@ export const Link = withMetadata( // we've already rendered the metadata wrapper - so set metadata to null to prevent duplicate wrapping reactLinkProps.field.metadata = null; return ; - }), - true + }) ); Link.defaultProps = { diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index 052d88ec7a..109d845bf2 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -6,13 +6,13 @@ import { ImageProps, ImageField, ImageFieldValue, - withMetadata, + withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react'; import Image, { ImageProps as NextImageProperties } from 'next/image'; type NextImageProps = Omit & Partial; -export const NextImage: React.FC = withMetadata( +export const NextImage: React.FC = withFieldMetadata( ({ editable, imageParams, field, mediaUrlPrefix, fill, priority, ...otherProps }) => { // next handles src and we use a custom loader, // throw error if these are present diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 92e5340652..4c2ada75f7 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,6 +1,9 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; +import { + withFieldMetadataForwardRef, + FieldMetadataPropTypes, +} from '../enhancers/withFieldMetadata'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface LinkFieldValue { @@ -43,7 +46,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link = withFieldMetadata( +export const Link = withFieldMetadataForwardRef( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { @@ -129,8 +132,7 @@ export const Link = withFieldMetadata( return {resultTags}; } - ), - true + ) ); export const LinkPropTypes = { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index e7c67e0359..2ffda528f9 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,6 +1,9 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; +import { + withFieldMetadataForwardRef, + FieldMetadataPropTypes, +} from '../enhancers/withFieldMetadata'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface RichTextField { @@ -26,7 +29,7 @@ export interface RichTextProps { editable?: boolean; } -export const RichText: React.FC = withFieldMetadata( +export const RichText: React.FC = withFieldMetadataForwardRef( // eslint-disable-next-line react/display-name forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { @@ -42,8 +45,7 @@ export const RichText: React.FC = withFieldMetadata( }; return React.createElement(tag || 'div', htmlProps); - }), - true + }) ); export const RichTextPropTypes = { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index c54e780d73..f49c5bda70 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -34,30 +34,10 @@ const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { /** * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages * @param {ComponentType} FieldComponent the field component - * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ export function withFieldMetadata>( - FieldComponent: ComponentType, - isForwardRef = false + FieldComponent: ComponentType ) { - if (isForwardRef) { - // eslint-disable-next-line react/display-name - return forwardRef( - ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = (props as any).field?.metadata; - - if (!metadata || !props?.editable) { - return ; - } - - return ( - - - - ); - } - ); - } // eslint-disable-next-line react/display-name return ({ ...props }: FieldComponentProps) => { const metadata = (props as any).field?.metadata; @@ -74,6 +54,31 @@ export function withFieldMetadata} FieldComponent the field component + */ +export function withFieldMetadataForwardRef>( + FieldComponent: ComponentType +) { + // eslint-disable-next-line react/display-name + return forwardRef( + ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = (props as any).field?.metadata; + + if (!metadata || !props?.editable) { + return ; + } + + return ( + + + + ); + } + ); +} + export const FieldMetadataPropTypes = PropTypes.shape({ contextItem: PropTypes.shape({ id: PropTypes.string, diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index 3b636a8782..ccfd46a44d 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -101,4 +101,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { withFieldMetadata as withMetadata } from './enhancers/withFieldMetadata'; +export { withFieldMetadata, withFieldMetadataForwardRef } from './enhancers/withFieldMetadata'; From 5cd3c7547759df2b6e6acfca636688e2e8a3cc46 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 19 Apr 2024 12:16:44 +0300 Subject: [PATCH 37/49] single withFieldMetadata function with forwardref parameter --- .../src/components/Link.tsx | 7 +-- .../src/components/Link.tsx | 10 ++-- .../src/components/RichText.tsx | 10 ++-- .../src/enhancers/withFieldMetadata.tsx | 54 +++++++++---------- packages/sitecore-jss-react/src/index.ts | 2 +- 5 files changed, 38 insertions(+), 45 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 94b5c934ce..66fc2f03e9 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,7 +7,7 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - withFieldMetadataForwardRef, + withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -18,7 +18,7 @@ export type LinkProps = ReactLinkProps & { internalLinkMatcher?: RegExp; }; -export const Link = withFieldMetadataForwardRef( +export const Link = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef((props: LinkProps, ref): JSX.Element | null => { const { @@ -73,7 +73,8 @@ export const Link = withFieldMetadataForwardRef( // we've already rendered the metadata wrapper - so set metadata to null to prevent duplicate wrapping reactLinkProps.field.metadata = null; return ; - }) + }), + true ); Link.defaultProps = { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 4c2ada75f7..083d926e50 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,9 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { - withFieldMetadataForwardRef, - FieldMetadataPropTypes, -} from '../enhancers/withFieldMetadata'; +import { FieldMetadataPropTypes, withFieldMetadata } from '../enhancers/withFieldMetadata'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface LinkFieldValue { @@ -46,7 +43,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link = withFieldMetadataForwardRef( +export const Link = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { @@ -132,7 +129,8 @@ export const Link = withFieldMetadataForwardRef( return {resultTags}; } - ) + ), + true ); export const LinkPropTypes = { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 2ffda528f9..e7c67e0359 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,9 +1,6 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { - withFieldMetadataForwardRef, - FieldMetadataPropTypes, -} from '../enhancers/withFieldMetadata'; +import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface RichTextField { @@ -29,7 +26,7 @@ export interface RichTextProps { editable?: boolean; } -export const RichText: React.FC = withFieldMetadataForwardRef( +export const RichText: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { @@ -45,7 +42,8 @@ export const RichText: React.FC = withFieldMetadataForwardRef( }; return React.createElement(tag || 'div', htmlProps); - }) + }), + true ); export const RichTextPropTypes = { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index f49c5bda70..04c947dfb2 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -34,13 +34,34 @@ const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { /** * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages * @param {ComponentType} FieldComponent the field component + * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ export function withFieldMetadata>( - FieldComponent: ComponentType + FieldComponent: ComponentType, + isForwardRef = false ) { + if (isForwardRef) { + // eslint-disable-next-line react/display-name + return forwardRef( + ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = props.field?.metadata; + + if (!metadata || !props?.editable) { + return ; + } + + return ( + + + + ); + } + ); + } + // eslint-disable-next-line react/display-name - return ({ ...props }: FieldComponentProps) => { - const metadata = (props as any).field?.metadata; + return (({ ...props }: FieldComponentProps) => { + const metadata = props.field?.metadata; if (!metadata || !props?.editable) { return ; @@ -51,32 +72,7 @@ export function withFieldMetadata ); - }; -} - -/** - * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages - * @param {ComponentType} FieldComponent the field component - */ -export function withFieldMetadataForwardRef>( - FieldComponent: ComponentType -) { - // eslint-disable-next-line react/display-name - return forwardRef( - ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = (props as any).field?.metadata; - - if (!metadata || !props?.editable) { - return ; - } - - return ( - - - - ); - } - ); + }) as React.FC; } export const FieldMetadataPropTypes = PropTypes.shape({ diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index ccfd46a44d..5a58df330f 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -101,4 +101,4 @@ export { withPlaceholder } from './enhancers/withPlaceholder'; export { withDatasourceCheck } from './enhancers/withDatasourceCheck'; export { EditFrameProps, EditFrame } from './components/EditFrame'; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { withFieldMetadata, withFieldMetadataForwardRef } from './enhancers/withFieldMetadata'; +export { withFieldMetadata } from './enhancers/withFieldMetadata'; From 633e322a61d4660ba9e94d586c350a80bb65eec8 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 19 Apr 2024 13:19:51 +0300 Subject: [PATCH 38/49] update with metadata unit test to test the whole structure of markup --- .../src/enhancers/withFieldMetadata.test.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx index 3ec2e6a4d9..b167e06ddd 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx @@ -92,10 +92,23 @@ describe('withFieldMetadataWrapper', () => { }); const rendered = mount(); - - expect(rendered.find('code')).to.have.length(2); + const codeTags = rendered.find('code'); + + expect(codeTags).to.have.length(2); + expect(codeTags.at(0).text()).to.equal(stringifiedData); + expect(codeTags.at(0).html()).to.include(''); + expect(codeTags.at(0).html()).to.include('type="text/sitecore"'); + expect(codeTags.at(0).html()).to.include('chrometype="field"'); + expect(codeTags.at(0).html()).to.include('class="scpm"'); + expect(codeTags.at(0).html()).to.include('kind="open"'); expect(rendered.find('div')).to.have.length(1); expect(rendered.html()).to.contain('bar'); - expect(rendered.html()).to.contain(stringifiedData); + expect(codeTags.at(1).html()).to.include(''); + expect(codeTags.at(1).html()).to.include('type="text/sitecore"'); + expect(codeTags.at(1).html()).to.include('chrometype="field"'); + expect(codeTags.at(1).html()).to.include('class="scpm"'); + expect(codeTags.at(1).html()).to.include('kind="close"'); }); }); From c12dbb81831b2f6dcf7d178793ea1c643cff5c5b Mon Sep 17 00:00:00 2001 From: yavorsk Date: Fri, 19 Apr 2024 14:30:31 +0300 Subject: [PATCH 39/49] withMetadata refactoring wip --- .../src/enhancers/withFieldMetadata.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index 04c947dfb2..84a880fa76 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -2,6 +2,13 @@ import React, { ComponentType, forwardRef } from 'react'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; import PropTypes from 'prop-types'; +interface WithMetadataProps { + field: FieldType & { + metadata: FieldMetadataValue; + }; + editable: boolean; +} + interface FieldMetadataProps { metadata: FieldMetadataValue; children: React.ReactNode; @@ -36,7 +43,7 @@ const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { * @param {ComponentType} FieldComponent the field component * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ -export function withFieldMetadata>( +export function withFieldMetadata>( FieldComponent: ComponentType, isForwardRef = false ) { @@ -44,7 +51,7 @@ export function withFieldMetadata) => { - const metadata = props.field?.metadata; + const metadata = props.field?.metadata as FieldMetadataValue; if (!metadata || !props?.editable) { return ; @@ -61,7 +68,7 @@ export function withFieldMetadata { - const metadata = props.field?.metadata; + const metadata = props.field?.metadata as FieldMetadataValue; if (!metadata || !props?.editable) { return ; From 13473fdfba7aca69cee2dc500450100a9bace3c6 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 19 Apr 2024 14:52:44 +0300 Subject: [PATCH 40/49] Adjusted withFieldMetadata generic type --- .../sitecore-jss-react/src/components/Link.tsx | 7 ++++--- .../src/components/RichText.tsx | 2 +- .../sitecore-jss-react/src/components/Text.tsx | 2 +- .../src/enhancers/withFieldMetadata.tsx | 18 +++++++++--------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 083d926e50..c14449f1ba 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -20,7 +20,6 @@ export interface LinkField { value: LinkFieldValue; editableFirstPart?: string; editableLastPart?: string; - metadata?: FieldMetadataValue; } export type LinkProps = React.DetailedHTMLProps< @@ -28,7 +27,9 @@ export type LinkProps = React.DetailedHTMLProps< HTMLAnchorElement > & { /** The link field data. */ - field: LinkField | LinkFieldValue; + field: (LinkField | LinkFieldValue) & { + metadata?: FieldMetadataValue; + }; /** * Can be used to explicitly disable inline editing. * If true and `field.editable` has a value, then `field.editable` will be processed and rendered as component output. If false, `field.editable` value will be ignored and not rendered. @@ -43,7 +44,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link = withFieldMetadata( +export const Link: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index e7c67e0359..5d5c0582ab 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -26,7 +26,7 @@ export interface RichTextProps { editable?: boolean; } -export const RichText: React.FC = withFieldMetadata( +export const RichText: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 477e37aaed..9f6ab3016c 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -29,7 +29,7 @@ export interface TextProps { encode?: boolean; } -export const Text: FunctionComponent = withFieldMetadata( +export const Text: FunctionComponent = withFieldMetadata( ({ field, tag, editable, encode, ...otherProps }) => { if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { return null; diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index 84a880fa76..4cb70173f0 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -2,11 +2,11 @@ import React, { ComponentType, forwardRef } from 'react'; import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; import PropTypes from 'prop-types'; -interface WithMetadataProps { - field: FieldType & { - metadata: FieldMetadataValue; +interface WithMetadataProps { + field?: { + metadata?: FieldMetadataValue; }; - editable: boolean; + editable?: boolean; } interface FieldMetadataProps { @@ -43,7 +43,7 @@ const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { * @param {ComponentType} FieldComponent the field component * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ -export function withFieldMetadata>( +export function withFieldMetadata( FieldComponent: ComponentType, isForwardRef = false ) { @@ -51,7 +51,7 @@ export function withFieldMetadata) => { - const metadata = props.field?.metadata as FieldMetadataValue; + const metadata = props.field?.metadata; if (!metadata || !props?.editable) { return ; @@ -67,8 +67,8 @@ export function withFieldMetadata { - const metadata = props.field?.metadata as FieldMetadataValue; + return ({ ...props }: FieldComponentProps) => { + const metadata = props.field?.metadata; if (!metadata || !props?.editable) { return ; @@ -79,7 +79,7 @@ export function withFieldMetadata ); - }) as React.FC; + }; } export const FieldMetadataPropTypes = PropTypes.shape({ From 9120a7b5c77de43f6ab948d393e44055f606f05b Mon Sep 17 00:00:00 2001 From: yavorsk Date: Mon, 22 Apr 2024 13:44:07 +0300 Subject: [PATCH 41/49] update unit test --- .../src/enhancers/withFieldMetadata.test.tsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx index b167e06ddd..502f4426b2 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx @@ -96,19 +96,15 @@ describe('withFieldMetadataWrapper', () => { expect(codeTags).to.have.length(2); expect(codeTags.at(0).text()).to.equal(stringifiedData); - expect(codeTags.at(0).html()).to.include(''); - expect(codeTags.at(0).html()).to.include('type="text/sitecore"'); + expect(codeTags.at(0).props().type).to.equal('text/sitecore'); expect(codeTags.at(0).html()).to.include('chrometype="field"'); - expect(codeTags.at(0).html()).to.include('class="scpm"'); - expect(codeTags.at(0).html()).to.include('kind="open"'); + expect(codeTags.at(0).props().className).to.equal('scpm'); + expect(codeTags.at(0).props().kind).to.equal('open'); expect(rendered.find('div')).to.have.length(1); expect(rendered.html()).to.contain('bar'); - expect(codeTags.at(1).html()).to.include(''); - expect(codeTags.at(1).html()).to.include('type="text/sitecore"'); + expect(codeTags.at(1).props().type).to.equal('text/sitecore'); expect(codeTags.at(1).html()).to.include('chrometype="field"'); - expect(codeTags.at(1).html()).to.include('class="scpm"'); - expect(codeTags.at(1).html()).to.include('kind="close"'); + expect(codeTags.at(1).props().className).to.equal('scpm'); + expect(codeTags.at(1).props().kind).to.equal('close'); }); }); From e6252f6007bcba002fbfa4197824b1eeb62bce03 Mon Sep 17 00:00:00 2001 From: yavorsk Date: Mon, 22 Apr 2024 14:00:53 +0300 Subject: [PATCH 42/49] wip - refactor field metadata hoc --- .../src/components/File.tsx | 45 ++++++++++--------- .../src/components/Image.tsx | 5 +-- .../src/components/Link.tsx | 2 +- .../src/enhancers/withFieldMetadata.tsx | 28 ++++++------ 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 5324e55b08..8b50a21d66 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -12,42 +12,45 @@ export interface FileFieldValue { export interface FileField { value: FileFieldValue; - metadata?: FieldMetadataValue; } export interface FileProps { [attributeName: string]: unknown; /** The file field data. */ - field: FileFieldValue | FileField; + field: (FileFieldValue | FileField) & { + metadata?: FieldMetadataValue; + }; /** HTML attributes that will be appended to the rendered tag. */ children?: React.ReactNode; } -export const File: React.FC = withFieldMetadata(({ field, children, ...otherProps }) => { - /* +export const File: React.FC = withFieldMetadata( + ({ field, children, ...otherProps }) => { + /* File fields cannot be managed via the EE. We never output "editable." */ - const dynamicField: FileField | FileFieldValue = field; + const dynamicField: FileField | FileFieldValue = field; - if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { - return null; - } + if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { + return null; + } - // handle link directly on field for forgetful devs - const file = ((dynamicField as FileFieldValue).src - ? field - : dynamicField.value) as FileFieldValue; - if (!file) { - return null; - } + // handle link directly on field for forgetful devs + const file = ((dynamicField as FileFieldValue).src + ? field + : dynamicField.value) as FileFieldValue; + if (!file) { + return null; + } - const linkText = !children ? file.title || file.displayName : null; - const anchorAttrs = { - href: file.src, - }; - return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); -}); + const linkText = !children ? file.title || file.displayName : null; + const anchorAttrs = { + href: file.src, + }; + return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); + } +); File.propTypes = { field: PropTypes.oneOfType([ diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 13ef5ba361..345ce17092 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -15,7 +15,6 @@ export interface ImageFieldValue { export interface ImageField { value?: ImageFieldValue; editable?: string; - metadata?: FieldMetadataValue; } export interface ImageSizeParameters { @@ -45,7 +44,7 @@ export interface ImageProps { media?: ImageField | ImageFieldValue; /** Image field data (consistent with other field types) */ - field?: ImageField | ImageFieldValue; + field?: (ImageField | ImageFieldValue) & { metadata?: FieldMetadataValue }; /** * Can be used to explicitly disable inline editing. @@ -151,7 +150,7 @@ export const getEEMarkup = ( return getEditableWrapper(editableMarkup); }; -export const Image: React.FC = withFieldMetadata( +export const Image: React.FC = withFieldMetadata( ({ media, editable, imageParams, field, mediaUrlPrefix, ...otherProps }) => { // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) if (field && !media) { diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index c14449f1ba..178581f997 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -44,7 +44,7 @@ export type LinkProps = React.DetailedHTMLProps< showLinkTextWithChildrenPresent?: boolean; }; -export const Link: React.FC = withFieldMetadata( +export const Link = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index 4cb70173f0..cafd4b1d45 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -49,25 +49,23 @@ export function withFieldMetadata ) { if (isForwardRef) { // eslint-disable-next-line react/display-name - return forwardRef( - ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = props.field?.metadata; + return forwardRef(({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = props.field?.metadata; - if (!metadata || !props?.editable) { - return ; - } - - return ( - - - - ); + if (!metadata || !props?.editable) { + return ; } - ); + + return ( + + + + ); + }); } // eslint-disable-next-line react/display-name - return ({ ...props }: FieldComponentProps) => { + return (({ ...props }: FieldComponentProps) => { const metadata = props.field?.metadata; if (!metadata || !props?.editable) { @@ -79,7 +77,7 @@ export function withFieldMetadata ); - }; + }) as React.FC; } export const FieldMetadataPropTypes = PropTypes.shape({ From d5f49e9637118cf372424ce71dba301556ea98a7 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 11:30:15 +0300 Subject: [PATCH 43/49] Updates --- .../src/components/Link.tsx | 15 ++--- .../src/components/NextImage.tsx | 2 +- .../src/components/Date.tsx | 5 +- .../src/components/FieldMetadata.tsx | 38 +++++++++++++ .../src/components/File.test.tsx | 35 ++---------- .../src/components/File.tsx | 49 +++++++--------- .../src/components/Image.tsx | 8 +-- .../src/components/Link.tsx | 8 +-- .../src/components/RichText.tsx | 5 +- .../src/components/Text.tsx | 9 +-- .../src/enhancers/withFieldMetadata.tsx | 57 +++---------------- packages/sitecore-jss-react/src/index.ts | 1 - packages/sitecore-jss/src/layout/index.ts | 2 - packages/sitecore-jss/src/layout/models.ts | 19 ------- 14 files changed, 87 insertions(+), 166 deletions(-) create mode 100644 packages/sitecore-jss-react/src/components/FieldMetadata.tsx diff --git a/packages/sitecore-jss-nextjs/src/components/Link.tsx b/packages/sitecore-jss-nextjs/src/components/Link.tsx index 66fc2f03e9..28a8bc96bf 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.tsx @@ -7,7 +7,6 @@ import { LinkField, LinkProps as ReactLinkProps, LinkPropTypes, - withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react'; export type LinkProps = ReactLinkProps & { @@ -18,9 +17,8 @@ export type LinkProps = ReactLinkProps & { internalLinkMatcher?: RegExp; }; -export const Link = withFieldMetadata( - // eslint-disable-next-line react/display-name - forwardRef((props: LinkProps, ref): JSX.Element | null => { +export const Link = forwardRef( + (props: LinkProps, ref): JSX.Element | null => { const { field, editable, @@ -41,7 +39,9 @@ export const Link = withFieldMetadata( ? field : (field as LinkField).value) as LinkFieldValue; const { href, querystring, anchor } = value; - const isEditing = editable && (field as LinkFieldValue).editable; + + const isEditing = + editable && ((field as LinkFieldValue).editable || (field as LinkFieldValue).metadata); if (href && !isEditing) { const text = showLinkTextWithChildrenPresent || !children ? value.text || value.href : null; @@ -70,11 +70,8 @@ export const Link = withFieldMetadata( const reactLinkProps = { ...props }; delete reactLinkProps.internalLinkMatcher; - // we've already rendered the metadata wrapper - so set metadata to null to prevent duplicate wrapping - reactLinkProps.field.metadata = null; return ; - }), - true + } ); Link.defaultProps = { diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index 109d845bf2..ffc6801c4e 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -12,7 +12,7 @@ import Image, { ImageProps as NextImageProperties } from 'next/image'; type NextImageProps = Omit & Partial; -export const NextImage: React.FC = withFieldMetadata( +export const NextImage = withFieldMetadata( ({ editable, imageParams, field, mediaUrlPrefix, fill, priority, ...otherProps }) => { // next handles src and we use a custom loader, // throw error if these are present diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index 9fbbe3271d..c2b1e4ee26 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; +import { withFieldMetadata } from '../enhancers/withFieldMetadata'; export interface DateFieldProps { /** The date field data. */ @@ -9,7 +8,6 @@ export interface DateFieldProps { field: { value?: string; editable?: string; - metadata?: FieldMetadataValue; }; /** * The HTML element that will wrap the contents of the field. @@ -61,7 +59,6 @@ DateField.propTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, - metadata: FieldMetadataPropTypes, }).isRequired, tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx new file mode 100644 index 0000000000..0ec2974d38 --- /dev/null +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +interface FieldMetadataProps { + metadata: { [key: string]: unknown }; + children: React.ReactNode; +} + +/** + * The component which renders field metadata markup + * @param {FieldMetadataProps} props the props of the component + * @returns metadata markup wrapped around children + */ +export const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { + const data = JSON.stringify(props.metadata); + const attributes = { + type: 'text/sitecore', + chrometype: 'field', + className: 'scpm', + }; + const codeOpenAttributes = { ...attributes, kind: 'open' }; + const codeCloseAttributes = { ...attributes, kind: 'close' }; + + return ( + <> + {data} + {props.children} + + + ); +}; + +FieldMetadata.displayName = 'FieldMetadata'; + +FieldMetadata.propTypes = { + metadata: PropTypes.object.isRequired, + children: PropTypes.node.isRequired, +}; diff --git a/packages/sitecore-jss-react/src/components/File.test.tsx b/packages/sitecore-jss-react/src/components/File.test.tsx index 170649732a..4585efe89c 100644 --- a/packages/sitecore-jss-react/src/components/File.test.tsx +++ b/packages/sitecore-jss-react/src/components/File.test.tsx @@ -6,16 +6,16 @@ import { File, FileField } from './File'; describe('', () => { it('should render nothing with missing field', () => { const field = null as FileField; - const rendered = mount(); - expect(rendered.html()).to.equal(''); + const rendered = mount().children(); + expect(rendered).to.have.length(0); }); it('should render nothing with missing value', () => { const field = { editable: 'lorem', }; - const rendered = mount(); - expect(rendered.html()).to.equal(''); + const rendered = mount().children(); + expect(rendered).to.have.length(0); }); it('should render with src directly on provided field', () => { @@ -50,31 +50,4 @@ describe('', () => { expect(rendered.html()).to.contain('id="my-file"'); expect(rendered.html()).to.contain('class="my-css"'); }); - - it('should render field metadata when metadata property is present', () => { - const testMetadata = { - contextItem: { - id: '{09A07660-6834-476C-B93B-584248D3003B}', - language: 'en', - revision: 'a0b36ce0a7db49418edf90eb9621e145', - version: 1, - }, - fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', - fieldType: 'file', - rawValue: 'Test1', - }; - - const field = { - src: '/lorem', - title: 'ipsum', - metadata: testMetadata, - }; - - const rendered = mount(); - console.log(rendered.html()); - // expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); - }); }); diff --git a/packages/sitecore-jss-react/src/components/File.tsx b/packages/sitecore-jss-react/src/components/File.tsx index 8b50a21d66..510688b6b2 100644 --- a/packages/sitecore-jss-react/src/components/File.tsx +++ b/packages/sitecore-jss-react/src/components/File.tsx @@ -1,7 +1,5 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export interface FileFieldValue { [propName: string]: unknown; @@ -17,40 +15,36 @@ export interface FileField { export interface FileProps { [attributeName: string]: unknown; /** The file field data. */ - field: (FileFieldValue | FileField) & { - metadata?: FieldMetadataValue; - }; + field: FileFieldValue | FileField; /** HTML attributes that will be appended to the rendered tag. */ children?: React.ReactNode; } -export const File: React.FC = withFieldMetadata( - ({ field, children, ...otherProps }) => { - /* +export const File: React.FC = ({ field, children, ...otherProps }) => { + /* File fields cannot be managed via the EE. We never output "editable." - */ - - const dynamicField: FileField | FileFieldValue = field; + */ - if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { - return null; - } + const dynamicField: FileField | FileFieldValue = field; - // handle link directly on field for forgetful devs - const file = ((dynamicField as FileFieldValue).src - ? field - : dynamicField.value) as FileFieldValue; - if (!file) { - return null; - } + if (!field || (!dynamicField.value && !(dynamicField as FileFieldValue).src)) { + return null; + } - const linkText = !children ? file.title || file.displayName : null; - const anchorAttrs = { - href: file.src, - }; - return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); + // handle link directly on field for forgetful devs + const file = ((dynamicField as FileFieldValue).src + ? field + : dynamicField.value) as FileFieldValue; + if (!file) { + return null; } -); + + const linkText = !children ? file.title || file.displayName : null; + const anchorAttrs = { + href: file.src, + }; + return React.createElement('a', { ...anchorAttrs, ...otherProps }, linkText, children); +}; File.propTypes = { field: PropTypes.oneOfType([ @@ -59,7 +53,6 @@ File.propTypes = { }), PropTypes.shape({ value: PropTypes.object, - metadata: FieldMetadataPropTypes, }), ]).isRequired, }; diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 345ce17092..c80592cb74 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -3,8 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { addClassName, convertAttributesToReactProps } from '../utils'; import { getAttributesString } from '../utils'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; +import { withFieldMetadata } from '../enhancers/withFieldMetadata'; export interface ImageFieldValue { [attributeName: string]: unknown; @@ -44,7 +43,7 @@ export interface ImageProps { media?: ImageField | ImageFieldValue; /** Image field data (consistent with other field types) */ - field?: (ImageField | ImageFieldValue) & { metadata?: FieldMetadataValue }; + field?: ImageField | ImageFieldValue; /** * Can be used to explicitly disable inline editing. @@ -150,7 +149,7 @@ export const getEEMarkup = ( return getEditableWrapper(editableMarkup); }; -export const Image: React.FC = withFieldMetadata( +export const Image = withFieldMetadata( ({ media, editable, imageParams, field, mediaUrlPrefix, ...otherProps }) => { // allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers) if (field && !media) { @@ -206,7 +205,6 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, - metadata: FieldMetadataPropTypes, }), ]), editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 178581f997..8c508aa9b4 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,7 +1,6 @@ import React, { ReactElement, forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { FieldMetadataPropTypes, withFieldMetadata } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; +import { withFieldMetadata } from '../enhancers/withFieldMetadata'; export interface LinkFieldValue { [attributeName: string]: unknown; @@ -27,9 +26,7 @@ export type LinkProps = React.DetailedHTMLProps< HTMLAnchorElement > & { /** The link field data. */ - field: (LinkField | LinkFieldValue) & { - metadata?: FieldMetadataValue; - }; + field: LinkField | LinkFieldValue; /** * Can be used to explicitly disable inline editing. * If true and `field.editable` has a value, then `field.editable` will be processed and rendered as component output. If false, `field.editable` value will be ignored and not rendered. @@ -143,7 +140,6 @@ export const LinkPropTypes = { value: PropTypes.object, editableFirstPart: PropTypes.string, editableLastPart: PropTypes.string, - metadata: FieldMetadataPropTypes, }), ]).isRequired, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 5d5c0582ab..5043cdc725 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -1,12 +1,10 @@ import React, { forwardRef } from 'react'; import PropTypes from 'prop-types'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; +import { withFieldMetadata } from '../enhancers/withFieldMetadata'; export interface RichTextField { value?: string; editable?: string; - metadata?: FieldMetadataValue; } export interface RichTextProps { @@ -50,7 +48,6 @@ export const RichTextPropTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, - metadata: FieldMetadataPropTypes, }), tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 9f6ab3016c..812fb2c203 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -1,12 +1,10 @@ -import React, { ReactElement, FunctionComponent } from 'react'; -import { withFieldMetadata, FieldMetadataPropTypes } from '../enhancers/withFieldMetadata'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; +import React, { ReactElement } from 'react'; +import { withFieldMetadata } from '../enhancers/withFieldMetadata'; import PropTypes from 'prop-types'; export interface TextField { value?: string | number; editable?: string; - metadata?: FieldMetadataValue; } export interface TextProps { @@ -29,7 +27,7 @@ export interface TextProps { encode?: boolean; } -export const Text: FunctionComponent = withFieldMetadata( +export const Text: React.FC = withFieldMetadata( ({ field, tag, editable, encode, ...otherProps }) => { if (!field || (!field.editable && (field.value === undefined || field.value === ''))) { return null; @@ -100,7 +98,6 @@ Text.propTypes = { field: PropTypes.shape({ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), editable: PropTypes.string, - metadata: FieldMetadataPropTypes, }), tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index cafd4b1d45..72b203cff3 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -1,52 +1,21 @@ import React, { ComponentType, forwardRef } from 'react'; -import { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; -import PropTypes from 'prop-types'; +import { FieldMetadata } from '../components/FieldMetadata'; -interface WithMetadataProps { - field?: { - metadata?: FieldMetadataValue; +interface WithMetadataProps { + field?: Field & { + metadata?: { [key: string]: unknown }; }; editable?: boolean; } -interface FieldMetadataProps { - metadata: FieldMetadataValue; - children: React.ReactNode; -} - -/** - * The component which renders field metadata markup - * @param {FieldMetadataProps} props the props of the component - * @returns metadata markup wrapped around children - */ -const FieldMetadata = (props: FieldMetadataProps): JSX.Element => { - const data = JSON.stringify(props.metadata); - const attributes = { - type: 'text/sitecore', - chrometype: 'field', - className: 'scpm', - }; - const codeOpenAttributes = { ...attributes, kind: 'open' }; - const codeCloseAttributes = { ...attributes, kind: 'close' }; - - return ( - <> - {data} - {props.children} - - - ); -}; - /** * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages * @param {ComponentType} FieldComponent the field component * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ -export function withFieldMetadata( - FieldComponent: ComponentType, - isForwardRef = false -) { +export function withFieldMetadata< + FieldComponentProps extends WithMetadataProps +>(FieldComponent: ComponentType, isForwardRef = false) { if (isForwardRef) { // eslint-disable-next-line react/display-name return forwardRef(({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { @@ -79,15 +48,3 @@ export function withFieldMetadata ); }) as React.FC; } - -export const FieldMetadataPropTypes = PropTypes.shape({ - contextItem: PropTypes.shape({ - id: PropTypes.string, - language: PropTypes.string, - revision: PropTypes.string, - version: PropTypes.number, - }), - fieldId: PropTypes.string, - fieldType: PropTypes.string, - rawValue: PropTypes.string, -}); diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts index 5a58df330f..28c93a48cf 100644 --- a/packages/sitecore-jss-react/src/index.ts +++ b/packages/sitecore-jss-react/src/index.ts @@ -1,5 +1,4 @@ export { constants, enableDebug, ClientError } from '@sitecore-jss/sitecore-jss'; -export { FieldMetadataValue } from '@sitecore-jss/sitecore-jss/layout'; export { isEditorActive, resetEditorChromes, diff --git a/packages/sitecore-jss/src/layout/index.ts b/packages/sitecore-jss/src/layout/index.ts index 0afbfc1c41..8262ececf2 100644 --- a/packages/sitecore-jss/src/layout/index.ts +++ b/packages/sitecore-jss/src/layout/index.ts @@ -16,8 +16,6 @@ export { RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, - FieldMetadataValue, - FieldMetadataContextItem, } from './models'; export { getFieldValue, getChildPlaceholder } from './utils'; diff --git a/packages/sitecore-jss/src/layout/models.ts b/packages/sitecore-jss/src/layout/models.ts index a1c192c688..87657e0613 100644 --- a/packages/sitecore-jss/src/layout/models.ts +++ b/packages/sitecore-jss/src/layout/models.ts @@ -155,22 +155,3 @@ export interface PlaceholderData { path: string; elements: Array; } - -/** The field metadata; should be returned by layout service; - * when present, metadata markup is rendered as a wrapper around every field component - * and is used for chromes hydration during editing in pages - */ -export interface FieldMetadataValue { - contextItem?: FieldMetadataContextItem; - fieldId?: string; - fieldType?: string; - rawValue?: string; -} - -/** The field's context item metadata, a property of the FIeldMetadata interface */ -export interface FieldMetadataContextItem { - id?: string; - language?: string; - revision?: string; - version?: number; -} From 8277ee6b4c38a48745507a5ccb5e8daa6dc2de2b Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 16:47:57 +0300 Subject: [PATCH 44/49] Updated unit tests, simplified types --- .../src/components/Link.test.tsx | 19 +- .../src/components/NextImage.test.tsx | 14 +- .../src/components/NextImage.tsx | 2 +- .../src/components/RichText.test.tsx | 15 ++ .../src/components/Date.test.tsx | 13 +- .../src/components/Date.tsx | 1 + .../src/components/FieldMetadata.test.tsx | 40 ++++ .../src/components/Image.test.tsx | 14 +- .../src/components/Image.tsx | 7 +- .../src/components/Link.test.tsx | 13 +- .../src/components/Link.tsx | 42 ++-- .../src/components/RichText.test.tsx | 10 + .../src/components/RichText.tsx | 3 +- .../src/components/Text.test.tsx | 15 +- .../src/components/Text.tsx | 1 + .../src/enhancers/withFieldMetadata.test.tsx | 194 +++++++++++++----- .../src/enhancers/withFieldMetadata.tsx | 37 ++-- 17 files changed, 324 insertions(+), 116 deletions(-) create mode 100644 packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx diff --git a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx index de095e9714..e271966888 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx @@ -351,15 +351,14 @@ describe('', () => { it('should render nothing with missing field', () => { const field = (null as unknown) as LinkField; - const rendered = mount(); - expect(rendered.html()).to.equal(''); + const rendered = mount().children(); + expect(rendered).to.have.length(0); }); it('should render nothing with missing field', () => { const field = {}; - const rendered = mount(); - expect(rendered.children()).to.have.length(1); - expect(rendered.html()).to.equal(''); + const rendered = mount().children(); + expect(rendered).to.have.length(0); }); it('should render field metadata component when metadata property is present', () => { @@ -390,6 +389,16 @@ describe('', () => { ); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + 'ipsum', + '', + ].join('') + ); + expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx index ba1e13d9ed..cf99249d21 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx @@ -307,10 +307,14 @@ describe('', () => { const rendered = mount(); - expect(rendered.find('code')).to.have.length(2); - expect(rendered.find('img')).to.have.length(1); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + 'my image', + '', + ].join('') + ); }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx index dd1de2e527..c3ffda9a3e 100644 --- a/packages/sitecore-jss-nextjs/src/components/NextImage.tsx +++ b/packages/sitecore-jss-nextjs/src/components/NextImage.tsx @@ -12,7 +12,7 @@ import Image, { ImageProps as NextImageProperties } from 'next/image'; type NextImageProps = ImageProps & Partial; -export const NextImage = withFieldMetadata( +export const NextImage: React.FC = withFieldMetadata( ({ editable, imageParams, field, mediaUrlPrefix, fill, priority, ...otherProps }) => { // next handles src and we use a custom loader, // throw error if these are present diff --git a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx index 9612b0d1de..cca7b9374f 100644 --- a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx @@ -419,6 +419,21 @@ describe('RichText', () => { { attachTo: app } ); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}
+ `, + `
+

Hello!

+ 1 + 2 + Title +
`, + ].join('') + ); + expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('
'); expect(rendered.html()).to.contain('kind="open"'); diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 03c1aad816..0ba2ef9009 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -104,9 +104,14 @@ describe('', () => { const rendered = mount(); - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + '23-11-2001', + '', + ].join('') + ); }); }); diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index c2b1e4ee26..e7d52c7725 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -8,6 +8,7 @@ export interface DateFieldProps { field: { value?: string; editable?: string; + metadata?: { [key: string]: unknown }; }; /** * The HTML element that will wrap the contents of the field. diff --git a/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx new file mode 100644 index 0000000000..dc9b479e48 --- /dev/null +++ b/packages/sitecore-jss-react/src/components/FieldMetadata.test.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { FieldMetadata } from './FieldMetadata'; +import { mount } from 'enzyme'; +import { expect } from 'chai'; + +describe('', () => { + it('should render field metadata', () => { + const props = { + metadata: { + contextItem: { + id: '{09A07660-6834-476C-B93B-584248D3003B}', + language: 'en', + revision: 'a0b36ce0a7db49418edf90eb9621e145', + version: 1, + }, + fieldId: '{414061F4-FBB1-4591-BC37-BFFA67F745EB}', + fieldType: 'date', + rawValue: 'Test1', + }, + }; + + const Foo = () =>

foo

; + + const rendered = mount( + + + + ); + + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + props.metadata + )}`, + '

foo

', + '', + ].join('') + ); + }); +}); diff --git a/packages/sitecore-jss-react/src/components/Image.test.tsx b/packages/sitecore-jss-react/src/components/Image.test.tsx index 9cdb668e71..eb212bb02b 100644 --- a/packages/sitecore-jss-react/src/components/Image.test.tsx +++ b/packages/sitecore-jss-react/src/components/Image.test.tsx @@ -315,10 +315,14 @@ describe('', () => { }; const rendered = mount(); - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain('src="/assets/img/test0.png"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + '', + '', + ].join('') + ); }); }); diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 01d0a8eb58..a6c10d4290 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -37,7 +37,7 @@ export interface ImageSizeParameters { export interface ImageProps { [attributeName: string]: unknown; /** Image field data (consistent with other field types) */ - field?: ImageField | ImageFieldValue; + field?: (ImageField | ImageFieldValue) & { metadata?: { [key: string]: unknown } }; /** * Can be used to explicitly disable inline editing. @@ -168,6 +168,11 @@ export const Image: React.FC = withFieldMetadata( return null; } + // prevent metadata from being passed to the img tag + if (img.metadata) { + delete img.metadata; + } + const attrs = getImageAttrs({ ...img, ...otherProps }, imageParams, mediaUrlPrefix); if (attrs) { return ; diff --git a/packages/sitecore-jss-react/src/components/Link.test.tsx b/packages/sitecore-jss-react/src/components/Link.test.tsx index 1f7c91d230..e9fc1942c4 100644 --- a/packages/sitecore-jss-react/src/components/Link.test.tsx +++ b/packages/sitecore-jss-react/src/components/Link.test.tsx @@ -147,9 +147,14 @@ describe('', () => { }; const rendered = mount(); - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + 'ipsum', + '', + ].join('') + ); }); }); diff --git a/packages/sitecore-jss-react/src/components/Link.tsx b/packages/sitecore-jss-react/src/components/Link.tsx index 8c508aa9b4..9eeeff30e7 100644 --- a/packages/sitecore-jss-react/src/components/Link.tsx +++ b/packages/sitecore-jss-react/src/components/Link.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, forwardRef } from 'react'; +import React, { ReactElement, RefAttributes, forwardRef } from 'react'; import PropTypes from 'prop-types'; import { withFieldMetadata } from '../enhancers/withFieldMetadata'; @@ -21,27 +21,25 @@ export interface LinkField { editableLastPart?: string; } -export type LinkProps = React.DetailedHTMLProps< - React.AnchorHTMLAttributes, - HTMLAnchorElement -> & { - /** The link field data. */ - field: LinkField | LinkFieldValue; - /** - * Can be used to explicitly disable inline editing. - * If true and `field.editable` has a value, then `field.editable` will be processed and rendered as component output. If false, `field.editable` value will be ignored and not rendered. - * @default true - */ - editable?: boolean; - - /** - * Displays a link text ('description' in Sitecore) even when children exist - * NOTE: when in Sitecore Experience Editor, this setting is ignored due to technical limitations, and the description is always rendered. - */ - showLinkTextWithChildrenPresent?: boolean; -}; - -export const Link = withFieldMetadata( +export type LinkProps = React.AnchorHTMLAttributes & + RefAttributes & { + /** The link field data. */ + field: (LinkField | LinkFieldValue) & { metadata?: { [key: string]: unknown } }; + /** + * Can be used to explicitly disable inline editing. + * If true and `field.editable` has a value, then `field.editable` will be processed and rendered as component output. If false, `field.editable` value will be ignored and not rendered. + * @default true + */ + editable?: boolean; + + /** + * Displays a link text ('description' in Sitecore) even when children exist + * NOTE: when in Sitecore Experience Editor, this setting is ignored due to technical limitations, and the description is always rendered. + */ + showLinkTextWithChildrenPresent?: boolean; + }; + +export const Link: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name forwardRef( ({ field, editable, showLinkTextWithChildrenPresent, ...otherProps }, ref) => { diff --git a/packages/sitecore-jss-react/src/components/RichText.test.tsx b/packages/sitecore-jss-react/src/components/RichText.test.tsx index 42d64ba2ef..e76172c0c9 100644 --- a/packages/sitecore-jss-react/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.test.tsx @@ -115,6 +115,16 @@ describe('', () => { const rendered = mount(); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + '
value
', + '', + ].join('') + ); + expect(rendered.find('code')).to.have.length(2); expect(rendered.html()).to.contain('kind="open"'); expect(rendered.html()).to.contain('kind="close"'); diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 5043cdc725..3dd2f0b6df 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -5,6 +5,7 @@ import { withFieldMetadata } from '../enhancers/withFieldMetadata'; export interface RichTextField { value?: string; editable?: string; + metadata?: { [key: string]: unknown }; } export interface RichTextProps { @@ -26,7 +27,7 @@ export interface RichTextProps { export const RichText: React.FC = withFieldMetadata( // eslint-disable-next-line react/display-name - forwardRef(({ field, tag, editable, ...otherProps }, ref) => { + forwardRef(({ field, tag, editable, ...otherProps }, ref) => { if (!field || (!field.editable && !field.value)) { return null; } diff --git a/packages/sitecore-jss-react/src/components/Text.test.tsx b/packages/sitecore-jss-react/src/components/Text.test.tsx index 3f75896a7d..633cd1fbb9 100644 --- a/packages/sitecore-jss-react/src/components/Text.test.tsx +++ b/packages/sitecore-jss-react/src/components/Text.test.tsx @@ -193,7 +193,7 @@ describe('', () => { }; const field = { - editable: eeTextData, + value: 'value', metadata: testMetadata, }; @@ -203,9 +203,14 @@ describe('', () => { ); - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + 'value', + '', + ].join('') + ); }); }); diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index 812fb2c203..d5e07e1060 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; export interface TextField { value?: string | number; editable?: string; + metadata?: { [key: string]: unknown }; } export interface TextProps { diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx index 502f4426b2..52701c72dc 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.test.tsx @@ -1,11 +1,11 @@ /* eslint-disable no-unused-expressions */ -import React from 'react'; +import React, { forwardRef } from 'react'; import { expect } from 'chai'; import { mount } from 'enzyme'; -import { FieldMetadata, withFieldMetadata } from './withFieldMetadata'; +import { withFieldMetadata } from './withFieldMetadata'; import { describe } from 'node:test'; -describe('withFieldMetadataWrapper', () => { +describe('withFieldMetadata', () => { const testMetadata = { contextItem: { id: '{09A07660-6834-476C-B93B-584248D3003B}', @@ -17,94 +17,196 @@ describe('withFieldMetadataWrapper', () => { fieldType: 'single-line', rawValue: 'Test1', }; - const stringifiedData = JSON.stringify(testMetadata); - const TestComponent: React.FC = (props: any) => { + type TestComponentProps = { + field?: { + value?: string; + metadata?: { [key: string]: unknown }; + }; + editable?: boolean; + }; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const TestComponent = (props: TestComponentProps) => { return ( -
+
+

{props.field?.value}

foo

bar

); }; - it('Should return component if field is empty', () => { + // eslint-disable-next-line react/display-name + const TestComponentWithRef = forwardRef( + (props: TestComponentProps, ref: React.ForwardedRef) => { + return ( +
+

{props.field?.value}

+

foo

+

bar

+
+ ); + } + ); + + it('should return component if field is empty', () => { const props = { editable: true, }; - const WrappedComponent = withFieldMetadata((props) => { - return ; - }); + const WrappedComponent = withFieldMetadata(TestComponent); const rendered = mount(); - expect(rendered.find('code')).to.have.length(0); - expect(rendered.find('div')).to.have.length(1); - expect(rendered.html()).to.contain('bar'); + expect(rendered.html()).to.equal('

foo

bar

'); }); - it('Should render unwrapped component if metadata field is not provided', () => { + it('should render unwrapped component if metadata field is not provided', () => { const props = { - field: {}, + field: { + value: 'test', + }, + editable: true, }; - const WrappedComponent = withFieldMetadata((props) => { - return ; - }); + const WrappedComponent = withFieldMetadata(TestComponent); const rendered = mount(); - expect(rendered.find('code')).to.have.length(0); - expect(rendered.find('div')).to.have.length(1); - expect(rendered.html()).to.contain('bar'); + expect(rendered.html()).to.equal('

test

foo

bar

'); }); - it('Should render unwrapped component if metadata is provided but field is not editable', () => { + it('should render unwrapped component if metadata is provided but field is not editable', () => { const props = { field: { + value: 'test', metadata: testMetadata, }, editable: false, }; - const WrappedComponent = withFieldMetadata((props) => { - return ; - }); + const WrappedComponent = withFieldMetadata(TestComponent); const rendered = mount(); - expect(rendered.find('code')).to.have.length(0); - expect(rendered.find('div')).to.have.length(1); - expect(rendered.html()).to.contain('bar'); + expect(rendered.html()).to.equal('

test

foo

bar

'); }); - it('Should wrap field with provided metadata', () => { + it('should wrap field with provided metadata', () => { const props = { field: { + value: 'car', metadata: testMetadata, }, editable: true, }; - const WrappedComponent = withFieldMetadata((props) => { - return ; - }); + const WrappedComponent = withFieldMetadata(TestComponent); const rendered = mount(); - const codeTags = rendered.find('code'); - - expect(codeTags).to.have.length(2); - expect(codeTags.at(0).text()).to.equal(stringifiedData); - expect(codeTags.at(0).props().type).to.equal('text/sitecore'); - expect(codeTags.at(0).html()).to.include('chrometype="field"'); - expect(codeTags.at(0).props().className).to.equal('scpm'); - expect(codeTags.at(0).props().kind).to.equal('open'); - expect(rendered.find('div')).to.have.length(1); - expect(rendered.html()).to.contain('bar'); - expect(codeTags.at(1).props().type).to.equal('text/sitecore'); - expect(codeTags.at(1).html()).to.include('chrometype="field"'); - expect(codeTags.at(1).props().className).to.equal('scpm'); - expect(codeTags.at(1).props().kind).to.equal('close'); + + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + '
', + '

car

', + '

foo

', + '

bar

', + '
', + '', + ].join('') + ); + }); + + describe('with forwardRef', () => { + it('should return component if field is empty', () => { + const props = { + editable: true, + }; + + const ref = React.createRef(); + + const WrappedComponent = withFieldMetadata(TestComponentWithRef, true); + + const rendered = mount(); + + expect(ref.current?.outerHTML).to.equal('

foo

'); + + expect(rendered.html()).to.equal('

foo

bar

'); + }); + + it('should render unwrapped component if metadata field is not provided', () => { + const props = { + field: { + value: 'test', + }, + editable: true, + }; + + const ref = React.createRef(); + + const WrappedComponent = withFieldMetadata(TestComponentWithRef, true); + + const rendered = mount(); + + expect(ref.current?.outerHTML).to.equal('

foo

'); + + expect(rendered.html()).to.equal('

test

foo

bar

'); + }); + + it('should render unwrapped component if metadata is provided but field is not editable', () => { + const props = { + field: { + value: 'test', + metadata: testMetadata, + }, + editable: false, + }; + + const ref = React.createRef(); + + const WrappedComponent = withFieldMetadata(TestComponentWithRef, true); + + const rendered = mount(); + + expect(ref.current?.outerHTML).to.equal('

foo

'); + + expect(rendered.html()).to.equal('

test

foo

bar

'); + }); + + it('should wrap field with provided metadata', () => { + const props = { + field: { + value: 'car', + metadata: testMetadata, + }, + editable: true, + }; + + const ref = React.createRef(); + + const WrappedComponent = withFieldMetadata(TestComponentWithRef, true); + + const rendered = mount(); + + expect(ref.current?.outerHTML).to.equal('

foo

'); + + expect(rendered.html()).to.equal( + [ + `${JSON.stringify( + testMetadata + )}`, + '
', + '

car

', + '

foo

', + '

bar

', + '
', + '', + ].join('') + ); + }); }); }); diff --git a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx index 72b203cff3..5702c3d9d2 100644 --- a/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx +++ b/packages/sitecore-jss-react/src/enhancers/withFieldMetadata.tsx @@ -1,8 +1,8 @@ import React, { ComponentType, forwardRef } from 'react'; import { FieldMetadata } from '../components/FieldMetadata'; -interface WithMetadataProps { - field?: Field & { +interface WithMetadataProps { + field?: { metadata?: { [key: string]: unknown }; }; editable?: boolean; @@ -14,30 +14,33 @@ interface WithMetadataProps { * @param {boolean} isForwardRef set to 'true' if forward reference is needed */ export function withFieldMetadata< - FieldComponentProps extends WithMetadataProps + FieldComponentProps extends WithMetadataProps, + RefElementType = HTMLElement >(FieldComponent: ComponentType, isForwardRef = false) { if (isForwardRef) { // eslint-disable-next-line react/display-name - return forwardRef(({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { - const metadata = props.field?.metadata; + return forwardRef( + ({ ...props }: FieldComponentProps, ref: React.ForwardedRef) => { + const metadata = props.field?.metadata; - if (!metadata || !props?.editable) { - return ; - } + if (!metadata || !props.editable) { + return ; + } - return ( - - - - ); - }); + return ( + + + + ); + } + ); } // eslint-disable-next-line react/display-name - return (({ ...props }: FieldComponentProps) => { + return ({ ...props }: FieldComponentProps) => { const metadata = props.field?.metadata; - if (!metadata || !props?.editable) { + if (!metadata || !props.editable) { return ; } @@ -46,5 +49,5 @@ export function withFieldMetadata< ); - }) as React.FC; + }; } From f8e823294deaca09b5f5a92d985b96a7ba5688bc Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 16:52:46 +0300 Subject: [PATCH 45/49] Update --- packages/sitecore-jss-react/src/components/Date.test.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Date.test.tsx b/packages/sitecore-jss-react/src/components/Date.test.tsx index 0ba2ef9009..b234517d5c 100644 --- a/packages/sitecore-jss-react/src/components/Date.test.tsx +++ b/packages/sitecore-jss-react/src/components/Date.test.tsx @@ -5,13 +5,12 @@ import React from 'react'; import { DateField } from './Date'; describe('', () => { - it('should render nothing is field is missing', () => { + it('should return null if no editable or value', () => { const p = { field: {}, }; const c = shallow(); - console.log(c.type()); expect(c.html()).to.equal(''); }); From 15c9f40a323cd1a0b487bd6f0dab37fc3e6a9153 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 16:53:57 +0300 Subject: [PATCH 46/49] Expose withFieldMetadata as a part of nextjs sdk --- packages/sitecore-jss-nextjs/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index c6fd7ddb35..ebebcc5a15 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -167,4 +167,5 @@ export { ComponentConsumerProps, WithSitecoreContextOptions, WithSitecoreContextProps, + withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react'; From bdbddd8ef02b3a08d96fd389fef6b9995ea965c3 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 17:23:35 +0300 Subject: [PATCH 47/49] Updated PropTypes --- packages/sitecore-jss-react/src/components/Date.tsx | 1 + packages/sitecore-jss-react/src/components/Image.tsx | 1 + packages/sitecore-jss-react/src/components/RichText.tsx | 1 + packages/sitecore-jss-react/src/components/Text.tsx | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/sitecore-jss-react/src/components/Date.tsx b/packages/sitecore-jss-react/src/components/Date.tsx index e7d52c7725..41739729cd 100644 --- a/packages/sitecore-jss-react/src/components/Date.tsx +++ b/packages/sitecore-jss-react/src/components/Date.tsx @@ -60,6 +60,7 @@ DateField.propTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, + metadata: PropTypes.objectOf(PropTypes.any), }).isRequired, tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index a6c10d4290..7742f71947 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -190,6 +190,7 @@ Image.propTypes = { PropTypes.shape({ value: PropTypes.object, editable: PropTypes.string, + metadata: PropTypes.objectOf(PropTypes.any), }), ]), field: PropTypes.oneOfType([ diff --git a/packages/sitecore-jss-react/src/components/RichText.tsx b/packages/sitecore-jss-react/src/components/RichText.tsx index 3dd2f0b6df..b522981a39 100644 --- a/packages/sitecore-jss-react/src/components/RichText.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.tsx @@ -49,6 +49,7 @@ export const RichTextPropTypes = { field: PropTypes.shape({ value: PropTypes.string, editable: PropTypes.string, + metadata: PropTypes.objectOf(PropTypes.any), }), tag: PropTypes.string, editable: PropTypes.bool, diff --git a/packages/sitecore-jss-react/src/components/Text.tsx b/packages/sitecore-jss-react/src/components/Text.tsx index d5e07e1060..47f685f71c 100644 --- a/packages/sitecore-jss-react/src/components/Text.tsx +++ b/packages/sitecore-jss-react/src/components/Text.tsx @@ -99,6 +99,7 @@ Text.propTypes = { field: PropTypes.shape({ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), editable: PropTypes.string, + metadata: PropTypes.objectOf(PropTypes.any), }), tag: PropTypes.string, editable: PropTypes.bool, From c86c0d2ede08dc54c490d8a809c1e094aa85ae99 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 23 Apr 2024 17:48:40 +0300 Subject: [PATCH 48/49] Removed extra asserts --- packages/sitecore-jss-nextjs/src/components/Link.test.tsx | 5 ----- .../sitecore-jss-nextjs/src/components/RichText.test.tsx | 6 ------ .../sitecore-jss-react/src/components/RichText.test.tsx | 5 ----- 3 files changed, 16 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx index e271966888..921c7eb942 100644 --- a/packages/sitecore-jss-nextjs/src/components/Link.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/Link.test.tsx @@ -398,10 +398,5 @@ describe('', () => { '', ].join('') ); - - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx index cca7b9374f..f8879a7dc0 100644 --- a/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-nextjs/src/components/RichText.test.tsx @@ -433,11 +433,5 @@ describe('RichText', () => {
`, ].join('') ); - - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('
'); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); diff --git a/packages/sitecore-jss-react/src/components/RichText.test.tsx b/packages/sitecore-jss-react/src/components/RichText.test.tsx index e76172c0c9..1a741ca318 100644 --- a/packages/sitecore-jss-react/src/components/RichText.test.tsx +++ b/packages/sitecore-jss-react/src/components/RichText.test.tsx @@ -124,10 +124,5 @@ describe('', () => { '', ].join('') ); - - expect(rendered.find('code')).to.have.length(2); - expect(rendered.html()).to.contain('kind="open"'); - expect(rendered.html()).to.contain('kind="close"'); - expect(rendered.html()).to.contain(JSON.stringify(testMetadata)); }); }); From 59cf116cf8e00abf3cfad26168c7833c2117c24a Mon Sep 17 00:00:00 2001 From: yavorsk Date: Wed, 24 Apr 2024 11:03:21 +0300 Subject: [PATCH 49/49] remove media property from propTypes --- packages/sitecore-jss-react/src/components/Image.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/sitecore-jss-react/src/components/Image.tsx b/packages/sitecore-jss-react/src/components/Image.tsx index 7742f71947..c00cb6257c 100644 --- a/packages/sitecore-jss-react/src/components/Image.tsx +++ b/packages/sitecore-jss-react/src/components/Image.tsx @@ -183,16 +183,6 @@ export const Image: React.FC = withFieldMetadata( ); Image.propTypes = { - media: PropTypes.oneOfType([ - PropTypes.shape({ - src: PropTypes.string, - }), - PropTypes.shape({ - value: PropTypes.object, - editable: PropTypes.string, - metadata: PropTypes.objectOf(PropTypes.any), - }), - ]), field: PropTypes.oneOfType([ PropTypes.shape({ src: PropTypes.string,