@griddo/ax 11.11.8-rc.1 → 11.12.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 (99) hide show
  1. package/config/jest/componentsMock.js +5 -7
  2. package/package.json +2 -2
  3. package/src/__tests__/components/Browser/Browser.test.tsx +87 -438
  4. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +3 -1
  5. package/src/__tests__/components/Fields/Button/Button.test.tsx +27 -29
  6. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
  7. package/src/components/Browser/index.tsx +149 -294
  8. package/src/components/Browser/style.tsx +6 -75
  9. package/src/components/Button/index.tsx +1 -2
  10. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +4 -2
  11. package/src/components/Fields/AsyncSelect/style.tsx +0 -13
  12. package/src/components/Fields/FieldGroup/index.tsx +2 -5
  13. package/src/components/Fields/FieldGroup/style.tsx +7 -32
  14. package/src/components/Fields/HeadingField/index.tsx +2 -2
  15. package/src/components/Fields/HiddenField/style.tsx +1 -1
  16. package/src/components/Fields/NumberField/index.tsx +16 -15
  17. package/src/components/Fields/NumberField/style.tsx +0 -2
  18. package/src/components/Fields/ReferenceField/index.tsx +1 -1
  19. package/src/components/Fields/Select/index.tsx +1 -5
  20. package/src/components/Fields/Select/style.tsx +0 -56
  21. package/src/components/Fields/SummaryButton/index.tsx +9 -18
  22. package/src/components/Fields/SummaryButton/style.tsx +2 -1
  23. package/src/components/Fields/TagsField/index.tsx +9 -8
  24. package/src/components/Fields/UrlField/index.tsx +27 -26
  25. package/src/components/Fields/index.tsx +0 -2
  26. package/src/components/FloatingPanel/index.tsx +2 -5
  27. package/src/components/FloatingPanel/style.tsx +1 -2
  28. package/src/components/IconAction/index.tsx +1 -1
  29. package/src/components/MainWrapper/AppBar/index.tsx +1 -8
  30. package/src/components/MainWrapper/index.tsx +1 -7
  31. package/src/components/Notification/index.tsx +2 -2
  32. package/src/components/PageFinder/index.tsx +1 -1
  33. package/src/components/ResizePanel/index.tsx +3 -4
  34. package/src/components/ResizePanel/style.tsx +1 -1
  35. package/src/components/SearchField/style.tsx +2 -2
  36. package/src/components/SideModal/index.tsx +1 -2
  37. package/src/components/Tabs/index.tsx +4 -13
  38. package/src/components/Tabs/style.tsx +8 -7
  39. package/src/components/Toast/index.tsx +2 -4
  40. package/src/components/Tooltip/index.tsx +3 -4
  41. package/src/components/index.tsx +0 -8
  42. package/src/forms/fields.tsx +68 -70
  43. package/src/hooks/forms.tsx +1 -22
  44. package/src/hooks/index.tsx +3 -13
  45. package/src/hooks/modals.tsx +15 -103
  46. package/src/hooks/users.tsx +8 -25
  47. package/src/modules/Forms/atoms.tsx +2 -2
  48. package/src/modules/FramePreview/index.tsx +16 -55
  49. package/src/modules/FramePreview/style.tsx +2 -34
  50. package/src/modules/GlobalEditor/Editor/index.tsx +3 -37
  51. package/src/modules/GlobalEditor/PageBrowser/index.tsx +2 -19
  52. package/src/modules/GlobalEditor/Preview/index.tsx +2 -0
  53. package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
  54. package/src/modules/GlobalEditor/index.tsx +57 -119
  55. package/src/modules/PageEditor/Editor/index.tsx +2 -33
  56. package/src/modules/PageEditor/PageBrowser/index.tsx +2 -20
  57. package/src/modules/PageEditor/Preview/index.tsx +2 -0
  58. package/src/modules/PageEditor/Preview/style.tsx +1 -1
  59. package/src/modules/PageEditor/atoms.tsx +1 -1
  60. package/src/modules/PageEditor/index.tsx +66 -130
  61. package/src/modules/PublicPreview/index.tsx +2 -5
  62. package/src/schemas/pages/GlobalPage.ts +70 -87
  63. package/src/schemas/pages/Page.ts +70 -87
  64. package/src/types/index.tsx +0 -12
  65. package/src/__tests__/components/Browser/Browser.utils.test.ts +0 -55
  66. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +0 -158
  67. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +0 -90
  68. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +0 -178
  69. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +0 -150
  70. package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +0 -91
  71. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +0 -122
  72. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +0 -15
  73. package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +0 -101
  74. package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +0 -318
  75. package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +0 -242
  76. package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +0 -185
  77. package/src/components/Browser/utils.tsx +0 -13
  78. package/src/components/Fields/SEOPreview/index.tsx +0 -36
  79. package/src/components/Fields/SEOPreview/style.tsx +0 -24
  80. package/src/components/FloatingNote/index.tsx +0 -35
  81. package/src/components/FloatingNote/style.tsx +0 -26
  82. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +0 -85
  83. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +0 -80
  84. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +0 -57
  85. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +0 -82
  86. package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +0 -71
  87. package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +0 -77
  88. package/src/components/HeadingsPreviewModal/index.tsx +0 -146
  89. package/src/components/HeadingsPreviewModal/style.tsx +0 -82
  90. package/src/components/HeadingsPreviewModal/utils.tsx +0 -257
  91. package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +0 -46
  92. package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +0 -64
  93. package/src/components/KeywordsPreviewModal/atoms.tsx +0 -96
  94. package/src/components/KeywordsPreviewModal/index.tsx +0 -99
  95. package/src/components/KeywordsPreviewModal/style.tsx +0 -87
  96. package/src/components/KeywordsPreviewModal/utils.tsx +0 -22
  97. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +0 -113
  98. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +0 -24
  99. package/src/modules/FramePreview/utils.tsx +0 -140
