@griddo/ax 11.12.0 → 11.12.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/jest/componentsMock.js +7 -5
- package/package.json +2 -2
- package/src/__tests__/components/Browser/Browser.test.tsx +438 -87
- package/src/__tests__/components/Browser/Browser.utils.test.ts +55 -0
- package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +1 -3
- package/src/__tests__/components/Fields/Button/Button.test.tsx +29 -27
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +158 -0
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +90 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +178 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +150 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +91 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +122 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +15 -0
- package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +101 -0
- package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
- package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +318 -0
- package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +242 -0
- package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +185 -0
- package/src/components/Browser/index.tsx +294 -149
- package/src/components/Browser/style.tsx +75 -6
- package/src/components/Browser/utils.tsx +13 -0
- package/src/components/Button/index.tsx +2 -1
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
- package/src/components/Fields/AsyncSelect/style.tsx +13 -0
- package/src/components/Fields/FieldGroup/index.tsx +5 -2
- package/src/components/Fields/FieldGroup/style.tsx +32 -7
- package/src/components/Fields/HeadingField/index.tsx +22 -22
- package/src/components/Fields/HiddenField/style.tsx +1 -1
- package/src/components/Fields/NumberField/index.tsx +15 -16
- package/src/components/Fields/NumberField/style.tsx +2 -0
- package/src/components/Fields/ReferenceField/index.tsx +1 -1
- package/src/components/Fields/SEOPreview/index.tsx +36 -0
- package/src/components/Fields/SEOPreview/style.tsx +24 -0
- package/src/components/Fields/Select/index.tsx +5 -1
- package/src/components/Fields/Select/style.tsx +56 -0
- package/src/components/Fields/SummaryButton/index.tsx +18 -9
- package/src/components/Fields/SummaryButton/style.tsx +1 -2
- package/src/components/Fields/TagsField/index.tsx +8 -9
- package/src/components/Fields/UrlField/index.tsx +26 -27
- package/src/components/Fields/index.tsx +2 -0
- package/src/components/FloatingNote/index.tsx +35 -0
- package/src/components/FloatingNote/style.tsx +26 -0
- package/src/components/FloatingPanel/index.tsx +5 -2
- package/src/components/FloatingPanel/style.tsx +2 -1
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
- package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
- package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
- package/src/components/HeadingsPreviewModal/index.tsx +148 -0
- package/src/components/HeadingsPreviewModal/style.tsx +82 -0
- package/src/components/HeadingsPreviewModal/utils.tsx +329 -0
- package/src/components/Icon/index.tsx +1 -2
- package/src/components/IconAction/index.tsx +1 -1
- package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +46 -0
- package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +64 -0
- package/src/components/KeywordsPreviewModal/atoms.tsx +96 -0
- package/src/components/KeywordsPreviewModal/index.tsx +99 -0
- package/src/components/KeywordsPreviewModal/style.tsx +87 -0
- package/src/components/KeywordsPreviewModal/utils.tsx +22 -0
- package/src/components/MainWrapper/AppBar/index.tsx +8 -1
- package/src/components/MainWrapper/index.tsx +7 -1
- package/src/components/Notification/index.tsx +2 -2
- package/src/components/PageFinder/index.tsx +1 -1
- package/src/components/ResizePanel/index.tsx +4 -3
- package/src/components/ResizePanel/style.tsx +1 -1
- package/src/components/SearchField/style.tsx +2 -2
- package/src/components/SideModal/index.tsx +2 -1
- package/src/components/Tabs/index.tsx +13 -4
- package/src/components/Tabs/style.tsx +7 -8
- package/src/components/Toast/index.tsx +4 -2
- package/src/components/Tooltip/index.tsx +4 -3
- package/src/components/index.tsx +8 -0
- package/src/forms/fields.tsx +70 -68
- package/src/hooks/forms.tsx +22 -1
- package/src/hooks/index.tsx +13 -3
- package/src/hooks/modals.tsx +103 -15
- package/src/hooks/users.tsx +25 -8
- package/src/modules/Forms/atoms.tsx +2 -2
- package/src/modules/FramePreview/HeadingsOverlay/index.tsx +116 -0
- package/src/modules/FramePreview/HeadingsOverlay/style.tsx +34 -0
- package/src/modules/FramePreview/index.tsx +55 -16
- package/src/modules/FramePreview/style.tsx +34 -2
- package/src/modules/FramePreview/utils.tsx +140 -0
- package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
- package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
- package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
- package/src/modules/GlobalEditor/index.tsx +119 -57
- package/src/modules/PageEditor/Editor/index.tsx +33 -2
- package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
- package/src/modules/PageEditor/Preview/index.tsx +0 -2
- package/src/modules/PageEditor/Preview/style.tsx +1 -1
- package/src/modules/PageEditor/atoms.tsx +1 -1
- package/src/modules/PageEditor/index.tsx +130 -66
- package/src/modules/PublicPreview/index.tsx +5 -2
- package/src/schemas/pages/GlobalPage.ts +87 -70
- package/src/schemas/pages/Page.ts +87 -70
- package/src/types/index.tsx +12 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { Icon } from "@ax/components";
|
|
3
2
|
|
|
4
3
|
import * as S from "./style";
|
|
@@ -20,6 +19,7 @@ const IconAction = (props: IIconActionProps): JSX.Element => {
|
|
|
20
19
|
size={size}
|
|
21
20
|
inversed={inversed}
|
|
22
21
|
active={active}
|
|
22
|
+
tabIndex={0}
|
|
23
23
|
>
|
|
24
24
|
<S.Icon data-testid={`icon-action-${icon}`}>
|
|
25
25
|
<Icon name={icon} />
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { IconAction } from "@ax/components";
|
|
2
|
+
import { useModal } from "@ax/hooks";
|
|
3
|
+
|
|
4
|
+
import { DeleteKeywordsModal } from "../atoms";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const KeywordItem = (props: IKeywordItemProps) => {
|
|
9
|
+
const { keyword, count, isSelected, onClick, deleteKeyword } = props;
|
|
10
|
+
|
|
11
|
+
const { isOpen, toggleModal } = useModal();
|
|
12
|
+
|
|
13
|
+
const handleDeleteKeyword = (e: React.MouseEvent) => {
|
|
14
|
+
e.stopPropagation();
|
|
15
|
+
if (count > 0) {
|
|
16
|
+
toggleModal();
|
|
17
|
+
} else {
|
|
18
|
+
deleteKeyword(keyword);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<S.KeywordItem tabIndex={0} role="button" onClick={onClick} isSelected={isSelected}>
|
|
25
|
+
<S.KeyName>{keyword}</S.KeyName>
|
|
26
|
+
<S.ActionsWrapper>
|
|
27
|
+
<S.Counter isZero={!count}>{count} used</S.Counter>
|
|
28
|
+
<S.IconWrapper>
|
|
29
|
+
<IconAction icon="delete" size="s" onClick={handleDeleteKeyword} />
|
|
30
|
+
</S.IconWrapper>
|
|
31
|
+
</S.ActionsWrapper>
|
|
32
|
+
</S.KeywordItem>
|
|
33
|
+
<DeleteKeywordsModal isOpen={isOpen} toggleModal={toggleModal} deleteKeyword={() => deleteKeyword(keyword)} />
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface IKeywordItemProps {
|
|
39
|
+
keyword: string;
|
|
40
|
+
count: number;
|
|
41
|
+
isSelected: boolean;
|
|
42
|
+
onClick: () => void;
|
|
43
|
+
deleteKeyword: (keyword: string) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default KeywordItem;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const IconWrapper = styled.div`
|
|
4
|
+
margin-left: ${(p) => p.theme.spacing.xs};
|
|
5
|
+
opacity: 0;
|
|
6
|
+
`;
|
|
7
|
+
|
|
8
|
+
const KeywordItem = styled.div<{ isSelected: boolean }>`
|
|
9
|
+
position: relative;
|
|
10
|
+
background-color: ${(p) => p.theme.colors.uiBackground02};
|
|
11
|
+
display: flex;
|
|
12
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
13
|
+
min-height: ${(p) => p.theme.spacing.l};
|
|
14
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
17
|
+
padding-left: ${(p) => p.theme.spacing.s};
|
|
18
|
+
padding-right: ${(p) => p.theme.spacing.xs};
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
outline: ${(p) => (p.isSelected ? `2px solid ${p.theme.colors.interactive01}` : "none")};
|
|
22
|
+
|
|
23
|
+
&:before {
|
|
24
|
+
content: "";
|
|
25
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0;
|
|
28
|
+
left: 0;
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
opacity: 0;
|
|
32
|
+
transition: opacity 0.1s;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:hover {
|
|
36
|
+
${IconWrapper} {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:hover:before {
|
|
42
|
+
background-color: ${(p) => p.theme.colors.overlayHoverPrimary};
|
|
43
|
+
opacity: 1;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const KeyName = styled.div`
|
|
48
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
49
|
+
color: ${(p) => p.theme.colors.textHighEmphasis};
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const Counter = styled.div<{ isZero: boolean }>`
|
|
53
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
54
|
+
color: ${(p) => (p.isZero ? p.theme.colors.error : p.theme.colors.textLowEmphasis)};
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
padding-left: ${(p) => p.theme.spacing.xxs};
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const ActionsWrapper = styled.div`
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
export { KeywordItem, KeyName, Counter, ActionsWrapper, IconWrapper };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { IModal } from "@ax/types";
|
|
4
|
+
import { Modal, FieldsBehavior } from "@ax/components";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const AddKeywordsModal = (props: IAddKeywordsModal) => {
|
|
9
|
+
const { isOpen, toggleModal, addNewKeyword } = props;
|
|
10
|
+
|
|
11
|
+
const [value, setValue] = useState<string[]>([]);
|
|
12
|
+
|
|
13
|
+
const handleChange = (newValue: string[]) => setValue(newValue);
|
|
14
|
+
|
|
15
|
+
const handleClick = () => {
|
|
16
|
+
addNewKeyword(value);
|
|
17
|
+
setValue([]);
|
|
18
|
+
toggleModal();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const handleClose = () => {
|
|
22
|
+
isOpen && toggleModal();
|
|
23
|
+
setValue([]);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const mainModalAction = {
|
|
27
|
+
title: "Create keyword",
|
|
28
|
+
onClick: handleClick,
|
|
29
|
+
disabled: !value.length,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const secondaryModalAction = { title: "Cancel", onClick: handleClose };
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Modal
|
|
36
|
+
isOpen={isOpen}
|
|
37
|
+
hide={handleClose}
|
|
38
|
+
title="New keyword"
|
|
39
|
+
secondaryAction={secondaryModalAction}
|
|
40
|
+
mainAction={mainModalAction}
|
|
41
|
+
size="S"
|
|
42
|
+
height={282}
|
|
43
|
+
>
|
|
44
|
+
<S.ModalContent>
|
|
45
|
+
<FieldsBehavior
|
|
46
|
+
fieldType="TagsField"
|
|
47
|
+
name="keywords"
|
|
48
|
+
title="Keywords"
|
|
49
|
+
value={value}
|
|
50
|
+
onChange={handleChange}
|
|
51
|
+
placeholder="Type a keyword..."
|
|
52
|
+
helptext="Type a tag and press enter to create it"
|
|
53
|
+
/>
|
|
54
|
+
</S.ModalContent>
|
|
55
|
+
</Modal>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface IAddKeywordsModal extends IModal {
|
|
60
|
+
addNewKeyword: (value: string[]) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const DeleteKeywordsModal = (props: IDeleteKeywordsModal) => {
|
|
64
|
+
const { isOpen, toggleModal, deleteKeyword } = props;
|
|
65
|
+
|
|
66
|
+
const mainModalAction = {
|
|
67
|
+
title: "Delete keyword",
|
|
68
|
+
onClick: deleteKeyword,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const secondaryModalAction = { title: "Cancel", onClick: toggleModal };
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Modal
|
|
75
|
+
isOpen={isOpen}
|
|
76
|
+
hide={toggleModal}
|
|
77
|
+
title="Delete keyword"
|
|
78
|
+
secondaryAction={secondaryModalAction}
|
|
79
|
+
mainAction={mainModalAction}
|
|
80
|
+
size="S"
|
|
81
|
+
height={240}
|
|
82
|
+
>
|
|
83
|
+
<S.ModalContent>
|
|
84
|
+
You are about to <strong>delete a keyword that is currently in use.</strong>
|
|
85
|
+
<br />
|
|
86
|
+
Please make sure you want to delete it.
|
|
87
|
+
</S.ModalContent>
|
|
88
|
+
</Modal>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
interface IDeleteKeywordsModal extends IModal {
|
|
93
|
+
deleteKeyword: () => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { AddKeywordsModal, DeleteKeywordsModal };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { FloatingPanel, Icon, Toast } from "@ax/components";
|
|
4
|
+
import { useModal, useToast } from "@ax/hooks";
|
|
5
|
+
|
|
6
|
+
import { AddKeywordsModal } from "./atoms";
|
|
7
|
+
import KeywordItem from "./KeywordItem";
|
|
8
|
+
import { countKeywords } from "./utils";
|
|
9
|
+
|
|
10
|
+
import * as S from "./style";
|
|
11
|
+
|
|
12
|
+
const KeywordsPreviewModal = (props: IKeywordsPreviewProps) => {
|
|
13
|
+
const { isOpen, browserRef, keywords, keywordsFilter, toggleModal, setKeywordsFilter, addKeywords, deleteKeyword } =
|
|
14
|
+
props;
|
|
15
|
+
const [keywordCounts, setKeywordCounts] = useState<Record<string, number>>({});
|
|
16
|
+
const [deletedKeyword, setDeletedKeyword] = useState<string | null>(null);
|
|
17
|
+
const { isOpen: isAddOpen, toggleModal: toggleAddModal } = useModal();
|
|
18
|
+
const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (isOpen && browserRef.current) {
|
|
22
|
+
const keywordCount = countKeywords(browserRef.current, keywords);
|
|
23
|
+
setKeywordCounts(keywordCount);
|
|
24
|
+
}
|
|
25
|
+
}, [isOpen, browserRef, keywords]);
|
|
26
|
+
|
|
27
|
+
const handleDeleteTag = () => setKeywordsFilter([]);
|
|
28
|
+
|
|
29
|
+
const handleAddTag = (tag: string) => () =>
|
|
30
|
+
keywordsFilter.includes(tag) ? setKeywordsFilter([]) : setKeywordsFilter([tag]);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (deletedKeyword && !keywords.includes(deletedKeyword)) {
|
|
34
|
+
toggleToast("1 Keyword deleted");
|
|
35
|
+
setDeletedKeyword(null);
|
|
36
|
+
}
|
|
37
|
+
}, [keywords, deletedKeyword, toggleToast]);
|
|
38
|
+
|
|
39
|
+
const handleDeleteKeyword = (value: string) => {
|
|
40
|
+
setDeletedKeyword(value);
|
|
41
|
+
deleteKeyword(value);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<S.Wrapper>
|
|
46
|
+
<FloatingPanel title="Keywords" toggleModal={toggleModal} closeOnOutsideClick={false} isOpen={isOpen} width={358}>
|
|
47
|
+
{isOpen && (
|
|
48
|
+
<S.KeywordsWrapper>
|
|
49
|
+
{keywordsFilter.length > 0 && (
|
|
50
|
+
<S.FilterWrapper>
|
|
51
|
+
<S.FilterText>Show keyword:</S.FilterText>
|
|
52
|
+
<S.TagList>
|
|
53
|
+
{keywordsFilter.map((tag: string) => {
|
|
54
|
+
return <S.StyledTag key={tag} text={tag} color="#FFFFFF" onDeleteAction={handleDeleteTag} />;
|
|
55
|
+
})}
|
|
56
|
+
</S.TagList>
|
|
57
|
+
</S.FilterWrapper>
|
|
58
|
+
)}
|
|
59
|
+
<S.KeywordsListWrapper>
|
|
60
|
+
{keywords.length === 0 && <S.StyledSummaryButton />}
|
|
61
|
+
{Object.keys(keywordCounts).map((key, index) => {
|
|
62
|
+
const isSelected = keywordsFilter.includes(key);
|
|
63
|
+
return (
|
|
64
|
+
<KeywordItem
|
|
65
|
+
keyword={key}
|
|
66
|
+
count={keywordCounts[key]}
|
|
67
|
+
isSelected={isSelected}
|
|
68
|
+
onClick={handleAddTag(key)}
|
|
69
|
+
deleteKeyword={handleDeleteKeyword}
|
|
70
|
+
key={`${key}-${index}`}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
})}
|
|
74
|
+
<S.AddKeywordButton onClick={toggleAddModal}>
|
|
75
|
+
<Icon name="add" size="16" />
|
|
76
|
+
Add new keyword
|
|
77
|
+
</S.AddKeywordButton>
|
|
78
|
+
</S.KeywordsListWrapper>
|
|
79
|
+
</S.KeywordsWrapper>
|
|
80
|
+
)}
|
|
81
|
+
</FloatingPanel>
|
|
82
|
+
<AddKeywordsModal isOpen={isAddOpen} toggleModal={toggleAddModal} addNewKeyword={addKeywords} />
|
|
83
|
+
{isVisible && <Toast message={toastState} setIsVisible={setIsVisible} />}
|
|
84
|
+
</S.Wrapper>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
interface IKeywordsPreviewProps {
|
|
89
|
+
isOpen: boolean;
|
|
90
|
+
browserRef: React.RefObject<HTMLDivElement>;
|
|
91
|
+
toggleModal: () => void;
|
|
92
|
+
keywords: string[];
|
|
93
|
+
keywordsFilter: string[];
|
|
94
|
+
setKeywordsFilter: (value: string[]) => void;
|
|
95
|
+
addKeywords: (value: string[]) => void;
|
|
96
|
+
deleteKeyword: (value: string) => void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default KeywordsPreviewModal;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
import SummaryButton from "../Fields/SummaryButton";
|
|
4
|
+
import Tag from "../Tag";
|
|
5
|
+
|
|
6
|
+
const Wrapper = styled.div``;
|
|
7
|
+
|
|
8
|
+
const KeywordsWrapper = styled.div`
|
|
9
|
+
padding: ${(p) => `${p.theme.spacing.m} ${p.theme.spacing.m} 80px ${p.theme.spacing.s}`};
|
|
10
|
+
overflow-y: auto;
|
|
11
|
+
height: 100%;
|
|
12
|
+
width: 100%;
|
|
13
|
+
position: relative;
|
|
14
|
+
|
|
15
|
+
::-webkit-scrollbar {
|
|
16
|
+
-webkit-appearance: none;
|
|
17
|
+
width: 4px;
|
|
18
|
+
height: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
::-webkit-scrollbar-thumb {
|
|
22
|
+
border-radius: 4px;
|
|
23
|
+
background-color: ${(p) => p.theme.colors.iconNonActive};
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const KeywordsListWrapper = styled.div``;
|
|
28
|
+
|
|
29
|
+
const FilterWrapper = styled.div`
|
|
30
|
+
display: flex;
|
|
31
|
+
background-color: ${(p) => p.theme.color.uiBackground03};
|
|
32
|
+
width: 100%;
|
|
33
|
+
padding: ${(p) => `${p.theme.spacing.xs} ${p.theme.spacing.xs}`};
|
|
34
|
+
align-items: center;
|
|
35
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
36
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const FilterText = styled.div`
|
|
40
|
+
${(p) => p.theme.textStyle.uiS};
|
|
41
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
42
|
+
margin-right: ${(p) => p.theme.spacing.xs};
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const StyledTag = styled(Tag)`
|
|
47
|
+
margin-right: ${(p) => p.theme.spacing.xs};
|
|
48
|
+
margin-bottom: 0;
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const TagList = styled.div``;
|
|
52
|
+
|
|
53
|
+
const AddKeywordButton = styled.button`
|
|
54
|
+
display: flex;
|
|
55
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
56
|
+
color: ${(p) => p.theme.colors.interactive01};
|
|
57
|
+
min-height: 40px;
|
|
58
|
+
width: 100%;
|
|
59
|
+
border: ${(p) => `1px dashed ${p.theme.colors.interactive01}`};
|
|
60
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
svg {
|
|
64
|
+
margin-right: ${(p) => p.theme.spacing.xxs};
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const ModalContent = styled.div`
|
|
69
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const StyledSummaryButton = styled(SummaryButton)`
|
|
73
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
Wrapper,
|
|
78
|
+
KeywordsWrapper,
|
|
79
|
+
KeywordsListWrapper,
|
|
80
|
+
FilterWrapper,
|
|
81
|
+
FilterText,
|
|
82
|
+
StyledTag,
|
|
83
|
+
TagList,
|
|
84
|
+
AddKeywordButton,
|
|
85
|
+
ModalContent,
|
|
86
|
+
StyledSummaryButton,
|
|
87
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const countKeywords = (html: HTMLDivElement, keywords: string[]) => {
|
|
2
|
+
const frameObject = html.querySelector<HTMLIFrameElement>(".frame-content");
|
|
3
|
+
const frameContent = frameObject?.contentWindow?.document.getElementById("___griddo") as HTMLElement;
|
|
4
|
+
|
|
5
|
+
if (!frameContent) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const htmlContent = frameContent.innerText.toLowerCase();
|
|
10
|
+
const keywordCounts: Record<string, number> = {};
|
|
11
|
+
|
|
12
|
+
keywords.forEach((keyword) => {
|
|
13
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
14
|
+
const regex = new RegExp(lowerKeyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
|
|
15
|
+
const matches = htmlContent.match(regex);
|
|
16
|
+
keywordCounts[keyword] = matches ? matches.length : 0;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return keywordCounts;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { countKeywords };
|
|
@@ -190,6 +190,7 @@ const AppBar = (props: IProps): JSX.Element => {
|
|
|
190
190
|
setSelectedTab={tabs.action}
|
|
191
191
|
isInAppBar={true}
|
|
192
192
|
inversed={inversed}
|
|
193
|
+
disabled={tabs.disabled}
|
|
193
194
|
/>
|
|
194
195
|
</S.TabsContent>
|
|
195
196
|
</S.WrapperTabs>
|
|
@@ -291,7 +292,13 @@ export interface IAppBarProps {
|
|
|
291
292
|
backLink?: boolean | string;
|
|
292
293
|
rightButton?: { label: string; disabled?: boolean; action: (e: any) => void };
|
|
293
294
|
rightLineButton?: { label: string; disabled?: boolean; action: (e: any) => void };
|
|
294
|
-
tabs?: {
|
|
295
|
+
tabs?: {
|
|
296
|
+
tabSet?: any;
|
|
297
|
+
icons?: { name: string; text: string }[];
|
|
298
|
+
selectedTab: string;
|
|
299
|
+
action: (e: any) => void;
|
|
300
|
+
disabled?: boolean;
|
|
301
|
+
};
|
|
295
302
|
downArrowMenu?: { displayed: boolean; options: any; button: any };
|
|
296
303
|
title: string;
|
|
297
304
|
subtitle?: string;
|
|
@@ -38,7 +38,13 @@ export interface IWrapperProps {
|
|
|
38
38
|
fixedAppBar?: boolean;
|
|
39
39
|
additionalClass?: string;
|
|
40
40
|
downArrowMenu?: { displayed: boolean; options: any; button: any };
|
|
41
|
-
tabs?: {
|
|
41
|
+
tabs?: {
|
|
42
|
+
tabSet?: any;
|
|
43
|
+
icons?: { name: string; text: string }[];
|
|
44
|
+
selectedTab: string;
|
|
45
|
+
action: (e: any) => void;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
};
|
|
42
48
|
pageStatus?: string;
|
|
43
49
|
language?: { locale: string; id: number | null } | null;
|
|
44
50
|
availableLanguages?: ILanguage[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { Icon } from "@ax/components";
|
|
3
3
|
import SubNotification from "./SubNotification";
|
|
4
4
|
|
|
@@ -45,7 +45,7 @@ const Notification = (props: INotificationProps): JSX.Element => {
|
|
|
45
45
|
const handleClick = () => (onClick ? onClick() : handleErrorClick());
|
|
46
46
|
|
|
47
47
|
const handleClose = () => {
|
|
48
|
-
resetError
|
|
48
|
+
resetError?.();
|
|
49
49
|
setIsVisible(false);
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -120,7 +120,7 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
|
|
|
120
120
|
if (isReqOk(response.status)) {
|
|
121
121
|
setState((state) => ({ ...state, items: response.data.items, totalItems: response.data.totalItems }));
|
|
122
122
|
} else {
|
|
123
|
-
console.
|
|
123
|
+
console.error("Error en getAndSetItems");
|
|
124
124
|
}
|
|
125
125
|
setIsLoading(false);
|
|
126
126
|
};
|
|
@@ -4,10 +4,10 @@ import ResizeHandle from "./ResizeHandle";
|
|
|
4
4
|
|
|
5
5
|
import * as S from "./style";
|
|
6
6
|
|
|
7
|
-
const MIN_WIDTH =
|
|
7
|
+
const MIN_WIDTH = 368;
|
|
8
8
|
|
|
9
9
|
const ResizePanel = (props: IResizePanelProps): JSX.Element => {
|
|
10
|
-
const { leftPanel, rightPanel, fixed = true, full = false } = props;
|
|
10
|
+
const { leftPanel, rightPanel, fixed = true, full = false, disabled } = props;
|
|
11
11
|
|
|
12
12
|
const [rwidth, setRwidth] = useState(MIN_WIDTH);
|
|
13
13
|
const rightPanelRef = useRef<HTMLDivElement>(null);
|
|
@@ -44,7 +44,7 @@ const ResizePanel = (props: IResizePanelProps): JSX.Element => {
|
|
|
44
44
|
leftPanel
|
|
45
45
|
)}
|
|
46
46
|
</S.LeftPanel>
|
|
47
|
-
<ResizeHandle onMouseMove={resize} />
|
|
47
|
+
{!disabled && <ResizeHandle onMouseMove={resize} />}
|
|
48
48
|
<S.RightPanel ref={rightPanelRef} data-testid="right-panel" style={{ width: rwidth ? `${rwidth}px` : "auto" }}>
|
|
49
49
|
{rightPanel}
|
|
50
50
|
</S.RightPanel>
|
|
@@ -57,6 +57,7 @@ export interface IResizePanelProps {
|
|
|
57
57
|
rightPanel: JSX.Element | JSX.Element[];
|
|
58
58
|
fixed?: boolean;
|
|
59
59
|
full?: boolean;
|
|
60
|
+
disabled?: boolean;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export default ResizePanel;
|
|
@@ -11,7 +11,7 @@ const RightPanel = styled.section`
|
|
|
11
11
|
position: relative;
|
|
12
12
|
padding: ${(p) => `0 ${p.theme.spacing.m} ${p.theme.spacing.m} ${p.theme.spacing.m}`};
|
|
13
13
|
flex-shrink: 0;
|
|
14
|
-
min-width:
|
|
14
|
+
min-width: 368px;
|
|
15
15
|
max-width: ${(p) => `calc(100% - 500px - ${p.theme.spacing.m})`};
|
|
16
16
|
flex-direction: column;
|
|
17
17
|
`;
|
|
@@ -140,6 +140,7 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
140
140
|
|
|
141
141
|
const filteredOptions = options.options?.map((option) => {
|
|
142
142
|
const optionComponent = typeof option === "string" ? option : option.component;
|
|
143
|
+
const optionKey = typeof option === "string" ? option : (option.id ?? option.editorID ?? optionComponent);
|
|
143
144
|
const displayName = getDisplayName(optionComponent);
|
|
144
145
|
if (searchQuery.length > 0) {
|
|
145
146
|
const name = displayName.toLowerCase();
|
|
@@ -147,7 +148,7 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
147
148
|
if (!name.includes(search)) return null;
|
|
148
149
|
}
|
|
149
150
|
return (
|
|
150
|
-
<SideModalOption option={option} handleClick={handleClick} key={
|
|
151
|
+
<SideModalOption option={option} handleClick={handleClick} key={optionKey} theme={theme}>
|
|
151
152
|
{displayName}
|
|
152
153
|
</SideModalOption>
|
|
153
154
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect } from "react";
|
|
2
2
|
import { Icon, Tooltip } from "@ax/components";
|
|
3
3
|
|
|
4
4
|
import * as S from "./style";
|
|
5
5
|
|
|
6
6
|
const Tabs = (props: ITabsProps): JSX.Element => {
|
|
7
|
-
const { tabs, icons, active, setSelectedTab, isInAppBar, noMargins, inversed } = props;
|
|
7
|
+
const { tabs, icons, active, setSelectedTab, isInAppBar, noMargins, inversed, disabled } = props;
|
|
8
8
|
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
tabs && tabs.length === 1 && setSelectedTab(tabs[0]);
|
|
@@ -14,7 +14,10 @@ const Tabs = (props: ITabsProps): JSX.Element => {
|
|
|
14
14
|
return (
|
|
15
15
|
<S.TabsRow isInAppBar={isInAppBar} noMargins={noMargins} data-testid="tabs-row">
|
|
16
16
|
{tabs.map((tab: any) => {
|
|
17
|
-
const handleClick = () =>
|
|
17
|
+
const handleClick = () => {
|
|
18
|
+
if (disabled) return;
|
|
19
|
+
setSelectedTab(tab);
|
|
20
|
+
};
|
|
18
21
|
return (
|
|
19
22
|
<S.TabItem
|
|
20
23
|
isInAppBar={isInAppBar}
|
|
@@ -22,6 +25,7 @@ const Tabs = (props: ITabsProps): JSX.Element => {
|
|
|
22
25
|
active={tab === active}
|
|
23
26
|
onClick={handleClick}
|
|
24
27
|
data-testid="tab"
|
|
28
|
+
isDisabled={disabled}
|
|
25
29
|
>
|
|
26
30
|
{tab}
|
|
27
31
|
</S.TabItem>
|
|
@@ -35,13 +39,17 @@ const Tabs = (props: ITabsProps): JSX.Element => {
|
|
|
35
39
|
return (
|
|
36
40
|
<S.TabsRow icons={true} isInAppBar={isInAppBar} data-testid="icons-tabs-row">
|
|
37
41
|
{icons.map((tab: ITabIcon) => {
|
|
38
|
-
const handleClick = () =>
|
|
42
|
+
const handleClick = () => {
|
|
43
|
+
if (disabled) return;
|
|
44
|
+
setSelectedTab(tab.name);
|
|
45
|
+
};
|
|
39
46
|
return (
|
|
40
47
|
<S.TabItem
|
|
41
48
|
key={tab.name}
|
|
42
49
|
active={tab.name === active}
|
|
43
50
|
onClick={handleClick}
|
|
44
51
|
inversed={inversed}
|
|
52
|
+
isDisabled={disabled}
|
|
45
53
|
data-testid="icon-tab"
|
|
46
54
|
>
|
|
47
55
|
<S.TabIcon>
|
|
@@ -67,6 +75,7 @@ export interface ITabsProps {
|
|
|
67
75
|
setSelectedTab: any;
|
|
68
76
|
noMargins?: boolean;
|
|
69
77
|
inversed?: boolean;
|
|
78
|
+
disabled?: boolean;
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
export interface ITabIcon {
|
|
@@ -8,15 +8,14 @@ const TabsRow = styled.div<{ icons?: boolean; isInAppBar?: boolean; noMargins?:
|
|
|
8
8
|
height: ${(p) => (p.noMargins || p.isInAppBar ? "auto" : "100%")};
|
|
9
9
|
`;
|
|
10
10
|
|
|
11
|
-
const TabItem = styled.button<{ active: boolean; isInAppBar?: boolean; inversed?: boolean }>`
|
|
11
|
+
const TabItem = styled.button<{ active: boolean; isInAppBar?: boolean; inversed?: boolean; isDisabled?: boolean }>`
|
|
12
12
|
flex-grow: 1;
|
|
13
|
-
border: none;
|
|
14
|
-
border-bottom: 4px solid
|
|
15
|
-
${(p) =>
|
|
16
|
-
!p.active ? "transparent" : p.inversed ? p.theme.color.iconHighEmphasisInverse : p.theme.color.interactive01};
|
|
17
13
|
${(p) => p.theme.textStyle.headingXS};
|
|
14
|
+
border: none;
|
|
15
|
+
border-bottom: ${(p) => (p.active ? "4px solid" : "none")};
|
|
16
|
+
border-color: ${(p) => (p.isDisabled ? p.theme.color.interactiveDisabled : p.inversed ? p.theme.color.iconHighEmphasisInverse : p.theme.color.interactive01)};
|
|
18
17
|
color: ${(p) => (p.active ? p.theme.color.textHighEmphasis : p.theme.color.textMediumEmphasis)};
|
|
19
|
-
cursor: ${(p) => (p.active ? "initial" : "pointer")};
|
|
18
|
+
cursor: ${(p) => (p.active || p.isDisabled ? "initial" : "pointer")};
|
|
20
19
|
background: transparent;
|
|
21
20
|
height: ${(p) => (p.isInAppBar ? "100%" : "48px")};
|
|
22
21
|
|
|
@@ -24,7 +23,7 @@ const TabItem = styled.button<{ active: boolean; isInAppBar?: boolean; inversed?
|
|
|
24
23
|
color: ${(p) => (p.active ? p.theme.color.textHighEmphasis : p.theme.color.interactive01)};
|
|
25
24
|
svg {
|
|
26
25
|
path {
|
|
27
|
-
fill: ${(p) => (p.inversed ? p.theme.color.iconHighEmphasisInverse : p.theme.color.interactive01)};
|
|
26
|
+
fill: ${(p) => (p.isDisabled ? p.theme.color.interactiveDisabled : p.inversed ? p.theme.color.iconHighEmphasisInverse : p.theme.color.interactive01)};
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
}
|
|
@@ -39,7 +38,7 @@ const TabItem = styled.button<{ active: boolean; isInAppBar?: boolean; inversed?
|
|
|
39
38
|
height: ${(p) => p.theme.spacing.m};
|
|
40
39
|
path {
|
|
41
40
|
fill: ${(p) =>
|
|
42
|
-
!p.active
|
|
41
|
+
!p.active || p.isDisabled
|
|
43
42
|
? p.theme.color.interactiveDisabled
|
|
44
43
|
: p.inversed
|
|
45
44
|
? p.theme.color.iconHighEmphasisInverse
|