@griddo/ax 11.13.2 → 11.13.3-rc.1

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 (59) hide show
  1. package/config/jest/setup.js +10 -0
  2. package/package.json +2 -2
  3. package/src/GlobalStore.tsx +1 -1
  4. package/src/__tests__/components/Fields/AsyncCheckGroup/AsyncCheckGroup.test.tsx +276 -66
  5. package/src/__tests__/components/FloatingMenu/FloatingMenu.test.tsx +300 -99
  6. package/src/__tests__/modules/Settings/Social/Social.test.tsx +12 -4
  7. package/src/api/checkgroups.tsx +4 -3
  8. package/src/api/selects.tsx +12 -5
  9. package/src/components/ActionMenu/index.tsx +1 -3
  10. package/src/components/Browser/index.tsx +12 -3
  11. package/src/components/Browser/style.tsx +7 -0
  12. package/src/components/ConfigPanel/Form/index.tsx +47 -53
  13. package/src/components/Fields/AnalyticsField/PageAnalytics/atoms.tsx +9 -13
  14. package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +37 -29
  15. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +9 -13
  16. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +17 -11
  17. package/src/components/Fields/AnalyticsField/index.tsx +1 -2
  18. package/src/components/Fields/AnalyticsField/utils.tsx +4 -4
  19. package/src/components/Fields/AsyncCheckGroup/index.tsx +97 -79
  20. package/src/components/Fields/AsyncSelect/index.tsx +33 -22
  21. package/src/components/Fields/DateField/DatePickerInput/index.tsx +2 -2
  22. package/src/components/Fields/DateField/index.tsx +3 -3
  23. package/src/components/Fields/IntegrationsField/SideModal/index.tsx +2 -2
  24. package/src/components/Fields/IntegrationsField/index.tsx +14 -10
  25. package/src/components/Fields/MultiCheckSelect/index.tsx +6 -6
  26. package/src/components/Fields/MultiCheckSelectGroup/index.tsx +39 -37
  27. package/src/components/Fields/MultiCheckSelectGroup/style.tsx +1 -1
  28. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +0 -2
  29. package/src/components/Fields/RichText/index.tsx +15 -7
  30. package/src/components/Fields/TextArea/index.tsx +9 -6
  31. package/src/components/FloatingMenu/index.tsx +32 -31
  32. package/src/components/FloatingMenu/style.tsx +23 -5
  33. package/src/components/Loader/components/SmallCircle.js +3 -3
  34. package/src/components/MainWrapper/AppBar/style.tsx +1 -0
  35. package/src/components/SideModal/index.tsx +1 -1
  36. package/src/components/TableFilters/CategoryFilter/index.tsx +14 -15
  37. package/src/containers/App/actions.tsx +7 -1
  38. package/src/containers/App/constants.tsx +2 -0
  39. package/src/containers/App/interfaces.tsx +5 -0
  40. package/src/containers/App/reducer.tsx +11 -2
  41. package/src/containers/Forms/actions.tsx +5 -7
  42. package/src/containers/Integrations/actions.tsx +1 -3
  43. package/src/containers/Navigation/Menu/actions.tsx +2 -2
  44. package/src/containers/PageEditor/actions.tsx +3 -2
  45. package/src/containers/Settings/DataPacks/actions.tsx +35 -29
  46. package/src/containers/Sites/actions.tsx +40 -33
  47. package/src/containers/StructuredData/actions.tsx +3 -9
  48. package/src/modules/ActivityLog/LogFilters/DateFilter/index.tsx +5 -4
  49. package/src/modules/Content/NewContentModal/PageImporter/index.tsx +1 -2
  50. package/src/modules/Content/index.tsx +8 -3
  51. package/src/modules/Content/style.tsx +7 -0
  52. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +58 -45
  53. package/src/modules/Navigation/Defaults/index.tsx +103 -104
  54. package/src/modules/PageEditor/index.tsx +9 -1
  55. package/src/modules/PublicPreview/index.tsx +2 -1
  56. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +60 -44
  57. package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +32 -37
  58. package/src/modules/Sites/index.tsx +3 -3
  59. package/src/modules/Users/UserList/index.tsx +1 -1
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
2
2
 
