@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.
- package/config/jest/componentsMock.js +1528 -27
- package/package.json +2 -2
- package/src/__mocks__/reducers/app.tsx +10 -0
- package/src/__mocks__/reducers/sites.tsx +10 -0
- package/src/__tests__/{AnalyticsField.test.tsx → components/Fields/AnalyticsField/AnalyticsField.test.tsx} +5 -5
- package/src/__tests__/{PageAnalytics.test.tsx → components/Fields/AnalyticsField/PageAnalytics/PageAnalytics.test.tsx} +2 -2
- package/src/__tests__/{StructuredDataAnalytics.test.tsx → components/Fields/AnalyticsField/StructuredDataAnalytics/StructuredDataAnalytics.test.tsx} +2 -2
- package/src/__tests__/{ArrayFieldGroup.test.tsx → components/Fields/ArrayFieldGroup/ArrayFieldGroup.test.tsx} +2 -2
- package/src/__tests__/{AsyncCheckGroup.test.tsx → components/Fields/AsyncCheckGroup/AsyncCheckGroup.test.tsx} +2 -2
- package/src/__tests__/{AsyncSelect.test.tsx → components/Fields/AsyncSelect/AsyncSelect.test.tsx} +2 -2
- package/src/__tests__/{CheckField.test.tsx → components/Fields/CheckField/CheckField.test.tsx} +2 -2
- package/src/__tests__/{CheckGroup.test.tsx → components/Fields/CheckGroup/CheckGroup.test.tsx} +2 -2
- package/src/__tests__/components/Fields/ColorPicker/ColorPicker.test.tsx +195 -0
- package/src/__tests__/components/Fields/ComponentArray/ComponentArray.test.tsx +184 -0
- package/src/__tests__/components/Fields/ComponentArray/MixableComponentArray/MixableComponentArray.test.tsx +315 -0
- package/src/__tests__/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/PasteModuleButton.test.tsx +95 -0
- package/src/__tests__/components/Fields/ComponentArray/SameComponentArray/SameComponentArray.test.tsx +225 -0
- package/src/__tests__/{FieldGroup.test.tsx → components/Fields/FieldGroup/FieldGroup.test.tsx} +2 -2
- package/src/__tests__/components/Fields/FieldsDivider/FieldsDivider.test.tsx +24 -0
- package/src/__tests__/components/Fields/FileField/FileField.test.tsx +135 -0
- package/src/__tests__/{HeadingField.test.tsx → components/Fields/HeadingField/HeadingField.test.tsx} +2 -2
- package/src/__tests__/components/Fields/HiddenField/HiddenField.test.tsx +76 -0
- package/src/__tests__/components/Fields/MultiCheckSelect/MultiCheckSelect.test.tsx +70 -0
- package/src/__tests__/components/Fields/NoteField/NoteField.test.tsx +67 -0
- package/src/__tests__/components/Fields/NumberField/NumberField.test.tsx +109 -0
- package/src/__tests__/components/Fields/RadioField/RadioField.test.tsx +106 -0
- package/src/__tests__/components/Fields/RichText/RichText.test.tsx +52 -0
- package/src/__tests__/components/Fields/Select/Select.test.tsx +75 -0
- package/src/__tests__/components/Fields/SliderField/SliderField.test.tsx +82 -0
- package/src/__tests__/{TagField.test.tsx → components/Fields/TagField/TagField.test.tsx} +2 -2
- package/src/__tests__/{TextArea.test.tsx → components/Fields/TextArea/TextArea.test.tsx} +2 -2
- package/src/__tests__/{TextField.test.tsx → components/Fields/TextField/TextField.test.tsx} +2 -2
- package/src/__tests__/components/Fields/ToggleField/ToggleField.test.tsx +100 -0
- package/src/__tests__/{UniqueCheck.test.tsx → components/Fields/UniqueCheck/UniqueCheck.test.tsx} +2 -2
- package/src/__tests__/components/Fields/UrlField/UrlField.test.tsx +446 -0
- package/src/__tests__/components/Fields/UrlField/mockedAxios.ts +2214 -0
- package/src/__tests__/components/Fields/VisualUniqueSelection/ImageSelection/ImageSelection.test.tsx +99 -0
- package/src/__tests__/components/Fields/VisualUniqueSelection/ScrollableSelection/ScrollableSelection.test.tsx +176 -0
- package/src/__tests__/components/Fields/VisualUniqueSelection/VisualUniqueSelection.test.tsx +78 -0
- package/src/components/ActionMenu/index.tsx +1 -0
- package/src/components/Browser/index.tsx +39 -47
- package/src/components/Browser/style.tsx +15 -15
- package/src/components/BrowserContent/index.tsx +78 -0
- package/src/components/ConfigPanel/Form/ConnectedField/NavConnectedField/index.tsx +3 -5
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +2 -6
- package/src/components/ConfigPanel/Header/index.tsx +28 -11
- package/src/components/ConfigPanel/index.tsx +2 -2
- package/src/components/ErrorCenter/index.tsx +11 -4
- package/src/components/Fields/ArrayFieldGroup/index.tsx +4 -2
- package/src/components/Fields/ArrayFieldGroup/style.tsx +7 -0
- package/src/components/Fields/AsyncCheckGroup/index.tsx +1 -1
- package/src/components/Fields/CheckField/index.tsx +1 -1
- package/src/components/Fields/ColorPicker/Picker/index.tsx +9 -3
- package/src/components/Fields/ColorPicker/index.tsx +4 -9
- package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx +2 -1
- package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +27 -22
- package/src/components/Fields/ComponentArray/MixableComponentArray/style.tsx +3 -38
- package/src/components/Fields/ComponentArray/SameComponentArray/index.tsx +3 -2
- package/src/components/Fields/ComponentArray/SameComponentArray/style.tsx +1 -28
- package/src/components/Fields/ComponentArray/helpers.tsx +1 -1
- package/src/components/Fields/ComponentContainer/index.tsx +3 -1
- package/src/components/Fields/FileField/FileDragAndDrop/index.tsx +1 -1
- package/src/components/Fields/FileField/FileDragAndDrop/style.tsx +2 -3
- package/src/components/Fields/FileField/index.tsx +6 -6
- package/src/components/Fields/HiddenField/index.tsx +3 -3
- package/src/components/Fields/MultiCheckSelect/index.tsx +8 -27
- package/src/components/Fields/NoteField/index.tsx +3 -3
- package/src/components/Fields/NumberField/index.tsx +6 -3
- package/src/components/Fields/RadioField/index.tsx +10 -2
- package/src/components/Fields/ReferenceField/index.tsx +8 -1
- package/src/components/Fields/ReferenceField/style.tsx +5 -0
- package/src/components/Fields/RichText/index.tsx +1 -1
- package/src/components/Fields/SliderField/index.tsx +11 -7
- package/src/components/Fields/ToggleField/index.tsx +12 -3
- package/src/components/Fields/UrlField/PageFinder/SelectionListItem/index.tsx +1 -1
- package/src/components/Fields/UrlField/index.tsx +6 -4
- package/src/components/Fields/UrlField/style.tsx +4 -2
- package/src/components/Fields/VisualOption/index.tsx +10 -2
- package/src/components/Fields/VisualUniqueSelection/ImageSelection/index.tsx +2 -2
- package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/index.tsx +4 -3
- package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/style.tsx +1 -1
- package/src/components/Fields/VisualUniqueSelection/index.tsx +3 -3
- package/src/components/FieldsBehavior/index.tsx +4 -4
- package/src/components/FieldsBehavior/style.tsx +5 -12
- package/src/components/FloatingMenu/index.tsx +8 -4
- package/src/components/Loader/index.tsx +12 -8
- package/src/components/MainWrapper/AppBar/index.tsx +1 -0
- package/src/components/MainWrapper/index.tsx +1 -0
- package/src/components/Toast/index.tsx +1 -1
- package/src/components/Tooltip/index.tsx +1 -1
- package/src/components/index.tsx +2 -0
- package/src/containers/App/actions.tsx +3 -7
- package/src/containers/PageEditor/actions.tsx +36 -5
- package/src/forms/editor.tsx +35 -1
- package/src/forms/fields.tsx +6 -2
- package/src/forms/index.tsx +2 -0
- package/src/forms/validators.tsx +29 -8
- package/src/guards/error/index.tsx +1 -1
- package/src/helpers/containerEvaluations.tsx +32 -4
- package/src/helpers/index.tsx +2 -0
- package/src/helpers/structuredData.tsx +2 -2
- package/src/hooks/forms.tsx +1 -28
- package/src/hooks/index.tsx +1 -2
- package/src/modules/FramePreview/index.tsx +70 -36
- package/src/modules/FramePreview/style.tsx +3 -0
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +2 -7
- package/src/modules/GlobalEditor/index.tsx +8 -6
- package/src/modules/GlobalEditor/style.tsx +1 -1
- package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +0 -4
- package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +3 -2
- package/src/modules/PageEditor/PageBrowser/index.tsx +1 -4
- package/src/modules/PageEditor/index.tsx +6 -6
- package/src/modules/PublicPreview/index.tsx +17 -34
- package/src/modules/PublicPreview/style.tsx +0 -2
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +0 -4
- package/src/modules/Sites/index.tsx +1 -1
- package/src/modules/StructuredData/Form/ConnectedField/index.tsx +1 -1
- package/src/modules/StructuredData/Form/index.tsx +3 -1
- package/src/modules/StructuredData/StructuredDataList/index.tsx +1 -0
- package/src/schemas/pages/GlobalPage.tsx +1 -0
- package/src/types/index.tsx +1 -0
package/src/forms/editor.tsx
CHANGED
|
@@ -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
|
};
|
package/src/forms/fields.tsx
CHANGED
|
@@ -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} />;
|
package/src/forms/index.tsx
CHANGED
|
@@ -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
|
};
|
package/src/forms/validators.tsx
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
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 = (
|
|
401
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|
package/src/helpers/index.tsx
CHANGED
|
@@ -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]
|
|
29
|
+
const isStructuredDataFromPage = (name: string): boolean => schemas.structuredData[name]?.fromPage;
|
|
30
30
|
|
|
31
|
-
const isGlobalStructuredData = (name: string): boolean => !schemas.structuredData[name]
|
|
31
|
+
const isGlobalStructuredData = (name: string): boolean => !schemas.structuredData[name]?.local;
|
|
32
32
|
|
|
33
33
|
const getGlobalPageTypes = (): Record<string, string>[] => {
|
|
34
34
|
const { structuredData } = schemas;
|
package/src/hooks/forms.tsx
CHANGED
|
@@ -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 {
|
|
109
|
+
export { useDebounce, useEqualStructured, usePrevious, useIsDirty };
|
package/src/hooks/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useBulkSelection } from "./bulk";
|
|
2
|
-
import { useDebounce, useEqualStructured,
|
|
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,
|
|
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
|
-
} =
|
|
38
|
+
} = state;
|
|
22
39
|
|
|
23
40
|
document.body.classList.add("preview");
|
|
24
41
|
|
|
25
|
-
const
|
|
26
|
-
|
|
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
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
@@ -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,
|