@griddo/ax 1.63.4 → 1.64.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/config/griddo-config/cx-polyfills/builder.ssr.js +6 -0
  2. package/config/griddo-config/cx-polyfills/componentsBundle.js +4 -0
  3. package/config/{griddo-config.js → griddo-config/index.js} +36 -15
  4. package/config/griddo-config/ssrHelpers.js +47 -0
  5. package/config/jest/componentsMock.js +29 -0
  6. package/config/jest/fileMock.js +1 -0
  7. package/config/jest/setup.js +5 -0
  8. package/config/jest/styleMock.js +1 -0
  9. package/config/jest/test-utils.js +17 -0
  10. package/config/paths.js +36 -5
  11. package/config/webpack.config.js +1 -1
  12. package/config/webpackDevServer.config.js +4 -1
  13. package/config/webpackSchemas.config.js +4 -1
  14. package/package.json +35 -64
  15. package/scripts/build.js +9 -2
  16. package/src/__mocks__/reducers/analyticsState.tsx +14 -0
  17. package/src/__mocks__/reducers/pageEditor.tsx +30 -0
  18. package/src/api/sites.tsx +28 -6
  19. package/src/api/structuredData.tsx +1 -1
  20. package/src/api/users.tsx +5 -4
  21. package/src/components/ActionMenu/style.tsx +2 -0
  22. package/src/components/Browser/index.tsx +9 -5
  23. package/src/{modules/Content/PageItem/atoms.tsx → components/CategoryCell/index.tsx} +4 -6
  24. package/src/components/CategoryCell/style.tsx +11 -0
  25. package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +19 -19
  26. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +26 -16
  27. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +8 -13
  28. package/src/components/Fields/AnalyticsField/index.test.tsx +100 -0
  29. package/src/components/Fields/AnalyticsField/index.tsx +9 -2
  30. package/src/components/Fields/AnalyticsField/utils.tsx +2 -2
  31. package/src/components/Fields/ArrayFieldGroup/ArrayFieldItem/style.tsx +2 -1
  32. package/src/components/Fields/CheckField/index.test.tsx +95 -0
  33. package/src/components/Fields/CheckField/index.tsx +9 -3
  34. package/src/components/Fields/CheckField/style.tsx +32 -24
  35. package/src/components/Fields/CheckGroup/index.test.tsx +274 -0
  36. package/src/components/Fields/CheckGroup/index.tsx +2 -1
  37. package/src/components/Fields/FileField/FileDragAndDrop/style.tsx +3 -2
  38. package/src/components/Fields/FileField/style.tsx +2 -1
  39. package/src/components/Fields/MultiCheckSelect/style.tsx +18 -18
  40. package/src/components/Fields/NoteField/style.tsx +9 -9
  41. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +1 -1
  42. package/src/components/Fields/Select/style.tsx +41 -37
  43. package/src/components/Fields/TagField/index.test.tsx +136 -0
  44. package/src/components/Fields/TagField/index.tsx +8 -12
  45. package/src/components/Fields/TextArea/index.test.tsx +69 -0
  46. package/src/components/Fields/TextArea/index.tsx +4 -13
  47. package/src/components/Fields/TextArea/style.tsx +2 -2
  48. package/src/components/Fields/TextField/index.test.tsx +144 -0
  49. package/src/components/Fields/TextField/index.tsx +23 -19
  50. package/src/components/Fields/TextField/style.tsx +16 -7
  51. package/src/components/Fields/UniqueCheck/index.test.tsx +43 -0
  52. package/src/components/Fields/UrlField/utils.tsx +8 -6
  53. package/src/components/FieldsBehavior/index.tsx +0 -2
  54. package/src/components/FieldsBehavior/style.tsx +21 -21
  55. package/src/components/Gallery/GalleryFilters/Orientation/style.tsx +2 -1
  56. package/src/components/Gallery/GalleryFilters/SortBy/style.tsx +2 -1
  57. package/src/components/Icon/index.tsx +12 -10
  58. package/src/components/IconAction/index.tsx +7 -1
  59. package/src/components/IconAction/style.tsx +10 -10
  60. package/src/components/SearchField/index.tsx +11 -8
  61. package/src/components/SearchField/style.tsx +21 -12
  62. package/src/components/TableFilters/CategoryFilter/index.tsx +1 -1
  63. package/src/components/TableFilters/CategoryFilter/style.tsx +2 -1
  64. package/src/components/TableFilters/DateFilter/style.tsx +2 -1
  65. package/src/components/TableFilters/LiveFilter/index.tsx +2 -2
  66. package/src/components/TableFilters/LiveFilter/style.tsx +2 -1
  67. package/src/components/TableFilters/NameFilter/style.tsx +2 -1
  68. package/src/components/TableFilters/SiteFilter/index.tsx +38 -24
  69. package/src/components/TableFilters/SiteFilter/style.tsx +2 -1
  70. package/src/components/TableFilters/StatusFilter/style.tsx +2 -1
  71. package/src/components/TableFilters/TranslationsFilter/style.tsx +2 -1
  72. package/src/components/TableFilters/TypeFilter/style.tsx +2 -1
  73. package/src/components/Tag/index.tsx +9 -7
  74. package/src/components/Tag/style.tsx +20 -8
  75. package/src/components/index.tsx +4 -2
  76. package/src/containers/App/reducer.tsx +0 -2
  77. package/src/containers/PageEditor/actions.tsx +2 -2
  78. package/src/containers/Sites/actions.tsx +30 -19
  79. package/src/containers/Users/actions.tsx +10 -2
  80. package/src/containers/Users/reducer.tsx +3 -1
  81. package/src/helpers/fields.tsx +2 -4
  82. package/src/helpers/index.tsx +3 -0
  83. package/src/helpers/themes.tsx +9 -0
  84. package/src/index.tsx +3 -0
  85. package/src/modules/Analytics/GroupPanel/utils.tsx +3 -3
  86. package/src/modules/App/Routing/NavMenu/index.tsx +13 -12
  87. package/src/modules/Content/PageItem/index.tsx +31 -9
  88. package/src/modules/Content/PageItem/style.tsx +0 -7
  89. package/src/modules/Content/atoms.tsx +78 -0
  90. package/src/modules/Content/index.tsx +104 -33
  91. package/src/modules/Content/style.tsx +10 -7
  92. package/src/modules/GlobalEditor/PageBrowser/index.tsx +0 -4
  93. package/src/modules/GlobalEditor/index.tsx +3 -3
  94. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +0 -4
  95. package/src/modules/PageEditor/PageBrowser/index.tsx +0 -4
  96. package/src/modules/PageEditor/atoms.tsx +74 -0
  97. package/src/modules/PageEditor/index.tsx +30 -9
  98. package/src/modules/PageEditor/style.tsx +4 -0
  99. package/src/modules/PublicPreview/index.tsx +3 -5
  100. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +0 -4
  101. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +2 -3
  102. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +1 -1
  103. package/src/modules/Settings/Globals/index.tsx +3 -3
  104. package/src/modules/StructuredData/Form/index.tsx +2 -4
  105. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +22 -18
  106. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/atoms.tsx +3 -24
  107. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +2 -2
  108. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +0 -7
  109. package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +2 -4
  110. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +46 -14
  111. package/src/modules/StructuredData/StructuredDataList/hooks.tsx +21 -9
  112. package/src/modules/StructuredData/StructuredDataList/index.tsx +48 -20
  113. package/src/modules/Users/Profile/index.tsx +12 -7
  114. package/src/modules/Users/UserCreate/SiteItem/index.tsx +44 -0
  115. package/src/modules/Users/UserCreate/SiteItem/style.tsx +30 -0
  116. package/src/modules/Users/UserCreate/index.tsx +120 -10
  117. package/src/modules/Users/UserCreate/style.tsx +54 -1
  118. package/src/modules/Users/UserEdit/index.tsx +53 -15
  119. package/src/modules/Users/UserForm/index.tsx +152 -5
  120. package/src/modules/Users/UserForm/style.tsx +40 -2
  121. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +40 -2
  122. package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +0 -1
  123. package/src/modules/Users/UserList/BulkHeader/index.tsx +10 -1
  124. package/src/modules/Users/UserList/UserItem/index.tsx +70 -15
  125. package/src/modules/Users/UserList/hooks.tsx +58 -1
  126. package/src/modules/Users/UserList/index.tsx +80 -34
  127. package/src/modules/Users/index.tsx +18 -11
  128. package/src/routes/site.tsx +8 -0
  129. package/src/types/index.tsx +7 -0
  130. package/tsconfig.json +2 -0
  131. package/patches/connected-react-router+6.9.2.patch +0 -12
  132. package/scripts/test.js +0 -45
