@griddo/ax 1.75.158 → 1.75.159

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 (29) hide show
  1. package/package.json +2 -2
  2. package/public/manifest.json +2 -2
  3. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +5 -0
  4. package/src/__tests__/components/ConfigPanel/NavigationForm/Field/Field.test.tsx +111 -11
  5. package/src/__tests__/components/ConfigPanel/NavigationForm/NavigationForm.test.tsx +8 -1
  6. package/src/__tests__/components/SideModal/SideModal.test.tsx +0 -17
  7. package/src/components/Browser/index.tsx +1 -1
  8. package/src/components/ConfigPanel/NavigationForm/Field/index.tsx +76 -23
  9. package/src/components/SideModal/SideModalOption/index.tsx +10 -1
  10. package/src/components/SideModal/SideModalOption/style.tsx +18 -12
  11. package/src/components/SideModal/index.tsx +2 -15
  12. package/src/components/SideModal/style.tsx +0 -5
  13. package/src/containers/Navigation/Defaults/actions.tsx +28 -6
  14. package/src/containers/Navigation/Defaults/index.tsx +2 -5
  15. package/src/containers/Navigation/Defaults/utils.tsx +43 -1
  16. package/src/containers/PageEditor/actions.tsx +21 -5
  17. package/src/containers/PageEditor/utils.tsx +12 -5
  18. package/src/containers/Settings/DataPacks/actions.tsx +3 -2
  19. package/src/forms/errors.tsx +1 -1
  20. package/src/forms/validators.tsx +5 -0
  21. package/src/hooks/forms.tsx +2 -2
  22. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +1 -1
  23. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +1 -1
  24. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +5 -8
  25. package/src/modules/PageEditor/index.tsx +9 -1
  26. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/index.tsx +24 -14
  27. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +2 -15
  28. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +6 -4
  29. package/src/modules/Navigation/Defaults/DefaultsEditor/utils.tsx +0 -37
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "1.75.158",
4
+ "version": "1.75.159",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -230,5 +230,5 @@
230
230
  "publishConfig": {
231
231
  "access": "public"
232
232
  },
233
- "gitHead": "6a2f4572bd0e693a0cc2d5b46b68f1ec3448ef89"
233
+ "gitHead": "6063ff6bcdbd5673c1eba467d6f0898538069ae4"
234
234
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "short_name": "Thesaurus AX",
3
- "name": "Thesaurus AX",
2
+ "short_name": "Griddo AX",
3
+ "name": "Griddo AX",
4
4
  "start_url": ".",
5
5
  "display": "standalone",
6
6
  "theme_color": "#000000",
@@ -99,6 +99,11 @@ const initialStore = {
99
99
  },
100
100
  dataPacks: {
101
101
  templates: [],
102
+ configFormData: {
103
+ defaultParent: null,
104
+ indexDefault: true,
105
+ modifiableOnPage: false,
106
+ },
102
107
  },