3
3
  import type { IShareData } from "@ax/api";
4
4
  import { shareToken as shareTokenApi } from "@ax/api";
5
- import { BrowserContent, Icon, OcassionalToast, Select, SharePageModal, Toast, Tooltip } from "@ax/components";
5
+ import { BrowserContent, Icon, Loading, OcassionalToast, Select, SharePageModal, Toast, Tooltip } from "@ax/components";
6
6
  import { findByEditorID } from "@ax/forms";
7
7
  import { DEV_NOW, getShareTokenInfo } from "@ax/helpers";
8
8
  import { useModal, useOnMessageReceivedFromIframe, useToast } from "@ax/hooks";
@@ -65,10 +65,10 @@ const Browser = (props: IBrowserProps): JSX.Element => {
65
65
  const isKeywordsEditor = editorType === "keywords";
66
66
  const isZoomEditor = isPageEditor || isHeadingsEditor || isKeywordsEditor;
67
67
 
68
- const frameWrapperRef = useRef<HTMLDivElement>(null);
69
-
68
+ const [isIframeLoading, setIsIframeLoading] = useState(!!showIframe);
70
69
  const [previewResolution, setPreviewResolution] = useState("desktop");
71
70
  const [dimensions, setDimensions] = useState({ resolution: DEFAULT_RESOLUTION, zoom: "100" });
71
+ const frameWrapperRef = useRef<HTMLDivElement>(null);
72
72
 
73
73
  const urlPreview = `${domain}/editor/page-preview?preview=${!!isPreview}&disabled=${!!disabled}&type=${editorType}${headingFilterParam}${keywordsFilterParam}`;
74
74
  const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
@@ -297,6 +297,11 @@ const Browser = (props: IBrowserProps): JSX.Element => {
297
297
  />
298
298
  )}
299
299
  <S.FrameWrapper hasBorder={isZoomEditor} isFormEditor={isFormEditor} data-testid="navbar-iframe-wrapper">