@@ -1,35 +1,39 @@
1
1
  import styled, { css } from "styled-components";
2
2
  import Select from "react-select";
3
3
 
4
- export const StyledSelect = styled(Select)<{ isDisabled: boolean | undefined; error: boolean | undefined, alignRight: boolean | undefined }>`
5
- ${(p) => p.theme.textStyle.fieldContent};
6
- color: ${(p) => p.theme.color.textHighEmphasis};
7
- margin-bottom: ${(p) => p.theme.spacing.xxs};
4
+ export const StyledSelect = styled(Select)<{
5
+ isDisabled: boolean | undefined;
6
+ error: boolean | undefined;
7
+ alignRight: boolean | undefined;
8
+ }>`
9
+ ${(p) => p.theme.textStyle?.fieldContent};
10
+ color: ${(p) => p.theme.color?.textHighEmphasis};
11
+ margin-bottom: ${(p) => p.theme.spacing?.xxs};
8
12
 
9
13
  .react-select__control {
10
- background-color: ${(p) => p.theme.color.interactiveBackground};
14
+ background-color: ${(p) => p.theme.color?.interactiveBackground};
11
15
  box-sizing: border-box;
12
16
  border-radius: 4px;
13
- border-color: ${(p) => (p.error ? p.theme.color.error : p.theme.color.uiLine)};
17
+ border-color: ${(p) => (p.error ? p.theme.color?.error : p.theme.color?.uiLine)};
14
18
  box-shadow: none;
15
- height: ${(p) => p.theme.spacing.l};
16
- max-width: calc(${(p) => p.theme.spacing.xl} * 6);
19
+ height: ${(p) => p.theme.spacing?.l};
20
+ max-width: calc(${(p) => p.theme.spacing?.xl} * 6);
17
21
  :hover {
18
- border-color: ${(p) => (p.error ? p.theme.color.error : p.theme.color.uiLine)};
22
+ border-color: ${(p) => (p.error ? p.theme.color?.error : p.theme.color?.uiLine)};
19
23
  }
20
24
  .react-select__placeholder {
21
- color: ${(p) => p.theme.color.textLowEmphasis};
25
+ color: ${(p) => p.theme.color?.textLowEmphasis};
22
26
  }
23
27
  .react-select__indicator-separator {
24
28
  display: none;
25
29
  }
26
30
  .react-select__indicator {
27
- color: ${(p) => (p.isDisabled ? p.theme.color.interactiveDisabled : p.theme.color.interactive01)};
31
+ color: ${(p) => (p.isDisabled ? p.theme.color?.interactiveDisabled : p.theme.color?.interactive01)};
28
32
  }
29
33
  }
