@sitecore-jss/sitecore-jss-react 22.1.0-canary.9 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE.txt +202 -202
  2. package/README.md +10 -10
  3. package/dist/cjs/components/Date.js +12 -4
  4. package/dist/cjs/components/DefaultEmptyFieldEditingComponents.js +22 -0
  5. package/dist/cjs/components/EditFrame.js +2 -2
  6. package/dist/cjs/components/EditingScripts.js +27 -0
  7. package/dist/cjs/components/ErrorBoundary.js +22 -7
  8. package/dist/cjs/components/FieldMetadata.js +33 -0
  9. package/dist/cjs/components/File.js +2 -1
  10. package/dist/cjs/components/HiddenRendering.js +7 -4
  11. package/dist/cjs/components/Image.js +15 -14
  12. package/dist/cjs/components/Link.js +13 -6
  13. package/dist/cjs/components/Placeholder.js +9 -6
  14. package/dist/cjs/components/PlaceholderCommon.js +69 -21
  15. package/dist/cjs/components/PlaceholderMetadata.js +67 -0
  16. package/dist/cjs/components/RichText.js +14 -3
  17. package/dist/cjs/components/Text.js +12 -4
  18. package/dist/cjs/enhancers/withEmptyFieldEditingComponent.js +56 -0
  19. package/dist/cjs/enhancers/withFieldMetadata.js +60 -0
  20. package/dist/cjs/enhancers/withPlaceholder.js +4 -3
  21. package/dist/cjs/index.js +17 -6
  22. package/dist/esm/components/Date.js +12 -3
  23. package/dist/esm/components/DefaultEmptyFieldEditingComponents.js +14 -0
  24. package/dist/esm/components/EditFrame.js +1 -1
  25. package/dist/esm/components/EditingScripts.js +20 -0
  26. package/dist/esm/components/ErrorBoundary.js +22 -7
  27. package/dist/esm/components/FieldMetadata.js +26 -0
  28. package/dist/esm/components/File.js +2 -1
  29. package/dist/esm/components/HiddenRendering.js +6 -3
  30. package/dist/esm/components/Image.js +15 -13
  31. package/dist/esm/components/Link.js +13 -6
  32. package/dist/esm/components/Placeholder.js +7 -4
  33. package/dist/esm/components/PlaceholderCommon.js +67 -21
  34. package/dist/esm/components/PlaceholderMetadata.js +60 -0
  35. package/dist/esm/components/RichText.js +14 -3
  36. package/dist/esm/components/Text.js +12 -3
  37. package/dist/esm/enhancers/withEmptyFieldEditingComponent.js +29 -0
  38. package/dist/esm/enhancers/withFieldMetadata.js +33 -0
  39. package/dist/esm/enhancers/withPlaceholder.js +4 -3
  40. package/dist/esm/index.js +6 -2
  41. package/package.json +5 -5
  42. package/types/components/Date.d.ts +4 -8
  43. package/types/components/DefaultEmptyFieldEditingComponents.d.ts +4 -0
  44. package/types/components/EditFrame.d.ts +1 -1
  45. package/types/components/EditingScripts.d.ts +5 -0
  46. package/types/components/ErrorBoundary.d.ts +4 -3
  47. package/types/components/FieldMetadata.d.ts +23 -0
  48. package/types/components/HiddenRendering.d.ts +0 -1
  49. package/types/components/Image.d.ts +4 -8
  50. package/types/components/Link.d.ts +7 -10
  51. package/types/components/Placeholder.d.ts +1 -4
  52. package/types/components/PlaceholderCommon.d.ts +22 -4
  53. package/types/components/PlaceholderMetadata.d.ts +30 -0
  54. package/types/components/RichText.d.ts +8 -8
  55. package/types/components/Text.d.ts +6 -10
  56. package/types/components/sharedTypes.d.ts +26 -1
  57. package/types/enhancers/withEmptyFieldEditingComponent.d.ts +28 -0
  58. package/types/enhancers/withFieldMetadata.d.ts +17 -0
  59. package/types/enhancers/withPlaceholder.d.ts +2 -4
  60. package/types/enhancers/withSitecoreContext.d.ts +4 -6
  61. package/types/index.d.ts +9 -4
@@ -12,13 +12,30 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import React from 'react';
13
13
  import PropTypes from 'prop-types';
14
14
  import { MissingComponent } from './MissingComponent';
15
+ import { EditMode, } from '@sitecore-jss/sitecore-jss/layout';
16
+ import { constants } from '@sitecore-jss/sitecore-jss';
15
17
  import { convertAttributesToReactProps } from '../utils';
16
- import { HiddenRendering, HIDDEN_RENDERING_NAME } from './HiddenRendering';
18
+ import { HiddenRendering } from './HiddenRendering';
17
19
  import { FEaaSComponent, FEAAS_COMPONENT_RENDERING_NAME } from './FEaaSComponent';