103
108
  menu: {
104
109
  savedMenus: null,
@@ -22,12 +22,11 @@ const initialStore = {
22
22
  pageEditor: {
23
23
  selectedContent: {
24
24
  editorID: 0,
25
- title: "selected content field",
26
- id: 1,
27
- component: "module",
25
+ title: "Header name azul",
26
+ id: 144,
27
+ component: "header",
28
28
  },
29
29
  editorContent: {
30
- module: 0,
31
30
  editorID: 0,
32
31
  parentEditorID: null,
33
32
  setAsHome: false,
@@ -38,11 +37,89 @@ const initialStore = {
38
37
  navigation: {
39
38
  currentDefaultsContent: [
40
39
  {
41
- type: "module",
42
- id: 2,
40
+ component: "Header",
41
+ type: "header",
42
+ title: "Header name azul",
43
+ setAsDefault: true,
44
+ id: 144,
45
+ isDefault: true,
46
+ thumbnail: {
47
+ id: 1255,
48
+ name: "navigation-thumbnail.png",
49
+ title: "Thumbnail for Header name azul",
50
+ description: "",
51
+ alt: "",
52
+ tags: [],
53
+ url: "https://images.dev.griddo.io/navigation-thumbnail_186",
54
+ thumb: "https://images.dev.griddo.io/w/215/h/161/navigation-thumbnail_186",
55
+ publicId: "thesaurus-dev/navigation-thumbnail_f1622a50-f703-41ae-b5ed-6a5b0212bb4b",
56
+ damId: "navigation-thumbnail_186",
57
+ published: "2023-01-10T10:10:55.732Z",
58
+ size: 4365,
59
+ width: 526,
60
+ height: 120,
61
+ orientation: "L",
62
+ site: 81,
63
+ },
64
+ site: 81,
65
+ },
66
+ {
67
+ component: "Header",
68
+ type: "header",
69
+ title: "Secondary Header",
70
+ setAsDefault: false,
71
+ id: 165,
72
+ isDefault: false,
73
+ thumbnail: {
74
+ id: 922,
75
+ name: ".png",
76
+ title: "Thumbnail for Secondary Header",
77
+ description: "",
78
+ alt: "",
79
+ tags: [],
80
+ url: "https://images.dev.griddo.io/navigation-thumbnail_28",
81
+ thumb: "https://images.dev.griddo.io/w/215/h/161/navigation-thumbnail_28",
82
+ publicId: "thesaurus-dev/navigation-thumbnail_f73c78ec-af02-47fb-82aa-0727c44e47e3",
83
+ damId: "navigation-thumbnail_28",
84
+ published: "2022-06-08T14:07:11.022Z",
85
+ size: 3150,
86
+ width: 528,
87
+ height: 92,
88
+ orientation: "L",
89
+ site: 81,
90
+ },
91
+ site: 81,
92
+ },
93
+ {
94
+ component: "Footer",
95
+ type: "footer",
96
+ title: "Footer name",
97
+ setAsDefault: true,
98
+ id: 65,
99
+ isDefault: true,
100
+ site: 81,
101
+ thumbnail: null,
102
+ },
103
+ {
104
+ component: "Footer",
105
+ type: "footer",
106
+ title: "Footer name 2",
107
+ setAsDefault: false,
108
+ id: 83,
109
+ site: 81,
110
+ thumbnail: null,
111
+ isDefault: false,
43
112
  },
44
113
  ],
45
114
  },
115
+ dataPacks: {
116
+ configFormData: {
117
+ defaultParent: null,
118
+ indexDefault: true,
119
+ modifiableOnPage: false,
120
+ templates: {},
121
+ },
122
+ },
46
123
  };
47
124
 
48
125
  const store = mockStore(initialStore);
@@ -51,12 +128,11 @@ const defaultFieldProps = mock<IField>();
51
128
  const defaultStateProps = mock<IStateProps>();
52
129
  const defaultDispatchProps = mock<IDispatchProps>();
53
130
 
54
- defaultFieldProps.type = "module";
55
-
56
131
  const defaultProps = { ...defaultFieldProps, ...defaultStateProps, ...defaultDispatchProps };
57
132
 
58
133
  describe("Field component rendering", () => {
59
134
  it("should render component", () => {
135
+ defaultProps.type = "header";
60
136
  render(
61
137
  <ThemeProvider theme={parseTheme(globalTheme)}>
62
138
  <Field {...defaultProps} />
@@ -64,11 +140,32 @@ describe("Field component rendering", () => {
64
140
  { store }
65
141
  );
66
142
 
67
- const componentWrapper = screen.getByText(/selected content field/i);
143
+ const componentWrapper = screen.getByText(/Header name azul/i);
68
144
  expect(componentWrapper).toBeInTheDocument();
69
145
  });
70
146
 
71
- it("should render SideModal because has multiple options", () => {
147
+ it("should render SideModal with 1 header options because 1 is selected", () => {
148
+ defaultProps.type = "header";
149
+ render(
150
+ <ThemeProvider theme={parseTheme(globalTheme)}>
151
+ <Field {...defaultProps} />
152
+ </ThemeProvider>,
153
+ { store }
154
+ );
155
+
156
+ const moreInfo = screen.getByTestId("more-info-button");
157
+ fireEvent.click(moreInfo);
158
+ const listItem = screen.getAllByTestId("action-menu-item");
159
+ expect(listItem).toHaveLength(2);
160
+ fireEvent.click(listItem[0]);
161
+ const sideModalWrapper = screen.getByTestId("side-modal");
162
+ expect(sideModalWrapper).toBeTruthy();
163
+ const sideModalOptions = screen.getAllByTestId("side-modal-option");
164
+ expect(sideModalOptions).toHaveLength(1);
165
+ });
166
+
167
+ it("should render SideModal with 2 footer option because none is selected", () => {
168
+ defaultProps.type = "footer";
72
169
  render(
73
170
  <ThemeProvider theme={parseTheme(globalTheme)}>
74
171
  <Field {...defaultProps} />
@@ -83,9 +180,12 @@ describe("Field component rendering", () => {
83
180
  fireEvent.click(listItem[0]);
84
181
  const sideModalWrapper = screen.getByTestId("side-modal");
85
182
  expect(sideModalWrapper).toBeTruthy();
183
+ const sideModalOptions = screen.getAllByTestId("side-modal-option");
184
+ expect(sideModalOptions).toHaveLength(2);
86
185
  });
87
186
 
88
187
  it("should trigger the handleRemove action", () => {
188
+ defaultProps.type = "header";
89
189
  render(
90
190
  <ThemeProvider theme={parseTheme(globalTheme)}>
91
191
  <Field {...defaultProps} />
@@ -99,7 +199,7 @@ describe("Field component rendering", () => {
99
199
  expect(listItem).toHaveLength(2);
100
200
  fireEvent.click(listItem[1]);
101
201
  expect(store.getActions()).toContainEqual({
102
- payload: { editorContent: initialStore.pageEditor.editorContent },
202
+ payload: { editorContent: { ...initialStore.pageEditor.editorContent, header: 0 } },
103
203
  type: "pageEditor/SET_EDITOR_CONTENT",
104
204
  });
105
205
  });
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import { ThemeProvider } from "styled-components";
4
- import { render, cleanup, screen, fireEvent } from "../../../../../config/jest/test-utils";
4
+ import { render, cleanup, screen } from "../../../../../config/jest/test-utils";
5
5
  import { mock } from "jest-mock-extended";
6
6
  import configureStore from "redux-mock-store";
7
7
  import "@testing-library/jest-dom";
@@ -43,6 +43,13 @@ const initialStore = {
43
43
  },
44
44
  ],
45
45
  },
46
+ dataPacks: {
47
+ configFormData: {
48
+ defaultParent: null,
49
+ indexDefault: true,
50
+ modifiableOnPage: false,
51
+ },
52
+ },
46
53
  };
47
54
 
48
55
  const store = mockStore(initialStore);
@@ -85,24 +85,7 @@ describe("SideModal component rendering", () => {
85
85
  expect(closeButton).toBeTruthy();
86
86
  });
87
87
 
88
- it("should render default check if setDefault prop exists", () => {
89
- defaultProps.setDefault = {
90
- action: jest.fn(),
91
- checked: false,
92
- title: "Set the default header",
93
- };
94
- render(
95
- <ThemeProvider theme={parseTheme(globalTheme)}>
96
- <SideModal {...defaultProps} />
97
- </ThemeProvider>
98
- );
99
-
100
- const checkfield = screen.getByTestId("check-field-wrapper");
101
- expect(checkfield).toBeTruthy();
102
- });
103
-
104
88
  it("should render options", () => {
105
- defaultProps.setDefault = undefined;
106
89
  render(
107
90
  <ThemeProvider theme={parseTheme(globalTheme)}>
108
91
  <SideModal {...defaultProps} />
@@ -188,7 +188,7 @@ export interface IBrowserProps {
188
188
  siteID?: number;
189
189
  isPreview?: boolean;
190
190
  showIframe?: boolean;
191
- browserRef?: any;
191
+ browserRef?: React.RefObject<HTMLDivElement>;
192
192
  actions?: {
193
193
  setSelectedContentAction: any;
194
194
  deleteModuleAction(editorID: number): void;
@@ -8,10 +8,31 @@ import { IRootState } from "@ax/types";
8
8
  import * as S from "./style";
9
9
 
10
10
  const Field = (props: IProps) => {
11
- const { type, defaults, updateEditorContent, selectedContent, theme, removeNavigationFromPage } = props;
11
+ const {
12
+ type,
13
+ defaults,
14
+ updateEditorContent,
15
+ selectedContent,
16
+ theme,
17
+ removeNavigationFromPage,
18
+ template,
19
+ configFormData,
20
+ } = props;
12
21
 
13
22
  const { isOpen, toggleModal } = useModal();
14
- const isDefault = selectedContent.setAsDefault;
23
+
24
+ const defaultSiteHeader = defaults.find(
25
+ (component: any) => component.setAsDefault && component.type === "header"
26
+ )?.id;
27
+ const defaultSiteFooter = defaults.find(
28
+ (component: any) => component.setAsDefault && component.type === "footer"
29
+ )?.id;
30
+
31
+ const { defaultHeader: defaultTemplateHeader, defaultFooter: defaultTemplateFooter } =
32
+ (configFormData.templates && configFormData.templates[template]) || {};
33
+
34
+ const defaultOption =
35
+ type === "header" ? defaultTemplateHeader || defaultSiteHeader : defaultTemplateFooter || defaultSiteFooter;
15
36
 
16
37
  const options = defaults.filter((component: any) => {
17
38
  const isCurrentType = component.type === type;
@@ -19,34 +40,63 @@ const Field = (props: IProps) => {
19
40
  return isCurrentType && isNotSelected;
20
41
  });
21
42
 
22
- const hasMultipleOptions: boolean | undefined = options && options.length > 0;
43
+ const templateDefaultOption = options
44
+ .filter(
45
+ (option: any) =>
46
+ (type === "header" && option.id === defaultTemplateHeader) ||
47
+ (type === "footer" && option.id === defaultTemplateFooter)
48
+ )
49
+ .map((option: any) =>
50
+ option.id === defaultOption
51
+ ? { ...option, isDefault: true, tag: "template default" }
52
+ : { ...option, isDefault: false, tag: "template default" }
53
+ )?.[0];
54
+
55
+ const siteDefaultOption = options
56
+ .filter(
57
+ (option: any) =>
58
+ (type === "header" && option.id === defaultSiteHeader) || (type === "footer" && option.id === defaultSiteFooter)
59
+ )
60
+ .map((option: any) => {
61
+ const templateHasDefaultSiteHeader = type === "header" && defaultTemplateHeader === null;
62
+ const templateHasDefaultSiteFooter = type === "footer" && defaultTemplateFooter === null;
63
+
64
+ const tag = templateHasDefaultSiteHeader || templateHasDefaultSiteFooter ? "template default" : "site default";
65
+
66
+ const hasDefaultTemplateHeader = type === "header" && defaultTemplateHeader;
67
+ const hasDefaultTemplateFooter = type === "footer" && defaultTemplateFooter;
68
+
69
+ return option.id === defaultOption
70
+ ? {
71
+ ...option,
72
+ isDefault: true,
73
+ tag: hasDefaultTemplateHeader || hasDefaultTemplateFooter ? "" : tag,
74
+ }
75
+ : {
76
+ ...option,
77
+ isDefault: false,
78
+ tag: hasDefaultTemplateHeader || hasDefaultTemplateFooter ? "" : tag,
79
+ };
80
+ })?.[0];
81
+
82
+ const otherOptions = options.filter(
83
+ (option: any) => option.id !== templateDefaultOption?.id && option.id !== siteDefaultOption?.id
84
+ );
23
85
 
24
- let filteredByDefaultOptions = options;
25
- if (!isDefault && hasMultipleOptions) {
26
- filteredByDefaultOptions = options.filter((component: any) => !component.setAsDefault);
27
- }
86
+ let orderedOptions = siteDefaultOption ? [siteDefaultOption, ...otherOptions] : otherOptions;
87
+ orderedOptions = templateDefaultOption ? [templateDefaultOption, ...orderedOptions] : orderedOptions;
28
88
 
89
+ const hasMultipleOptions: boolean | undefined = options && options.length > 0;
29
90
  const optionsType = `${type}s`;
30
91
  const actionMenuIcon = "more";
31
92
  const pageEditorID = 0;
32
93
 
33
94
  const handleReplace = (option: any) => {
34
- updateEditorContent(pageEditorID, type, option.id);
35
- };
36
-
37
- const handleRemove = () => {
38
- removeNavigationFromPage(type);
95
+ const optionID = option.isDefault ? null : option.id;
96
+ updateEditorContent(pageEditorID, type, optionID);
39
97
  };
40
98
 
41
- const setDefault = {
42
- title: `Set the default ${type}`,
43
- action: () => {
44
- const option = defaults.find((component: any) => component.type === type && component.setAsDefault);
45
- handleReplace(option);
46
- toggleModal();
47
- },
48
- checked: selectedContent.setAsDefault,
49
- };
99
+ const handleRemove = () => removeNavigationFromPage(type);
50
100
 
51
101
  const actionMenuOptions = [
52
102
  {
@@ -75,11 +125,10 @@ const Field = (props: IProps) => {
75
125
  {hasMultipleOptions && (
76
126
  <SideModal
77
127
  optionsType={optionsType}
78
- whiteList={filteredByDefaultOptions}
128
+ whiteList={orderedOptions}
79
129
  handleClick={handleReplace}
80
130
  toggleModal={toggleModal}
81
131
  isOpen={isOpen}
82
- setDefault={setDefault}
83
132
  theme={theme}
84
133
  />
85
134
  )}
@@ -95,6 +144,8 @@ export interface IField {
95
144
  export interface IStateProps {
96
145
  defaults: any;
97
146
  selectedContent: any;
147
+ template: string;
148
+ configFormData: any;
98
149
  }
99
150
 
100
151
  export interface IDispatchProps {
@@ -107,6 +158,8 @@ type IProps = IField & IStateProps & IDispatchProps;
107
158
  const mapStateToProps = (state: IRootState) => ({
108
159
  defaults: state.navigation.currentDefaultsContent,
109
160
  selectedContent: state.pageEditor.selectedContent,
161
+ template: state.pageEditor.template,
162
+ configFormData: state.dataPacks.configFormData,
110
163
  });
111
164
 
112
165
  const mapDispatchToProps = {
@@ -1,7 +1,9 @@
1
1
  import React, { memo } from "react";
2
2
 
3
- import * as S from "./style";
4
3
  import { getDisplayName, getThumbnailProps, filterImageText } from "@ax/helpers";
4
+ import { Tag } from "@ax/components";
5
+
6
+ import * as S from "./style";
5
7
 
6
8
  const getThumbnailData = (option: any, theme: string) => {
7
9
  option = filterImageText(option.component ? option.component : option);
@@ -26,10 +28,17 @@ const SideModalOption = (props: IProps) => {
26
28
  toggleModal();
27
29
  };
28
30
 
31
+ const defaultTag = option.tag ? (
32
+ <S.TagWrapper data-testid="side-modal-option-tag">
33
+ <Tag text={option.tag} type="square" />
34
+ </S.TagWrapper>
35
+ ) : null;
36
+
29
37
  return (
30
38
  <S.Item onClick={setOption} data-testid="side-modal-option">
31
39
  <S.Thumbnail data-testid="side-modal-option-img" {...thumbnailProps} />
32
40
  {label}
41
+ {defaultTag}
33
42
  </S.Item>
34
43
  );
35
44
  };
@@ -1,23 +1,29 @@
1
1
  import styled from "styled-components";
2
2
 
3
- export const Item = styled.li`
3
+ const Item = styled.li`
4
4
  cursor: pointer;
5
- padding: ${p => p.theme.spacing.xs};
6
- padding-bottom: ${p => p.theme.spacing.s};
7
- margin-bottom: ${p => p.theme.spacing.s};
8
- box-shadow: ${p => p.theme.shadow.shadowS};
9
- border-radius: ${p => p.theme.radii.s};
10
- ${p => p.theme.textStyle.uiS};
11
- background-color: ${p => p.theme.color.interactiveBackground};
5
+ padding: ${(p) => p.theme.spacing.xs};
6
+ padding-bottom: ${(p) => p.theme.spacing.s};
7
+ margin-bottom: ${(p) => p.theme.spacing.s};
8
+ box-shadow: ${(p) => p.theme.shadow.shadowS};
9
+ border-radius: ${(p) => p.theme.radii.s};
10
+ ${(p) => p.theme.textStyle.uiS};
11
+ background-color: ${(p) => p.theme.color.interactiveBackground};
12
12
  &:hover {
13
- background: ${p => p.theme.color.overlayHoverPrimary};
13
+ background: ${(p) => p.theme.color.overlayHoverPrimary};
14
14
  }
15
15
  &:focus {
16
- background-color: ${p => p.theme.color.overlayFocusPrimary};
16
+ background-color: ${(p) => p.theme.color.overlayFocusPrimary};
17
17
  }
18
18
  `;
19
19
 
20
- export const Thumbnail = styled.img`
20
+ const Thumbnail = styled.img`
21
21
  cursor: pointer;
22
- padding-bottom: ${p => p.theme.spacing.s};
22
+ padding-bottom: ${(p) => p.theme.spacing.s};
23
23
  `;
24
+
25
+ const TagWrapper = styled.div`
26
+ margin-top: ${(p) => p.theme.spacing.xs};
27
+ `;
28
+
29
+ export { Item, Thumbnail, TagWrapper };
@@ -4,7 +4,7 @@ import { createPortal } from "react-dom";
4
4
  import { useHandleClickOutside } from "@ax/hooks";
5
5
  import { getDisplayName, filterByCategory } from "@ax/helpers";
6
6
  import SideModalOption from "@ax/components/SideModal/SideModalOption";
7
- import { CheckField, MenuItem, SearchField, IconAction } from "@ax/components";
7
+ import { MenuItem, SearchField, IconAction } from "@ax/components";
8
8
  import { ModuleCategoryInfo } from "@ax/types";
9
9
 
10
10
  import * as S from "./style";
@@ -19,7 +19,6 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
19
19
  categories,
20
20
  handleClick,
21
21
  current,
22
- setDefault,
23
22
  showSearch,
24
23
  theme,
25
24
  } = props;
@@ -158,17 +157,6 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
158
157
  </S.ButtonWrapper>
159
158
  )}
160
159
  </S.Header>
161
- {setDefault && !setDefault.checked && (
162
- <S.CheckFieldWrapper data-testid="check-field-wrapper">
163
- <CheckField
164
- name="setDefault"
165
- value="setDefault"
166
- title={setDefault.title}
167
- onChange={setDefault.action}
168
- checked={setDefault.checked}
169
- />
170
- </S.CheckFieldWrapper>
171
- )}
172
160
  <S.ColumnsWrapper>
173
161
  {(filters || featuredFilters) && (
174
162
  <S.Content>
@@ -186,14 +174,13 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
186
174
 
187
175
  export interface ISideModalProps {
188
176
  isOpen: boolean;
189
- whiteList: string[] | undefined;
177
+ whiteList?: string[];
190
178
  categories?: ModuleCategoryInfo[];
191
179
  optionsType: string;
192
180
  toggleModal: () => void;
193
181
  handleClick?: (moduleType: string) => void;
194
182
  componentOptions?: any;
195
183
  current?: any;
196
- setDefault?: any;
197
184
  showSearch?: boolean;
198
185
  theme: string;
199
186
  }
@@ -52,10 +52,6 @@ const Content = styled.div`
52
52
 
53
53
  const NavLink = styled.a``;
54
54
 
55
- const CheckFieldWrapper = styled.div`
56
- padding: ${(p) => p.theme.spacing.m} ${(p) => p.theme.spacing.m} 0;
57
- `;
58
-
59
55
  const FeaturedWrapper = styled.div`
60
56
  border-bottom: 1px solid ${(p) => p.theme.colors.uiLine};
61
57
  padding-bottom: ${(p) => p.theme.spacing.xs};
@@ -79,7 +75,6 @@ export {
79
75
  Title,
80
76
  ColumnsWrapper,
81
77
  NavLink,
82
- CheckFieldWrapper,
83
78
  FeaturedWrapper,
84
79
  SearchWrapper,
85
80
  Link,
@@ -24,7 +24,7 @@ import {
24
24
 
25
25
  import { appActions } from "@ax/containers/App";
26
26
 
27
- import { getFormData, getStateValues } from "./utils";
27
+ import { getFormData, getStateValues, getImage } from "./utils";
28
28
 
29
29
  import {
30
30
  SET_EDITOR_CONTENT,
@@ -221,9 +221,11 @@ function getNavigationByType(type: string): (dispatch: Dispatch, getState: any)
221
221
  };
222
222
  }
223
223
 
224
- function createNavigation(image: File | null): (dispatch: Dispatch, getState: any) => Promise<boolean> {
224
+ function createNavigation(navHtml?: HTMLDivElement | null): (dispatch: Dispatch, getState: any) => Promise<boolean> {
225
225
  return async (dispatch, getState) => {
226
226
  try {
227
+ dispatch(appActions.setIsSaving(true));
228
+
227
229
  const {
228
230
  navigation: { editorContent, isNewTranslation },
229
231
  sites: { currentSiteInfo },
@@ -234,6 +236,11 @@ function createNavigation(image: File | null): (dispatch: Dispatch, getState: an
234
236
  delete editorContent.id;
235
237
  }
236
238
 
239
+ let image = null;
240
+ if (navHtml) {
241
+ image = await getImage(navHtml);
242
+ }
243
+
237
244
  const navigationValues = { ...editorContent, site: currentSiteInfo.id, language: lang.id };
238
245
  const cleanValues = removeEditorIds(navigationValues);
239
246
 
@@ -243,16 +250,20 @@ function createNavigation(image: File | null): (dispatch: Dispatch, getState: an
243
250
  const updatedContent = { ...editorContent, ...response };
244
251
  generateContent(updatedContent, dispatch, getState);
245
252
  dispatch(setIsNewTranslation(false));
253
+ dispatch(appActions.setIsSaving(false));
246
254
  };
247
255
 
248
256
  const responseActions = {
249
257
  handleSuccess: (response: any) => successAction(response),
250
- handleError: (response: any) => appActions.handleError(response)(dispatch),
258
+ handleError: (response: any) => {
259
+ appActions.handleError(response)(dispatch);
260
+ dispatch(appActions.setIsSaving(false));
261
+ },
251
262
  };
252
263
 
253
264
  const callback = async () => navigation.createNavigation(form);
254
265
 
255
- return await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
266
+ return await handleRequest(callback, responseActions, [])(dispatch);
256
267
  } catch (e) {
257
268
  console.log(e);
258
269
  return false;
@@ -264,15 +275,22 @@ function updateNavigation(
264
275
  navID: number,
265
276
  data: any,
266
277
  fromEditor?: boolean,
267
- image?: File | null
278
+ navHtml?: HTMLDivElement | null
268
279
  ): (dispatch: Dispatch, getState: any) => Promise<boolean> {
269
280
  return async (dispatch, getState) => {
270
281
  try {
282
+ dispatch(appActions.setIsSaving(true));
283
+
271
284
  const { isNewTranslation } = getStateValues(getState);
272
285
  if (isNewTranslation) {
273
286
  delete data.id;
274
287
  }
275
288
 
289
+ let image = null;
290
+ if (navHtml) {
291
+ image = await getImage(navHtml);
292
+ }
293
+
276
294
  const cleanValues = removeEditorIds(data);
277
295
 
278
296
  const form = getFormData(cleanValues, image);
@@ -283,10 +301,14 @@ function updateNavigation(
283
301
  } else {
284
302
  getNavigationByType(data.type)(dispatch, getState);
285
303
  }
304
+ dispatch(appActions.setIsSaving(false));
286
305
  };
287
306
  const responseActions = {
288
307
  handleSuccess: (response: any) => successAction(response),
289
- handleError: (response: any) => appActions.handleError(response)(dispatch),
308
+ handleError: (response: any) => {
309
+ appActions.handleError(response)(dispatch);
310
+ dispatch(appActions.setIsSaving(false));
311
+ },
290
312
  };
291
313
 
292
314
  const callback = async () => navigation.updateNavigation(navID, form);
@@ -1,7 +1,4 @@
1
1
  import * as navigationActions from "./actions";
2
- import { navigationReducer } from "./reducer";
2
+ import { navigationReducer } from "./reducer";
3
3
 
4
- export {
5
- navigationActions,
6
- navigationReducer
7
- };
4
+ export { navigationActions, navigationReducer };
@@ -1,3 +1,5 @@
1
+ import { toBlob } from "html-to-image";
2
+
1
3
  const getStateValues = (getState: any) => {
2
4
  const {
3
5
  sites: { currentSiteInfo },
@@ -42,4 +44,44 @@ const getFormData = (values: any, image?: File | null): FormData => {
42
44
  return form;
43
45
  };
44
46
 
45
- export { getStateValues, getFormData };
47
+ const getImage = async (navHtml: HTMLDivElement): Promise<File | null> => {
48
+ const browserContent = navHtml.querySelector<HTMLElement>(".browser-content");
49
+
50
+ if (!browserContent) {
51
+ return null;
52
+ }
53
+
54
+ const { height, overflow } = getComputedStyle(browserContent);
55
+ browserContent.style.height = "auto";
56
+ browserContent.style.overflow = "visible";
57
+ const originalHeight = browserContent.clientHeight;
58
+
59
+ const elChildren = browserContent.querySelectorAll("*");
60
+ let maxAbsoluteElementHeight = 0;
61
+ [].forEach.call(elChildren, function (element: any) {
62
+ const isAbsolutePosition = getComputedStyle(element).position === "absolute";
63
+ const isMaxAbsolutePositionHeight = element.clientHeight > maxAbsoluteElementHeight;
64
+ if (isAbsolutePosition && isMaxAbsolutePositionHeight) {
65
+ maxAbsoluteElementHeight = element.clientHeight;
66
+ }
67
+ });
68
+ if (maxAbsoluteElementHeight) {
69
+ const actualHeight = originalHeight + maxAbsoluteElementHeight;
70
+ browserContent.style.height = `${actualHeight}px`;
71
+ }
72
+
73
+ return toBlob(browserContent, { quality: 0.95, pixelRatio: 0.5 })
74
+ .then((imageBlob) => {
75
+ return imageBlob && new File([imageBlob], "navigation-thumbnail.png", { type: "image/png" });
76
+ })
77
+ .finally(() => {
78
+ browserContent.style.height = height;
79
+ browserContent.style.overflow = overflow;
80
+ })
81
+ .catch((err) => {
82
+ //console.log(err);
83
+ return null;
84
+ });
85
+ };
86
+
87
+ export { getStateValues, getFormData, getImage };
@@ -197,9 +197,7 @@ function setTranslatedParent(): (dispatch: Dispatch, getState: any) => void {
197
197
  app: { lang },
198
198
  pageEditor: {
199
199
  selectedEditorID,
200
- editorContent: {
201
- editorContent: { parent, site, entity, language },
202
- },
200
+ editorContent: { parent, site, entity, language },
203
201
  },
204
202
  } = getState();
205
203
 
@@ -227,9 +225,15 @@ function setTranslatedParent(): (dispatch: Dispatch, getState: any) => void {
227
225
  function createNewTranslation(isNewTranslation: boolean): (dispatch: Dispatch, getState: any) => void {
228
226
  return async (dispatch, getState) => {
229
227
  try {
228
+ const {
229
+ sites: { currentSiteInfo },
230
+ } = getState();
231
+
230
232
  dispatch(setIsNewTranslation(isNewTranslation));
231
233
  dispatch(setCurrentPageStatus("offline"));
232
- await getSiteDefaults()(dispatch, getState);
234
+ if (currentSiteInfo) {
235
+ await getSiteDefaults()(dispatch, getState);
236
+ }
233
237
  } catch (e) {
234
238
  console.log(e); // TODO: capturar error bien
235
239
  }
@@ -385,7 +389,7 @@ function savePage(
385
389
  pageEditor: {
386
390
  selectedEditorID,
387
391
  isNewTranslation,
388
- editorContent: { header, footer },
392
+ editorContent: { header, footer, templateConfig, template },
389
393
  },
390
394
  sites: { currentSiteInfo },
391
395
  app: { lang },
@@ -413,6 +417,18 @@ function savePage(
413
417
  delete values.id;
414
418
  }
415
419
 
420
+ /* remove header and footer if page should get the default */
421
+ const { defaultHeader, defaultFooter } =
422
+ (templateConfig && templateConfig.templates && templateConfig.templates[template.templateType]) || {};
423
+
424
+ if (header && ((header.setAsDefault && !defaultHeader) || header.id === defaultHeader)) {
425
+ values["header"] = null;
426
+ }
427
+
428
+ if (footer && ((footer.setAsDefault && !defaultFooter) || footer.id === defaultFooter)) {
429
+ values["footer"] = null;
430
+ }
431
+
416
432
  const saveResponse =
417
433
  isNewPage || isNewTranslation || createDraft
418
434
  ? await pages.createPage(values)
@@ -70,19 +70,26 @@ const getPageNavigation = (
70
70
  const headers: any = defaultsContent.filter((content: any) => content.type === "header");
71
71
  const footers: any = defaultsContent.filter((content: any) => content.type === "footer");
72
72
 
73
+ const headerDefault = headers.find((content: any) => content.setAsDefault);
74
+ const footerDefault = footers.find((content: any) => content.setAsDefault);
75
+
73
76
  const header =
74
77
  headerID === 0
75
78
  ? headerID
76
79
  : headerID === undefined || headerID === null
77
- ? headers.find((content: any) => content.setAsDefault)
78
- : headers.find((content: any) => content.id === headerID);
80
+ ? headerDefault
81
+ : headers.find((content: any) =>
82
+ content.navigationLanguages.some((navLang: any) => navLang.navigationId === headerID)
83
+ ) || headerDefault;
79
84
 
80
85
  const footer =
81
86
  footerID === 0
82
- ? headerID
87
+ ? footerID
83
88
  : footerID === undefined || footerID === null
84
- ? footers.find((content: any) => content.setAsDefault)
85
- : footers.find((content: any) => content.id === footerID);
89
+ ? footerDefault
90
+ : footers.find((content: any) =>
91
+ content.navigationLanguages.some((navLang: any) => navLang.navigationId === footerID)
92
+ ) || footerDefault;
86
93
 
87
94
  return { header, footer };
88
95
  };
@@ -203,7 +203,7 @@ function deleteSiteDataPack(
203
203
  };
204
204
  }
205
205
 
206
- function updateDataPack(dataPackID: string): (dispatch: Dispatch, getState: any) => Promise<void> {
206
+ function updateDataPack(dataPackID: string): (dispatch: Dispatch, getState: any) => Promise<boolean> {
207
207
  return async (dispatch, getState) => {
208
208
  try {
209
209
  const {
@@ -231,9 +231,10 @@ function updateDataPack(dataPackID: string): (dispatch: Dispatch, getState: any)
231
231
 
232
232
  const callback = async () => dataPack.updateDataPack(currentSiteID, dataPackID, configFormData);
233
233
 
234
- await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
234
+ return await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
235
235
  } catch (e) {
236
236
  console.log(e); // TODO: capturar error bien
237
+ return false;
237
238
  }
238
239
  };
239
240
  }
@@ -17,7 +17,7 @@ const ERRORS: Record<string, string> = {
17
17
  ERR016: "This field is still filled with mockup data.",
18
18
  ERR017: "The URL is already in use.",
19
19
  ERR018: "No H1 in this page.",
20
- ERR019: "Not enough entries of {value} published",
20
+ ERR019: "Not enough entries published. {value} items required",
21
21
  ERR020: "Sorry, email or password are not correct. ",
22
22
  ERR021: "Sorry, we need some info to let you in. Check the fields below.",
23
23
  ERR022: "Sorry, this page has not been published. We are working to solve it.",
@@ -128,6 +128,10 @@ const VALIDATORS = {
128
128
  const isValid = pass1 === pass2;
129
129
  return { isValid, errorCode: "ERR041" };
130
130
  },
131
+ minItems: (val: any, len: number): IError => {
132
+ const isValid = val.mode === "auto" || (val.fixed && val.fixed.length >= len);
133
+ return { isValid, errorCode: "ERR019" };
134
+ },
131
135
  };
132
136
 
133
137
  const getErrorMessage = (key: string, val: number | string | null): string => {
@@ -269,6 +273,7 @@ const getValidationErrors = (
269
273
 
270
274
  let fieldValidators: Record<string, unknown> = field.maxValue ? { maxValue: field.maxValue } : {};
271
275
  fieldValidators = field.minValue ? { ...fieldValidators, minValue: field.minValue } : fieldValidators;
276
+ fieldValidators = field.minItems ? { ...fieldValidators, minItems: field.minItems } : fieldValidators;
272
277
 
273
278
  if (hasProps(field, ["validators"]) || Object.keys(fieldValidators).length) {
274
279
  const allValidators = { ...field.validators, ...fieldValidators };
@@ -77,10 +77,10 @@ const useIsDirty = (
77
77
  return false;
78
78
  };
79
79
 
80
- const resetDirty = () => {
80
+ const resetDirty = (reseting = true) => {
81
81
  setIsDirty(false);
82
82
  setIsSaved(true);
83
- setIsResetting(true);
83
+ reseting && setIsResetting(true);
84
84
  };
85
85
 
86
86
  useEffect(() => {
@@ -36,7 +36,7 @@ const DefaultsBrowser = (props: IProps) => {
36
36
 
37
37
  interface IDefaultsBrowserProps {
38
38
  actions: any;
39
- browserRef?: any;
39
+ browserRef?: React.RefObject<HTMLDivElement>;
40
40
  }
41
41
 
42
42
  interface IDefaultsBrowserStateProps {
@@ -79,7 +79,7 @@ interface IPageBrowserDispatchProps {
79
79
  replaceModule(module: any, parent: any, objKey: string): void;
80
80
  replaceElementsInCollection(newValue: string, reference: string): void;
81
81
  moveModule(moduleID: number, selectedContent: any, newIndex: number, key: string): void;
82
- browserRef?: any;
82
+ browserRef?: React.RefObject<HTMLDivElement>;
83
83
  }
84
84
 
85
85
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -11,7 +11,6 @@ import { ErrorToast, Loading, MainWrapper, Modal } from "@ax/components";
11
11
  import Editor from "./Editor";
12
12
 
13
13
  import * as S from "./style";
14
- import { getImage } from "./utils";
15
14
 
16
15
  const DefaultsEditor = (props: IProps) => {
17
16
  const {
@@ -41,7 +40,7 @@ const DefaultsEditor = (props: IProps) => {
41
40
  const isNew = !editorContent?.id;
42
41
 
43
42
  const isSetAsDefault = editorContent && editorContent.setAsDefault;
44
- const browserRef = useRef<any>(null);
43
+ const browserRef = useRef<HTMLDivElement>(null);
45
44
 
46
45
  useEffect(() => {
47
46
  getValues();
@@ -67,12 +66,10 @@ const DefaultsEditor = (props: IProps) => {
67
66
  }, [currentSiteInfo, editorContent?.id]);
68
67
 
69
68
  const save = async () => {
70
- const image = await getImage(browserRef);
71
-
72
69
  const isSaved =
73
70
  isNew || isNewTranslation
74
- ? await createNavigation(image)
75
- : await updateNavigation(editorContent.id, editorContent, true, image);
71
+ ? await createNavigation(browserRef?.current)
72
+ : await updateNavigation(editorContent.id, editorContent, true, browserRef?.current);
76
73
  if (isSaved) resetDirty();
77
74
  };
78
75
 
@@ -204,8 +201,8 @@ interface IDispatchProps {
204
201
  setHistoryPush(path: string, isEditor?: boolean): void;
205
202
  setLanguage?(lang: { locale: string; id: number | null }): void;
206
203
  getValues(): void;
207
- createNavigation(image: File | null): Promise<boolean>;
208
- updateNavigation(navID: number, data: any, fromEditor?: boolean, image?: File | null): Promise<boolean>;
204
+ createNavigation(navHtml?: HTMLDivElement | null): Promise<boolean>;
205
+ updateNavigation(navID: number, data: any, fromEditor?: boolean, navHtml?: HTMLDivElement | null): Promise<boolean>;
209
206
  createTranslation(isNewTranslation: boolean): void;
210
207
  setHeader(id: number | null): void;
211
208
  setFooter(id: number | null): void;
@@ -126,6 +126,7 @@ const PageEditor = (props: IProps) => {
126
126
 
127
127
  const allPageVersions = pageLanguages.map((lang: IPageLanguage) => lang.pageId);
128
128
  const isDeleted = deleteAllVersions ? await deleteBulk(allPageVersions) : await deletePage();
129
+
129
130
  toggleDeleteModal();
130
131
  if (isDeleted) {
131
132
  setRoute(path);
@@ -149,7 +150,14 @@ const PageEditor = (props: IProps) => {
149
150
  }
150
151
  };
151
152
 
152
- pageID ? updatePageStatus([pageID], pageStatus.UPLOAD_PENDING) : await handleSavePage();
153
+ const handleUpdatePageStatus = async () => {
154
+ const isUpdated = await updatePageStatus([pageID], pageStatus.UPLOAD_PENDING);
155
+ if (isUpdated) {
156
+ resetDirty();
157
+ }
158
+ };
159
+
160
+ pageID ? await handleUpdatePageStatus() : await handleSavePage();
153
161
  } else {
154
162
  setNotification({ text: errorNotificationText, type: "error" });
155
163
  }
@@ -19,11 +19,27 @@ const Field = (props: IField): JSX.Element => {
19
19
  theme,
20
20
  } = props;
21
21
  const { isOpen, toggleModal } = useModal();
22
+
23
+ const optionsType = `${type}s`;
24
+
25
+ const defaultSiteHeader = defaults.find(
26
+ (component: any) => component.setAsDefault && component.type === "header"
27
+ )?.id;
28
+ const defaultSiteFooter = defaults.find(
29
+ (component: any) => component.setAsDefault && component.type === "footer"
30
+ )?.id;
31
+
32
+ const defaultOption = type === "header" ? defaultSiteHeader : defaultSiteFooter;
33
+
22
34
  const options = defaults.filter(
23
- (component: any) =>
24
- component.type === type && content[component.type]?.id !== component.id && !component.setAsDefault
35
+ (component: any) => component.type === type && content[component.type]?.id !== component.id
36
+ );
37
+
38
+ const mappedOptions = options.map((option: any) =>
39
+ option.id === defaultOption
40
+ ? { ...option, isDefault: true, tag: "site default" }
41
+ : { ...option, isDefault: false, tag: null }
25
42
  );
26
- const optionsType = `${type}s`;
27
43
 
28
44
  const actionMenuOptions = [
29
45
  {
@@ -36,19 +52,20 @@ const Field = (props: IField): JSX.Element => {
36
52
  const actionMenuIcon = "more";
37
53
 
38
54
  const handleReplace = (option: any) => {
55
+ const optionID = option.isDefault ? null : option.id;
39
56
  const configFormData = {
40
57
  ...dataPackConfigFormData,
41
58
  templates: {
42
59
  ...dataPackConfigFormData.templates,
43
60
  [template]: {
44
61
  ...(!!dataPackConfigFormData.templates && dataPackConfigFormData.templates[template]),
45
- ...(type === "header" && { defaultHeader: option.id }),
46
- ...(type === "footer" && { defaultFooter: option.id }),
62
+ ...(type === "header" && { defaultHeader: optionID }),
63
+ ...(type === "footer" && { defaultFooter: optionID }),
47
64
  },
48
65
  },
49
66
  };
50
67
  updateDataPackFormValue(configFormData);
51
- updateEditorContent(pageEditorID, type, option.id ? option : null);
68
+ updateEditorContent(pageEditorID, type, optionID ? option : null);
52
69
  toggleModal();
53
70
  };
54
71
 
@@ -61,12 +78,6 @@ const Field = (props: IField): JSX.Element => {
61
78
  return !!siteDefaultNavigation?.isDefault;
62
79
  };
63
80
 
64
- const setDefault = {
65
- title: `Put the default ${type}`,
66
- action: () => handleReplace({ id: null }),
67
- checked: isDefaultNavigation(),
68
- };
69
-
70
81
  const hasOptions: boolean | undefined = options?.length > 0 || !isDefaultNavigation();
71
82
 
72
83
  if (!content[type]) return <></>;
@@ -82,11 +93,10 @@ const Field = (props: IField): JSX.Element => {
82
93
  {hasOptions && (
83
94
  <SideModal
84
95
  optionsType={optionsType}
85
- whiteList={options}
96
+ whiteList={mappedOptions}
86
97
  handleClick={handleReplace}
87
98
  toggleModal={toggleModal}
88
99
  isOpen={isOpen}
89
- setDefault={setDefault}
90
100
  theme={theme}
91
101
  />
92
102
  )}
@@ -9,28 +9,15 @@ import TemplateBrowser from "./TemplateBrowser";
9
9
  import ConfigPanel from "./ConfigPanel";
10
10
 
11
11
  const Editor = (props: IProps) => {
12
- const { isLoading, template } = props;
13
-
14
12
  const theme = getDefaultTheme();
15
13
 
16
- return (
17
- <ResizePanel
18
- leftPanel={<TemplateBrowser />}
19
- rightPanel={
20
- <ConfigPanel
21
- template={template}
22
- isLoading={isLoading}
23
- theme={theme}
24
- />
25
- }
26
- />
27
- );
14
+ return <ResizePanel leftPanel={<TemplateBrowser />} rightPanel={<ConfigPanel theme={theme} {...props} />} />;
28
15
  };
29
16
 
30
17
  type IProps = {
31
18
  isLoading: boolean;
32
19
  template: string;
33
- }
20
+ };
34
21
 
35
22
  const mapStateToProps = (state: IRootState): IProps => ({
36
23
  isLoading: state.app.isLoading,
@@ -14,14 +14,16 @@ import * as S from "./style";
14
14
  const TemplateEditor = (props: IProps) => {
15
15
  const { isLoading, isSaving, editorContent, setHistoryPush, template, updateDataPack, dataPackSelected } = props;
16
16
 
17
- const { isDirty, setIsDirty } = useIsDirty(editorContent);
17
+ const { isDirty, resetDirty } = useIsDirty(editorContent);
18
18
 
19
19
  const rightButtonProps = {
20
20
  label: !isDirty ? "Saved" : isSaving ? "Saving" : "Save",
21
21
  disabled: isSaving || !isDirty,
22
22
  action: async () => {
23
- await updateDataPack(dataPackSelected.id);
24
- setIsDirty(false);
23
+ const isSaved = await updateDataPack(dataPackSelected.id);
24
+ if (isSaved) {
25
+ resetDirty(false);
26
+ }
25
27
  },
26
28
  };
27
29
 
@@ -71,7 +73,7 @@ const mapDispatchToProps = {
71
73
 
72
74
  interface IDispatchProps {
73
75
  setHistoryPush(path: string, isEditor?: boolean): void;
74
- updateDataPack: (dataPack?: any) => Promise<void>;
76
+ updateDataPack: (dataPack?: any) => Promise<boolean>;
75
77
  }
76
78
 
77
79
  type IProps = IStateProps & IDispatchProps;
@@ -1,37 +0,0 @@
1
- import { toBlob } from "html-to-image";
2
-
3
- const getImage = async (browserRef: any): Promise<File | null> => {
4
- if (browserRef?.current) {
5
- const browserContent = browserRef.current.querySelector(".browser-content");
6
-
7
- const { height, overflow } = getComputedStyle(browserContent);
8
- browserContent.style.height = "auto";
9
- browserContent.style.overflow = "visible";
10
- const originalHeight = browserContent.clientHeight;
11
-
12
- const elChildren = browserContent.querySelectorAll("*");
13
- let maxAbsoluteElementHeight = 0;
14
- [].forEach.call(elChildren, function (element: any) {
15
- const isAbsolutePosition = getComputedStyle(element).position === "absolute";
16
- const isMaxAbsolutePositionHeight = element.clientHeight > maxAbsoluteElementHeight;
17
- if (isAbsolutePosition && isMaxAbsolutePositionHeight) {
18
- maxAbsoluteElementHeight = element.clientHeight;
19
- }
20
- });
21
- if (maxAbsoluteElementHeight) {
22
- const actualHeight = originalHeight + maxAbsoluteElementHeight;
23
- browserContent.style.height = `${actualHeight}px`;
24
- }
25
-
26
- const imageBlob = await toBlob(browserContent, { quality: 0.95, pixelRatio: 0.5 });
27
- const imageFile = imageBlob && new File([imageBlob], "navigation-thumbnail.png", { type: "image/png" });
28
-
29
- browserContent.style.height = height;
30
- browserContent.style.overflow = overflow;
31
-
32
- return imageFile;
33
- }
34
- return null;
35
- };
36
-
37
- export { getImage };