@griddo/ax 1.67.8 → 1.68.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 (124) hide show
  1. package/config/jest/componentsMock.js +1528 -27
  2. package/package.json +2 -2
  3. package/src/__mocks__/reducers/app.tsx +10 -0
  4. package/src/__mocks__/reducers/sites.tsx +10 -0
  5. package/src/__tests__/{AnalyticsField.test.tsx → components/Fields/AnalyticsField/AnalyticsField.test.tsx} +5 -5
  6. package/src/__tests__/{PageAnalytics.test.tsx → components/Fields/AnalyticsField/PageAnalytics/PageAnalytics.test.tsx} +2 -2
  7. package/src/__tests__/{StructuredDataAnalytics.test.tsx → components/Fields/AnalyticsField/StructuredDataAnalytics/StructuredDataAnalytics.test.tsx} +2 -2
  8. package/src/__tests__/{ArrayFieldGroup.test.tsx → components/Fields/ArrayFieldGroup/ArrayFieldGroup.test.tsx} +2 -2
  9. package/src/__tests__/{AsyncCheckGroup.test.tsx → components/Fields/AsyncCheckGroup/AsyncCheckGroup.test.tsx} +2 -2
  10. package/src/__tests__/{AsyncSelect.test.tsx → components/Fields/AsyncSelect/AsyncSelect.test.tsx} +2 -2
  11. package/src/__tests__/{CheckField.test.tsx → components/Fields/CheckField/CheckField.test.tsx} +2 -2
  12. package/src/__tests__/{CheckGroup.test.tsx → components/Fields/CheckGroup/CheckGroup.test.tsx} +2 -2
  13. package/src/__tests__/components/Fields/ColorPicker/ColorPicker.test.tsx +195 -0
  14. package/src/__tests__/components/Fields/ComponentArray/ComponentArray.test.tsx +184 -0
  15. package/src/__tests__/components/Fields/ComponentArray/MixableComponentArray/MixableComponentArray.test.tsx +315 -0
  16. package/src/__tests__/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/PasteModuleButton.test.tsx +95 -0
  17. package/src/__tests__/components/Fields/ComponentArray/SameComponentArray/SameComponentArray.test.tsx +225 -0
  18. package/src/__tests__/{FieldGroup.test.tsx → components/Fields/FieldGroup/FieldGroup.test.tsx} +2 -2
  19. package/src/__tests__/components/Fields/FieldsDivider/FieldsDivider.test.tsx +24 -0
  20. package/src/__tests__/components/Fields/FileField/FileField.test.tsx +135 -0
  21. package/src/__tests__/{HeadingField.test.tsx → components/Fields/HeadingField/HeadingField.test.tsx} +2 -2
  22. package/src/__tests__/components/Fields/HiddenField/HiddenField.test.tsx +76 -0
  23. package/src/__tests__/components/Fields/MultiCheckSelect/MultiCheckSelect.test.tsx +70 -0
  24. package/src/__tests__/components/Fields/NoteField/NoteField.test.tsx +67 -0
  25. package/src/__tests__/components/Fields/NumberField/NumberField.test.tsx +109 -0
  26. package/src/__tests__/components/Fields/RadioField/RadioField.test.tsx +106 -0
  27. package/src/__tests__/components/Fields/RichText/RichText.test.tsx +52 -0
  28. package/src/__tests__/components/Fields/Select/Select.test.tsx +75 -0
  29. package/src/__tests__/components/Fields/SliderField/SliderField.test.tsx +82 -0
  30. package/src/__tests__/{TagField.test.tsx → components/Fields/TagField/TagField.test.tsx} +2 -2
  31. package/src/__tests__/{TextArea.test.tsx → components/Fields/TextArea/TextArea.test.tsx} +2 -2
  32. package/src/__tests__/{TextField.test.tsx → components/Fields/TextField/TextField.test.tsx} +2 -2
  33. package/src/__tests__/components/Fields/ToggleField/ToggleField.test.tsx +100 -0
  34. package/src/__tests__/{UniqueCheck.test.tsx → components/Fields/UniqueCheck/UniqueCheck.test.tsx} +2 -2
  35. package/src/__tests__/components/Fields/UrlField/UrlField.test.tsx +446 -0
  36. package/src/__tests__/components/Fields/UrlField/mockedAxios.ts +2214 -0
  37. package/src/__tests__/components/Fields/VisualUniqueSelection/ImageSelection/ImageSelection.test.tsx +99 -0
  38. package/src/__tests__/components/Fields/VisualUniqueSelection/ScrollableSelection/ScrollableSelection.test.tsx +176 -0
  39. package/src/__tests__/components/Fields/VisualUniqueSelection/VisualUniqueSelection.test.tsx +78 -0
  40. package/src/components/ActionMenu/index.tsx +1 -0
  41. package/src/components/Browser/index.tsx +39 -47
  42. package/src/components/Browser/style.tsx +15 -15
  43. package/src/components/BrowserContent/index.tsx +78 -0
  44. package/src/components/ConfigPanel/Form/ConnectedField/NavConnectedField/index.tsx +3 -5
  45. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +2 -6
  46. package/src/components/ConfigPanel/Header/index.tsx +28 -11
  47. package/src/components/ConfigPanel/index.tsx +2 -2
  48. package/src/components/ErrorCenter/index.tsx +11 -4
  49. package/src/components/Fields/ArrayFieldGroup/index.tsx +4 -2
  50. package/src/components/Fields/ArrayFieldGroup/style.tsx +7 -0
  51. package/src/components/Fields/AsyncCheckGroup/index.tsx +1 -1
  52. package/src/components/Fields/CheckField/index.tsx +1 -1
  53. package/src/components/Fields/ColorPicker/Picker/index.tsx +9 -3
  54. package/src/components/Fields/ColorPicker/index.tsx +4 -9
  55. package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx +2 -1
  56. package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +27 -22
  57. package/src/components/Fields/ComponentArray/MixableComponentArray/style.tsx +3 -38
  58. package/src/components/Fields/ComponentArray/SameComponentArray/index.tsx +3 -2
  59. package/src/components/Fields/ComponentArray/SameComponentArray/style.tsx +1 -28
  60. package/src/components/Fields/ComponentArray/helpers.tsx +1 -1
  61. package/src/components/Fields/ComponentContainer/index.tsx +3 -1
  62. package/src/components/Fields/FileField/FileDragAndDrop/index.tsx +1 -1
  63. package/src/components/Fields/FileField/FileDragAndDrop/style.tsx +2 -3
  64. package/src/components/Fields/FileField/index.tsx +6 -6
  65. package/src/components/Fields/HiddenField/index.tsx +3 -3
  66. package/src/components/Fields/MultiCheckSelect/index.tsx +8 -27
  67. package/src/components/Fields/NoteField/index.tsx +3 -3
  68. package/src/components/Fields/NumberField/index.tsx +6 -3
  69. package/src/components/Fields/RadioField/index.tsx +10 -2
  70. package/src/components/Fields/ReferenceField/index.tsx +8 -1
  71. package/src/components/Fields/ReferenceField/style.tsx +5 -0
  72. package/src/components/Fields/RichText/index.tsx +1 -1
  73. package/src/components/Fields/SliderField/index.tsx +11 -7
  74. package/src/components/Fields/ToggleField/index.tsx +12 -3
  75. package/src/components/Fields/UrlField/PageFinder/SelectionListItem/index.tsx +1 -1
  76. package/src/components/Fields/UrlField/index.tsx +6 -4
  77. package/src/components/Fields/UrlField/style.tsx +4 -2
  78. package/src/components/Fields/VisualOption/index.tsx +10 -2
  79. package/src/components/Fields/VisualUniqueSelection/ImageSelection/index.tsx +2 -2
  80. package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/index.tsx +4 -3
  81. package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/style.tsx +1 -1
  82. package/src/components/Fields/VisualUniqueSelection/index.tsx +3 -3
  83. package/src/components/FieldsBehavior/index.tsx +4 -4
  84. package/src/components/FieldsBehavior/style.tsx +5 -12
  85. package/src/components/FloatingMenu/index.tsx +8 -4
  86. package/src/components/Loader/index.tsx +12 -8
  87. package/src/components/MainWrapper/AppBar/index.tsx +1 -0
  88. package/src/components/MainWrapper/index.tsx +1 -0
  89. package/src/components/Modal/index.tsx +3 -2
  90. package/src/components/Modal/style.tsx +2 -1
  91. package/src/components/Toast/index.tsx +1 -1
  92. package/src/components/Tooltip/index.tsx +1 -1
  93. package/src/components/index.tsx +2 -0
  94. package/src/containers/App/actions.tsx +3 -7
  95. package/src/containers/PageEditor/actions.tsx +36 -5
  96. package/src/forms/editor.tsx +35 -1
  97. package/src/forms/fields.tsx +6 -2
  98. package/src/forms/index.tsx +2 -0
  99. package/src/forms/validators.tsx +29 -8
  100. package/src/guards/error/index.tsx +1 -1
  101. package/src/helpers/containerEvaluations.tsx +32 -4
  102. package/src/helpers/index.tsx +2 -0
  103. package/src/helpers/structuredData.tsx +2 -2
  104. package/src/hooks/forms.tsx +1 -28
  105. package/src/hooks/index.tsx +1 -2
  106. package/src/modules/Content/atoms.tsx +1 -0
  107. package/src/modules/FramePreview/index.tsx +70 -36
  108. package/src/modules/FramePreview/style.tsx +3 -0
  109. package/src/modules/GlobalEditor/PageBrowser/index.tsx +2 -7
  110. package/src/modules/GlobalEditor/index.tsx +8 -6
  111. package/src/modules/GlobalEditor/style.tsx +1 -1
  112. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +0 -4
  113. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +3 -2
  114. package/src/modules/PageEditor/PageBrowser/index.tsx +1 -4
  115. package/src/modules/PageEditor/index.tsx +6 -6
  116. package/src/modules/PublicPreview/index.tsx +17 -34
  117. package/src/modules/PublicPreview/style.tsx +0 -2
  118. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +0 -4
  119. package/src/modules/Sites/index.tsx +1 -1
  120. package/src/modules/StructuredData/Form/ConnectedField/index.tsx +1 -1
  121. package/src/modules/StructuredData/Form/index.tsx +3 -1
  122. package/src/modules/StructuredData/StructuredDataList/index.tsx +1 -0
  123. package/src/schemas/pages/GlobalPage.tsx +1 -0
  124. package/src/types/index.tsx +1 -0
