@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.
Files changed (100) hide show
  1. package/config/jest/componentsMock.js +7 -5
  2. package/package.json +2 -2
  3. package/src/__tests__/components/Browser/Browser.test.tsx +438 -87
  4. package/src/__tests__/components/Browser/Browser.utils.test.ts +55 -0
  5. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +1 -3
  6. package/src/__tests__/components/Fields/Button/Button.test.tsx +29 -27
  7. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +158 -0
  8. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +90 -0
  9. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +178 -0
  10. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +150 -0
  11. package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +91 -0
  12. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +122 -0
  13. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +15 -0
  14. package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +101 -0
  15. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
  16. package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +318 -0
  17. package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +242 -0
  18. package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +185 -0
  19. package/src/components/Browser/index.tsx +294 -149
  20. package/src/components/Browser/style.tsx +75 -6
  21. package/src/components/Browser/utils.tsx +13 -0
  22. package/src/components/Button/index.tsx +2 -1
  23. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
  24. package/src/components/Fields/AsyncSelect/style.tsx +13 -0
  25. package/src/components/Fields/FieldGroup/index.tsx +5 -2
  26. package/src/components/Fields/FieldGroup/style.tsx +32 -7
  27. package/src/components/Fields/HeadingField/index.tsx +22 -22
  28. package/src/components/Fields/HiddenField/style.tsx +1 -1
  29. package/src/components/Fields/NumberField/index.tsx +15 -16
  30. package/src/components/Fields/NumberField/style.tsx +2 -0
  31. package/src/components/Fields/ReferenceField/index.tsx +1 -1
  32. package/src/components/Fields/SEOPreview/index.tsx +36 -0
  33. package/src/components/Fields/SEOPreview/style.tsx +24 -0
  34. package/src/components/Fields/Select/index.tsx +5 -1
  35. package/src/components/Fields/Select/style.tsx +56 -0
  36. package/src/components/Fields/SummaryButton/index.tsx +18 -9
  37. package/src/components/Fields/SummaryButton/style.tsx +1 -2
  38. package/src/components/Fields/TagsField/index.tsx +8 -9
  39. package/src/components/Fields/UrlField/index.tsx +26 -27
  40. package/src/components/Fields/index.tsx +2 -0
  41. package/src/components/FloatingNote/index.tsx +35 -0
  42. package/src/components/FloatingNote/style.tsx +26 -0
  43. package/src/components/FloatingPanel/index.tsx +5 -2
  44. package/src/components/FloatingPanel/style.tsx +2 -1
  45. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
  46. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
  47. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
  48. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
  49. package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
  50. package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
  51. package/src/components/HeadingsPreviewModal/index.tsx +148 -0
  52. package/src/components/HeadingsPreviewModal/style.tsx +82 -0
  53. package/src/components/HeadingsPreviewModal/utils.tsx +329 -0
  54. package/src/components/Icon/index.tsx +1 -2
  55. package/src/components/IconAction/index.tsx +1 -1
  56. package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +46 -0
  57. package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +64 -0
  58. package/src/components/KeywordsPreviewModal/atoms.tsx +96 -0
  59. package/src/components/KeywordsPreviewModal/index.tsx +99 -0
  60. package/src/components/KeywordsPreviewModal/style.tsx +87 -0
  61. package/src/components/KeywordsPreviewModal/utils.tsx +22 -0
  62. package/src/components/MainWrapper/AppBar/index.tsx +8 -1
  63. package/src/components/MainWrapper/index.tsx +7 -1
  64. package/src/components/Notification/index.tsx +2 -2
  65. package/src/components/PageFinder/index.tsx +1 -1
  66. package/src/components/ResizePanel/index.tsx +4 -3
  67. package/src/components/ResizePanel/style.tsx +1 -1
  68. package/src/components/SearchField/style.tsx +2 -2
  69. package/src/components/SideModal/index.tsx +2 -1
  70. package/src/components/Tabs/index.tsx +13 -4
  71. package/src/components/Tabs/style.tsx +7 -8
  72. package/src/components/Toast/index.tsx +4 -2
  73. package/src/components/Tooltip/index.tsx +4 -3
  74. package/src/components/index.tsx +8 -0
  75. package/src/forms/fields.tsx +70 -68
  76. package/src/hooks/forms.tsx +22 -1
  77. package/src/hooks/index.tsx +13 -3
  78. package/src/hooks/modals.tsx +103 -15
  79. package/src/hooks/users.tsx +25 -8
  80. package/src/modules/Forms/atoms.tsx +2 -2
  81. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +116 -0
  82. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +34 -0
  83. package/src/modules/FramePreview/index.tsx +55 -16
  84. package/src/modules/FramePreview/style.tsx +34 -2
  85. package/src/modules/FramePreview/utils.tsx +140 -0
  86. package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
  87. package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
  88. package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
  89. package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
  90. package/src/modules/GlobalEditor/index.tsx +119 -57
  91. package/src/modules/PageEditor/Editor/index.tsx +33 -2
  92. package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
  93. package/src/modules/PageEditor/Preview/index.tsx +0 -2
  94. package/src/modules/PageEditor/Preview/style.tsx +1 -1
  95. package/src/modules/PageEditor/atoms.tsx +1 -1
  96. package/src/modules/PageEditor/index.tsx +130 -66
  97. package/src/modules/PublicPreview/index.tsx +5 -2
  98. package/src/schemas/pages/GlobalPage.ts +87 -70
  99. package/src/schemas/pages/Page.ts +87 -70
  100. package/src/types/index.tsx +12 -0
@@ -1,8 +1,7 @@
1
1
  const getImage = (name: string) => {
2
2
  try {
3
3
  return require(`./components/${name}`).default;
4
- } catch (err) {
5
- console.log(name);
4
+ } catch (_err) {
6
5
  return null;
7
6
  }
8
7
  };
@@ -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?: { tabSet?: any; icons?: { name: string; text: string }[]; selectedTab: string; action: (e: any) => void };
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?: { tabSet?: any; icons?: { name: string; text: string }[]; selectedTab: string; action: (e: any) => void };
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 React, { useState } from "react";
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 && 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.log("Error en getAndSetItems");
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 = 344;
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: 344px;
14
+ min-width: 368px;
15
15
  max-width: ${(p) => `calc(100% - 500px - ${p.theme.spacing.m})`};
16
16
  flex-direction: column;
17
17
  `;
@@ -1,7 +1,7 @@
1
- import { Button } from "@ax/components";
2
-
3
1
  import styled from "styled-components";
4
2
 
3
+ import Button from "../Button";
4
+
5
5
  const Wrapper = styled.div`
6
6
  display: flex;
7
7
  position: relative;
@@ -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={optionComponent} theme={theme}>
151
+ <SideModalOption option={option} handleClick={handleClick} key={optionKey} theme={theme}>
151
152
  {displayName}
152
153
  </SideModalOption>
153
154
  );
@@ -1,10 +1,10 @@
1
- import React, { useEffect } from "react";
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 = () => setSelectedTab(tab);
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 = () => setSelectedTab(tab.name);
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