@griddo/ax 1.67.10 → 1.68.2

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 (121) 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/Toast/index.tsx +1 -1
  90. package/src/components/Tooltip/index.tsx +1 -1
  91. package/src/components/index.tsx +2 -0
  92. package/src/containers/App/actions.tsx +3 -7
  93. package/src/containers/PageEditor/actions.tsx +36 -5
  94. package/src/forms/editor.tsx +35 -1
  95. package/src/forms/fields.tsx +6 -2
  96. package/src/forms/index.tsx +2 -0
  97. package/src/forms/validators.tsx +29 -8
  98. package/src/guards/error/index.tsx +1 -1
  99. package/src/helpers/containerEvaluations.tsx +32 -4
  100. package/src/helpers/index.tsx +2 -0
  101. package/src/helpers/structuredData.tsx +2 -2
  102. package/src/hooks/forms.tsx +1 -28
  103. package/src/hooks/index.tsx +1 -2
  104. package/src/modules/FramePreview/index.tsx +70 -36
  105. package/src/modules/FramePreview/style.tsx +3 -0
  106. package/src/modules/GlobalEditor/PageBrowser/index.tsx +2 -7
  107. package/src/modules/GlobalEditor/index.tsx +8 -6
  108. package/src/modules/GlobalEditor/style.tsx +1 -1
  109. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +0 -4
  110. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +3 -2
  111. package/src/modules/PageEditor/PageBrowser/index.tsx +1 -4
  112. package/src/modules/PageEditor/index.tsx +6 -6
  113. package/src/modules/PublicPreview/index.tsx +17 -34
  114. package/src/modules/PublicPreview/style.tsx +0 -2
  115. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +0 -4
  116. package/src/modules/Sites/index.tsx +1 -1
  117. package/src/modules/StructuredData/Form/ConnectedField/index.tsx +1 -1
  118. package/src/modules/StructuredData/Form/index.tsx +3 -1
  119. package/src/modules/StructuredData/StructuredDataList/index.tsx +1 -0
  120. package/src/schemas/pages/GlobalPage.tsx +1 -0
  121. package/src/types/index.tsx +1 -0
@@ -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,
@@ -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
  });
@@ -127,7 +127,7 @@ const GlobalEditor = (props: IProps) => {
127
127
  const publishPage = async () => {
128
128
  const { updatePageStatus, savePage, pageID, validatePage } = props;
129
129
 
130
- const validated = await validatePage(true, browserRef);
130
+ const validated = await validatePage(true);
131
131
 
132
132
  if (validated) {
133
133
  const publishPage = {
@@ -147,7 +147,7 @@ const GlobalEditor = (props: IProps) => {
147
147
  const publishChanges = async () => {
148
148
  const { savePage, validatePage } = props;
149
149
 
150
- const validated = await validatePage(true, browserRef);
150
+ const validated = await validatePage(true);
151
151
 
152
152
  if (validated) {
153
153
  const publishPage = {
@@ -176,13 +176,13 @@ const GlobalEditor = (props: IProps) => {
176
176
 
177
177
  const reviewPage = () => {
178
178
  const { validatePage } = props;
179
- validatePage(undefined, browserRef);
179
+ validatePage(undefined);
180
180
  };
181
181
 
182
182
  const handlePublishDraft = async () => {
183
183
  const { savePage, validatePage } = props;
184
184
 
185
- const validated = await validatePage(true, browserRef);
185
+ const validated = await validatePage(true);
186
186
 
187
187
  if (validated) {
188
188
  const isSaved = await savePage(false, null, true);
@@ -307,6 +307,8 @@ const GlobalEditor = (props: IProps) => {
307
307
  setTab(tab);
308
308
  };
309
309
 
310
+ const goToPackage = () => setHistoryPush("/sites/settings/content-types", false);
311
+
310
312
  const modalText = (
311
313
  <>
312
314
  Some content <strong>is not saved</strong> on this page.
@@ -425,7 +427,7 @@ const GlobalEditor = (props: IProps) => {
425
427
  currentPageID={pageID}
426
428
  fullWidth={true}
427
429
  errors={errors}
428
- errorActions={{ goToError }}
430
+ errorActions={{ goToError, goToPackage }}
429
431
  inversed={true}
430
432
  isFromEditor={true}
431
433
  pageStatusActions={pageStatusActions}
@@ -583,7 +585,7 @@ interface IPageEditorDispatchProps {
583
585
  getPage(pageID?: number, global?: boolean): Promise<void>;
584
586
  savePage(createDraft: boolean, publishPage?: any, publishDraft?: boolean): Promise<boolean>;
585
587
  deletePage(params?: ISavePageParams): Promise<boolean>;
586
- validatePage(publish?: boolean, browserRef?: any): Promise<boolean>;
588
+ validatePage(publish?: boolean): Promise<boolean>;
587
589
  updatePageStatus(id: number[], status: string): Promise<boolean>;
588
590
  setHistoryPush(path: string, isEditor: boolean): void;
589
591
  setLanguage?(lang: { locale: string; id: number | null }): void;
@@ -11,7 +11,7 @@ const NotificationWrapper = styled.div`
11
11
  top: ${(p) => `calc(${p.theme.spacing.s} * 4)`};
12
12
  left: 0;
13
13
  right: 0;
14
- z-index: 2;
14
+ z-index: 3;
15
15
  `;
16
16
 
17
17
  const ModalContent = styled.div`
@@ -10,7 +10,6 @@ const DefaultsBrowser = (props: IProps) => {
10
10
  socials,
11
11
  cloudinaryName,
12
12
  content,
13
- selectedEditorID,
14
13
  setSelectedContent,
15
14
  currentSiteInfo: { theme, id: siteID },
16
15
  siteLangs,
@@ -23,7 +22,6 @@ const DefaultsBrowser = (props: IProps) => {
23
22
  isPage={false}
24
23
  socials={socials}
25
24
  content={updatedContent}
26
- selectedEditorID={selectedEditorID}
27
25
  setSelectedContent={setSelectedContent}
28
26
  url={content.slug}
29
27
  theme={theme}
@@ -37,7 +35,6 @@ const DefaultsBrowser = (props: IProps) => {
37
35
  interface IEditorStateProps {
38
36
  // TODO: Define content Type
39
37
  content: any;
40
- selectedEditorID: number;
41
38
  currentSiteInfo: any;
42
39
  socials: ISocialState;
43
40
  cloudinaryName: string | null;
@@ -52,7 +49,6 @@ type IProps = IEditorStateProps & IPageBrowserDispatchProps;
52
49
 
53
50
  const mapStateToProps = (state: IRootState): IEditorStateProps => ({
54
51
  content: { ...state.navigation.editorContent },
55
- selectedEditorID: state.navigation.selectedEditorID as number,
56
52
  currentSiteInfo: state.sites.currentSiteInfo,
57
53
  socials: state.social,
58
54
  cloudinaryName: state.app.globalSettings.cloudinaryName,