30
34
 
31
35
  .react-select__control--is-focused {
32
- border-color: ${(p) => (p.error ? p.theme.color.error : p.theme.color.interactive01)} !important;
36
+ border-color: ${(p) => (p.error ? p.theme.color?.error : p.theme.color?.interactive01)} !important;
33
37
  }
34
38
 
35
39
  .react-select__control--menu-is-open {
@@ -39,61 +43,61 @@ export const StyledSelect = styled(Select)<{ isDisabled: boolean | undefined; er
39
43
  }
40
44
 
41
45
  .react-select__control--is-disabled {
42
- border-color: ${(p) => p.theme.color.interactiveDisabled};
46
+ border-color: ${(p) => p.theme.color?.interactiveDisabled};
43
47
  }
44
48
 
45
49
  .react-select__menu {
46
50
  box-shadow: 0px 6px 6px rgba(32, 34, 76, 0.08);
47
51
  border-radius: 4px;
48
- min-width: calc(${(p) => p.theme.spacing.xl} * 2);
49
- max-width: calc(${(p) => p.theme.spacing.xl} * 6);
52
+ min-width: calc(${(p) => p.theme.spacing?.xl} * 2);
53
+ max-width: calc(${(p) => p.theme.spacing?.xl} * 6);
50
54
  margin-top: 0;
51
55
  z-index: 99;
52
56
  .react-select__menu-list {
53
- max-height: calc(${(p) => p.theme.spacing.l} * 3);
57
+ max-height: calc(${(p) => p.theme.spacing?.l} * 3);
54
58
  overflow: auto;
55
59
  padding: 0;
56
60
  .react-select__option {
57
- background-color: ${(p) => p.theme.color.interactiveBackground};
58
- min-height: ${(p) => p.theme.spacing.l};
59
- padding: ${(p) => p.theme.spacing.s};
61
+ background-color: ${(p) => p.theme.color?.interactiveBackground};
62
+ min-height: ${(p) => p.theme.spacing?.l};
63
+ padding: ${(p) => p.theme.spacing?.s};
60
64
  &:first-child {
61
- ${(p) => p.hasEmptyOption &&
65
+ ${(p) =>
66
+ p.hasEmptyOption &&
62
67
  css`
63
68
  font-style: italic;
64
- color: ${(p) => p.theme.color.textLowEmphasis};
65
- background-color: ${(p) => p.theme.color.uiLineInverse};
66
- `
67
- }
69
+ color: ${(p) => p.theme.color?.textLowEmphasis};
70
+ background-color: ${(p) => p.theme.color?.uiLineInverse};
71
+ `}
68
72
  }
69
73
  :hover {
70
74
  cursor: pointer;
71
- background-color: ${(p) => p.theme.color.overlayHoverPrimary};
75
+ background-color: ${(p) => p.theme.color?.overlayHoverPrimary};
72
76
  }
73
77
  :focus {
74
- background-color: ${(p) => p.theme.color.overlayFocusPrimary};
75
- border: 0.5px solid ${(p) => p.theme.color.interactive02};
78
+ background-color: ${(p) => p.theme.color?.overlayFocusPrimary};
79
+ border: 0.5px solid ${(p) => p.theme.color?.interactive02};
76
80
  }
77
81
  :active {
78
- background-color: ${(p) => p.theme.color.overlayPressedPrimary};
82
+ background-color: ${(p) => p.theme.color?.overlayPressedPrimary};
79
83
  }
80
84
  }
81
85
  .react-select__option--is-selected {
82
- color: ${(p) => p.theme.color.textHighEmphasis};
83
- background-color: ${(p) => p.theme.color.overlayPressedPrimary};
86
+ color: ${(p) => p.theme.color?.textHighEmphasis};
87
+ background-color: ${(p) => p.theme.color?.overlayPressedPrimary};
84
88
  }
85
89
  }
86
90
  }
87
91
 
88
92
  &.inline {
89
- ${(p) => p.theme.textStyle.uiS};
93
+ ${(p) => p.theme.textStyle?.uiS};
90
94
  text-transform: capitalize;
91
95
 
92
96
  .react-select__control {
93
97
  background: transparent;
94
98
  border: none;
95
99
  height: auto;
96
- justify-content: ${(p) => p.alignRight ? "flex-end" : "flex-start"};
100
+ justify-content: ${(p) => (p.alignRight ? "flex-end" : "flex-start")};
97
101
 
98
102
  .react-select__value-container {
99
103
  flex: 0 1 auto;
@@ -101,15 +105,15 @@ export const StyledSelect = styled(Select)<{ isDisabled: boolean | undefined; er
101
105
  .react-select__single-value {
102
106
  position: relative;
103
107
  transform: none;
104
- color: ${(p) => p.theme.color.interactive01};
108
+ color: ${(p) => p.theme.color?.interactive01};
105
109
  overflow: visible;
106
110
  }
107
111
  }
108
112
  .react-select__indicator {
109
113
  padding: 0;
110
114
  svg {
111
- width: ${(p) => p.theme.spacing.s};
112
- height: ${(p) => p.theme.spacing.s};
115
+ width: ${(p) => p.theme.spacing?.s};
116
+ height: ${(p) => p.theme.spacing?.s};
113
117
  }
114
118
  }
115
119
  .react-select__input {
@@ -122,13 +126,13 @@ export const StyledSelect = styled(Select)<{ isDisabled: boolean | undefined; er
122
126
  .react-select__control--is-disabled {
123
127
  .react-select__value-container {
124
128
  .react-select__single-value {
125
- color: ${(p) => p.theme.color.interactiveDisabled};
129
+ color: ${(p) => p.theme.color?.interactiveDisabled};
126
130
  }
127
131
  }
128
132
  }
129
133
 
130
134
  .react-select__menu {
131
- ${(p) => p.theme.textStyle.fieldContent};
135
+ ${(p) => p.theme.textStyle?.fieldContent};
132
136
  }
133
137
  }
134
138
  `;
@@ -0,0 +1,136 @@
1
+ import * as React from "react";
2
+ import TagField from "./index";
3
+ import { ThemeProvider } from "styled-components";
4
+ import { parseTheme } from "@griddo/core";
5
+ import globalTheme from "../../../themes/theme.json";
6
+ import { render, screen, within, cleanup, fireEvent } from "@testing-library/react";
7
+
8
+ afterEach(cleanup);
9
+
10
+ const props = {
11
+ value: [],
12
+ onChange: jest.fn(),
13
+ };
14
+
15
+ describe("TagField component", () => {
16
+ test("should render the component", () => {
17
+ render(
18
+ <ThemeProvider theme={parseTheme(globalTheme)}>
19
+ <TagField {...props} />
20
+ </ThemeProvider>
21
+ );
22
+
23
+ const tagFieldWrapper = screen.getAllByTestId("tagFieldWrapper");
24
+ expect(tagFieldWrapper).toBeTruthy();
25
+ expect(tagFieldWrapper).toHaveLength(1);
26
+
27
+ const tagFieldInput = screen.getAllByTestId("tagFieldInput");
28
+ expect(tagFieldInput).toBeTruthy();
29
+ expect(tagFieldInput).toHaveLength(1);
30
+ });
31
+
32
+ test("should render the tags if have values", () => {
33
+ const defaultProps = {
34
+ value: ["one", "two"],
35
+ onChange: jest.fn(),
36
+ };
37
+
38
+ render(
39
+ <ThemeProvider theme={parseTheme(globalTheme)}>
40
+ <TagField {...defaultProps} />
41
+ </ThemeProvider>
42
+ );
43
+
44
+ const tagFieldWrapper = screen.getByTestId("tagFieldWrapper");
45
+ expect(tagFieldWrapper).toBeTruthy();
46
+
47
+ const tagFieldInput = screen.getByTestId("tagFieldInput");
48
+ expect(tagFieldInput).toBeTruthy();
49
+
50
+ const tagRendered = within(tagFieldWrapper).getAllByTestId("tagComponent");
51
+ expect(tagRendered).toHaveLength(2);
52
+
53
+ const tagComponent = screen.getByTestId("tagFieldWrapper");
54
+ expect(tagComponent.textContent).toBe("onetwo");
55
+ });
56
+ });
57
+
58
+ describe("TagField events", () => {
59
+ test("should call the value when call onchange", () => {
60
+ render(
61
+ <ThemeProvider theme={parseTheme(globalTheme)}>
62
+ <TagField {...props} />
63
+ </ThemeProvider>
64
+ );
65
+ const tagFieldInput = screen.getByTestId<HTMLInputElement>("tagFieldInput");
66
+
67
+ fireEvent.change(tagFieldInput, { target: { value: "test" } });
68
+ expect(tagFieldInput.value).toBe("test");
69
+ });
70
+
71
+ test("should add new value on keyDown if it's not in array", () => {
72
+ const onChange = jest.fn();
73
+ const defaultProps = {
74
+ value: [],
75
+ onChange,
76
+ };
77
+ render(
78
+ <ThemeProvider theme={parseTheme(globalTheme)}>
79
+ <TagField {...defaultProps} />
80
+ </ThemeProvider>
81
+ );
82
+
83
+ const tagFieldInput = screen.getByTestId<HTMLInputElement>("tagFieldInput");
84
+ fireEvent.input(tagFieldInput, { target: { value: "test" } });
85
+ fireEvent.keyDown(tagFieldInput, { key: "Enter", code: "Enter", charCode: 13 });
86
+ expect(onChange).toHaveBeenCalledWith(["test"]);
87
+ });
88
+ test("shouldn't add new value on keyDown if it's in array", () => {
89
+ const onChange = jest.fn();
90
+ const defaultProps = {
91
+ value: ["test"],
92
+ onChange,
93
+ };
94
+ render(
95
+ <ThemeProvider theme={parseTheme(globalTheme)}>
96
+ <TagField {...defaultProps} />
97
+ </ThemeProvider>
98
+ );
99
+
100
+ const tagFieldInput = screen.getByTestId<HTMLInputElement>("tagFieldInput");
101
+ fireEvent.input(tagFieldInput, { target: { value: "test" } });
102
+ fireEvent.keyDown(tagFieldInput, { key: "Enter", code: "Enter", charCode: 13 });
103
+ expect(onChange).toBeCalledTimes(0);
104
+ });
105
+ test("should delete value from array", () => {
106
+ const onChange = jest.fn();
107
+ const defaultProps = {
108
+ value: ["one", "two"],
109
+ onChange,
110
+ };
111
+
112
+ render(
113
+ <ThemeProvider theme={parseTheme(globalTheme)}>
114
+ <TagField {...defaultProps} />
115
+ </ThemeProvider>
116
+ );
117
+
118
+ const tagFieldWrapper = screen.getByTestId("tagFieldWrapper");
119
+ const tagRendered = within(tagFieldWrapper).getAllByTestId("tagComponent");
120
+ expect(tagRendered).toHaveLength(2);
121
+ fireEvent.click(tagRendered[0], 0);
122
+ expect(onChange).toBeCalledTimes(1);
123
+ });
124
+ test("should call the focus function", () => {
125
+ // we can check if the focus function is been called
126
+ const useRefSpy = jest.spyOn(React, "useRef").mockReturnValueOnce({ current: { focus: jest.fn() } });
127
+ render(
128
+ <ThemeProvider theme={parseTheme(globalTheme)}>
129
+ <TagField {...props} />
130
+ </ThemeProvider>
131
+ );
132
+ const tagFieldWrapper = screen.getByTestId("tagFieldWrapper");
133
+ fireEvent.click(tagFieldWrapper);
134
+ expect(useRefSpy).toBeCalledTimes(1);
135
+ });
136
+ });
@@ -3,11 +3,9 @@ import { Tag } from "@ax/components";
3
3
 
4
4
  import * as S from "./style";
5
5
 
6
- const TagField = (props: IProps) => {
6
+ const TagField = (props: IProps): JSX.Element => {
7
7
  const { value, onChange } = props;
8
-
9
8
  const valueArray = value && Array.isArray(value) ? value : [];
10
-
11
9
  const [inputValue, setInputValue] = useState("");
12
10
  const inputRef = useRef<HTMLInputElement>(null);
13
11
 
@@ -35,19 +33,17 @@ const TagField = (props: IProps) => {
35
33
  onChange(newValue);
36
34
  };
37
35
 
38
- const Tags =
39
- valueArray &&
40
- valueArray.map((tag: string, index: number) => {
41
- const handleDelete = () => deleteTag(index);
42
- return <Tag key={tag} text={tag} deleteAction={handleDelete} />;
43
- });
44
-
45
36
  const placeholder = valueArray.length > 0 ? "" : "Type a tag...";
46
37
 
47
38
  return (
48
- <S.Wrapper onClick={_handleClick}>
49
- {Tags}
39
+ <S.Wrapper data-testid="tagFieldWrapper" onClick={_handleClick}>
40
+ {valueArray &&
41
+ valueArray.map((tag: string, index: number) => {
42
+ const handleDelete = () => deleteTag(index);
43
+ return <Tag key={tag} text={tag} onDeleteAction={handleDelete} />;
44
+ })}
50
45
  <S.Input
46
+ data-testid="tagFieldInput"
51
47
  ref={inputRef}
52
48
  type="text"
53
49
  value={inputValue}
@@ -0,0 +1,69 @@
1
+ import React from "react";
2
+ import TextArea from "./index";
3
+ import { ThemeProvider } from "styled-components";
4
+ import { parseTheme } from "@griddo/core";
5
+ import globalTheme from "../../../themes/theme.json";
6
+ import { render, screen, cleanup, fireEvent } from "@testing-library/react";
7
+
8
+ afterEach(cleanup);
9
+
10
+ const props = {
11
+ name: "",
12
+ value: "",
13
+ onChange: jest.fn(),
14
+ };
15
+
16
+ describe("TextArea component rendering", () => {
17
+ test("should render the component with no CheckField", () => {
18
+ render(
19
+ <ThemeProvider theme={parseTheme(globalTheme)}>
20
+ <TextArea {...props} />
21
+ </ThemeProvider>
22
+ );
23
+
24
+ const textAreaComponent = screen.getAllByTestId("textAreaComponent");
25
+ expect(textAreaComponent).toHaveLength(1);
26
+ });
27
+ });
28
+
29
+ describe("TextArea events", () => {
30
+ test("should call the onChange", () => {
31
+ const setStateMock = jest.fn();
32
+ const useStateMock: any = (useState: string) => [useState, setStateMock];
33
+ jest.spyOn(React, "useState").mockImplementation(useStateMock);
34
+
35
+ render(
36
+ <ThemeProvider theme={parseTheme(globalTheme)}>
37
+ <TextArea {...props} />
38
+ </ThemeProvider>
39
+ );
40
+
41
+ const textAreaComponent = screen.getByTestId("textAreaComponent");
42
+ expect(textAreaComponent).toBeTruthy();
43
+ fireEvent.change(textAreaComponent, { target: { value: "test" } });
44
+ expect(setStateMock).toHaveBeenCalledWith("test");
45
+ });
46
+
47
+ test("should call the onBlur", () => {
48
+ const handleValidationMock = jest.fn();
49
+ const defaultProps = {
50
+ name: "name",
51
+ value: "value",
52
+ onChange: jest.fn(),
53
+ handleValidation: handleValidationMock,
54
+ };
55
+
56
+ render(
57
+ <ThemeProvider theme={parseTheme(globalTheme)}>
58
+ <TextArea {...defaultProps} />
59
+ </ThemeProvider>
60
+ );
61
+
62
+ const textAreaComponent = screen.getByTestId("textAreaComponent");
63
+
64
+ expect(textAreaComponent).toBeTruthy();
65
+ fireEvent.blur(textAreaComponent, { target: { value: "name" } });
66
+ expect(handleValidationMock).toHaveBeenCalledTimes(1);
67
+ expect(handleValidationMock).toHaveBeenCalledWith("name");
68
+ });
69
+ });
@@ -1,21 +1,11 @@
1
- import React, { memo, useEffect, useRef, useState } from "react";
1
+ import React, { memo, useEffect, useRef } from "react";
2
2
 
3
3
  import * as S from "./style";
4
4
 
5
5
  const TextArea = (props: ITextAreaProps): JSX.Element => {
6
- const {
7
- value,
8
- placeholder,
9
- error,
10
- name,
11
- onChange,
12
- disabled,
13
- handleValidation,
14
- editorID,
15
- rows = 1
16
- } = props;
6
+ const { value, placeholder, error, name, onChange, disabled, handleValidation, editorID, rows = 1 } = props;
17
7
 
18
- const [state, setState] = useState<string>(value);
8
+ const [state, setState] = React.useState<string>(value);
19
9
  const timeOutRef = useRef<ReturnType<typeof setTimeout>>();
20
10
 
21
11
  useEffect(() => {
@@ -45,6 +35,7 @@ const TextArea = (props: ITextAreaProps): JSX.Element => {
45
35
 
46
36
  return (
47
37
  <S.TextArea
38
+ data-testid="textAreaComponent"
48
39
  name={name}
49
40
  minRows={rows}
50
41
  maxRows={4}
@@ -1,9 +1,9 @@
1
1
  import styled from "styled-components";
2
2
  import TextareaAutosize from "react-textarea-autosize";
3
3
 
4
- export const TextArea = styled(TextareaAutosize) <{ $error: boolean | undefined }>`
4
+ export const TextArea = styled(TextareaAutosize)<{ $error: boolean | undefined }>`
5
5
  ${(p) => p.theme.textStyle.fieldContent};
6
- border: 1px solid ${p => (p.$error === true ? p.theme.color.error : p.theme.color.uiLine)};
6
+ border: 1px solid ${(p) => (p.$error === true ? p.theme.color.error : p.theme.color.uiLine)};
7
7
  border-radius: 4px;
8
8
  background-color: ${(p) => p.theme.color.uiBackground02};
9
9
  color: ${(p) => p.theme.color.textHighEmphasis};
@@ -0,0 +1,144 @@
1
+ import * as React from "react";
2
+ import TextField from "./index";
3
+ import { ThemeProvider } from "styled-components";
4
+ import { parseTheme } from "@griddo/core";
5
+ import globalTheme from "../../../themes/theme.json";
6
+ import { render, screen, within, cleanup, fireEvent } from "@testing-library/react";
7
+
8
+ afterEach(cleanup);
9
+
10
+ const defaultProps = {
11
+ value: "",
12
+ onChange: jest.fn(),
13
+ };
14
+
15
+ describe("TextField component rendering", () => {
16
+ test("should render the component", () => {
17
+ render(
18
+ <ThemeProvider theme={parseTheme(globalTheme)}>
19
+ <TextField {...defaultProps} />
20
+ </ThemeProvider>
21
+ );
22
+
23
+ const textFieldContainer = screen.getByTestId("textFieldContainer");
24
+ const inputComponent = screen.getByTestId("inputComponent");
25
+
26
+ expect(textFieldContainer).toBeTruthy();
27
+ expect(inputComponent).toBeTruthy();
28
+ });
29
+
30
+ test("should render the component with prefix", () => {
31
+ const props = {
32
+ value: "value",
33
+ onChange: jest.fn(),
34
+ prefix: "prefix",
35
+ };
36
+
37
+ render(
38
+ <ThemeProvider theme={parseTheme(globalTheme)}>
39
+ <TextField {...props} />
40
+ </ThemeProvider>
41
+ );
42
+
43
+ const textFieldContainer = screen.getByTestId("textFieldContainer");
44
+ const inputComponent = screen.getByTestId("inputComponent");
45
+ const prefix = screen.getByTestId("prefix");
46
+ expect(textFieldContainer).toBeTruthy();
47
+ expect(inputComponent).toBeTruthy();
48
+ expect(prefix).toBeTruthy();
49
+ });
50
+
51
+ it("should render the component with backgroundIcon", () => {
52
+ const props = {
53
+ value: "value",
54
+ onChange: jest.fn(),
55
+ icon: "icon",
56
+ iconPosition: "in",
57
+ };
58
+ render(
59
+ <ThemeProvider theme={parseTheme(globalTheme)}>
60
+ <TextField {...props} />
61
+ </ThemeProvider>
62
+ );
63
+
64
+ const textFieldContainer = screen.getByTestId("textFieldContainer");
65
+ const inputComponent = screen.getByTestId("inputComponent");
66
+ const backgroundIconComponent = screen.getByTestId("backgroundIconComponent");
67
+
68
+ expect(textFieldContainer).toBeTruthy();
69
+ expect(inputComponent).toBeTruthy();
70
+ expect(backgroundIconComponent).toBeTruthy();
71
+ });
72
+
73
+ it("should render the component with button", () => {
74
+ const props = {
75
+ value: "value",
76
+ onChange: jest.fn(),
77
+ icon: "icon",
78
+ iconPosition: "out",
79
+ };
80
+ render(
81
+ <ThemeProvider theme={parseTheme(globalTheme)}>
82
+ <TextField {...props} />
83
+ </ThemeProvider>
84
+ );
85
+
86
+ const textFieldContainer = screen.getByTestId("textFieldContainer");
87
+ const inputComponent = screen.getByTestId("inputComponent");
88
+ const iconWrapperComponent = screen.getByTestId("iconWrapperComponent");
89
+
90
+ expect(textFieldContainer).toBeTruthy();
91
+ expect(inputComponent).toBeTruthy();
92
+ expect(iconWrapperComponent).toBeTruthy();
93
+ });
94
+ });
95
+
96
+ describe("onClick from buttons", () => {
97
+ test("onClickIcon been called in BackgroundIcon", () => {
98
+ const onClickIconMock = jest.fn();
99
+ const props = {
100
+ value: "value",
101
+ onChange: jest.fn(),
102
+ icon: "icon",
103
+ iconPosition: "in",
104
+ onClickIcon: onClickIconMock,
105
+ };
106
+
107
+ render(
108
+ <ThemeProvider theme={parseTheme(globalTheme)}>
109
+ <TextField {...props} />
110
+ </ThemeProvider>
111
+ );
112
+
113
+ const backgroundIconComponent = screen.getByTestId("backgroundIconComponent");
114
+ expect(backgroundIconComponent).toBeTruthy();
115
+
116
+ // onClick called with simulate method
117
+ fireEvent.click(backgroundIconComponent);
118
+ expect(onClickIconMock).toBeCalledTimes(1);
119
+ });
120
+
121
+ test("onClickIcon been called in BackgroundIcon", () => {
122
+ const onClickIconMock = jest.fn();
123
+ const props = {
124
+ value: "value",
125
+ onChange: jest.fn(),
126
+ icon: "icon",
127
+ iconPosition: "out",
128
+ onClickIcon: onClickIconMock,
129
+ };
130
+
131
+ render(
132
+ <ThemeProvider theme={parseTheme(globalTheme)}>
133
+ <TextField {...props} />
134
+ </ThemeProvider>
135
+ );
136
+
137
+ const iconWrapperComponent = screen.getByTestId("iconWrapperComponent");
138
+ expect(iconWrapperComponent).toBeTruthy();
139
+ const iconActionComponent = within(iconWrapperComponent).getByTestId("iconActionComponent");
140
+ expect(iconActionComponent).toBeTruthy();
141
+ fireEvent.click(iconActionComponent);
142
+ expect(onClickIconMock).toBeCalledTimes(1);
143
+ });
144
+ });
@@ -66,31 +66,22 @@ const TextField = (props: ITextFieldProps): JSX.Element => {
66
66
  handleValidation && handleValidation(newValue, validators);
67
67
  };
68
68
 
69
- const Button = () =>
70
- icon && iconPosition === "out" ? (
71
- <S.IconWrapper readonly={readonly} error={error}>
72
- <IconAction icon={icon} onClick={onClickIcon} disabled={disabled} size="s" />
73
- </S.IconWrapper>
74
- ) : null;
75
-
76
- const BackgroundIcon = () =>
77
- icon && iconPosition === "in" ? (
78
- <S.BackgroundIcon onClick={onClickIcon}>
79
- <Icon name={icon} size="24" />
80
- </S.BackgroundIcon>
81
- ) : null;
82
-
83
69
  const hasBackgroundIcon = !!icon && iconPosition === "in";
84
70
  const hasButton = !!icon && iconPosition === "out";
85
71
 
86
- const Prefix = () => (prefix ? <S.Prefix ref={prefixRef}>{prefix}</S.Prefix> : null);
72
+ const Prefix = () => (
73
+ <S.Prefix data-testid="prefix" ref={prefixRef}>
74
+ {prefix}
75
+ </S.Prefix>
76
+ );
87
77
 
88
78
  const inputValue = !autoComplete ? state : value || "";
89
79
 
90
80
  return (
91
- <S.FieldWrapper error={error} className={className}>
92
- <Prefix />
81
+ <S.FieldWrapper data-testid="textFieldContainer" error={error} className={className}>
82
+ {prefix && <Prefix />}
93
83
  <S.Input
84
+ data-testid="inputComponent"
94
85
  type={inputType}
95
86
  value={inputValue}
96
87
  name={name}
@@ -106,8 +97,21 @@ const TextField = (props: ITextFieldProps): JSX.Element => {
106
97
  hasPrefix={!!prefix}
107
98
  prefixWidth={width}
108
99
  />
109
- <BackgroundIcon />
110
- <Button />
100
+ {hasBackgroundIcon && (
101
+ <S.BackgroundIcon data-testid="backgroundIconComponent" onClick={onClickIcon}>
102
+ <Icon name={icon || ""} size="24" />
103
+ </S.BackgroundIcon>
104
+ )}
105
+ {hasButton && (
106
+ <S.IconWrapper data-testid="iconWrapperComponent" readonly={readonly} error={error}>
107
+ <IconAction
108
+ icon={icon || ""}
109
+ onClick={onClickIcon}
110
+ disabled={disabled}
111
+ size="s"
112
+ />
113
+ </S.IconWrapper>
114
+ )}
111
115
  </S.FieldWrapper>
112
116
  );
113
117
  };