300
+ {isIframeLoading && (
301
+ <S.FrameLoading>
302
+ <Loading />
303
+ </S.FrameLoading>
304
+ )}
300
305
  {isPreview ? (
301
306
  <iframe
302
307
  title="Preview"
@@ -305,6 +310,8 @@ const Browser = (props: IBrowserProps): JSX.Element => {
305
310
  src={urlPreview}
306
311
  loading="lazy"
307
312
  className="frame-content"
313
+ onLoad={() => setIsIframeLoading(false)}
314
+ style={{ visibility: isIframeLoading ? "hidden" : "visible" }}
308
315
  />
309
316
  ) : (
310
317
  <div
@@ -321,11 +328,13 @@ const Browser = (props: IBrowserProps): JSX.Element => {
321
328
  src={urlPreview}
322
329
  loading="lazy"
323
330
  className="frame-content"
331
+ onLoad={() => setIsIframeLoading(false)}
324
332
  style={{
325
333
  display: "block",
326
334
  transform: isZoomEditor ? `scale(${parseInt(dimensions.zoom) / 100})` : "scale(1)",
327
335
  transformOrigin: "0 0",
328
336
  height: isZoomEditor ? `${Math.round(100 / (parseInt(dimensions.zoom) / 100))}%` : "100%",
337
+ visibility: isIframeLoading ? "hidden" : "visible",
329
338
  }}
330
339
  />
331
340
  </div>
@@ -78,6 +78,12 @@ const OuterContainer = styled.div`
78
78
  height: 100%;
79
79
  `;
80
80
 
81
+ const FrameLoading = styled.div`
82
+ position: absolute;
83
+ inset: 0;
84
+ z-index: 1;
85
+ `;
86
+
81
87
  const Wrapper = styled.div`
82
88
  border-left: 1px solid ${(p) => p.theme.color.uiLine};
83
89
  border-right: 1px solid ${(p) => p.theme.color.uiLine};
@@ -146,4 +152,5 @@ export {
146
152
  ResolutionWrapper,
147
153
  ZoomWrapper,
148
154
  SelectLabel,
155
+ FrameLoading,
149
156
  };
@@ -1,18 +1,25 @@
1
- import React, { useState } from "react";
1
+ import { useState } from "react";
2
2
 
3
- import { ISchema, ISchemaTab, ISchemaField } from "@ax/types";
4
3
  import { Tabs } from "@ax/components";
5
4
  import { usePermission } from "@ax/hooks";
5
+ import type { ISchema, ISchemaField, ISchemaTab } from "@ax/types";
6
6
 
7
7
  import ConnectedField from "./ConnectedField";
8
8
 
9
9
  import * as S from "./style";
10
10
 
11
+ const tabPermissions: Record<string, string> = {
12
+ content: "content",
13
+ config: "config",
14
+ "seo & analytics": "seo",
15
+ };
16
+
11
17
  export const Form = (props: IFormProps): JSX.Element => {
12
18
  const {
13
19
  schema,
14
20
  selectedTab,
15
21
  setSelectedTab,
22
+ setSelectedContent,
16
23
  actions,
17
24
  isPage,
18
25
  isGlobal,
@@ -37,15 +44,19 @@ export const Form = (props: IFormProps): JSX.Element => {
37
44
  (!isGlobal && isAllowedToEditConfigPages) || (isGlobal && isAllowedToEditGlobalData);
38
45
  const isAllowedToEditPageSEO = (!isGlobal && isAllowedToEditSiteSeo) || (isGlobal && isAllowedToEditGlobalSeo);
39
46
 
47
+ const permissionsByTab: Record<string, boolean | undefined> = {
48
+ content: isAllowedToEditPageContent,
49
+ config: isAllowedToEditPageConfig,
50
+ seo: isAllowedToEditPageSEO,
51
+ };
52
+
40
53
  const tabContent = schema.configTabs.find((tab: ISchemaTab) => tab.title.toLowerCase() === selectedTab);
41
- const setTab = (tab: string) => setSelectedTab(tab);
54
+
55
+ const goTo = (editorID: number) => setSelectedContent(editorID);
42
56
 
43
57
  const generateFields = (field: ISchemaField) => {
44
- const { setSelectedContent } = props;
45
58
  const { key, whiteList } = field;
46
59
 
47
- const goTo = (editorID: number) => setSelectedContent(editorID);
48
-
49
60
  return (
50
61
  <ConnectedField
51
62
  selectedTab={selectedTab}
@@ -67,48 +78,47 @@ export const Form = (props: IFormProps): JSX.Element => {
67
78
  );
68
79
  };
69
80
 
70
- const getTabs = () => {
71
- let mappedTabs;
81
+ const getTabs = (): string[] => {
72
82
  const isHeader = schema.type === "header";
73
83
  const isPageSchema = schema.schemaType === "page";
84
+
74
85
  if (isHeader && isPage) {
75
- mappedTabs = schema.configTabs
76
- .map((tab: ISchemaTab) => {
77
- const hasModifiableFields = tab.fields.some((field: any) => field.modifiable);
78
- return hasModifiableFields && tab.title;
79
- })
80
- .filter((value: string | boolean) => !!value);
81
- } else {
82
- mappedTabs = schema.configTabs.reduce((acc: string[], curr: ISchemaTab) => {
83
- const currTitle = curr.title.toLowerCase();
84
- if (
85
- !isPageSchema ||
86
- (isPageSchema &&
87
- ((currTitle === "content" && isAllowedToEditPageContent) ||
88
- (currTitle === "config" && isAllowedToEditPageConfig) ||
89
- (currTitle === "seo & analytics" && isAllowedToEditPageSEO)))
90
- ) {
91
- return [...acc, currTitle];
92
- }
93
- return acc;
94
- }, []);
86
+ return schema.configTabs
87
+ .filter((tab) => tab.fields.some((field: any) => field.modifiable))
88
+ .map((tab) => tab.title);
95
89
  }
96
90
 
97
- return mappedTabs;
91
+ return schema.configTabs
92
+ .filter((tab: ISchemaTab) => {
93
+ const currTitle = tab.title.toLowerCase();
94
+ const permKey = tabPermissions[currTitle];
95
+ return !isPageSchema || (permKey && permissionsByTab[permKey]);
96
+ })
97
+ .map((tab: ISchemaTab) => tab.title.toLowerCase());
98
98
  };
99
99
 
100
100
  const tabs = getTabs();
101
101
 
102
- const handleRestoreHeader = () =>
103
- actions.restorePageNavigationAction && actions.restorePageNavigationAction("header");
102
+ const handleRestore = (type: "header" | "footer") => actions.restorePageNavigationAction?.(type);
104
103
 
105
- const handleRestoreFooter = () =>
106
- actions.restorePageNavigationAction && actions.restorePageNavigationAction("footer");
104
+ const renderRestoreNotice = (type: "header" | "footer", value?: number | null) => {
105
+ if (selectedTab !== "content" || isGlobal || value !== 0) return null;
106
+
107
+ return (
108
+ <S.FieldWrapper>
109
+ This page doesn&apos;t have a {type}. Click{" "}
110
+ <S.Link data-testid={`${type}-link`} onClick={() => handleRestore(type)}>
111
+ here
112
+ </S.Link>{" "}
113
+ to restore it.
114
+ </S.FieldWrapper>
115
+ );
116
+ };
107
117
 
108
118
  return (
109
119
  <S.Wrapper data-testid="form-section">
110
120
  <S.TabsWrapper headerHeight={headerHeight}>
111
- <Tabs tabs={tabs} active={selectedTab} setSelectedTab={setTab} />
121
+ <Tabs tabs={tabs} active={selectedTab} setSelectedTab={setSelectedTab} />
112
122
  </S.TabsWrapper>
113
123
  {selectedTab === "content" && isEditLive && (
114
124
  <S.FieldWrapper>
@@ -116,25 +126,9 @@ export const Form = (props: IFormProps): JSX.Element => {
116
126
  published version, you should <strong>discard or publish your draft</strong>.
117
127
  </S.FieldWrapper>
118
128
  )}
119
- {selectedTab === "content" && !isGlobal && header === 0 && (
120
- <S.FieldWrapper>
121
- This page doesn&apos;t have a header. Click{" "}
122
- <S.Link data-testid="header-link" onClick={handleRestoreHeader}>
123
- here
124
- </S.Link>{" "}
125
- to restore it.
126
- </S.FieldWrapper>
127
- )}
128
- {selectedTab === "content" && !isGlobal && footer === 0 && (
129
- <S.FieldWrapper>
130
- This page doesn&apos;t have a footer. Click{" "}
131
- <S.Link data-testid="footer-link" onClick={handleRestoreFooter}>
132
- here
133
- </S.Link>{" "}
134
- to restore it.
135
- </S.FieldWrapper>
136
- )}
137
- {tabContent && tabContent.fields.map((field: ISchemaField) => generateFields(field))}
129
+ {renderRestoreNotice("header", header)}
130
+ {renderRestoreNotice("footer", footer)}
131
+ {tabContent?.fields.map((field: ISchemaField) => generateFields(field))}
138
132
  </S.Wrapper>
139
133
  );
140
134
  };
@@ -1,10 +1,8 @@
1
- import React from "react";
2
-
3
1
  import { FieldsBehavior } from "@ax/components";
4
2
  import { splitAndTrim } from "@ax/helpers";
5
- import { IAnalytics, IDimension } from "@ax/types";
3
+ import type { IAnalytics, IDimension } from "@ax/types";
6
4
 
7
- import { IState } from "..";
5
+ import type { IState } from "..";
8
6
  import * as S from "../style";
9
7
 
10
8
  const DimensionsGroup = (props: {
@@ -25,11 +23,10 @@ const DimensionsGroup = (props: {
25
23
  return (
26
24
  <>
27
25
  <S.FieldsDivider />
28
- {groupDimensions &&
29
- groupDimensions.map(
30
- (dimension, idx) =>
31
- dimension && <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />,
32
- )}
26
+ {groupDimensions?.map(
27
+ (dimension, idx) =>
28
+ dimension && <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />,
29
+ )}
33
30
  </>
34
31
  );
35
32
  };
@@ -47,10 +44,9 @@ const DimensionsSelection = (props: {
47
44
  return (
48
45
  <>
49
46
  {selectedDimensions?.length ? <S.FieldsDivider /> : <></>}
50
- {selectedDimensions &&
51
- selectedDimensions.map((dimension, idx) => (
52
- <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />
53
- ))}
47
+ {selectedDimensions?.map((dimension, idx) => (
48
+ <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />
49
+ ))}
54
50
  </>
55
51
  );
56
52
  };
@@ -1,26 +1,32 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
 
3
- import { NoteField, FieldsBehavior } from "@ax/components";
3
+ import { FieldsBehavior, NoteField } from "@ax/components";
4
4
 
5
- import { IAnalyticsFieldProps, IState } from "..";
6
- import { DimensionsGroup, DimensionsSelection } from "./atoms";
5
+ import type { IAnalyticsFieldProps, IState } from "..";
7
6
  import * as S from "../style";
7
+ import { DimensionsGroup, DimensionsSelection } from "./atoms";
8
+
9
+ const initialState: IState = {
10
+ contentSelect: "",
11
+ groupSelect: "",
12
+ dimensionsSelect: [],
13
+ values: {},
14
+ };
8
15
 
9
16
  const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
10
17
  const { value, onChange, analytics, disabled } = props;
11
18
 
12
- const initialState = {
13
- contentSelect: "",
14
- groupSelect: "",
15
- dimensionsSelect: [],
16
- values: {},
17
- };
18
-
19
19
  const [state, setState] = useState<IState>(value || initialState);
20
20
 
21
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
21
+ const isMountRef = useRef(true);
22
+
23
+ // biome-ignore lint/correctness/useExhaustiveDependencies: onChange is not memoized by parent, adding it causes infinite loop
22
24
  useEffect(() => {
23
- onChange && onChange(state);
25
+ if (isMountRef.current) {
26
+ isMountRef.current = false;
27
+ return;
28
+ }
29
+ onChange?.(state);
24
30
  }, [state]);
25
31
 
26
32
  const handleContentSelect = (contentSelect: string) => {
@@ -28,7 +34,7 @@ const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
28
34
  };
29
35
 
30
36
  const handleGroupSelect = (groupSelect: string) => {
31
- setState((state: IState) => ({ ...state, groupSelect, values: {} }));
37
+ setState((prev: IState) => ({ ...prev, groupSelect, values: {} }));
32
38
  };
33
39
 
34
40
  const handleDimensionsSelect = (selection: string[]) => {
@@ -36,15 +42,17 @@ const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
36
42
  let dimensionsSelect = selection;
37
43
 
38
44
  if (selection.includes("all")) {
39
- dimensionsSelect = analytics.dimensions?.map((dimension) => dimension.name);
45
+ dimensionsSelect = analytics.dimensions?.map((dimension) => dimension.name) ?? [];
40
46
  }
41
47
 
42
- dimensionsSelect?.forEach((dimension) => (values[dimension] = state.values[dimension]));
43
- setState((state: IState) => ({ ...state, dimensionsSelect, values }));
48
+ dimensionsSelect.forEach((dimension) => {
49
+ values[dimension] = state.values[dimension];
50
+ });
51
+ setState((prev: IState) => ({ ...prev, dimensionsSelect, values }));
44
52
  };
45
53
 
46
54
  const setDimension = (value: Record<string, string>) => {
47
- setState((state: IState) => ({ ...state, values: { ...state.values, ...value } }));
55
+ setState((prev: IState) => ({ ...prev, values: { ...prev.values, ...value } }));
48
56
  };
49
57
 
50
58
  const contentOptions = [
@@ -58,10 +66,11 @@ const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
58
66
  },
59
67
  ];
60
68
 
61
- const groupOptions: { label: string; value: string }[] = [];
62
- analytics.groups?.forEach((group) => {
63
- groupOptions.push({ label: group.name, value: group.name });
64
- });
69
+ const groupOptions =
70
+ analytics.groups?.map((group) => ({
71
+ label: group.name,
72
+ value: group.name,
73
+ })) ?? [];
65
74
 
66
75
  const noteText = (
67
76
  <>
@@ -82,15 +91,12 @@ const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
82
91
  value: "all",
83
92
  title: "Select All",
84
93
  },
85
- ];
86
- analytics.dimensions?.forEach((dimension) => {
87
- const option = {
94
+ ...(analytics.dimensions?.map((dimension) => ({
88
95
  name: dimension.name,
89
96
  value: dimension.name,
90
97
  title: dimension.name,
91
- };
92
- dimensionOptions.push(option);
93
- });
98
+ })) ?? []),
99
+ ];
94
100
 
95
101
  return (
96
102
  <>
@@ -133,7 +139,9 @@ const PageAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
133
139
  disabled={disabled}
134
140
  floating
135
141
  />
136
- {!!state.dimensionsSelect && <DimensionsSelection {...{ analytics, state, setDimension, disabled }} />}
142
+ {state.dimensionsSelect.length > 0 && (
143
+ <DimensionsSelection {...{ analytics, state, setDimension, disabled }} />
144
+ )}
137
145
  </>
138
146
  )}
139
147
  </>
@@ -1,10 +1,8 @@
1
- import React from "react";
2
-
3
1
  import { FieldsBehavior } from "@ax/components";
4
2
  import { splitAndTrim } from "@ax/helpers";
5
- import { IAnalytics, IDimension } from "@ax/types";
3
+ import type { IAnalytics, IDimension } from "@ax/types";
6
4
 
7
- import { IState } from "..";
5
+ import type { IState } from "..";
8
6
 
9
7
  const TemplateDimensions = (props: ITemplateDimensions): JSX.Element => {
10
8
  const { analytics, state, setDimension, dimensionNames, disabled } = props;
@@ -16,11 +14,10 @@ const TemplateDimensions = (props: ITemplateDimensions): JSX.Element => {
16
14
 
17
15
  return (
18
16
  <>
19
- {groupDimensions &&
20
- groupDimensions.map(
21
- (dimension, idx) =>
22
- dimension && <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />,
23
- )}
17
+ {groupDimensions?.map(
18
+ (dimension, idx) =>
19
+ dimension && <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />,
20
+ )}
24
21
  </>
25
22
  );
26
23
  };
@@ -37,10 +34,9 @@ const DimensionsSelection = (props: {
37
34
 
38
35
  return (
39
36
  <>
40
- {selectedDimensions &&
41
- selectedDimensions.map((dimension, idx) => (
42
- <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />
43
- ))}
37
+ {selectedDimensions?.map((dimension, idx) => (
38
+ <DimensionValue key={idx} {...{ dimension, setDimension, values, disabled }} />
39
+ ))}
44
40
  </>
45
41
  );
46
42
  };
@@ -1,11 +1,11 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState } from "react";
2
2
 
3
3
  import { FieldsBehavior } from "@ax/components";
4
4
 
5
- import { IAnalyticsFieldProps, IState } from "..";
6
- import { getTemplateDimensions } from "../utils";
7
- import { TemplateDimensions, DimensionsSelection } from "./atoms";
5
+ import type { IAnalyticsFieldProps, IState } from "..";
8
6
  import * as S from "../style";
7
+ import { getTemplateDimensions } from "../utils";
8
+ import { DimensionsSelection, TemplateDimensions } from "./atoms";
9
9
 
10
10
  const StructuredDataAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
11
11
  const { value, onChange, analytics, template, disabled } = props;
@@ -17,28 +17,32 @@ const StructuredDataAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
17
17
 
18
18
  const [state, setState] = useState<IState>({ ...initialState, ...value });
19
19
 
20
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
20
+ // biome-ignore lint/correctness/useExhaustiveDependencies: onChange is not memoized by parent, adding it causes infinite loop
21
21
  useEffect(() => {
22
- onChange && onChange(state);
22
+ onChange?.(state);
23
23
  }, [state]);
24
24
 
25
25
  const templateDimensions = getTemplateDimensions(analytics.groups, template);
26
26
 
27
27
  const handleDimensionsSelect = (selection: string[]) => {
28
28
  const values: Record<string, string> = {};
29
- templateDimensions.forEach((dimension) => (values[dimension] = state.values[dimension]));
29
+ templateDimensions.forEach((dimension) => {
30
+ values[dimension] = state.values[dimension];
31
+ });
30
32
  let dimensionsSelect = selection;
31
33
 
32
34
  if (selection.includes("all")) {
33
35
  dimensionsSelect = dimensionOptions.map((dimension) => dimension.name);
34
36
  }
35
37
 
36
- dimensionsSelect?.forEach((dimension) => (values[dimension] = state.values[dimension]));
37
- setState((state: IState) => ({ ...state, dimensionsSelect, values }));
38
+ dimensionsSelect?.forEach((dimension) => {
39
+ values[dimension] = state.values[dimension];
40
+ });
41
+ setState((prev: IState) => ({ ...prev, dimensionsSelect, values }));
38
42
  };
39
43
 
40
44
  const setDimension = (value: Record<string, string>) => {
41
- setState((state: IState) => ({ ...state, values: { ...state.values, ...value } }));
45
+ setState((prev: IState) => ({ ...prev, values: { ...prev.values, ...value } }));
42
46
  };
43
47
 
44
48
  const dimensionOptions = [
@@ -76,7 +80,9 @@ const StructuredDataAnalytics = (props: IAnalyticsFieldProps): JSX.Element => {
76
80
  disabled={disabled}
77
81
  floating
78
82
  />
79
- {!!state.dimensionsSelect && <DimensionsSelection {...{ analytics, state, setDimension, disabled }} />}
83
+ {state.dimensionsSelect.length > 0 && (
84
+ <DimensionsSelection {...{ analytics, state, setDimension, disabled }} />
85
+ )}
80
86
  </>
81
87
  )}
82
88
  </>
@@ -1,7 +1,6 @@
1
- import React from "react";
2
1
  import { connect } from "react-redux";
3
2
 
4
- import { IAnalytics, IRootState } from "@ax/types";
3
+ import type { IAnalytics, IRootState } from "@ax/types";
5
4
  import { isEmptyArray } from "@ax/helpers";
6
5
 
7
6
  import PageAnalytics from "./PageAnalytics";
@@ -1,11 +1,11 @@
1
- import { IDimensionsGroup } from "@ax/types";
2
1
  import { splitAndTrim } from "@ax/helpers";
2
+ import type { IDimensionsGroup } from "@ax/types";
3
3
 
4
4
  const getTemplateDimensions = (groups: IDimensionsGroup[], template: string): string[] => {
5
5
  const templateDimensionsGroups = groups?.filter((group) => group.templates?.includes(template));
6
- const templateDimensions = templateDimensionsGroups?.reduce((dimensions: string[], group: IDimensionsGroup) => {
7
- return [...dimensions, ...splitAndTrim(group.dimensions, ";")];
8
- }, []);
6
+ const templateDimensions = templateDimensionsGroups?.flatMap((group: IDimensionsGroup) =>
7
+ splitAndTrim(group.dimensions, ";"),
8
+ );
9
9
  const uniqueTemplateDimensions = [...new Set(templateDimensions)];
10
10
  return uniqueTemplateDimensions;
11
11
  };