18
20
  import { FEaaSWrapper, FEAAS_WRAPPER_RENDERING_NAME } from './FEaaSWrapper';
19
21
  import { BYOCComponent, BYOC_COMPONENT_RENDERING_NAME } from './BYOCComponent';
20
22
  import { BYOCWrapper, BYOC_WRAPPER_RENDERING_NAME } from './BYOCWrapper';
23
+ import { PlaceholderMetadata } from './PlaceholderMetadata';
21
24
  import ErrorBoundary from './ErrorBoundary';
25
+ /**
26
+ * Returns a regular expression pattern for a dynamic placeholder name.
27
+ * @param {string} placeholder Placeholder name with a dynamic segment (e.g. 'main-{*}')
28
+ * @returns Regular expression pattern for the dynamic segment
29
+ */
30
+ export const getDynamicPlaceholderPattern = (placeholder) => {
31
+ return new RegExp(`^${placeholder.replace(/\{\*\}+/i, '\\d+')}$`);
32
+ };
33
+ /**
34
+ * Checks if the placeholder name is dynamic.
35
+ * @param {string} placeholder Placeholder name
36
+ * @returns True if the placeholder name is dynamic
37
+ */
38
+ export const isDynamicPlaceholder = (placeholder) => placeholder.indexOf('{*}') !== -1;
22
39
  export class PlaceholderCommon extends React.Component {
23
40
  constructor(props) {
24
41
  super(props);
@@ -28,29 +45,38 @@ export class PlaceholderCommon extends React.Component {
28
45
  this.updateKeyAttributes = this.updateKeyAttributes.bind(this);
29
46
  this.createRawElement = this.createRawElement.bind(this);
30
47
  }
31
- static getPlaceholderDataFromRenderingData(rendering, name) {
48
+ static getPlaceholderDataFromRenderingData(rendering, name, editMode) {
32
49
  let result;
33
- /** [SXA] it needs for deleting dynamics placeholder when we set him number(props.name) of container.
34
- from backend side we get common name of placeholder is called 'nameOfContainer-{*}' where '{*}' marker for replacing **/
50
+ let phName = name.slice();
51
+ /**
52
+ * [Chromes Mode]: [SXA] it needs for deleting dynamics placeholder when we set him number(props.name) of container.
53
+ * from backend side we get common name of placeholder is called 'nameOfContainer-{*}' where '{*}' marker for replacing.
54
+ * [Metadata Mode]: We need to keep the raw placeholder name. e.g 'nameOfContainer-{*}' instead of 'nameOfContainer-1'
55
+ */
35
56
  if (rendering === null || rendering === void 0 ? void 0 : rendering.placeholders) {
36
57
  Object.keys(rendering.placeholders).forEach((placeholder) => {
37
- const patternPlaceholder = placeholder.indexOf('{*}') !== -1
38
- ? new RegExp(`^${placeholder.replace(/\{\*\}+/i, '\\d+')}$`)
58
+ const patternPlaceholder = isDynamicPlaceholder(placeholder)
59
+ ? getDynamicPlaceholderPattern(placeholder)
39
60
  : null;
40
- if (patternPlaceholder && patternPlaceholder.test(name)) {
41
- rendering.placeholders[name] = rendering.placeholders[placeholder];
42
- delete rendering.placeholders[placeholder];
61
+ if (patternPlaceholder && patternPlaceholder.test(phName)) {
62
+ if (editMode === EditMode.Metadata) {
63
+ phName = placeholder;
64
+ }
65
+ else {
66
+ rendering.placeholders[phName] = rendering.placeholders[placeholder];
67
+ delete rendering.placeholders[placeholder];
68
+ }
43
69
  }
44
70
  });
45
71
  }
46
72
  if (rendering && rendering.placeholders && Object.keys(rendering.placeholders).length > 0) {
47
- result = rendering.placeholders[name];
73
+ result = rendering.placeholders[phName];
48
74
  }
49
75
  else {
50
76
  result = null;
51
77
  }
52
78
  if (!result) {
53
- console.warn(`Placeholder '${name}' was not found in the current rendering data`, JSON.stringify(rendering, null, 2));
79
+ console.warn(`Placeholder '${phName}' was not found in the current rendering data`, JSON.stringify(rendering, null, 2));
54
80
  return [];
55
81
  }
56
82
  return result;
@@ -72,13 +98,16 @@ export class PlaceholderCommon extends React.Component {
72
98
  });
73
99
  }
74
100
  getComponentsForRenderingData(placeholderData) {
75
- const _a = this.props, { name, fields: placeholderFields, params: placeholderParams, missingComponentComponent, hiddenRenderingComponent } = _a, placeholderProps = __rest(_a, ["name", "fields", "params", "missingComponentComponent", "hiddenRenderingComponent"]);
76
- return placeholderData
101
+ var _a;
102
+ const _b = this.props, { name, fields: placeholderFields, params: placeholderParams, missingComponentComponent, hiddenRenderingComponent } = _b, placeholderProps = __rest(_b, ["name", "fields", "params", "missingComponentComponent", "hiddenRenderingComponent"]);
103
+ const transformedComponents = placeholderData
77
104
  .map((rendering, index) => {
105
+ var _a, _b;
78
106
  const key = rendering.uid
79
107
  ? rendering.uid
80
108
  : `component-${index}`;
81
109
  const commonProps = { key };
110
+ let isEmpty = false;
82
111
  // if the element is not a 'component rendering', render it 'raw'
83
112
  if (!rendering.componentName &&
84
113
  rendering.name) {
@@ -86,11 +115,13 @@ export class PlaceholderCommon extends React.Component {
86
115
  }
87
116
  const componentRendering = rendering;
88
117
  let component;
89
- if (componentRendering.componentName === HIDDEN_RENDERING_NAME) {
118
+ if (componentRendering.componentName === constants.HIDDEN_RENDERING_NAME) {
90
119
  component = hiddenRenderingComponent !== null && hiddenRenderingComponent !== void 0 ? hiddenRenderingComponent : HiddenRendering;
120
+ isEmpty = true;
91
121
  }
92
122
  else if (!componentRendering.componentName) {
93
123
  component = () => React.createElement(React.Fragment, null);
124
+ isEmpty = true;
94
125
  }
95
126
  else {
96
127
  component = this.getComponentForRendering(componentRendering);
@@ -113,20 +144,34 @@ export class PlaceholderCommon extends React.Component {
113
144
  if (!component) {
114
145
  console.error(`Placeholder ${name} contains unknown component ${componentRendering.componentName}. Ensure that a React component exists for it, and that it is registered in your componentFactory.js.`);
115
146
  component = missingComponentComponent !== null && missingComponentComponent !== void 0 ? missingComponentComponent : MissingComponent;
147
+ isEmpty = true;
116
148
  }
117
149
  const finalProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, commonProps), placeholderProps), ((placeholderFields || componentRendering.fields) && {
118
150
  fields: Object.assign(Object.assign({}, placeholderFields), componentRendering.fields),
119
151
  })), ((placeholderParams || componentRendering.params) && {
120
152
  params: Object.assign(Object.assign(Object.assign({}, placeholderParams), componentRendering.params), this.getSXAParams(componentRendering)),
121
153
  })), { rendering: componentRendering });
122
- return React.createElement(component, this.props.modifyComponentProps ? this.props.modifyComponentProps(finalProps) : finalProps);
154
+ let rendered = React.createElement(component, this.props.modifyComponentProps ? this.props.modifyComponentProps(finalProps) : finalProps);
155
+ if (!isEmpty) {
156
+ // assign type based on passed element - type='text/sitecore' should be ignored when renderEach Placeholder prop function is being used
157
+ const type = rendered.props.type === 'text/sitecore' ? rendered.props.type : '';
158
+ // wrapping with error boundary could cause problems in case where parent component uses withPlaceholder HOC and tries to access its children props
159
+ // that's why we need to expose element's props here
160
+ rendered = (React.createElement(ErrorBoundary, Object.assign({ key: rendered.type + '-' + index, errorComponent: this.props.errorComponent, componentLoadingMessage: this.props.componentLoadingMessage, type: type, isDynamic: ((_a = component.render) === null || _a === void 0 ? void 0 : _a.preload) ? true : false }, rendered.props), rendered));
161
+ }
162
+ // if editMode is equal to 'metadata' then emit shallow chromes for hydration in Pages
163
+ if (((_b = this.props.sitecoreContext) === null || _b === void 0 ? void 0 : _b.editMode) === EditMode.Metadata) {
164
+ return (React.createElement(PlaceholderMetadata, { key: key, rendering: rendering }, rendered));
165
+ }
166
+ return rendered;
123
167
  })
124
- .filter((element) => element)
125
- .map((element, id) => {
126
- // assign type based on passed element - type='text/sitecore' should be ignored when renderEach Placeholder prop function is being used
127
- const type = element.props.type === 'text/sitecore' ? element.props.type : '';
128
- return (React.createElement(ErrorBoundary, { key: element.type + '-' + id, customErrorComponent: this.props.errorComponent, rendering: element.props.rendering, componentLoadingMessage: this.props.componentLoadingMessage, type: type }, element));
129
- });
168
+ .filter((element) => element); // remove nulls
169
+ if (((_a = this.props.sitecoreContext) === null || _a === void 0 ? void 0 : _a.editMode) === EditMode.Metadata) {
170
+ return [
171
+ React.createElement(PlaceholderMetadata, { key: this.props.rendering.uid, placeholderName: name, rendering: this.props.rendering }, transformedComponents),
172
+ ];
173
+ }
174
+ return transformedComponents;
130
175
  }
131
176
  getComponentForRendering(renderingDefinition) {
132
177
  var _a;
@@ -201,4 +246,5 @@ PlaceholderCommon.propTypes = {
201
246
  PropTypes.func,
202
247
  ]),
203
248
  modifyComponentProps: PropTypes.func,
249
+ sitecoreContext: PropTypes.object,
204
250
  };
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { getDynamicPlaceholderPattern, isDynamicPlaceholder } from './PlaceholderCommon';
3
+ /**
4
+ * default value of uid for root placeholder when uid is not present.
5
+ */
6
+ const DEFAULT_PLACEHOLDER_UID = '00000000-0000-0000-0000-000000000000';
7
+ /**
8
+ * A React component to generate metadata blocks for a placeholder or rendering.
9
+ * It utilizes dynamic attributes based on whether the component acts as a placeholder
10
+ * or as a rendering to properly render the surrounding code blocks.
11
+ *
12
+ * @param {object} props The properties passed to the component.
13
+ * @param {ComponentRendering} props.rendering The rendering data.
14
+ * @param {string} [props.placeholderName] The name of the placeholder.
15
+ * @param {JSX.Element} props.children The child components or elements to be wrapped by the metadata code blocks.
16
+ * @returns {JSX.Element} A React fragment containing open and close code blocks surrounding the children elements.
17
+ */
18
+ export const PlaceholderMetadata = ({ rendering, placeholderName, children, }) => {
19
+ const getCodeBlockAttributes = (kind, id, placeholderName) => {
20
+ const chrometype = placeholderName ? 'placeholder' : 'rendering';
21
+ const attributes = {
22
+ type: 'text/sitecore',
23
+ chrometype: chrometype,
24
+ className: 'scpm',
25
+ kind: kind,
26
+ };
27
+ if (kind === 'open') {
28
+ if (chrometype === 'placeholder' && placeholderName) {
29
+ let phId = '';
30
+ for (const placeholder of Object.keys(rendering.placeholders)) {
31
+ if (placeholderName === placeholder) {
32
+ phId = id
33
+ ? `${placeholderName}_${id}`
34
+ : `${placeholderName}_${DEFAULT_PLACEHOLDER_UID}`;
35
+ break;
36
+ }
37
+ // Check if the placeholder is a dynamic placeholder
38
+ if (isDynamicPlaceholder(placeholder)) {
39
+ const pattern = getDynamicPlaceholderPattern(placeholder);
40
+ // Check if the placeholder matches the dynamic placeholder pattern
41
+ if (pattern.test(placeholderName)) {
42
+ phId = id ? `${placeholder}_${id}` : `${placeholder}_${DEFAULT_PLACEHOLDER_UID}`;
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ attributes.id = phId;
48
+ }
49
+ else {
50
+ attributes.id = id;
51
+ }
52
+ }
53
+ return attributes;
54
+ };
55
+ const renderComponent = (uid, placeholderName) => (React.createElement(React.Fragment, null,
56
+ React.createElement("code", Object.assign({}, getCodeBlockAttributes('open', uid, placeholderName))),
57
+ children,
58
+ React.createElement("code", Object.assign({}, getCodeBlockAttributes('close', uid, placeholderName)))));
59
+ return React.createElement(React.Fragment, null, renderComponent(rendering.uid, placeholderName));
60
+ };
@@ -11,23 +11,34 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import React, { forwardRef } from 'react';
13
13
  import PropTypes from 'prop-types';
14
- export const RichText = forwardRef((_a, ref) => {
14
+ import { withFieldMetadata } from '../enhancers/withFieldMetadata';
15
+ import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
16
+ import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
17
+ import { isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';
18
+ export const RichText = withFieldMetadata(withEmptyFieldEditingComponent(
19
+ // eslint-disable-next-line react/display-name
20
+ forwardRef((_a, ref) => {
15
21
  var { field, tag = 'div', editable = true } = _a, otherProps = __rest(_a, ["field", "tag", "editable"]);
16
- if (!field || (!field.editable && !field.value)) {
22
+ if (!field || (!field.editable && isFieldValueEmpty(field))) {
17
23
  return null;
18
24
  }
19
25
  const htmlProps = Object.assign({ dangerouslySetInnerHTML: {
20
26
  __html: field.editable && editable ? field.editable : field.value,
21
27
  }, ref }, otherProps);
22
28
  return React.createElement(tag || 'div', htmlProps);
23
- });
29
+ }), { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText, isForwardRef: true }), true);
24
30
  export const RichTextPropTypes = {
25
31
  field: PropTypes.shape({
26
32
  value: PropTypes.string,
27
33
  editable: PropTypes.string,
34
+ metadata: PropTypes.objectOf(PropTypes.any),
28
35
  }),
29
36
  tag: PropTypes.string,
30
37
  editable: PropTypes.bool,
38
+ emptyFieldEditingComponent: PropTypes.oneOfType([
39
+ PropTypes.object,
40
+ PropTypes.func,
41
+ ]),
31
42
  };
32
43
  RichText.propTypes = RichTextPropTypes;
33
44
  RichText.displayName = 'RichText';
@@ -10,10 +10,14 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import React from 'react';
13
+ import { withFieldMetadata } from '../enhancers/withFieldMetadata';
14
+ import { withEmptyFieldEditingComponent } from '../enhancers/withEmptyFieldEditingComponent';
15
+ import { DefaultEmptyFieldEditingComponentText } from './DefaultEmptyFieldEditingComponents';
13
16
  import PropTypes from 'prop-types';
14
- export const Text = (_a) => {
17
+ import { isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';
18
+ export const Text = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
15
19
  var { field, tag, editable = true, encode = true } = _a, otherProps = __rest(_a, ["field", "tag", "editable", "encode"]);
16
- if (!field || (!field.editable && (field.value === undefined || field.value === ''))) {
20
+ if (!field || (!field.editable && isFieldValueEmpty(field))) {
17
21
  return null;
18
22
  }
19
23
  // can't use editable value if we want to output unencoded
@@ -59,14 +63,19 @@ export const Text = (_a) => {
59
63
  else {
60
64
  return React.createElement(React.Fragment, null, children);
61
65
  }
62
- };
66
+ }, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentText }));
63
67
  Text.propTypes = {
64
68
  field: PropTypes.shape({
65
69
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
66
70
  editable: PropTypes.string,
71
+ metadata: PropTypes.objectOf(PropTypes.any),
67
72
  }),
68
73
  tag: PropTypes.string,
69
74
  editable: PropTypes.bool,
70
75
  encode: PropTypes.bool,
76
+ emptyFieldEditingComponent: PropTypes.oneOfType([
77
+ PropTypes.object,
78
+ PropTypes.func,
79
+ ]),
71
80
  };
72
81
  Text.displayName = 'Text';
@@ -0,0 +1,29 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { isFieldValueEmpty, } from '@sitecore-jss/sitecore-jss/layout';
3
+ /**
4
+ * Returns the passed field component or default component in case field value is empty and edit mode is 'metadata'
5
+ * @param {ComponentType<FieldComponentProps>} FieldComponent the field component
6
+ * @param {WithEmptyFieldEditingComponentProps} options the options of the HOC;
7
+ */
8
+ export function withEmptyFieldEditingComponent(FieldComponent, options) {
9
+ const getEmptyFieldEditingComponent = (props) => {
10
+ var _a;
11
+ const { editable = true } = props;
12
+ if (((_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata) && editable && isFieldValueEmpty(props.field)) {
13
+ return props.emptyFieldEditingComponent || options.defaultEmptyFieldEditingComponent;
14
+ }
15
+ return null;
16
+ };
17
+ if (options.isForwardRef) {
18
+ // eslint-disable-next-line react/display-name
19
+ return forwardRef((props, ref) => {
20
+ const EmptyFieldEditingComponent = getEmptyFieldEditingComponent(props);
21
+ return (React.createElement(React.Fragment, null, (EmptyFieldEditingComponent && React.createElement(EmptyFieldEditingComponent, null)) || (React.createElement(FieldComponent, Object.assign({}, props, { ref: ref })))));
22
+ });
23
+ }
24
+ // eslint-disable-next-line react/display-name
25
+ return (props) => {
26
+ const EmptyFieldEditingComponent = getEmptyFieldEditingComponent(props);
27
+ return (React.createElement(React.Fragment, null, (EmptyFieldEditingComponent && React.createElement(EmptyFieldEditingComponent, null)) || (React.createElement(FieldComponent, Object.assign({}, props)))));
28
+ };
29
+ }
@@ -0,0 +1,33 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { FieldMetadata } from '../components/FieldMetadata';
3
+ /**
4
+ * Wraps the field component with metadata markup intended to be used for chromes hydration in Pages
5
+ * @param {ComponentType<FieldComponentProps>} FieldComponent the field component
6
+ * @param {boolean} isForwardRef set to 'true' if forward reference is needed
7
+ */
8
+ export function withFieldMetadata(FieldComponent, isForwardRef = false) {
9
+ if (isForwardRef) {
10
+ // eslint-disable-next-line react/display-name
11
+ return forwardRef((props, ref) => {
12
+ var _a;
13
+ const { editable = true } = props;
14
+ const metadata = (_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata;
15
+ if (!metadata || !editable) {
16
+ return React.createElement(FieldComponent, Object.assign({}, props, { ref: ref }));
17
+ }
18
+ return (React.createElement(FieldMetadata, { metadata: metadata },
19
+ React.createElement(FieldComponent, Object.assign({}, props, { ref: ref }))));
20
+ });
21
+ }
22
+ // eslint-disable-next-line react/display-name
23
+ return (props) => {
24
+ var _a;
25
+ const { editable = true } = props;
26
+ const metadata = (_a = props.field) === null || _a === void 0 ? void 0 : _a.metadata;
27
+ if (!metadata || !editable) {
28
+ return React.createElement(FieldComponent, Object.assign({}, props));
29
+ }
30
+ return (React.createElement(FieldMetadata, { metadata: metadata },
31
+ React.createElement(FieldComponent, Object.assign({}, props))));
32
+ };
33
+ }
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { PlaceholderCommon } from '../components/PlaceholderCommon';
3
3
  import { withComponentFactory } from './withComponentFactory';
4
+ import { withSitecoreContext } from './withSitecoreContext';
4
5
  /**
5
6
  * @param {WithPlaceholderSpec} placeholders
6
7
  * @param {WithPlaceholderOptions} [options]
@@ -35,13 +36,13 @@ export function withPlaceholder(placeholders, options) {
35
36
  definitelyArrayPlacholders.forEach((placeholder) => {
36
37
  let placeholderData;
37
38
  if (typeof placeholder !== 'string' && placeholder.placeholder && placeholder.prop) {
38
- placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder.placeholder);
39
+ placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder.placeholder, childProps.sitecoreContext.editMode);
39
40
  if (placeholderData) {
40
41
  childProps[placeholder.prop] = this.getComponentsForRenderingData(placeholderData);
41
42
  }
42
43
  }
43
44
  else {
44
- placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder);
45
+ placeholderData = PlaceholderCommon.getPlaceholderDataFromRenderingData(renderingData, placeholder, childProps.sitecoreContext.editMode);
45
46
  if (placeholderData) {
46
47
  childProps[placeholder] = this.getComponentsForRenderingData(placeholderData);
47
48
  }
@@ -51,6 +52,6 @@ export function withPlaceholder(placeholders, options) {
51
52
  }
52
53
  }
53
54
  WithPlaceholder.propTypes = PlaceholderCommon.propTypes;
54
- return withComponentFactory(WithPlaceholder);
55
+ return withSitecoreContext()(withComponentFactory(WithPlaceholder));
55
56
  };
56
57
  }
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { constants, enableDebug, ClientError } from '@sitecore-jss/sitecore-jss';
2
- export { isEditorActive, resetEditorChromes, DefaultEditFrameButton, DefaultEditFrameButtons, } from '@sitecore-jss/sitecore-jss/utils';
3
- export { getContentStylesheetLink, getComponentLibraryStylesheetLinks, LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, } from '@sitecore-jss/sitecore-jss/layout';
2
+ export { isEditorActive, resetEditorChromes, DefaultEditFrameButton, DefaultEditFrameButtons, } from '@sitecore-jss/sitecore-jss/editing';
3
+ export { getContentStylesheetLink, getComponentLibraryStylesheetLinks, LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, EditMode, } from '@sitecore-jss/sitecore-jss/layout';
4
4
  export { trackingApi, } from '@sitecore-jss/sitecore-jss/tracking';
5
5
  export { GraphQLDictionaryService, RestDictionaryService, } from '@sitecore-jss/sitecore-jss/i18n';
6
6
  export { DefaultRetryStrategy, GraphQLRequestClient, } from '@sitecore-jss/sitecore-jss/graphql';
@@ -24,3 +24,7 @@ export { withPlaceholder } from './enhancers/withPlaceholder';
24
24
  export { withDatasourceCheck } from './enhancers/withDatasourceCheck';
25
25
  export { EditFrame } from './components/EditFrame';
26
26
  export { ComponentBuilder } from './ComponentBuilder';
27
+ export { withFieldMetadata } from './enhancers/withFieldMetadata';
28
+ export { withEmptyFieldEditingComponent } from './enhancers/withEmptyFieldEditingComponent';
29
+ export { EditingScripts } from './components/EditingScripts';
30
+ export { DefaultEmptyFieldEditingComponentText, DefaultEmptyFieldEditingComponentImage, } from './components/DefaultEmptyFieldEditingComponents';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-jss/sitecore-jss-react",
3
- "version": "22.1.0-canary.9",
3
+ "version": "22.1.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -14,7 +14,7 @@
14
14
  "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-react src/index.ts --githubPages false"
15
15
  },
16
16
  "engines": {
17
- "node": ">=18"
17
+ "node": ">=20"
18
18
  },
19
19
  "author": {
20
20
  "name": "Sitecore Corporation",
@@ -31,7 +31,7 @@
31
31
  "@types/chai-string": "^1.4.2",
32
32
  "@types/enzyme": "^3.10.12",
33
33
  "@types/mocha": "^10.0.1",
34
- "@types/node": "18.11.10",
34
+ "@types/node": "20.14.2",
35
35
  "@types/prop-types": "^15.7.5",
36
36
  "@types/react": "^18.2.22",
37
37
  "@types/react-dom": "^18.0.10",
@@ -61,14 +61,14 @@
61
61
  "react-dom": "^18.2.0"
62
62
  },
63
63
  "dependencies": {
64
- "@sitecore-jss/sitecore-jss": "^22.1.0-canary.9",
64
+ "@sitecore-jss/sitecore-jss": "22.1.0",
65
65
  "fast-deep-equal": "^3.1.3",
66
66
  "prop-types": "^15.8.1",
67
67
  "style-attr": "^1.3.0"
68
68
  },
69
69
  "description": "",
70
70
  "types": "types/index.d.ts",
71
- "gitHead": "9b249f8d03fb9d74e74f74f3040fc2668137bdcc",
71
+ "gitHead": "8f6fa5709f6b306bf40fb2b0775ae6c5ac4182e7",
72
72
  "files": [
73
73
  "dist",
74
74
  "types"
@@ -1,9 +1,11 @@
1
1
  /// <reference types="@types/react" />
2
2
  import React from 'react';
3
- export interface DateFieldProps {
3
+ import { EditableFieldProps } from './sharedTypes';
4
+ import { FieldMetadata } from '@sitecore-jss/sitecore-jss/layout';
5
+ export interface DateFieldProps extends EditableFieldProps {
4
6
  /** The date field data. */
5
7
  [htmlAttributes: string]: unknown;
6
- field: {
8
+ field: FieldMetadata & {
7
9
  value?: string;
8
10
  editable?: string;
9
11
  };
@@ -11,12 +13,6 @@ export interface DateFieldProps {
11
13
  * The HTML element that will wrap the contents of the field.
12
14
  */
13
15
  tag?: string;
14
- /**
15
- * Can be used to explicitly disable inline editing.
16
- * 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.
17
- * @default true
18
- */
19
- editable?: boolean;
20
16
  render?: (date: Date | null) => React.ReactNode;
21
17
  }
22
18
  export declare const DateField: React.FC<DateFieldProps>;
@@ -0,0 +1,4 @@
1
+ /// <reference types="@types/react" />
2
+ import React from 'react';
3
+ export declare const DefaultEmptyFieldEditingComponentText: React.FC;
4
+ export declare const DefaultEmptyFieldEditingComponentImage: React.FC;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@types/react" />
2
2
  import React, { PropsWithChildren } from 'react';
3
- import { EditFrameDataSource, EditButtonTypes } from '@sitecore-jss/sitecore-jss/utils';
3
+ import { EditFrameDataSource, EditButtonTypes } from '@sitecore-jss/sitecore-jss/editing';
4
4
  export interface EditFrameProps {
5
5
  dataSource?: EditFrameDataSource;
6
6
  buttons?: EditButtonTypes[];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Renders client scripts and data for editing/preview mode in Pages.
3
+ * This script is only rendered when EditMode is Metadata, otherwise it renders nothing.
4
+ */
5
+ export declare const EditingScripts: () => JSX.Element;
@@ -2,16 +2,17 @@
2
2
  import React, { ReactNode } from 'react';
3
3
  import { ComponentRendering } from '@sitecore-jss/sitecore-jss/layout';
4
4
  import { SitecoreContextValue } from './SitecoreContext';
5
- type CustomErrorComponentProps = {
5
+ type ErrorComponentProps = {
6
6
  [prop: string]: unknown;
7
7
  };
8
8
  export type ErrorBoundaryProps = {
9
9
  children: ReactNode;
10
10
  sitecoreContext: SitecoreContextValue;
11
11
  type: string;
12
- customErrorComponent?: React.ComponentClass<CustomErrorComponentProps> | React.FC<CustomErrorComponentProps>;
12
+ isDynamic?: boolean;
13
+ errorComponent?: React.ComponentClass<ErrorComponentProps> | React.FC<ErrorComponentProps>;
13
14
  rendering?: ComponentRendering;
14
15
  componentLoadingMessage?: string;
15
16
  };
16
- declare const _default: (props: import("../enhancers/withSitecoreContext").WithSitecoreContextHocProps<ErrorBoundaryProps>) => React.JSX.Element;
17
+ declare const _default: (props: import("@sitecore-jss/sitecore-jss/utils").EnhancedOmit<ErrorBoundaryProps, keyof import("../enhancers/withSitecoreContext").WithSitecoreContextProps>) => React.JSX.Element;
17
18
  export default _default;
@@ -0,0 +1,23 @@
1
+ /// <reference types="@types/react" />
2
+ import React from 'react';
3
+ import PropTypes from 'prop-types';
4
+ interface FieldMetadataProps {
5
+ metadata: {
6
+ [key: string]: unknown;
7
+ };
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * The component which renders field metadata markup
12
+ * @param {FieldMetadataProps} props the props of the component
13
+ * @returns metadata markup wrapped around children
14
+ */
15
+ export declare const FieldMetadata: {
16
+ (props: FieldMetadataProps): JSX.Element;
17
+ displayName: string;
18
+ propTypes: {
19
+ metadata: PropTypes.Validator<object>;
20
+ children: PropTypes.Validator<NonNullable<PropTypes.ReactNodeLike>>;
21
+ };
22
+ };
23
+ export {};
@@ -1,4 +1,3 @@
1
1
  /// <reference types="@types/react" />
2
2
  import React from 'react';
3
3
  export declare const HiddenRendering: () => React.JSX.Element;
4
- export declare const HIDDEN_RENDERING_NAME = "Hidden Rendering";
@@ -1,5 +1,7 @@
1
1
  /// <reference types="@types/react" />
2
2
  import React from 'react';
3
+ import { EditableFieldProps } from './sharedTypes';
4
+ import { FieldMetadata } from '@sitecore-jss/sitecore-jss/layout';
3
5
  export interface ImageFieldValue {
4
6
  [attributeName: string]: unknown;
5
7
  src?: string;
@@ -25,16 +27,10 @@ export interface ImageSizeParameters {
25
27
  /** Image scale. Defaults to 1.0 */
26
28
  sc?: number;
27
29
  }
28
- export interface ImageProps {
30
+ export interface ImageProps extends EditableFieldProps {
29
31
  [attributeName: string]: unknown;
30
32
  /** Image field data (consistent with other field types) */
31
- field?: ImageField | ImageFieldValue;
32
- /**
33
- * Can be used to explicitly disable inline editing.
34
- * If true and `media.editable` has a value, then `media.editable` will be processed
35
- * and rendered as component output. If false, `media.editable` value will be ignored and not rendered.
36
- */
37
- editable?: boolean;
33
+ field?: (ImageField | ImageFieldValue) & FieldMetadata;
38
34
  /**
39
35
  * Parameters that will be attached to Sitecore media URLs
40
36
  */
@@ -1,6 +1,8 @@
1
1
  /// <reference types="@types/react" />
2
- import React from 'react';
2
+ import React, { RefAttributes } from 'react';
3
3
  import PropTypes from 'prop-types';
4
+ import { EditableFieldProps } from './sharedTypes';
5
+ import { FieldMetadata } from '@sitecore-jss/sitecore-jss/layout';
4
6
  export interface LinkFieldValue {
5
7
  [attributeName: string]: unknown;
6
8
  href?: string;
@@ -18,22 +20,16 @@ export interface LinkField {
18
20
  editableFirstPart?: string;
19
21
  editableLastPart?: string;
20
22
  }
21
- export type LinkProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement> & {
23
+ export type LinkProps = EditableFieldProps & React.AnchorHTMLAttributes<HTMLAnchorElement> & RefAttributes<HTMLAnchorElement> & {
22
24
  /** The link field data. */
23
- field: LinkField | LinkFieldValue;
24
- /**
25
- * Can be used to explicitly disable inline editing.
26
- * 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.
27
- * @default true
28
- */
29
- editable?: boolean;
25
+ field: (LinkField | LinkFieldValue) & FieldMetadata;
30
26
  /**
31
27
  * Displays a link text ('description' in Sitecore) even when children exist
32
28
  * NOTE: when in Sitecore Experience Editor, this setting is ignored due to technical limitations, and the description is always rendered.
33
29
  */
34
30
  showLinkTextWithChildrenPresent?: boolean;
35
31
  };
36
- export declare const Link: React.ForwardRefExoticComponent<Omit<LinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement>>;
32
+ export declare const Link: React.FC<LinkProps>;
37
33
  export declare const LinkPropTypes: {
38
34
  field: PropTypes.Validator<NonNullable<NonNullable<PropTypes.InferProps<{
39
35
  href: PropTypes.Requireable<any>;
@@ -44,4 +40,5 @@ export declare const LinkPropTypes: {
44
40
  }>>>>;
45
41
  editable: PropTypes.Requireable<boolean>;
46
42
  showLinkTextWithChildrenPresent: PropTypes.Requireable<boolean>;
43
+ emptyFieldEditingComponent: PropTypes.Requireable<NonNullable<React.ComponentClass<unknown, any> | React.FC<unknown>>>;
47
44
  };
@@ -19,7 +19,4 @@ export interface PlaceholderComponentProps extends PlaceholderProps {
19
19
  */
20
20
  renderEach?: (component: React.ReactNode, index: number) => React.ReactNode;
21
21
  }
22
- export declare const Placeholder: {
23
- (props: PlaceholderComponentProps): JSX.Element;
24
- displayName: string;
25
- };
22
+ export declare const Placeholder: (props: import("@sitecore-jss/sitecore-jss/utils").EnhancedOmit<PlaceholderComponentProps, keyof import("../enhancers/withSitecoreContext").WithSitecoreContextProps>) => React.JSX.Element;