@@ -59,110 +59,93 @@ export default {
59
59
  collapsed: true,
60
60
  fields: [
61
61
  {
62
- title: "SEO previews",
63
- type: "FieldGroup",
64
- key: "seopreviews",
65
- collapsed: true,
66
- solid: true,
67
- fields: [{ title: "", type: "SEOPreview", key: "seoPreview" }],
62
+ title: "",
63
+ type: "SummaryButton",
64
+ key: "summary",
68
65
  },
69
66
  {
70
- title: "SEO meta data",
71
- type: "FieldGroup",
72
- key: "metadata",
73
- collapsed: true,
74
- solid: true,
75
- fields: [
67
+ title: "Meta title",
68
+ type: "TextField",
69
+ key: "metaTitle",
70
+ },
71
+ {
72
+ title: "Meta description",
73
+ type: "TextArea",
74
+ key: "metaDescription",
75
+ },
76
+ {
77
+ title: "Keywords",
78
+ type: "TagsField",
79
+ key: "metaKeywords",
80
+ },
81
+ {
82
+ title: "Canonical URL",
83
+ type: "TextField",
84
+ key: "canonicalURL",
85
+ },
86
+ {
87
+ title: "Meta robots index",
88
+ type: "RadioGroup",
89
+ key: "isIndexed",
90
+ options: [
76
91
  {
77
- title: "",
78
- type: "SummaryButton",
79
- key: "summary",
92
+ value: true,
93
+ title: "Index",
94
+ name: "index",
80
95
  },
81
96
  {
82
- title: "Meta title",
83
- type: "TextField",
84
- key: "metaTitle",
97
+ value: false,
98
+ title: "No index",
99
+ name: "noindex",
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ title: "Meta robots follow",
105
+ type: "RadioGroup",
106
+ key: "follow",
107
+ options: [
108
+ {
109
+ value: true,
110
+ title: "Follow",
111
+ name: "follow",
85
112
  },
86
113
  {
87
- title: "Meta description",
88
- type: "TextArea",
89
- key: "metaDescription",
114
+ value: false,
115
+ title: "No follow",
116
+ name: "nofollow",
90
117
  },
118
+ ],
119
+ },
120
+ {
121
+ title: "Meta robots advanced",
122
+ type: "CheckGroup",
123
+ key: "metasAdvanced",
124
+ options: [
91
125
  {
92
- title: "Keywords",
93
- type: "TagsField",
94
- key: "metaKeywords",
126
+ value: "noimageindex",
127
+ title: "No image index",
128
+ name: "noimage",
95
129
  },
96
130
  {
97
- title: "Canonical URL",
98
- type: "TextField",
99
- key: "canonicalURL",
131
+ value: "nosnippet",
132
+ title: "No snippet",
133
+ name: "nosnippet",
100
134
  },
101
135
  {
102
- title: "Meta robots index",
103
- type: "RadioGroup",
104
- key: "isIndexed",
105
- options: [
106
- {
107
- value: true,
108
- title: "Index",
109
- name: "index",
110
- },
111
- {
112
- value: false,
113
- title: "No index",
114
- name: "noindex",
115
- },
116
- ],
136
+ value: "noodp",
137
+ title: "No ODP",
138
+ name: "noodp",
117
139
  },
118
140
  {
119
- title: "Meta robots follow",
120
- type: "RadioGroup",
121
- key: "follow",
122
- options: [
123
- {
124
- value: true,
125
- title: "Follow",
126
- name: "follow",
127
- },
128
- {
129
- value: false,
130
- title: "No follow",
131
- name: "nofollow",
132
- },
133
- ],
141
+ value: "noarchive",
142
+ title: "No archive",
143
+ name: "noarchive",
134
144
  },
135
145
  {
136
- title: "Meta robots advanced",
137
- type: "CheckGroup",
138
- key: "metasAdvanced",
139
- options: [
140
- {
141
- value: "noimageindex",
142
- title: "No image index",
143
- name: "noimage",
144
- },
145
- {
146
- value: "nosnippet",
147
- title: "No snippet",
148
- name: "nosnippet",
149
- },
150
- {
151
- value: "noodp",
152
- title: "No ODP",
153
- name: "noodp",
154
- },
155
- {
156
- value: "noarchive",
157
- title: "No archive",
158
- name: "noarchive",
159
- },
160
- {
161
- value: "noTranslate",
162
- title: "No translate",
163
- name: "noTranslate",
164
- },
165
- ],
146
+ value: "noTranslate",
147
+ title: "No translate",
148
+ name: "noTranslate",
166
149
  },
167
150
  ],
168
151
  },
@@ -125,110 +125,93 @@ export default {
125
125
  collapsed: true,
126
126
  fields: [
127
127
  {
128
- title: "SEO previews",
129
- type: "FieldGroup",
130
- key: "seopreviews",
131
- collapsed: true,
132
- solid: true,
133
- fields: [{ title: "", type: "SEOPreview", key: "seoPreview" }],
128
+ title: "",
129
+ type: "SummaryButton",
130
+ key: "summary",
134
131
  },
135
132
  {
136
- title: "SEO meta data",
137
- type: "FieldGroup",
138
- key: "metadata",
139
- collapsed: true,
140
- solid: true,
141
- fields: [
133
+ title: "Meta title",
134
+ type: "TextField",
135
+ key: "metaTitle",
136
+ },
137
+ {
138
+ title: "Meta description",
139
+ type: "TextArea",
140
+ key: "metaDescription",
141
+ },
142
+ {
143
+ title: "Keywords",
144
+ type: "TagsField",
145
+ key: "metaKeywords",
146
+ },
147
+ {
148
+ title: "Canonical URL",
149
+ type: "TextField",
150
+ key: "canonicalURL",
151
+ },
152
+ {
153
+ title: "Meta robots index",
154
+ type: "RadioGroup",
155
+ key: "isIndexed",
156
+ options: [
142
157
  {
143
- title: "",
144
- type: "SummaryButton",
145
- key: "summary",
158
+ value: true,
159
+ title: "Index",
160
+ name: "index",
161
+ },
162
+ {
163
+ value: false,
164
+ title: "No index",
165
+ name: "noindex",
146
166
  },
167
+ ],
168
+ },
169
+ {
170
+ title: "Meta robots follow",
171
+ type: "RadioGroup",
172
+ key: "follow",
173
+ options: [
147
174
  {
148
- title: "Meta title",
149
- type: "TextField",
150
- key: "metaTitle",
175
+ value: true,
176
+ title: "Follow",
177
+ name: "follow",
151
178
  },
152
179
  {
153
- title: "Meta description",
154
- type: "TextArea",
155
- key: "metaDescription",
180
+ value: false,
181
+ title: "No follow",
182
+ name: "nofollow",
156
183
  },
184
+ ],
185
+ },
186
+ {
187
+ title: "Meta robots advanced",
188
+ type: "CheckGroup",
189
+ key: "metasAdvanced",
190
+ options: [
157
191
  {
158
- title: "Keywords",
159
- type: "TagsField",
160
- key: "metaKeywords",
192
+ value: "noimageindex",
193
+ title: "No image index",
194
+ name: "noimage",
161
195
  },
162
196
  {
163
- title: "Canonical URL",
164
- type: "TextField",
165
- key: "canonicalURL",
197
+ value: "nosnippet",
198
+ title: "No snippet",
199
+ name: "nosnippet",
166
200
  },
167
201
  {
168
- title: "Meta robots index",
169
- type: "RadioGroup",
170
- key: "isIndexed",
171
- options: [
172
- {
173
- value: true,
174
- title: "Index",
175
- name: "index",
176
- },
177
- {
178
- value: false,
179
- title: "No index",
180
- name: "noindex",
181
- },
182
- ],
202
+ value: "noodp",
203
+ title: "No ODP",
204
+ name: "noodp",
183
205
  },
184
206
  {
185
- title: "Meta robots follow",
186
- type: "RadioGroup",
187
- key: "follow",
188
- options: [
189
- {
190
- value: true,
191
- title: "Follow",
192
- name: "follow",
193
- },
194
- {
195
- value: false,
196
- title: "No follow",
197
- name: "nofollow",
198
- },
199
- ],
207
+ value: "noarchive",
208
+ title: "No archive",
209
+ name: "noarchive",
200
210
  },
201
211
  {
202
- title: "Meta robots advanced",
203
- type: "CheckGroup",
204
- key: "metasAdvanced",
205
- options: [
206
- {
207
- value: "noimageindex",
208
- title: "No image index",
209
- name: "noimage",
210
- },
211
- {
212
- value: "nosnippet",
213
- title: "No snippet",
214
- name: "nosnippet",
215
- },
216
- {
217
- value: "noodp",
218
- title: "No ODP",
219
- name: "noodp",
220
- },
221
- {
222
- value: "noarchive",
223
- title: "No archive",
224
- name: "noarchive",
225
- },
226
- {
227
- value: "noTranslate",
228
- title: "No translate",
229
- name: "noTranslate",
230
- },
231
- ],
212
+ value: "noTranslate",
213
+ title: "No translate",
214
+ name: "noTranslate",
232
215
  },
233
216
  ],
234
217
  },
@@ -1118,18 +1118,6 @@ export interface IContainedComponent {
1118
1118
  componentID: number;
1119
1119
  }
1120
1120
 
1121
- export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
1122
-
1123
- export interface HeadingNode {
1124
- level: HeadingLevel;
1125
- tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
1126
- text: string;
1127
- isHidden: boolean;
1128
- children: HeadingNode[];
1129
- }
1130
-
1131
- export type HeadingFilter = "all" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
1132
-
1133
1121
  export type Field =
1134
1122
  | "AsyncCheckGroup"
1135
1123
  | "AsyncSelect"
@@ -1,55 +0,0 @@
1
- import { calcAutoZoom, calcDefaultResolution } from "@ax/components/Browser/utils";
2
-
3
- const resolutionOptions = [
4
- { value: "1920px" },
5
- { value: "1440px" },
6
- { value: "1280px" },
7
- { value: "1024px" },
8
- { value: "768px" },
9
- { value: "360px" },
10
- ];
11
-
12
- describe("calcAutoZoom", () => {
13
- it("should return '100' when containerWidth is 0", () => {
14
- expect(calcAutoZoom(0, "1280px")).toBe("100");
15
- });
16
-
17
- it("should return '100' when resolution is not parseable", () => {
18
- expect(calcAutoZoom(1000, "")).toBe("100");
19
- });
20
-
21
- it("should return '100' when container exactly matches resolution", () => {
22
- expect(calcAutoZoom(1280, "1280px")).toBe("100");
23
- });
24
-
25
- it("should cap at '100' when container is wider than resolution", () => {
26
- expect(calcAutoZoom(1920, "1280px")).toBe("100");
27
- });
28
-
29
- it("should return the floored percentage when container is narrower than resolution", () => {
30
- expect(calcAutoZoom(640, "1280px")).toBe("50");
31
- });
32
-
33
- it("should floor fractional zoom values", () => {
34
- // 700 / 1280 = 54.6875% -> floors to 54
35
- expect(calcAutoZoom(700, "1280px")).toBe("54");
36
- });
37
- });
38
-
39
- describe("calcDefaultResolution", () => {
40
- it("should return the smallest option >= containerWidth", () => {
41
- expect(calcDefaultResolution(500, resolutionOptions)).toBe("768px");
42
- });
43
-
44
- it("should return the exact match when containerWidth equals a resolution value", () => {
45
- expect(calcDefaultResolution(1280, resolutionOptions)).toBe("1280px");
46
- });
47
-
48
- it("should return the largest option when containerWidth exceeds all options", () => {
49
- expect(calcDefaultResolution(2560, resolutionOptions)).toBe("1920px");
50
- });
51
-
52
- it("should return the smallest option for a very small containerWidth", () => {
53
- expect(calcDefaultResolution(100, resolutionOptions)).toBe("360px");
54
- });
55
- });
@@ -1,158 +0,0 @@
1
- import React from "react";
2
-
3
- import { ThemeProvider } from "styled-components";
4
- import { render, cleanup, screen, fireEvent } from "@testing-library/react";
5
- import { parseTheme } from "@ax/helpers";
6
- import "@testing-library/jest-dom";
7
-
8
- import ErrorItem from "@ax/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem";
9
- import type { IHeadingError } from "@ax/components/HeadingsPreviewModal/utils";
10
- import globalTheme from "@ax/themes/theme.json";
11
-
12
- afterEach(cleanup);
13
-
14
- const mockError: IHeadingError = {
15
- message: "Check heading structure for SEO compliance",
16
- description: "Ensure headings are properly nested",
17
- headingIds: [1, 2, 3],
18
- };
19
-
20
- const defaultProps = {
21
- error: mockError,
22
- onSelectHeading: jest.fn(() => jest.fn()),
23
- };
24
-
25
- const renderComponent = (props = defaultProps) =>
26
- render(
27
- <ThemeProvider theme={parseTheme(globalTheme)}>
28
- <ErrorItem {...props} />
29
- </ThemeProvider>,
30
- );
31
-
32
- describe("ErrorItem component rendering", () => {
33
- it("should render the error message", () => {
34
- renderComponent();
35
- expect(screen.getByText("Check heading structure for SEO compliance")).toBeTruthy();
36
- });
37
-
38
- it("should show heading counter text", () => {
39
- renderComponent();
40
- expect(screen.getByText("3 headings")).toBeTruthy();
41
- });
42
-
43
- it("should show the open toggle button when headingIds are present", () => {
44
- renderComponent();
45
- const buttons = screen.getAllByRole("button");
46
- // toggle (index 0) + close (index 1) + prev (index 2) + next (index 3)
47
- expect(buttons.length).toBe(4);
48
- });
49
-
50
- it("should not show the open toggle button when headingIds is empty", () => {
51
- const props = {
52
- ...defaultProps,
53
- error: { ...mockError, headingIds: [] },
54
- };
55
- renderComponent(props);
56
- const buttons = screen.getAllByRole("button");
57
- // only close (index 0) + prev (index 1) + next (index 2) — no toggle
58
- expect(buttons.length).toBe(3);
59
- });
60
-
61
- it("should not render when dismissed", () => {
62
- renderComponent();
63
- expect(screen.getByText("Check heading structure for SEO compliance")).toBeTruthy();
64
-
65
- const buttons = screen.getAllByRole("button");
66
- // Close button is at index 1 (after toggle)
67
- fireEvent.click(buttons[1]);
68
-
69
- expect(screen.queryByText("Check heading structure for SEO compliance")).toBeNull();
70
- });
71
- });
72
-
73
- describe("ErrorItem component events", () => {
74
- it("should call onSelectHeading when the open button is clicked", () => {
75
- const onSelectHeadingMock = jest.fn(() => jest.fn());
76
- renderComponent({ ...defaultProps, onSelectHeading: onSelectHeadingMock });
77
-
78
- const buttons = screen.getAllByRole("button");
79
- // Toggle open is at index 0
80
- fireEvent.click(buttons[0]);
81
-
82
- expect(onSelectHeadingMock).toBeCalledWith(mockError.headingIds[0]);
83
- });
84
-
85
- it("should update counter to '1 of 3 headings' after opening", () => {
86
- renderComponent();
87
-
88
- const buttons = screen.getAllByRole("button");
89
- fireEvent.click(buttons[0]);
90
-
91
- expect(screen.getByText("1 of 3 headings")).toBeTruthy();
92
- });
93
-
94
- it("should call onSelectHeading with first heading ID when next button is clicked", () => {
95
- const onSelectHeadingMock = jest.fn(() => jest.fn());
96
- renderComponent({ ...defaultProps, onSelectHeading: onSelectHeadingMock });
97
-
98
- const buttons = screen.getAllByRole("button");
99
- // Next button is at index 3 (toggle, close, prev, next)
100
- fireEvent.click(buttons[3]);
101
-
102
- expect(onSelectHeadingMock).toBeCalledWith(mockError.headingIds[0]);
103
- });
104
-
105
- it("should update counter to '2 of 3 headings' after two next clicks", () => {
106
- renderComponent();
107
-
108
- const buttons = screen.getAllByRole("button");
109
- fireEvent.click(buttons[3]);
110
- fireEvent.click(buttons[3]);
111
-
112
- expect(screen.getByText("2 of 3 headings")).toBeTruthy();
113
- });
114
-
115
- it("should call onSelectHeading with previous heading ID when prev button is clicked", () => {
116
- const onSelectHeadingMock = jest.fn(() => jest.fn());
117
- renderComponent({ ...defaultProps, onSelectHeading: onSelectHeadingMock });
118
-
119
- const buttons = screen.getAllByRole("button");
120
- // Advance to index 1, then go back
121
- fireEvent.click(buttons[3]);
122
- fireEvent.click(buttons[3]);
123
-
124
- jest.clearAllMocks();
125
- fireEvent.click(buttons[2]);
126
-
127
- expect(onSelectHeadingMock).toBeCalledWith(mockError.headingIds[0]);
128
- });
129
-
130
- it("should not navigate prev when already at the first heading", () => {
131
- const onSelectHeadingMock = jest.fn(() => jest.fn());
132
- renderComponent({ ...defaultProps, onSelectHeading: onSelectHeadingMock });
133
-
134
- const buttons = screen.getAllByRole("button");
135
- // Open sets currentIndex to 0, then try to go prev
136
- fireEvent.click(buttons[0]);
137
- jest.clearAllMocks();
138
- fireEvent.click(buttons[2]);
139
-
140
- expect(onSelectHeadingMock).not.toBeCalled();
141
- });
142
-
143
- it("should not navigate next when already at the last heading", () => {
144
- const onSelectHeadingMock = jest.fn(() => jest.fn());
145
- renderComponent({ ...defaultProps, onSelectHeading: onSelectHeadingMock });
146
-
147
- const buttons = screen.getAllByRole("button");
148
- // Click next 3 times to reach the end (headingIds has 3 items, indices 0-2)
149
- fireEvent.click(buttons[3]);
150
- fireEvent.click(buttons[3]);
151
- fireEvent.click(buttons[3]);
152
- jest.clearAllMocks();
153
-
154
- // Fourth click should be a no-op
155
- fireEvent.click(buttons[3]);
156
- expect(onSelectHeadingMock).not.toBeCalled();
157
- });
158
- });
@@ -1,90 +0,0 @@
1
- import React from "react";
2
-
3
- import { ThemeProvider } from "styled-components";
4
- import { render, cleanup, screen, fireEvent } from "@testing-library/react";
5
- import { parseTheme } from "@ax/helpers";
6
- import "@testing-library/jest-dom";
7
-
8
- import ErrorsBanner from "@ax/components/HeadingsPreviewModal/ErrorsBanner";
9
- import type { IHeadingError } from "@ax/components/HeadingsPreviewModal/utils";
10
- import globalTheme from "@ax/themes/theme.json";
11
-
12
- afterEach(cleanup);
13
-
14
- const mockError: IHeadingError = {
15
- message: "No H1 in this page",
16
- description: "Add an H1 tag",
17
- headingIds: [],
18
- };
19
-
20
- const defaultProps = {
21
- errors: [mockError],
22
- onSelectHeading: jest.fn(() => jest.fn()),
23
- isOpen: false,
24
- setIsOpen: jest.fn(),
25
- resetKey: 0,
26
- };
27
-
28
- const renderComponent = (props = defaultProps) =>
29
- render(
30
- <ThemeProvider theme={parseTheme(globalTheme)}>
31
- <ErrorsBanner {...props} />
32
- </ThemeProvider>,
33
- );
34
-
35
- describe("ErrorsBanner component rendering", () => {
36
- it("should render the SEO Alerts header", () => {
37
- renderComponent();
38
- expect(screen.getByText("SEO Alerts")).toBeTruthy();
39
- });
40
-
41
- it("should render the error message from the errors list", () => {
42
- renderComponent();
43
- expect(screen.getByText("No H1 in this page")).toBeTruthy();
44
- });
45
-
46
- it("should render multiple errors", () => {
47
- const props = {
48
- ...defaultProps,
49
- errors: [
50
- { message: "Error one", description: "Desc", headingIds: [] },
51
- { message: "Error two", description: "Desc", headingIds: [] },
52
- ],
53
- };
54
- renderComponent(props);
55
- expect(screen.getByText("Error one")).toBeTruthy();
56
- expect(screen.getByText("Error two")).toBeTruthy();
57
- });
58
-
59
- it("should not render when dismissed", () => {
60
- renderComponent();
61
- expect(screen.getByText("SEO Alerts")).toBeTruthy();
62
-
63
- const buttons = screen.getAllByRole("button");
64
- // Close button is the second button in the header (after toggle)
65
- fireEvent.click(buttons[1]);
66
-
67
- expect(screen.queryByText("SEO Alerts")).toBeNull();
68
- });
69
- });
70
-
71
- describe("ErrorsBanner component events", () => {
72
- it("should call setIsOpen with true when toggle is clicked and isOpen is false", () => {
73
- const setIsOpenMock = jest.fn();
74
- renderComponent({ ...defaultProps, isOpen: false, setIsOpen: setIsOpenMock });
75
-
76
- const buttons = screen.getAllByRole("button");
77
- // Toggle is the first button in the header
78
- fireEvent.click(buttons[0]);
79
- expect(setIsOpenMock).toBeCalledWith(true);
80
- });
81
-
82
- it("should call setIsOpen with false when toggle is clicked and isOpen is true", () => {
83
- const setIsOpenMock = jest.fn();
84
- renderComponent({ ...defaultProps, isOpen: true, setIsOpen: setIsOpenMock });
85
-
86
- const buttons = screen.getAllByRole("button");
87
- fireEvent.click(buttons[0]);
88
- expect(setIsOpenMock).toBeCalledWith(false);
89
- });
90
- });