@griddo/ax 1.64.9 → 1.65.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.
- package/package.json +2 -2
- package/src/components/CategoryCell/style.tsx +3 -2
- package/src/components/Fields/FieldGroup/index.test.tsx +61 -0
- package/src/components/Fields/FieldGroup/index.tsx +7 -5
- package/src/components/Fields/ImageField/index.tsx +7 -2
- package/src/components/FloatingPanel/index.tsx +12 -2
- package/src/components/Modal/style.tsx +3 -3
- package/src/components/SideModal/index.tsx +6 -1
- package/src/components/SideModal/style.tsx +7 -2
- package/src/components/TableFilters/CategoryFilter/index.tsx +1 -1
- package/src/components/TableFilters/DateFilter/index.tsx +7 -4
- package/src/components/TableFilters/LiveFilter/index.tsx +1 -1
- package/src/components/TableFilters/NameFilter/index.tsx +7 -4
- package/src/components/TableFilters/SiteFilter/index.tsx +1 -1
- package/src/components/TableFilters/StatusFilter/index.tsx +7 -4
- package/src/components/TableFilters/TranslationsFilter/index.tsx +1 -1
- package/src/components/TableFilters/TypeFilter/index.tsx +2 -2
- package/src/containers/Navigation/Menu/reducer.tsx +2 -1
- package/src/containers/PageEditor/actions.tsx +9 -8
- package/src/containers/StructuredData/actions.tsx +4 -3
- package/src/forms/errors.tsx +1 -0
- package/src/forms/index.tsx +2 -2
- package/src/forms/validators.tsx +55 -41
- package/src/modules/Content/PageItem/index.tsx +2 -1
- package/src/modules/CreatePass/index.tsx +4 -2
- package/src/modules/GlobalEditor/index.tsx +48 -35
- package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +1 -0
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +16 -5
- package/src/modules/Navigation/Menus/List/Table/SidePanel/index.tsx +31 -22
- package/src/modules/Navigation/Menus/List/Table/SidePanel/style.tsx +18 -2
- package/src/modules/PageEditor/index.tsx +47 -34
- package/src/modules/Sites/SitesList/index.tsx +15 -6
- package/src/modules/StructuredData/Form/index.tsx +29 -21
- package/src/modules/StructuredData/StructuredDataList/utils.tsx +1 -1
- package/src/types/index.tsx +3 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.65.1",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -220,5 +220,5 @@
|
|
|
220
220
|
"publishConfig": {
|
|
221
221
|
"access": "public"
|
|
222
222
|
},
|
|
223
|
-
"gitHead": "
|
|
223
|
+
"gitHead": "6939563dc1aebe3986c9fe4e9739236cd2c41e05"
|
|
224
224
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import styled from "styled-components";
|
|
2
3
|
|
|
3
4
|
import { Cell } from "@ax/components/TableList/TableItem/style";
|
|
4
5
|
|
|
5
|
-
const CategoryCell = styled(Cell)`
|
|
6
|
+
const CategoryCell = styled((props) => <Cell {...props} />)`
|
|
6
7
|
flex: 0 0 150px;
|
|
7
8
|
align-items: center;
|
|
8
9
|
position: relative;
|
|
9
10
|
`;
|
|
10
11
|
|
|
11
|
-
export { CategoryCell }
|
|
12
|
+
export { CategoryCell };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import FieldGroup from "./index";
|
|
3
|
+
import { ThemeProvider } from "styled-components";
|
|
4
|
+
import "@testing-library/jest-dom";
|
|
5
|
+
import { parseTheme } from "@griddo/core";
|
|
6
|
+
import globalTheme from "../../../themes/theme.json";
|
|
7
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
8
|
+
|
|
9
|
+
afterEach(cleanup);
|
|
10
|
+
|
|
11
|
+
const defaultProps = {
|
|
12
|
+
title: "title",
|
|
13
|
+
children: "",
|
|
14
|
+
collapsed: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe("FieldGroup component rendering", () => {
|
|
18
|
+
it("should render the component", () => {
|
|
19
|
+
render(
|
|
20
|
+
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
21
|
+
<FieldGroup {...defaultProps} />
|
|
22
|
+
</ThemeProvider>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const fieldGroupWrapper = screen.getByTestId("fieldGroupWrapper");
|
|
26
|
+
const fieldGroupLabel = screen.getByTestId("fieldGroupLabel");
|
|
27
|
+
const fieldGroupContent = screen.getByTestId("fieldGroupContent");
|
|
28
|
+
|
|
29
|
+
expect(fieldGroupWrapper).toBeTruthy();
|
|
30
|
+
expect(fieldGroupLabel).toBeTruthy();
|
|
31
|
+
expect(fieldGroupLabel).toHaveTextContent("title");
|
|
32
|
+
expect(fieldGroupContent).toBeTruthy();
|
|
33
|
+
expect(fieldGroupContent).toHaveTextContent("");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should trigger the onClick", () => {
|
|
37
|
+
const props = {
|
|
38
|
+
title: "title",
|
|
39
|
+
children: "blablabla",
|
|
40
|
+
collapsed: false,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const setStateMock = jest.fn();
|
|
44
|
+
const useStateMock: any = (useState: string) => [useState, setStateMock];
|
|
45
|
+
jest.spyOn(React, "useState").mockImplementation(useStateMock);
|
|
46
|
+
|
|
47
|
+
render(
|
|
48
|
+
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
49
|
+
<FieldGroup {...props} />
|
|
50
|
+
</ThemeProvider>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const fieldGroupLabel = screen.getByTestId("fieldGroupLabel");
|
|
54
|
+
const fieldGroupContent = screen.getByTestId("fieldGroupContent");
|
|
55
|
+
expect(fieldGroupLabel).toBeTruthy();
|
|
56
|
+
expect(fieldGroupContent).toBeTruthy();
|
|
57
|
+
expect(fieldGroupContent).toHaveTextContent("blablabla");
|
|
58
|
+
fireEvent.click(fieldGroupLabel);
|
|
59
|
+
expect(setStateMock).toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
|
|
3
3
|
import * as S from "./style";
|
|
4
4
|
|
|
5
5
|
const FieldGroup = (props: IFieldsGroupProps): React.ReactElement => {
|
|
6
6
|
const { title, children, collapsed } = props;
|
|
7
|
-
const [isOpen, setIsOpen] = useState(!collapsed);
|
|
7
|
+
const [isOpen, setIsOpen] = React.useState(!collapsed);
|
|
8
8
|
|
|
9
9
|
const handleClick = () => {
|
|
10
10
|
setIsOpen(!isOpen);
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
|
-
<S.Wrapper>
|
|
15
|
-
<S.Label onClick={handleClick} isOpen={isOpen}>
|
|
14
|
+
<S.Wrapper data-testid="fieldGroupWrapper">
|
|
15
|
+
<S.Label data-testid="fieldGroupLabel" onClick={handleClick} isOpen={isOpen}>
|
|
16
16
|
{title}
|
|
17
17
|
</S.Label>
|
|
18
|
-
<S.Content isOpen={isOpen}>
|
|
18
|
+
<S.Content data-testid="fieldGroupContent" isOpen={isOpen}>
|
|
19
|
+
{children}
|
|
20
|
+
</S.Content>
|
|
19
21
|
</S.Wrapper>
|
|
20
22
|
);
|
|
21
23
|
};
|
|
@@ -8,7 +8,8 @@ import { useModal } from "@ax/hooks";
|
|
|
8
8
|
import * as S from "./style";
|
|
9
9
|
|
|
10
10
|
const ImageField = (props: IImageFieldProps) => {
|
|
11
|
-
const { value, error, onChange, selectedContent, disabled, handleValidation, validators, site } =
|
|
11
|
+
const { value, error, onChange, selectedContent, disabled, handleValidation, validators, site, setIsGalleryOpened } =
|
|
12
|
+
props;
|
|
12
13
|
|
|
13
14
|
const isLinkableImage = selectedContent && selectedContent.component === "LinkableImage";
|
|
14
15
|
const hasImage = value && Object.prototype.hasOwnProperty.call(value, "url");
|
|
@@ -34,18 +35,21 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
34
35
|
const url = typeof img === "string" ? img : img.url;
|
|
35
36
|
setPreviewSrc(url);
|
|
36
37
|
onChange(img);
|
|
38
|
+
setIsGalleryOpened && setIsGalleryOpened();
|
|
37
39
|
error && handleValidation && handleValidation(url, validators);
|
|
38
40
|
}
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
const handleClick = () => {
|
|
42
44
|
if (!disabled) {
|
|
45
|
+
setIsGalleryOpened && setIsGalleryOpened();
|
|
43
46
|
toggleModal();
|
|
44
47
|
}
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
const handleChange = () => {
|
|
48
51
|
if (!disabled) {
|
|
52
|
+
setIsGalleryOpened && setIsGalleryOpened();
|
|
49
53
|
toggleModal();
|
|
50
54
|
}
|
|
51
55
|
};
|
|
@@ -88,9 +92,10 @@ interface IImageFieldProps {
|
|
|
88
92
|
onChange: (value: any) => void;
|
|
89
93
|
selectedContent: any;
|
|
90
94
|
disabled?: boolean;
|
|
95
|
+
setIsGalleryOpened?: () => void;
|
|
91
96
|
handleValidation?: (value: string, validators?: Record<string, unknown>) => void;
|
|
92
97
|
validators?: Record<string, unknown>;
|
|
93
98
|
site: ISite;
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
export default memo(ImageField);
|
|
101
|
+
export default memo(ImageField);
|
|
@@ -7,12 +7,21 @@ import { IconAction } from "@ax/components";
|
|
|
7
7
|
import * as S from "./style";
|
|
8
8
|
|
|
9
9
|
const FloatingPanel = (props: IProps): JSX.Element | null => {
|
|
10
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
children,
|
|
12
|
+
title,
|
|
13
|
+
isOpen,
|
|
14
|
+
toggleModal,
|
|
15
|
+
isOpenedSecond,
|
|
16
|
+
handlePanel,
|
|
17
|
+
secondary,
|
|
18
|
+
closeOnOutsideClick = true,
|
|
19
|
+
} = props;
|
|
11
20
|
|
|
12
21
|
const node = useRef<HTMLElement>(null);
|
|
13
22
|
|
|
14
23
|
const handleClickOutside = (e: any) => {
|
|
15
|
-
if ((node.current && node.current.contains(e.target)) || isOpenedSecond) {
|
|
24
|
+
if ((node.current && node.current.contains(e.target)) || isOpenedSecond || !closeOnOutsideClick) {
|
|
16
25
|
return;
|
|
17
26
|
}
|
|
18
27
|
toggleModal();
|
|
@@ -54,6 +63,7 @@ interface IProps {
|
|
|
54
63
|
toggleModal: () => void;
|
|
55
64
|
handlePanel?: (value: boolean) => void;
|
|
56
65
|
secondary?: boolean;
|
|
66
|
+
closeOnOutsideClick?: boolean;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
export default memo(FloatingPanel);
|
|
@@ -30,7 +30,7 @@ export const ModalOverlay = styled.div`
|
|
|
30
30
|
position: fixed;
|
|
31
31
|
top: 0;
|
|
32
32
|
left: 0;
|
|
33
|
-
z-index:
|
|
33
|
+
z-index: 1240;
|
|
34
34
|
width: 100vw;
|
|
35
35
|
height: 100vh;
|
|
36
36
|
background: ${(p) => p.theme.color.overlay};
|
|
@@ -43,7 +43,7 @@ export const ModalWrapper = styled.div`
|
|
|
43
43
|
position: fixed;
|
|
44
44
|
top: 0;
|
|
45
45
|
left: 0;
|
|
46
|
-
z-index:
|
|
46
|
+
z-index: 1250;
|
|
47
47
|
width: 100%;
|
|
48
48
|
height: 100%;
|
|
49
49
|
overflow-x: hidden;
|
|
@@ -98,4 +98,4 @@ export const ModalFooter = styled.div`
|
|
|
98
98
|
margin-left: ${(p) => p.theme.spacing.s};
|
|
99
99
|
}
|
|
100
100
|
border-radius: 0 0 4px 4px;
|
|
101
|
-
`;
|
|
101
|
+
`;
|
|
@@ -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 } from "@ax/components";
|
|
7
|
+
import { CheckField, MenuItem, SearchField, IconAction } from "@ax/components";
|
|
8
8
|
import { ModuleCategoryInfo } from "@ax/types";
|
|
9
9
|
|
|
10
10
|
import * as S from "./style";
|
|
@@ -145,6 +145,11 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
145
145
|
<SearchField onChange={setSearchQuery} closeOnInactive />
|
|
146
146
|
</S.SearchWrapper>
|
|
147
147
|
)}
|
|
148
|
+
{!showSearch && (
|
|
149
|
+
<S.ButtonWrapper>
|
|
150
|
+
<IconAction icon="close" onClick={toggleModal} />
|
|
151
|
+
</S.ButtonWrapper>
|
|
152
|
+
)}
|
|
148
153
|
</S.Header>
|
|
149
154
|
{setDefault && !setDefault.checked && (
|
|
150
155
|
<S.CheckFieldWrapper>
|
|
@@ -6,8 +6,8 @@ const Wrapper = styled.div<{ optionsType?: string }>`
|
|
|
6
6
|
position: fixed;
|
|
7
7
|
left: ${(p) => (p.optionsType && placeRight.includes(p.optionsType) ? "unset" : 0)};
|
|
8
8
|
right: ${(p) => (p.optionsType && placeRight.includes(p.optionsType) ? 0 : "unset")};
|
|
9
|
-
top:
|
|
10
|
-
z-index:
|
|
9
|
+
top: 0;
|
|
10
|
+
z-index: 1000;
|
|
11
11
|
|
|
12
12
|
height: 100vh;
|
|
13
13
|
background: ${(p) => p.theme.colors.uiBackground01};
|
|
@@ -71,6 +71,10 @@ const Link = styled.div<{ active: boolean }>`
|
|
|
71
71
|
color: ${p => p.active ? p.theme.color.textHighEmphasis : p.theme.color.textMediumEmphasis};
|
|
72
72
|
`;
|
|
73
73
|
|
|
74
|
+
const ButtonWrapper = styled.div`
|
|
75
|
+
margin: 0 0 0 auto;
|
|
76
|
+
`;
|
|
77
|
+
|
|
74
78
|
export {
|
|
75
79
|
Wrapper,
|
|
76
80
|
Content,
|
|
@@ -82,4 +86,5 @@ export {
|
|
|
82
86
|
FeaturedWrapper,
|
|
83
87
|
SearchWrapper,
|
|
84
88
|
Link,
|
|
89
|
+
ButtonWrapper,
|
|
85
90
|
};
|
|
@@ -68,7 +68,7 @@ const CategoryFilter = (props: ICategoryFilterProps): JSX.Element => {
|
|
|
68
68
|
<S.HeaderWrapper isActive={isActive}>
|
|
69
69
|
{structuredData.title}
|
|
70
70
|
<S.IconsWrapper>
|
|
71
|
-
<Icon name="Filter" size="16" />
|
|
71
|
+
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
72
72
|
</S.IconsWrapper>
|
|
73
73
|
</S.HeaderWrapper>
|
|
74
74
|
);
|
|
@@ -19,10 +19,13 @@ const DateFilter = (props: IStatusFilterProps): JSX.Element => {
|
|
|
19
19
|
<S.Date isActive={sortedByDate}>
|
|
20
20
|
Date
|
|
21
21
|
<S.IconsWrapper>
|
|
22
|
-
{sortedByDate
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
{sortedByDate ? (
|
|
23
|
+
<SortedStateArrow />
|
|
24
|
+
) : (
|
|
25
|
+
<S.InteractiveArrow>
|
|
26
|
+
<Icon name="DownArrow" size="16" />
|
|
27
|
+
</S.InteractiveArrow>
|
|
28
|
+
)}
|
|
26
29
|
</S.IconsWrapper>
|
|
27
30
|
</S.Date>
|
|
28
31
|
);
|
|
@@ -80,7 +80,7 @@ const LiveFilter = (props: ILiveFilterProps): JSX.Element => {
|
|
|
80
80
|
<S.LiveOptions isActive={isActive}>
|
|
81
81
|
Live
|
|
82
82
|
<S.IconsWrapper>
|
|
83
|
-
<Icon name="Filter" size="16" />
|
|
83
|
+
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
84
84
|
</S.IconsWrapper>
|
|
85
85
|
</S.LiveOptions>
|
|
86
86
|
</S.MenuWrapper>
|
|
@@ -23,10 +23,13 @@ const NameFilter = (props: INameFilterProps): JSX.Element => {
|
|
|
23
23
|
<S.NameHeader isActive={isActive}>
|
|
24
24
|
{title}
|
|
25
25
|
<S.IconsWrapper>
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
{isActive ? (
|
|
27
|
+
<SortedStateArrow />
|
|
28
|
+
) : (
|
|
29
|
+
<S.InteractiveArrow>
|
|
30
|
+
<Icon name="DownArrow" size="16" />
|
|
31
|
+
</S.InteractiveArrow>
|
|
32
|
+
)}
|
|
30
33
|
</S.IconsWrapper>
|
|
31
34
|
</S.NameHeader>
|
|
32
35
|
);
|
|
@@ -78,7 +78,7 @@ const SiteFilter = (props: ISiteFilterProps): JSX.Element => {
|
|
|
78
78
|
<S.Site isActive={isActive} center={center}>
|
|
79
79
|
{label}
|
|
80
80
|
<S.IconsWrapper>
|
|
81
|
-
<Icon name="Filter" size="16" />
|
|
81
|
+
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
82
82
|
</S.IconsWrapper>
|
|
83
83
|
</S.Site>
|
|
84
84
|
);
|
|
@@ -20,10 +20,13 @@ const StatusFilter = (props: IStatusFilterProps) => {
|
|
|
20
20
|
<S.Status isActive={sortedByDate}>
|
|
21
21
|
Status
|
|
22
22
|
<S.IconsWrapper>
|
|
23
|
-
{sortedByDate
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
{sortedByDate ? (
|
|
24
|
+
<SortedStateArrow />
|
|
25
|
+
) : (
|
|
26
|
+
<S.InteractiveArrow>
|
|
27
|
+
<Icon name="DownArrow" size="16" />
|
|
28
|
+
</S.InteractiveArrow>
|
|
29
|
+
)}
|
|
27
30
|
</S.IconsWrapper>
|
|
28
31
|
</S.Status>
|
|
29
32
|
);
|
|
@@ -43,7 +43,7 @@ const TranslationsFilter = (props: ITranslationsFilterProps): JSX.Element => {
|
|
|
43
43
|
<S.Translations isActive={isActive}>
|
|
44
44
|
Trans.
|
|
45
45
|
<S.IconsWrapper>
|
|
46
|
-
<Icon name="Filter" size="16" />
|
|
46
|
+
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
47
47
|
</S.IconsWrapper>
|
|
48
48
|
</S.Translations>
|
|
49
49
|
);
|
|
@@ -12,7 +12,7 @@ const TypeFilter = (props: ITypeFilterProps): JSX.Element => {
|
|
|
12
12
|
const [selectedValue, setSelectedValue] = useState(initialState);
|
|
13
13
|
|
|
14
14
|
useEffect(() => {
|
|
15
|
-
setSelectedValue([value]);
|
|
15
|
+
value && setSelectedValue([value]);
|
|
16
16
|
}, [value]);
|
|
17
17
|
|
|
18
18
|
const setFilterQuery = (selection: any) => {
|
|
@@ -30,7 +30,7 @@ const TypeFilter = (props: ITypeFilterProps): JSX.Element => {
|
|
|
30
30
|
<S.Types isActive={isActive}>
|
|
31
31
|
Types
|
|
32
32
|
<S.IconsWrapper>
|
|
33
|
-
<Icon name="Filter" size="16" />
|
|
33
|
+
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
34
34
|
</S.IconsWrapper>
|
|
35
35
|
</S.Types>
|
|
36
36
|
);
|
|
@@ -34,6 +34,7 @@ const initialState = {
|
|
|
34
34
|
item: null,
|
|
35
35
|
form: {
|
|
36
36
|
itemLink: null,
|
|
37
|
+
itemImage: null,
|
|
37
38
|
itemLabel: "",
|
|
38
39
|
itemAuxText: "",
|
|
39
40
|
type: "link",
|
|
@@ -59,4 +60,4 @@ function reducer(state = initialState, action: MenuActionsCreators): IMenuState
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
export { initialState as menuInitialState, reducer as menuReducer };
|
|
63
|
+
export { initialState as menuInitialState, reducer as menuReducer };
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
getLastModuleEditorID,
|
|
25
25
|
getLastComponentEditorID,
|
|
26
26
|
replaceElements,
|
|
27
|
-
|
|
27
|
+
findFieldsErrors,
|
|
28
28
|
getParentKey,
|
|
29
29
|
} from "@ax/forms";
|
|
30
30
|
import { appActions } from "@ax/containers/App";
|
|
@@ -292,8 +292,8 @@ function getPage(pageID?: number, global?: boolean): (dispatch: Dispatch, getSta
|
|
|
292
292
|
const pageLiveStatus = page.draftFromPage
|
|
293
293
|
? pageStatus.MODIFIED
|
|
294
294
|
: isNewTranslation
|
|
295
|
-
|
|
296
|
-
|
|
295
|
+
? pageStatus.OFFLINE
|
|
296
|
+
: page.liveStatus.status;
|
|
297
297
|
|
|
298
298
|
if (isReqOk(response.status)) {
|
|
299
299
|
addTemplate(page.templateId)(dispatch);
|
|
@@ -741,7 +741,7 @@ function generatePageContent(editorContent: IPage, dispatch: Dispatch, getState:
|
|
|
741
741
|
dataPacks: { configFormData },
|
|
742
742
|
} = getState();
|
|
743
743
|
const { header, footer, isGlobal } = editorContent;
|
|
744
|
-
const { defaultHeader, defaultFooter } = configFormData.templates && configFormData.templates[template] || {};
|
|
744
|
+
const { defaultHeader, defaultFooter } = (configFormData.templates && configFormData.templates[template]) || {};
|
|
745
745
|
const { header: pageHeader, footer: pageFooter } = getPageNavigation(
|
|
746
746
|
header || defaultHeader,
|
|
747
747
|
footer || defaultFooter,
|
|
@@ -928,19 +928,20 @@ function getTemplateConfig(template: string): (dispatch: Dispatch, getState: any
|
|
|
928
928
|
};
|
|
929
929
|
}
|
|
930
930
|
|
|
931
|
-
function validatePage(): (dispatch: Dispatch, getState: any) => Promise<boolean> {
|
|
931
|
+
function validatePage(publish: boolean): (dispatch: Dispatch, getState: any) => Promise<boolean> {
|
|
932
932
|
return async (dispatch, getState) => {
|
|
933
933
|
try {
|
|
934
934
|
const { editorContent } = getStateValues(getState);
|
|
935
935
|
|
|
936
|
-
const errors =
|
|
936
|
+
const errors = findFieldsErrors(editorContent);
|
|
937
937
|
dispatch(setErrors(errors));
|
|
938
938
|
if (errors.length === 0) {
|
|
939
|
-
dispatch(setValidated(true));
|
|
939
|
+
!publish && dispatch(setValidated(true));
|
|
940
|
+
return true;
|
|
940
941
|
} else {
|
|
941
942
|
dispatch(setValidated(false));
|
|
943
|
+
return false;
|
|
942
944
|
}
|
|
943
|
-
return true;
|
|
944
945
|
} catch (e) {
|
|
945
946
|
console.log(e);
|
|
946
947
|
return false;
|
|
@@ -483,7 +483,7 @@ function setStatusStructuredDataContent(
|
|
|
483
483
|
};
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
function validateForm(): (dispatch: Dispatch, getState: any) => Promise<boolean> {
|
|
486
|
+
function validateForm(publish?: boolean): (dispatch: Dispatch, getState: any) => Promise<boolean> {
|
|
487
487
|
return async (dispatch, getState) => {
|
|
488
488
|
try {
|
|
489
489
|
const {
|
|
@@ -496,11 +496,12 @@ function validateForm(): (dispatch: Dispatch, getState: any) => Promise<boolean>
|
|
|
496
496
|
const errors = findMandatoryStructuredDataErrors(content, schema);
|
|
497
497
|
dispatch(setErrors(errors));
|
|
498
498
|
if (errors.length === 0) {
|
|
499
|
-
dispatch(setValidated(true));
|
|
499
|
+
!publish && dispatch(setValidated(true));
|
|
500
|
+
return true;
|
|
500
501
|
} else {
|
|
501
502
|
dispatch(setValidated(false));
|
|
503
|
+
return false;
|
|
502
504
|
}
|
|
503
|
-
return true;
|
|
504
505
|
} catch (e) {
|
|
505
506
|
console.log(e);
|
|
506
507
|
return false;
|
package/src/forms/errors.tsx
CHANGED
|
@@ -39,6 +39,7 @@ const ERRORS: Record<string, string> = {
|
|
|
39
39
|
ERR038: "Sorry, the content you are trying to link is broken or unpublished.",
|
|
40
40
|
ERR039: "Sorry, this color doesn't exist. Please add new one.",
|
|
41
41
|
ERR040: "Sorry, the file is not in a valid format.",
|
|
42
|
+
ERR041: "Sorry, the password doesn’t match.",
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export { ERRORS };
|
package/src/forms/index.tsx
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
replaceElements,
|
|
24
24
|
} from "./elements";
|
|
25
25
|
import { getInnerFields, getStructuredDataInnerFields } from "./fields";
|
|
26
|
-
import { getValidity,
|
|
26
|
+
import { getValidity, findFieldsErrors, findMandatoryStructuredDataErrors } from "./validators";
|
|
27
27
|
|
|
28
28
|
export {
|
|
29
29
|
parseData,
|
|
@@ -49,6 +49,6 @@ export {
|
|
|
49
49
|
getLastComponentEditorID,
|
|
50
50
|
getParentKey,
|
|
51
51
|
getValidity,
|
|
52
|
-
|
|
52
|
+
findFieldsErrors,
|
|
53
53
|
findMandatoryStructuredDataErrors,
|
|
54
54
|
};
|
package/src/forms/validators.tsx
CHANGED
|
@@ -7,12 +7,14 @@ const VALIDATORS = {
|
|
|
7
7
|
const isValid = flag && val !== undefined && val !== null && val.length > 0;
|
|
8
8
|
return { isValid, errorCode: "ERR001" };
|
|
9
9
|
},
|
|
10
|
-
maxChar: (val: string,
|
|
11
|
-
const
|
|
10
|
+
maxChar: (val: string | null, max: number): IError => {
|
|
11
|
+
const length = val ? val.length : 0;
|
|
12
|
+
const isValid = length <= max;
|
|
12
13
|
return { isValid, errorCode: "ERR003" };
|
|
13
14
|
},
|
|
14
|
-
minChar: (val: string,
|
|
15
|
-
const
|
|
15
|
+
minChar: (val: string | null, min: number): IError => {
|
|
16
|
+
const length = val ? val.length : 0;
|
|
17
|
+
const isValid = length >= min;
|
|
16
18
|
return { isValid, errorCode: "ERR002" };
|
|
17
19
|
},
|
|
18
20
|
maxValue: (val: string, len: number): IError => {
|
|
@@ -94,6 +96,10 @@ const VALIDATORS = {
|
|
|
94
96
|
return { isValid: true, errorCode: "" };
|
|
95
97
|
}
|
|
96
98
|
},
|
|
99
|
+
isSamePass: (pass1: string, pass2: string): IError => {
|
|
100
|
+
const isValid = pass1 === pass2;
|
|
101
|
+
return { isValid, errorCode: "ERR041" };
|
|
102
|
+
},
|
|
97
103
|
};
|
|
98
104
|
|
|
99
105
|
const getErrorMessage = (key: string, val: number | string | null): string => {
|
|
@@ -148,38 +154,17 @@ const isEmptyField = (value: any, fieldType: string) => {
|
|
|
148
154
|
}
|
|
149
155
|
};
|
|
150
156
|
|
|
151
|
-
const
|
|
152
|
-
|
|
157
|
+
const getValidationErrors = (
|
|
158
|
+
fields: Record<string, unknown>[],
|
|
159
|
+
current: any,
|
|
160
|
+
name: string | null,
|
|
161
|
+
tab: string,
|
|
162
|
+
template: boolean
|
|
163
|
+
) => {
|
|
164
|
+
let errors: IErrorItem[] = [];
|
|
165
|
+
|
|
153
166
|
fields.forEach((field: any) => {
|
|
154
167
|
if (field.mandatory) {
|
|
155
|
-
mandatory.push(field);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (field.fields) {
|
|
159
|
-
const innerMandatories = getMandatoryFields(field.fields);
|
|
160
|
-
if (innerMandatories) {
|
|
161
|
-
mandatory = [...mandatory, ...innerMandatories];
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
return mandatory;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const getMandatoryFieldsErrors = (content: any, current: any, name: string | null, tab: string, template: boolean) => {
|
|
169
|
-
let mandatoryFields = [];
|
|
170
|
-
const errors: IErrorItem[] = [];
|
|
171
|
-
|
|
172
|
-
const conditionalKey = content.find((field: { type: string }) => field.type === "ConditionalField")?.key;
|
|
173
|
-
if (conditionalKey) {
|
|
174
|
-
mandatoryFields = getMandatoryFields(content).filter((field) => {
|
|
175
|
-
return field.condition === undefined || field.condition === current[conditionalKey];
|
|
176
|
-
});
|
|
177
|
-
} else {
|
|
178
|
-
mandatoryFields = getMandatoryFields(content);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
mandatoryFields &&
|
|
182
|
-
mandatoryFields.forEach((field: any) => {
|
|
183
168
|
const isEmpty = isEmptyField(current[field.key], field.type);
|
|
184
169
|
if (isEmpty) {
|
|
185
170
|
errors.push({
|
|
@@ -194,12 +179,41 @@ const getMandatoryFieldsErrors = (content: any, current: any, name: string | nul
|
|
|
194
179
|
template,
|
|
195
180
|
});
|
|
196
181
|
}
|
|
197
|
-
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (Object.prototype.hasOwnProperty.call(field, "validators")) {
|
|
185
|
+
const { isValid, errorText } = getValidity(field.validators, current[field.key]);
|
|
186
|
+
|
|
187
|
+
if (!isValid) {
|
|
188
|
+
errors.push({
|
|
189
|
+
type: "Error",
|
|
190
|
+
message: errorText,
|
|
191
|
+
validator: field.validators,
|
|
192
|
+
editorID: current.editorID ? current.editorID : null,
|
|
193
|
+
component: current.component ? current.component : null,
|
|
194
|
+
name: name ? name : field.title,
|
|
195
|
+
key: field.key,
|
|
196
|
+
tab,
|
|
197
|
+
template,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (Object.prototype.hasOwnProperty.call(field, "fields") && field.fields.length) {
|
|
203
|
+
const innerFields =
|
|
204
|
+
field.type === "ConditionalField"
|
|
205
|
+
? field.fields.filter((f: any) => f.condition === undefined || f.condition === current[field.key])
|
|
206
|
+
: field.fields;
|
|
207
|
+
|
|
208
|
+
const innerErrors = getValidationErrors(innerFields, current, name, tab, template);
|
|
209
|
+
errors = [...errors, ...innerErrors];
|
|
210
|
+
}
|
|
211
|
+
});
|
|
198
212
|
|
|
199
213
|
return errors;
|
|
200
214
|
};
|
|
201
215
|
|
|
202
|
-
const
|
|
216
|
+
const findFieldsErrors = (content: any): IErrorItem[] => {
|
|
203
217
|
const queue: any[] = [content];
|
|
204
218
|
let errors: IErrorItem[] = [];
|
|
205
219
|
while (queue.length > 0) {
|
|
@@ -209,14 +223,14 @@ const findMandatoryFieldsErrors = (content: any): IErrorItem[] => {
|
|
|
209
223
|
let schemaErrors: any[] = [];
|
|
210
224
|
const schema: any = getSchema(currentObj.component);
|
|
211
225
|
schema.configTabs.forEach((tab: any) => {
|
|
212
|
-
const
|
|
226
|
+
const valErrors: IErrorItem[] = getValidationErrors(
|
|
213
227
|
tab.fields,
|
|
214
228
|
currentObj,
|
|
215
229
|
schema.displayName,
|
|
216
230
|
tab.title,
|
|
217
231
|
false
|
|
218
232
|
);
|
|
219
|
-
schemaErrors = [...schemaErrors, ...
|
|
233
|
+
schemaErrors = [...schemaErrors, ...valErrors];
|
|
220
234
|
});
|
|
221
235
|
errors = [...errors, ...schemaErrors];
|
|
222
236
|
}
|
|
@@ -224,7 +238,7 @@ const findMandatoryFieldsErrors = (content: any): IErrorItem[] => {
|
|
|
224
238
|
if (currentObj.type === "template") {
|
|
225
239
|
const templateSchema: any = getTemplate(currentObj.templateType);
|
|
226
240
|
const { content } = templateSchema;
|
|
227
|
-
const templateErrors: IErrorItem[] =
|
|
241
|
+
const templateErrors: IErrorItem[] = getValidationErrors(
|
|
228
242
|
content,
|
|
229
243
|
currentObj,
|
|
230
244
|
templateSchema.displayName,
|
|
@@ -247,7 +261,7 @@ const findMandatoryFieldsErrors = (content: any): IErrorItem[] => {
|
|
|
247
261
|
};
|
|
248
262
|
|
|
249
263
|
const findMandatoryStructuredDataErrors = (content: any, schema: any): IErrorItem[] => {
|
|
250
|
-
const errors: IErrorItem[] =
|
|
264
|
+
const errors: IErrorItem[] = getValidationErrors(schema.fields, content, null, "", false);
|
|
251
265
|
return errors;
|
|
252
266
|
};
|
|
253
267
|
|
|
@@ -256,4 +270,4 @@ interface IError {
|
|
|
256
270
|
errorCode: string;
|
|
257
271
|
}
|
|
258
272
|
|
|
259
|
-
export { getValidity,
|
|
273
|
+
export { getValidity, findFieldsErrors, findMandatoryStructuredDataErrors };
|