@@ -29,6 +29,7 @@ import {
29
29
  checkH1content,
30
30
  parseValidationErrors,
31
31
  findPackagesActivationErrors,
32
+ checkMaxModules,
32
33
  } from "@ax/forms";
33
34
  import { appActions } from "@ax/containers/App";
34
35
  import { navigationActions } from "@ax/containers/Navigation";
@@ -100,6 +101,15 @@ const { getSiteDefaults, getDefaults } = navigationActions;
100
101
  // FIXME: CHECK EDITOR CONTENT STRUCTURE (editorContent.editorContent)
101
102
 
102
103
  function setEditorContent(editorContent: IPage | Record<string, unknown>): ISetEditorContent {
104
+ const iframe = document.querySelector("iframe");
105
+ iframe?.contentWindow?.postMessage(
106
+ {
107
+ type: "content-update",
108
+ message: editorContent,
109
+ },
110
+ "*"
111
+ );
112
+
103
113
  return { type: SET_EDITOR_CONTENT, payload: { editorContent } };
104
114
  }
105
115
 
@@ -654,6 +664,13 @@ function addModule(
654
664
  ): (dispatch: Dispatch, getState: any) => void {
655
665
  return (dispatch, getState) => {
656
666
  const { editorContent, sections, editorID } = getStateValues(getState);
667
+
668
+ const { isMaxModules, errorMessage } = checkMaxModules(editorContent, type);
669
+ if (isMaxModules) {
670
+ handleError({ text: errorMessage })(dispatch);
671
+ return;
672
+ }
673
+
657
674
  const componentModule = {
658
675
  editorID,
659
676
  type,
@@ -763,6 +780,13 @@ function duplicateModule(editorID: number, key?: string): (dispatch: Dispatch, g
763
780
 
764
781
  const updatedSections: any = [...sections];
765
782
  const { element: originalItem, parent, grandParent } = findByEditorID(updatedSections, editorID);
783
+
784
+ const { isMaxModules, errorMessage } = checkMaxModules(editorContent, originalItem.component);
785
+ if (isMaxModules) {
786
+ handleError({ text: errorMessage })(dispatch);
787
+ return;
788
+ }
789
+
766
790
  const parentModule = Array.isArray(parent) ? grandParent : parent;
767
791
 
768
792
  const parentKey = key ? key : getParentKey(parentModule, editorID);
@@ -814,6 +838,15 @@ function pasteModule(editorID: number): (dispatch: Dispatch, getState: any) => P
814
838
 
815
839
  const { sections, editorContent } = getStateValues(getState);
816
840
 
841
+ const { isMaxModules, errorMessage } = checkMaxModules(editorContent, moduleCopy.element.component);
842
+ if (isMaxModules && errorMessage) {
843
+ const error: INotification = {
844
+ type: "error",
845
+ text: errorMessage,
846
+ };
847
+ return { error };
848
+ }
849
+
817
850
  const updatedSections: any = [...sections];
818
851
  const { element: originalElement } = findByEditorID(updatedSections, editorID);
819
852
 
@@ -1069,7 +1102,7 @@ function getTemplateConfig(template: string): (dispatch: Dispatch, getState: any
1069
1102
  };
1070
1103
  }
1071
1104
 
1072
- function validatePage(publish?: boolean, browserRef?: any): (dispatch: Dispatch, getState: any) => Promise<boolean> {
1105
+ function validatePage(publish?: boolean): (dispatch: Dispatch, getState: any) => Promise<boolean> {
1073
1106
  return async (dispatch, getState) => {
1074
1107
  try {
1075
1108
  const { editorContent } = getStateValues(getState);
@@ -1104,10 +1137,8 @@ function validatePage(publish?: boolean, browserRef?: any): (dispatch: Dispatch,
1104
1137
  errors = packagesActivationErrors ? [...errors, packagesActivationErrors] : errors;
1105
1138
 
1106
1139
  let warnings: IErrorItem[] = [];
1107
- if (browserRef && browserRef.current) {
1108
- const h1Warning = checkH1content(browserRef.current);
1109
- warnings = h1Warning ? [...warnings, h1Warning] : warnings;
1110
- }
1140
+ const h1Warning = checkH1content();
1141
+ warnings = h1Warning ? [...warnings, h1Warning] : warnings;
1111
1142
 
1112
1143
  const allErrors = [...errors, ...warnings];
1113
1144
 
@@ -1,4 +1,4 @@
1
- import { deepClone, getDisplayName } from "@ax/helpers";
1
+ import { deepClone, getSchema, getDisplayName } from "@ax/helpers";
2
2
  import { IPage, IBreadcrumbItem } from "@ax/types";
3
3
 
4
4
  const configKeys = ["headerConfig", "footerConfig"];
@@ -168,6 +168,39 @@ const getParentKey = (parentModule: any, editorID: number) => {
168
168
  return keyFound;
169
169
  };
170
170
 
171
+ const checkMaxModules = (content: any, type: string): { isMaxModules: boolean; errorMessage?: string } => {
172
+ const { maxModulesPerPage } = getSchema(type);
173
+ const queue: any[] = [content];
174
+ let counter = 0;
175
+
176
+ while (queue.length > 0 && counter < maxModulesPerPage) {
177
+ const obj = queue.shift();
178
+ const currentObj = obj;
179
+
180
+ if (currentObj.component === type) {
181
+ counter++;
182
+ }
183
+
184
+ const keys = currentObj instanceof Object ? Object.keys(currentObj) : [];
185
+
186
+ for (const key of keys) {
187
+ const objVal = currentObj[key];
188
+ if (objVal instanceof Object) {
189
+ queue.push(objVal);
190
+ }
191
+ }
192
+ }
193
+
194
+ const isMaxModules = counter >= maxModulesPerPage;
195
+ const maxModulesText = maxModulesPerPage === 1 ? "one" : maxModulesPerPage;
196
+ const errorMessage = `There can be only ${maxModulesText} ${type} on page. You already have it.`;
197
+
198
+ return {
199
+ isMaxModules,
200
+ ...(isMaxModules && { errorMessage }),
201
+ };
202
+ };
203
+
171
204
  export {
172
205
  parseData,
173
206
  cleanContent,
@@ -181,4 +214,5 @@ export {
181
214
  getLastModuleEditorID,
182
215
  getLastComponentEditorID,
183
216
  getParentKey,
217
+ checkMaxModules,
184
218
  };
@@ -56,7 +56,8 @@ const getStructuredDataInnerFields = (
56
56
  innerFields: any[],
57
57
  content: any,
58
58
  updateValue: (value: Record<string, unknown>) => void,
59
- theme: string
59
+ theme: string,
60
+ errors: IErrorItem[]
60
61
  ) => {
61
62
  let fieldArr: any[] = [];
62
63
 
@@ -72,9 +73,11 @@ const getStructuredDataInnerFields = (
72
73
  const value = content && content[key];
73
74
 
74
75
  if (type === "ConditionalField" || type === "ArrayFieldGroup") {
75
- fieldArr = getStructuredDataInnerFields(fields, content, updateValue, theme);
76
+ fieldArr = getStructuredDataInnerFields(fields, content, updateValue, theme, errors);
76
77
  }
77
78
 
79
+ const error = errors.find((err: any) => err.key === key);
80
+
78
81
  const fieldProps = {
79
82
  value,
80
83
  objKey: key,
@@ -84,6 +87,7 @@ const getStructuredDataInnerFields = (
84
87
  onChange: handleChange,
85
88
  ...singleFieldProps,
86
89
  theme,
90
+ error,
87
91
  };
88
92
 
89
93
  return <FieldsBehavior key={key} {...fieldProps} />;
@@ -10,6 +10,7 @@ import {
10
10
  getLastModuleEditorID,
11
11
  getLastComponentEditorID,
12
12
  getParentKey,
13
+ checkMaxModules,
13
14
  } from "./editor";
14
15
  import {
15
16
  getUpdatedComponents,
@@ -63,4 +64,5 @@ export {
63
64
  findMandatoryStructuredDataErrors,
64
65
  checkH1content,
65
66
  parseValidationErrors,
67
+ checkMaxModules,
66
68
  };
@@ -213,14 +213,19 @@ const getValidationErrors = (
213
213
  fields.forEach((field: any) => {
214
214
  if (field.mandatory) {
215
215
  const hasMultipleOptions = field.whiteList && field.whiteList.length > 1;
216
- const isEmpty = isEmptyField(current[field.key], field.type, hasMultipleOptions);
216
+
217
+ const isEmpty =
218
+ !current ||
219
+ current[field.key] === undefined ||
220
+ isEmptyField(current[field.key], field.type, hasMultipleOptions);
221
+
217
222
  if (isEmpty) {
218
223
  errors.push({
219
224
  type: "error",
220
225
  message: getErrorMessage("ERR015", null),
221
226
  validator: { mandatory: true },
222
- editorID: current.editorID ? current.editorID : null,
223
- component: current.component ? current.component : null,
227
+ editorID: current && current.editorID ? current.editorID : null,
228
+ component: current && current.component ? current.component : null,
224
229
  name: name ? name : field.title,
225
230
  key: field.key,
226
231
  tab,
@@ -229,7 +234,7 @@ const getValidationErrors = (
229
234
  }
230
235
  }
231
236
 
232
- if (current.component && field.isMockup) {
237
+ if (current && current.component && field.isMockup) {
233
238
  const { isMockup, defaultValue } = checkMockupContent(
234
239
  current.component,
235
240
  field.key,
@@ -275,7 +280,11 @@ const getValidationErrors = (
275
280
  }
276
281
  }
277
282
 
278
- if (Object.prototype.hasOwnProperty.call(field, "fields") && field.fields.length) {
283
+ if (
284
+ Object.prototype.hasOwnProperty.call(field, "fields") &&
285
+ field.fields.length &&
286
+ field.type !== "ArrayFieldGroup"
287
+ ) {
279
288
  let innerFields = field.fields;
280
289
 
281
290
  if (field.type === "ConditionalField") {
@@ -287,6 +296,15 @@ const getValidationErrors = (
287
296
  const innerErrors = getValidationErrors(innerFields, current, name, tab, template);
288
297
  errors = [...errors, ...innerErrors];
289
298
  }
299
+
300
+ if (field.type === "ArrayFieldGroup") {
301
+ current &&
302
+ current[field.key] &&
303
+ current[field.key].forEach((item: any) => {
304
+ const innerErrors = getValidationErrors(field.fields, item, name, tab, template);
305
+ errors = [...errors, ...innerErrors];
306
+ });
307
+ }
290
308
  });
291
309
 
292
310
  return errors;
@@ -338,6 +356,7 @@ const findPackagesActivationErrors = (
338
356
  key: "",
339
357
  tab: "",
340
358
  template: false,
359
+ hasDeactivatedPackage: true,
341
360
  };
342
361
  }
343
362
 
@@ -397,10 +416,12 @@ const findMandatoryStructuredDataErrors = (content: any, schema: any): IErrorIte
397
416
  return errors;
398
417
  };
399
418
 
400
- const checkH1content = (content: any): IErrorItem | null => {
401
- const h1s = content.getElementsByTagName("h1");
419
+ const checkH1content = (): IErrorItem | null => {
420
+ const iframe = document.querySelector("iframe");
421
+ const iframeContent = iframe?.contentWindow?.document;
422
+ const h1s = iframeContent ? iframeContent.getElementsByTagName("h1") : null;
402
423
 
403
- if (!h1s.length) {
424
+ if (h1s && !h1s.length) {
404
425
  return {
405
426
  type: "warning",
406
427
  message: getErrorMessage("ERR018", null),
@@ -50,7 +50,7 @@ const ErrorGuard = (props: IProps) => {
50
50
  return domNode && createPortal(Notifications, domNode);
51
51
  };
52
52
 
53
- return code ? isBlocking ? <ErrorView code={code} text={text} /> : createErrorNotification() : null;
53
+ return code || text ? isBlocking ? <ErrorView code={code} text={text} /> : createErrorNotification() : null;
54
54
  };
55
55
 
56
56
  const mapStateToProps = (state: IRootState) => {
@@ -1,14 +1,14 @@
1
1
  import { IComponent } from "@ax/types";
2
2
  import { getDisplayName } from "./schemas";
3
3
 
4
- export const isComponentEmpty = (component: IComponent): boolean => {
4
+ const isComponentEmpty = (component: IComponent): boolean => {
5
5
  const keys = Object.keys(component);
6
6
  const privateKeys = ["componentID", "component", "parentEditorID"];
7
7
 
8
8
  return keys.length <= privateKeys.length;
9
9
  };
10
10
 
11
- export const setAsContainedComponent = (component: IComponent): IContainedComponent => {
11
+ const setAsContainedComponent = (component: IComponent): IContainedComponent => {
12
12
  const displayName = getDisplayName(component.component);
13
13
  return {
14
14
  containerText: displayName,
@@ -17,7 +17,7 @@ export const setAsContainedComponent = (component: IComponent): IContainedCompon
17
17
  };
18
18
  };
19
19
 
20
- export const areAllComponentsEmpty = (container: any): IContainerEvaluation => {
20
+ const areAllComponentsEmpty = (container: any): IContainerEvaluation => {
21
21
  const components = Object.keys(container);
22
22
  const filledComponents: IComponent[] = [];
23
23
  let containedComponent: any;
@@ -35,7 +35,7 @@ export const areAllComponentsEmpty = (container: any): IContainerEvaluation => {
35
35
  };
36
36
  };
37
37
 
38
- export const isEmptyContainer = (container: any, isMultiple?: boolean): IContainerEvaluation => {
38
+ const isEmptyContainer = (container: any, isMultiple?: boolean): IContainerEvaluation => {
39
39
  return isMultiple
40
40
  ? areAllComponentsEmpty(container)
41
41
  : {
@@ -44,6 +44,32 @@ export const isEmptyContainer = (container: any, isMultiple?: boolean): IContain
44
44
  };
45
45
  };
46
46
 
47
+ const areEqual = (prevProps: any, newProps: any): boolean => {
48
+ if (
49
+ prevProps.field.type === "FieldGroup" ||
50
+ (prevProps.field.fields && prevProps.field.fields.length > 0) ||
51
+ prevProps.selectedEditorID !== newProps.selectedEditorID
52
+ ) {
53
+ return false;
54
+ }
55
+
56
+ const {
57
+ selectedContent: { type },
58
+ } = prevProps;
59
+ const isTemplate = type === "template";
60
+ const prevValue = isTemplate
61
+ ? prevProps.selectedContent.template[prevProps.objKey]
62
+ : prevProps.selectedContent[prevProps.objKey];
63
+ const newValue = isTemplate
64
+ ? newProps.selectedContent.template[newProps.objKey]
65
+ : newProps.selectedContent[newProps.objKey];
66
+
67
+ const isObject = typeof prevValue === "object";
68
+ if (isObject) return false;
69
+
70
+ return prevValue === newValue;
71
+ };
72
+
47
73
  interface IContainerEvaluation {
48
74
  isEmpty: boolean;
49
75
  containedComponent: IContainedComponent;
@@ -54,3 +80,5 @@ interface IContainedComponent {
54
80
  component: string;
55
81
  componentID: number;
56
82
  }
83
+
84
+ export { isComponentEmpty, setAsContainedComponent, areAllComponentsEmpty, isEmptyContainer, areEqual };
@@ -3,6 +3,7 @@ import {
3
3
  setAsContainedComponent,
4
4
  areAllComponentsEmpty,
5
5
  isEmptyContainer,
6
+ areEqual,
6
7
  } from "./containerEvaluations";
7
8
 
8
9
  import {
@@ -178,4 +179,5 @@ export {
178
179
  getNavigationModules,
179
180
  getDefaultNavigationModules,
180
181
  isMultipleNavigationModules,
182
+ areEqual,
181
183
  };
@@ -26,9 +26,9 @@ const getFilteredStructuredData = (activatedDataPacks: IDataPack[], structuredDa
26
26
 
27
27
  const getStructuredDataTitle = (name: string): string => schemas.structuredData[name]?.title;
28
28
 
29
- const isStructuredDataFromPage = (name: string): boolean => schemas.structuredData[name].fromPage;
29
+ const isStructuredDataFromPage = (name: string): boolean => schemas.structuredData[name]?.fromPage;
30
30
 
31
- const isGlobalStructuredData = (name: string): boolean => !schemas.structuredData[name].local;
31
+ const isGlobalStructuredData = (name: string): boolean => !schemas.structuredData[name]?.local;
32
32
 
33
33
  const getGlobalPageTypes = (): Record<string, string>[] => {
34
34
  const { structuredData } = schemas;
@@ -4,33 +4,6 @@ import isEqual from "lodash.isequal";
4
4
  import { deepClone } from "@ax/helpers";
5
5
  import { cleanPageValues, getIsSavedData } from "@ax/forms";
6
6
 
7
- const useEqualValue = (component: any) =>
8
- memo(component, (prevProps: any, newProps: any) => {
9
- if (
10
- prevProps.field.type === "FieldGroup" ||
11
- (prevProps.field.fields && prevProps.field.fields.length > 0) ||
12
- prevProps.selectedEditorID !== newProps.selectedEditorID
13
- ) {
14
- return false;
15
- }
16
-
17
- const {
18
- selectedContent: { type },
19
- } = prevProps;
20
- const isTemplate = type === "template";
21
- const prevValue = isTemplate
22
- ? prevProps.selectedContent.template[prevProps.objKey]
23
- : prevProps.selectedContent[prevProps.objKey];
24
- const newValue = isTemplate
25
- ? newProps.selectedContent.template[newProps.objKey]
26
- : newProps.selectedContent[newProps.objKey];
27
-
28
- const isObject = typeof prevValue === "object";
29
- if (isObject) return false;
30
-
31
- return prevValue === newValue;
32
- });
33
-
34
7
  const useDebounce = (value: any) => {
35
8
  // State and setters for debounced value
36
9
  const [debouncedValue, setDebouncedValue] = useState(value);
@@ -133,4 +106,4 @@ const useIsDirty = (
133
106
  return { isDirty, setIsDirty, resetDirty };
134
107
  };
135
108
 
136
- export { useEqualValue, useDebounce, useEqualStructured, usePrevious, useIsDirty };
109
+ export { useDebounce, useEqualStructured, usePrevious, useIsDirty };
@@ -1,5 +1,5 @@
1
1
  import { useBulkSelection } from "./bulk";
2
- import { useDebounce, useEqualStructured, useEqualValue, useIsDirty, usePrevious } from "./forms";
2
+ import { useDebounce, useEqualStructured, useIsDirty, usePrevious } from "./forms";
3
3
  import { useHandleClickOutside, useModal, useToast } from "./modals";
4
4
  import { useURLSearchParam } from "./location";
5
5
  import { useCategoryColors } from "./content";
@@ -7,7 +7,6 @@ import { useCategoryColors } from "./content";
7
7
  export {
8
8
  useModal,
9
9
  useHandleClickOutside,
10
- useEqualValue,
11
10
  useDebounce,
12
11
  useEqualStructured,
13
12
  usePrevious,
@@ -92,6 +92,7 @@ const CopyModal = (props: ICopyModal): JSX.Element => {
92
92
  title="Copy page in another site"
93
93
  mainAction={mainModalAction}
94
94
  secondaryAction={secondaryModalAction}
95
+ overflow="visible"
95
96
  >
96
97
  <S.ModalContent>
97
98
  <p>
@@ -1,29 +1,68 @@
1
- import React from "react";
1
+ import React, { useCallback, useEffect, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import * as components from "components";
5
- import { SiteProvider } from "components";
6
- import { Preview } from "@griddo/core";
7
4
  import { getDefaultTheme } from "@ax/helpers";
8
- import { Loading } from "@ax/components";
5
+ import { Loading, BrowserContent } from "@ax/components";
9
6
  import { ILanguage, IRootState, ISocialState } from "@ax/types";
7
+ import { pageEditorActions } from "@ax/containers/PageEditor";
8
+ import { findByEditorID } from "@ax/forms";
9
+ import { useURLSearchParam } from "@ax/hooks";
10
10
 
11
11
  import * as S from "./style";
12
12
 
13
13
  const FramePreview = (props: IProps) => {
14
- const { content, socials, selectedEditorID, cloudinaryName, isLoading, currentSiteInfo, siteLangs, globalLangs } =
14
+ const { content, socials, cloudinaryName, isLoading, currentSiteInfo, siteLangs, globalLangs, setSelectedContent } =
15
15
  props;
16
16
 
17
+ const [state, setState] = useState(content);
18
+ const isPreview = useURLSearchParam("preview");
19
+ const isDisabled = useURLSearchParam("disabled");
20
+
21
+ const onMessageReceivedFromOutside = useCallback((ev: MessageEvent<{ type: string; message: string }>) => {
22
+ if (typeof ev.data !== "object") return;
23
+ if (!ev.data.type) return;
24
+ if (ev.data.type !== "content-update") return;
25
+ if (!ev.data.message) return;
26
+ setState(ev.data.message);
27
+ }, []);
28
+
29
+ useEffect(() => {
30
+ window.addEventListener("message", onMessageReceivedFromOutside);
31
+ return () => window.removeEventListener("message", onMessageReceivedFromOutside);
32
+ }, [onMessageReceivedFromOutside]);
33
+
17
34
  const {
18
35
  editorContent: { canonicalSite, language, pageLanguages },
19
36
  header,
20
37
  footer,
21
- } = content;
38
+ } = state;
22
39
 
23
40
  document.body.classList.add("preview");
24
41
 
25
- const API_URL = process.env.REACT_APP_API_ENDPOINT;
26
- const PUBLIC_API_URL = process.env.REACT_APP_PUBLIC_API_ENDPOINT;
42
+ const selectEditorID = (
43
+ selectedComponent: { editorID: number; component: any; type: string; parentEditorID: number },
44
+ parentComponent: string | undefined | null,
45
+ e: React.SyntheticEvent
46
+ ) => {
47
+ const { element } = findByEditorID(content, selectedComponent.parentEditorID);
48
+ element && e.stopPropagation();
49
+
50
+ const { editorID } = selectedComponent;
51
+
52
+ const isNavigationModule = ["header", "footer"].includes(selectedComponent.type);
53
+
54
+ if (isPreview === "false" && (isDisabled === "false" || isNavigationModule)) {
55
+ window.parent.postMessage(
56
+ {
57
+ type: "module-click",
58
+ message: editorID,
59
+ },
60
+ "*"
61
+ );
62
+
63
+ setSelectedContent && setSelectedContent(editorID);
64
+ }
65
+ };
27
66
 
28
67
  const globalTheme = getDefaultTheme();
29
68
  const theme = currentSiteInfo ? currentSiteInfo.theme : globalTheme;
@@ -33,47 +72,38 @@ const FramePreview = (props: IProps) => {
33
72
  if (isLoading) return <Loading />;
34
73
 
35
74
  return (
36
- <SiteProvider
37
- cloudinaryCloudName={cloudinaryName}
38
- theme={theme}
39
- socials={socials}
40
- siteLangs={langs}
41
- selectEditorID={selectedEditorID}
42
- renderer="editor"
43
- apiUrl={API_URL}
44
- publicApiUrl={PUBLIC_API_URL}
45
- siteId={siteID}
46
- >
47
- <S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
48
- <Preview
49
- isPage={true}
50
- apiUrl={API_URL}
51
- library={components}
52
- content={content.editorContent}
53
- header={currentSiteInfo && header}
54
- footer={currentSiteInfo && footer}
55
- languageId={language}
56
- pageLanguages={pageLanguages}
57
- />
58
- </S.Wrapper>
59
- </SiteProvider>
75
+ <S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
76
+ <BrowserContent
77
+ cloudinaryName={cloudinaryName}
78
+ theme={theme}
79
+ socials={socials}
80
+ siteLangs={langs}
81
+ selectEditorID={selectEditorID}
82
+ siteID={siteID}
83
+ isPage={true}
84
+ content={state.editorContent}
85
+ header={currentSiteInfo && header}
86
+ footer={currentSiteInfo && footer}
87
+ languageID={language}
88
+ pageLanguages={pageLanguages}
89
+ />
90
+ </S.Wrapper>
60
91
  );
61
92
  };
62
93
 
63
94
  interface IProps {
64
95
  content: any;
65
- selectedEditorID: number;
66
96
  socials: ISocialState;
67
97
  cloudinaryName: string | null;
68
98
  currentSiteInfo: any;
69
99
  siteLangs: ILanguage[];
70
100
  globalLangs: ILanguage[];
71
101
  isLoading: boolean;
102
+ setSelectedContent(editorID: number): void;
72
103
  }
73
104
 
74
105
  const mapStateToProps = (state: IRootState) => ({
75
106
  content: { ...state.pageEditor.editorContent },
76
- selectedEditorID: state.pageEditor.selectedEditorID as number,
77
107
  socials: state.social,
78
108
  cloudinaryName: state.app.globalSettings.cloudinaryName,
79
109
  currentSiteInfo: state.sites.currentSiteInfo,
@@ -82,4 +112,8 @@ const mapStateToProps = (state: IRootState) => ({
82
112
  isLoading: state.app.isLoading,
83
113
  });
84
114
 
85
- export default connect(mapStateToProps)(FramePreview);
115
+ const mapDispatchToProps = {
116
+ setSelectedContent: pageEditorActions.setSelectedContent,
117
+ };
118
+
119
+ export default connect(mapStateToProps, mapDispatchToProps)(FramePreview);
@@ -1,6 +1,9 @@
1
1
  import styled from "styled-components";
2
2
 
3
3
  const Wrapper = styled.div`
4
+ border-left: 1px solid ${(p) => p.theme.color.uiLine};
5
+ border-right: 1px solid ${(p) => p.theme.color.uiLine};
6
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
4
7
  overflow: auto;
5
8
  scroll-behavior: smooth;
6
9
  height: 100%;
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
3
3
  import { pageEditorActions } from "@ax/containers/PageEditor";
4
4
 
5
5
  import { Browser } from "@ax/components";
6
- import { IBreadcrumbItem, ILanguage, IRootState, ISchema, ISocialState } from "@ax/types";
6
+ import { ILanguage, IRootState, ISchema, ISocialState } from "@ax/types";
7
7
 
8
8
  const PageBrowser = (props: IProps) => {
9
9
  const {
@@ -12,7 +12,6 @@ const PageBrowser = (props: IProps) => {
12
12
  content: {
13
13
  editorContent: { path, slug, canonicalSite },
14
14
  },
15
- selectedEditorID,
16
15
  setSelectedContent,
17
16
  globalLangs,
18
17
  theme,
@@ -30,7 +29,6 @@ const PageBrowser = (props: IProps) => {
30
29
  isPage={true}
31
30
  content={props.content.editorContent}
32
31
  socials={socials}
33
- selectedEditorID={selectedEditorID}
34
32
  setSelectedContent={setSelectedContent}
35
33
  url={url}
36
34
  theme={theme}
@@ -39,6 +37,7 @@ const PageBrowser = (props: IProps) => {
39
37
  disabled={isReadOnly}
40
38
  siteID={canonicalSite}
41
39
  isPreview={isPreview}
40
+ showIframe={true}
42
41
  browserRef={browserRef}
43
42
  />
44
43
  );
@@ -47,12 +46,10 @@ const PageBrowser = (props: IProps) => {
47
46
  interface IEditorStateProps {
48
47
  // TODO: Define content Type
49
48
  content: any;
50
- selectedEditorID: number;
51
49
  socials: ISocialState;
52
50
  cloudinaryName: string | null;
53
51
  globalLangs: ILanguage[];
54
52
  schema: ISchema | Record<string, unknown>;
55
- breadcrumb: IBreadcrumbItem[];
56
53
  selectedParent: any;
57
54
  activatedModules: string[];
58
55
  }
@@ -69,12 +66,10 @@ type IProps = IEditorStateProps & IPageBrowserDispatchProps;
69
66
 
70
67
  const mapStateToProps = (state: IRootState): IEditorStateProps => ({
71
68
  content: { ...state.pageEditor.editorContent },
72
- selectedEditorID: state.pageEditor.selectedEditorID as number,
73
69
  socials: state.social,
74
70
  cloudinaryName: state.app.globalSettings.cloudinaryName,
75
71
  globalLangs: state.app.globalLangs,
76
72
  schema: state.pageEditor.schema,
77
- breadcrumb: state.pageEditor.breadcrumb,
78
73
  selectedParent: state.pageEditor.selectedParent,
79
74
  activatedModules: state.dataPacks.modules,
80
75
  });