@blaze-cms/react-page-builder 0.146.0-node18-core-styles-tooltips.20 → 0.146.0-node18-tooltips.19

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 (72) hide show
  1. package/CHANGELOG.md +25 -63
  2. package/lib/components/ContentGroup/ContentGroup.js +41 -10
  3. package/lib/components/ContentGroup/ContentGroup.js.map +1 -1
  4. package/lib/components/ContentGroup/ContentGroupAccordion.js +175 -0
  5. package/lib/components/ContentGroup/ContentGroupAccordion.js.map +1 -0
  6. package/lib/components/ContentGroup/constants.js +13 -1
  7. package/lib/components/ContentGroup/constants.js.map +1 -1
  8. package/lib/components/ContentGroup/helpers/get-structured-data-properties.js +40 -0
  9. package/lib/components/ContentGroup/helpers/get-structured-data-properties.js.map +1 -0
  10. package/lib/components/Menu/Menu.js +1 -4
  11. package/lib/components/Menu/Menu.js.map +1 -1
  12. package/lib/components/Menu/MenuContext.js +1 -2
  13. package/lib/components/Menu/MenuContext.js.map +1 -1
  14. package/lib/components/MenuItem/MenuItemRender.js +12 -27
  15. package/lib/components/MenuItem/MenuItemRender.js.map +1 -1
  16. package/lib/components/MenuItem/helpers/index.js +0 -14
  17. package/lib/components/MenuItem/helpers/index.js.map +1 -1
  18. package/lib/components/SearchContent/SearchContent.js +4 -3
  19. package/lib/components/SearchContent/SearchContent.js.map +1 -1
  20. package/lib/system-components/EditorMode/helpers/add-editor-mode-event-listeners.js +0 -1
  21. package/lib/system-components/EditorMode/helpers/add-editor-mode-event-listeners.js.map +1 -1
  22. package/lib-es/components/ContentGroup/ContentGroup.js +25 -10
  23. package/lib-es/components/ContentGroup/ContentGroup.js.map +1 -1
  24. package/lib-es/components/ContentGroup/ContentGroupAccordion.js +138 -0
  25. package/lib-es/components/ContentGroup/ContentGroupAccordion.js.map +1 -0
  26. package/lib-es/components/ContentGroup/constants.js +12 -1
  27. package/lib-es/components/ContentGroup/constants.js.map +1 -1
  28. package/lib-es/components/ContentGroup/helpers/get-structured-data-properties.js +33 -0
  29. package/lib-es/components/ContentGroup/helpers/get-structured-data-properties.js.map +1 -0
  30. package/lib-es/components/Menu/Menu.js +1 -4
  31. package/lib-es/components/Menu/Menu.js.map +1 -1
  32. package/lib-es/components/Menu/MenuContext.js +1 -2
  33. package/lib-es/components/Menu/MenuContext.js.map +1 -1
  34. package/lib-es/components/MenuItem/MenuItemRender.js +11 -25
  35. package/lib-es/components/MenuItem/MenuItemRender.js.map +1 -1
  36. package/lib-es/components/MenuItem/helpers/index.js +1 -3
  37. package/lib-es/components/MenuItem/helpers/index.js.map +1 -1
  38. package/lib-es/components/SearchContent/SearchContent.js +4 -3
  39. package/lib-es/components/SearchContent/SearchContent.js.map +1 -1
  40. package/lib-es/system-components/EditorMode/helpers/add-editor-mode-event-listeners.js +0 -1
  41. package/lib-es/system-components/EditorMode/helpers/add-editor-mode-event-listeners.js.map +1 -1
  42. package/package.json +10 -10
  43. package/src/components/ContentGroup/ContentGroup.js +37 -13
  44. package/src/components/ContentGroup/ContentGroupAccordion.js +163 -0
  45. package/src/components/ContentGroup/constants.js +14 -1
  46. package/src/components/ContentGroup/helpers/get-structured-data-properties.js +36 -0
  47. package/src/components/Menu/Menu.js +1 -3
  48. package/src/components/Menu/MenuContext.js +1 -1
  49. package/src/components/MenuItem/MenuItemRender.js +12 -40
  50. package/src/components/MenuItem/helpers/index.js +1 -3
  51. package/src/components/SearchContent/SearchContent.js +6 -5
  52. package/src/system-components/EditorMode/helpers/add-editor-mode-event-listeners.js +3 -1
  53. package/tests/unit/src/components/ContentGroup/ContentGroup.test.js +32 -2
  54. package/tests/unit/src/components/ContentGroup/ContentGroupAccordion.test.js +283 -0
  55. package/tests/unit/src/components/ContentGroup/__snapshots__/ContentGroup.test.js.snap +2 -2
  56. package/tests/unit/src/components/ContentGroup/__snapshots__/ContentGroupAccordion.test.js.snap +243 -0
  57. package/tests/unit/src/components/ContentGroup/helpers/get-structured-data-properties.test.js +105 -0
  58. package/tests/unit/src/components/MenuItem/MenuItem.test.js +0 -5
  59. package/tests/unit/src/components/MenuItem/MenuItemRender.test.js +3 -11
  60. package/lib/components/MenuItem/helpers/has-active-child.js +0 -19
  61. package/lib/components/MenuItem/helpers/has-active-child.js.map +0 -1
  62. package/lib/components/MenuItem/helpers/isUrlPathMatch.js +0 -18
  63. package/lib/components/MenuItem/helpers/isUrlPathMatch.js.map +0 -1
  64. package/lib-es/components/MenuItem/helpers/has-active-child.js +0 -5
  65. package/lib-es/components/MenuItem/helpers/has-active-child.js.map +0 -1
  66. package/lib-es/components/MenuItem/helpers/isUrlPathMatch.js +0 -8
  67. package/lib-es/components/MenuItem/helpers/isUrlPathMatch.js.map +0 -1
  68. package/src/components/MenuItem/helpers/has-active-child.js +0 -10
  69. package/src/components/MenuItem/helpers/isUrlPathMatch.js +0 -10
  70. package/tests/unit/src/components/MenuItem/helpers/constants.js +0 -73
  71. package/tests/unit/src/components/MenuItem/helpers/has-active-child.test.js +0 -35
  72. package/tests/unit/src/components/MenuItem/helpers/is-url-path-match.test.js +0 -53
@@ -0,0 +1,283 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import React from 'react';
5
+ import '@testing-library/jest-dom';
6
+ import { render, screen, fireEvent } from '@testing-library/react';
7
+ import { useRouter } from 'next/router';
8
+ import ContentGroupAccordion from '../../../../../src/components/ContentGroup/ContentGroupAccordion';
9
+ import { OPEN_STATES } from '../../../../../src/components/ContentGroup/constants';
10
+
11
+ const RESOLVER = '/Resolver';
12
+
13
+ // Test constants
14
+ const TEST_CONSTANTS = {
15
+ ACCORDION_URL: 'accordion-url',
16
+ SECTION_ONE_ID: 'section-one',
17
+ SECTION_TWO_ID: 'section-two',
18
+ SECTION_ONE_CAMEL: 'sectionOne',
19
+ SECTION_TWO_CAMEL: 'sectionTwo',
20
+ SECTION_ONE_TEXT: 'section one',
21
+ SECTION_TWO_TEXT: 'section two',
22
+ TEST_ACCORDION_NAME: 'testAccordion',
23
+ VARIANT_TESTID: 'variant',
24
+ BUTTON_ROLE: 'button',
25
+ ARIA_EXPANDED: 'aria-expanded',
26
+ ARIA_CONTROLS: 'aria-controls',
27
+ TRUE: 'true',
28
+ FALSE: 'false'
29
+ };
30
+
31
+ jest.mock('next/router', () => ({
32
+ useRouter: jest.fn()
33
+ }));
34
+
35
+ const buildRouter = (overrides = {}) => ({
36
+ asPath: TEST_CONSTANTS.ACCORDION_URL,
37
+ push: jest.fn(),
38
+ replace: jest.fn(),
39
+ ...overrides
40
+ });
41
+
42
+ const VariantComponent = ({ children }) => (
43
+ <div data-testid={TEST_CONSTANTS.VARIANT_TESTID}>{children}</div>
44
+ );
45
+
46
+ const mockedProps = {
47
+ name: TEST_CONSTANTS.TEST_ACCORDION_NAME,
48
+ groupSections: [
49
+ <div key="s1">{TEST_CONSTANTS.SECTION_ONE_TEXT}</div>,
50
+ <div key="s2">{TEST_CONSTANTS.SECTION_TWO_TEXT}</div>
51
+ ],
52
+ sectionsData: [
53
+ [TEST_CONSTANTS.SECTION_ONE_ID, TEST_CONSTANTS.SECTION_ONE_CAMEL],
54
+ [TEST_CONSTANTS.SECTION_TWO_ID, TEST_CONSTANTS.SECTION_TWO_CAMEL]
55
+ ]
56
+ };
57
+
58
+ beforeEach(() => {
59
+ jest.clearAllMocks();
60
+ useRouter.mockReturnValue(buildRouter());
61
+ });
62
+
63
+ describe('ContentGroupAccordion component', () => {
64
+ it('matches snapshot and renders basic accordion', () => {
65
+ const { asFragment } = render(<ContentGroupAccordion {...mockedProps} />);
66
+ expect(asFragment()).toMatchSnapshot();
67
+ });
68
+
69
+ it('matches snapshot and renders variant accordion', () => {
70
+ const { asFragment } = render(
71
+ <ContentGroupAccordion {...mockedProps} VariantComponent={VariantComponent} />
72
+ );
73
+ expect(asFragment()).toMatchSnapshot();
74
+ });
75
+
76
+ it('opens the section indicated by the URL hash on first render', () => {
77
+ useRouter.mockReturnValue(
78
+ buildRouter({ asPath: `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_TWO_ID}` })
79
+ );
80
+
81
+ render(<ContentGroupAccordion {...mockedProps} />);
82
+
83
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
84
+ expect(headers.length).toBeGreaterThanOrEqual(2);
85
+
86
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
87
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
88
+ });
89
+
90
+ it('toggles a section when its header is clicked', () => {
91
+ const router = buildRouter();
92
+ useRouter.mockReturnValue(router);
93
+
94
+ render(<ContentGroupAccordion {...mockedProps} />);
95
+
96
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
97
+
98
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
99
+
100
+ fireEvent.click(headers[0]);
101
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
102
+ expect(router.replace).toHaveBeenLastCalledWith(
103
+ RESOLVER,
104
+ `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_ONE_ID}`,
105
+ expect.objectContaining({ shallow: true, scroll: false })
106
+ );
107
+
108
+ fireEvent.click(headers[0]);
109
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
110
+ expect(router.replace).toHaveBeenLastCalledWith(
111
+ RESOLVER,
112
+ TEST_CONSTANTS.ACCORDION_URL,
113
+ expect.objectContaining({ shallow: true, scroll: false })
114
+ );
115
+ });
116
+
117
+ it('opens another section when its header is clicked', () => {
118
+ render(<ContentGroupAccordion {...mockedProps} />);
119
+
120
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
121
+
122
+ fireEvent.click(headers[0]);
123
+ fireEvent.click(headers[1]);
124
+
125
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
126
+ });
127
+
128
+ it('renders accordion with child content inside (content present in DOM)', () => {
129
+ render(<ContentGroupAccordion {...mockedProps} />);
130
+ expect(screen.getByText(TEST_CONSTANTS.SECTION_ONE_TEXT)).toBeInTheDocument();
131
+ expect(screen.getByText(TEST_CONSTANTS.SECTION_TWO_TEXT)).toBeInTheDocument();
132
+ });
133
+
134
+ it('has basic accessibility attributes expected by Lighthouse (ARIA)', () => {
135
+ render(<ContentGroupAccordion {...mockedProps} />);
136
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
137
+
138
+ headers.forEach(hdr => {
139
+ expect(hdr).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED);
140
+ const controls = hdr.getAttribute(TEST_CONSTANTS.ARIA_CONTROLS);
141
+ expect(controls).toBeTruthy();
142
+ });
143
+ });
144
+
145
+ it('opens the item matching the hash after a "reload" (initial asPath contains hash)', () => {
146
+ useRouter.mockReturnValue(
147
+ buildRouter({ asPath: `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_ONE_ID}` })
148
+ );
149
+
150
+ render(<ContentGroupAccordion {...mockedProps} />);
151
+
152
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
153
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
154
+ });
155
+
156
+ it('supports opening more than one accordion item at a time', () => {
157
+ render(<ContentGroupAccordion {...mockedProps} />);
158
+
159
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
160
+
161
+ fireEvent.click(headers[0]);
162
+ fireEvent.click(headers[1]);
163
+
164
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
165
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
166
+ });
167
+
168
+ it('renders all item panels in HTML and uses CSS to show/hide (no unmount on close)', () => {
169
+ render(<ContentGroupAccordion {...mockedProps} />);
170
+
171
+ const contentOne = screen.getByText(TEST_CONSTANTS.SECTION_ONE_TEXT);
172
+ const contentTwo = screen.getByText(TEST_CONSTANTS.SECTION_TWO_TEXT);
173
+
174
+ expect(contentOne).toBeInTheDocument();
175
+ expect(contentTwo).toBeInTheDocument();
176
+
177
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
178
+ fireEvent.click(headers[0]);
179
+ fireEvent.click(headers[0]);
180
+
181
+ expect(contentOne).toBeInTheDocument();
182
+ expect(contentTwo).toBeInTheDocument();
183
+ });
184
+
185
+ it('openState="first open" opens only the first item by default no hash', () => {
186
+ render(<ContentGroupAccordion {...mockedProps} openState={OPEN_STATES.FIRST_OPEN} />);
187
+
188
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
189
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
190
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
191
+ });
192
+
193
+ it('openState="all open" opens all items by default no hash', () => {
194
+ render(<ContentGroupAccordion {...mockedProps} openState={OPEN_STATES.ALL_OPEN} />);
195
+
196
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
197
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
198
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
199
+ });
200
+
201
+ it('openState="all closed" starts with all items closed by default no hash', () => {
202
+ render(<ContentGroupAccordion {...mockedProps} openState={OPEN_STATES.ALL_CLOSED} />);
203
+
204
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
205
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
206
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
207
+ });
208
+
209
+ it('URL hash overrides openState on first render', () => {
210
+ useRouter.mockReturnValue(
211
+ buildRouter({ asPath: `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_TWO_ID}` })
212
+ );
213
+ render(<ContentGroupAccordion {...mockedProps} openState={OPEN_STATES.ALL_CLOSED} />);
214
+
215
+ const headers = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
216
+
217
+ expect(headers[0]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.FALSE);
218
+ expect(headers[1]).toHaveAttribute(TEST_CONSTANTS.ARIA_EXPANDED, TEST_CONSTANTS.TRUE);
219
+ });
220
+
221
+ it('toggling updates the URL hash appropriately with scroll disabled', () => {
222
+ const router = buildRouter();
223
+ useRouter.mockReturnValue(router);
224
+
225
+ render(<ContentGroupAccordion {...mockedProps} openState={OPEN_STATES.ALL_CLOSED} />);
226
+
227
+ const [first, second] = screen.getAllByRole(TEST_CONSTANTS.BUTTON_ROLE);
228
+
229
+ fireEvent.click(first);
230
+ expect(router.replace).toHaveBeenLastCalledWith(
231
+ RESOLVER,
232
+ `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_ONE_ID}`,
233
+ expect.objectContaining({ shallow: true, scroll: false })
234
+ );
235
+
236
+ fireEvent.click(second);
237
+ expect(router.replace).toHaveBeenLastCalledWith(
238
+ RESOLVER,
239
+ `${TEST_CONSTANTS.ACCORDION_URL}#${TEST_CONSTANTS.SECTION_TWO_ID}`,
240
+ expect.objectContaining({ shallow: true, scroll: false })
241
+ );
242
+
243
+ fireEvent.click(second);
244
+ expect(router.replace).toHaveBeenLastCalledWith(
245
+ RESOLVER,
246
+ TEST_CONSTANTS.ACCORDION_URL,
247
+ expect.objectContaining({ shallow: true, scroll: false })
248
+ );
249
+ });
250
+
251
+ describe('structured data props', () => {
252
+ it('applies groupSectionProps to accordion sections', () => {
253
+ const customProperties = {
254
+ groupSectionProps: {
255
+ 'data-testid': 'custom-section'
256
+ },
257
+ groupSectionTitleProps: {
258
+ 'data-testid': 'custom-title'
259
+ },
260
+ groupContentWrapperProps: {
261
+ 'data-testid': 'custom-content-wrapper'
262
+ },
263
+ groupContentProps: {
264
+ 'data-testid': 'custom-content'
265
+ }
266
+ };
267
+
268
+ render(<ContentGroupAccordion {...mockedProps} {...customProperties} />);
269
+
270
+ const sections = screen.getAllByTestId('custom-section');
271
+ expect(sections.length).toBe(2);
272
+
273
+ const titles = screen.getAllByTestId('custom-title');
274
+ expect(titles.length).toBe(2);
275
+
276
+ const contentWrappers = screen.getAllByTestId('custom-content-wrapper');
277
+ expect(contentWrappers.length).toBe(2);
278
+
279
+ const contents = screen.getAllByTestId('custom-content');
280
+ expect(contents.length).toBe(2);
281
+ });
282
+ });
283
+ });
@@ -1,5 +1,5 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`ContentGroup component should match snapshot and render based on contentType prop 1`] = `<DocumentFragment />`;
3
+ exports[`ContentGroup component matches snapshots for tab and sidepanel 1`] = `<DocumentFragment />`;
4
4
 
5
- exports[`ContentGroup component should match snapshot and render based on contentType prop 2`] = `<DocumentFragment />`;
5
+ exports[`ContentGroup component matches snapshots for tab and sidepanel 2`] = `<DocumentFragment />`;
@@ -0,0 +1,243 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ContentGroupAccordion component matches snapshot and renders basic accordion 1`] = `
4
+ <DocumentFragment>
5
+ <div
6
+ class="content-group-accordion"
7
+ >
8
+ <div
9
+ aria-label="testAccordion"
10
+ class="content-group-accordion__items"
11
+ >
12
+ <div
13
+ class="content-group-accordion__item"
14
+ >
15
+ <h3
16
+ class="content-group-accordion__heading"
17
+ >
18
+ <button
19
+ aria-controls="section-one-panel"
20
+ aria-expanded="false"
21
+ class="content-group-accordion__header-btn"
22
+ id="section-one"
23
+ type="button"
24
+ >
25
+ <span
26
+ class="content-group-accordion__title"
27
+ >
28
+ sectionOne
29
+ </span>
30
+ <span
31
+ aria-hidden="true"
32
+ class="content-group-accordion__chevron"
33
+ >
34
+ <svg
35
+ class="content-group-accordion__chevron-icon"
36
+ fill="currentColor"
37
+ height="1em"
38
+ stroke="currentColor"
39
+ stroke-width="0"
40
+ viewBox="0 0 16 16"
41
+ width="1em"
42
+ xmlns="http://www.w3.org/2000/svg"
43
+ >
44
+ <path
45
+ clip-rule="evenodd"
46
+ d="M7.976 10.072l4.357-4.357.62.618L8.284 11h-.618L3 6.333l.619-.618 4.357 4.357z"
47
+ fill-rule="evenodd"
48
+ />
49
+ </svg>
50
+ </span>
51
+ </button>
52
+ </h3>
53
+ <div
54
+ aria-labelledby="section-one"
55
+ class="content-group-accordion__panel"
56
+ data-section="section-one"
57
+ hidden=""
58
+ id="section-one-panel"
59
+ role="region"
60
+ >
61
+ <div>
62
+ <div>
63
+ section one
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ <div
69
+ class="content-group-accordion__item"
70
+ >
71
+ <h3
72
+ class="content-group-accordion__heading"
73
+ >
74
+ <button
75
+ aria-controls="section-two-panel"
76
+ aria-expanded="false"
77
+ class="content-group-accordion__header-btn"
78
+ id="section-two"
79
+ type="button"
80
+ >
81
+ <span
82
+ class="content-group-accordion__title"
83
+ >
84
+ sectionTwo
85
+ </span>
86
+ <span
87
+ aria-hidden="true"
88
+ class="content-group-accordion__chevron"
89
+ >
90
+ <svg
91
+ class="content-group-accordion__chevron-icon"
92
+ fill="currentColor"
93
+ height="1em"
94
+ stroke="currentColor"
95
+ stroke-width="0"
96
+ viewBox="0 0 16 16"
97
+ width="1em"
98
+ xmlns="http://www.w3.org/2000/svg"
99
+ >
100
+ <path
101
+ clip-rule="evenodd"
102
+ d="M7.976 10.072l4.357-4.357.62.618L8.284 11h-.618L3 6.333l.619-.618 4.357 4.357z"
103
+ fill-rule="evenodd"
104
+ />
105
+ </svg>
106
+ </span>
107
+ </button>
108
+ </h3>
109
+ <div
110
+ aria-labelledby="section-two"
111
+ class="content-group-accordion__panel"
112
+ data-section="section-two"
113
+ hidden=""
114
+ id="section-two-panel"
115
+ role="region"
116
+ >
117
+ <div>
118
+ <div>
119
+ section two
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </DocumentFragment>
127
+ `;
128
+
129
+ exports[`ContentGroupAccordion component matches snapshot and renders variant accordion 1`] = `
130
+ <DocumentFragment>
131
+ <div
132
+ class="content-group-accordion"
133
+ >
134
+ <div
135
+ aria-label="testAccordion"
136
+ class="content-group-accordion__items"
137
+ >
138
+ <div
139
+ class="content-group-accordion__item"
140
+ >
141
+ <h3
142
+ class="content-group-accordion__heading"
143
+ >
144
+ <button
145
+ aria-controls="section-one-panel"
146
+ aria-expanded="false"
147
+ class="content-group-accordion__header-btn"
148
+ id="section-one"
149
+ type="button"
150
+ >
151
+ <span
152
+ class="content-group-accordion__title"
153
+ >
154
+ sectionOne
155
+ </span>
156
+ <span
157
+ aria-hidden="true"
158
+ class="content-group-accordion__chevron"
159
+ >
160
+ <svg
161
+ class="content-group-accordion__chevron-icon"
162
+ fill="currentColor"
163
+ height="1em"
164
+ stroke="currentColor"
165
+ stroke-width="0"
166
+ viewBox="0 0 16 16"
167
+ width="1em"
168
+ xmlns="http://www.w3.org/2000/svg"
169
+ >
170
+ <path
171
+ clip-rule="evenodd"
172
+ d="M7.976 10.072l4.357-4.357.62.618L8.284 11h-.618L3 6.333l.619-.618 4.357 4.357z"
173
+ fill-rule="evenodd"
174
+ />
175
+ </svg>
176
+ </span>
177
+ </button>
178
+ </h3>
179
+ <div
180
+ data-testid="variant"
181
+ >
182
+ <div>
183
+ <div>
184
+ section one
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ <div
190
+ class="content-group-accordion__item"
191
+ >
192
+ <h3
193
+ class="content-group-accordion__heading"
194
+ >
195
+ <button
196
+ aria-controls="section-two-panel"
197
+ aria-expanded="false"
198
+ class="content-group-accordion__header-btn"
199
+ id="section-two"
200
+ type="button"
201
+ >
202
+ <span
203
+ class="content-group-accordion__title"
204
+ >
205
+ sectionTwo
206
+ </span>
207
+ <span
208
+ aria-hidden="true"
209
+ class="content-group-accordion__chevron"
210
+ >
211
+ <svg
212
+ class="content-group-accordion__chevron-icon"
213
+ fill="currentColor"
214
+ height="1em"
215
+ stroke="currentColor"
216
+ stroke-width="0"
217
+ viewBox="0 0 16 16"
218
+ width="1em"
219
+ xmlns="http://www.w3.org/2000/svg"
220
+ >
221
+ <path
222
+ clip-rule="evenodd"
223
+ d="M7.976 10.072l4.357-4.357.62.618L8.284 11h-.618L3 6.333l.619-.618 4.357 4.357z"
224
+ fill-rule="evenodd"
225
+ />
226
+ </svg>
227
+ </span>
228
+ </button>
229
+ </h3>
230
+ <div
231
+ data-testid="variant"
232
+ >
233
+ <div>
234
+ <div>
235
+ section two
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </DocumentFragment>
243
+ `;
@@ -0,0 +1,105 @@
1
+ import getStructuredDataProperties from '../../../../../../src/components/ContentGroup/helpers/get-structured-data-properties';
2
+
3
+ describe('getStructuredDataProperties helper', () => {
4
+ describe('when isFaqContent is false/true', () => {
5
+ it('should return empty properties object', () => {
6
+ const result = getStructuredDataProperties(false);
7
+
8
+ expect(result).toEqual({
9
+ topWrapperProps: {},
10
+ groupSectionProps: {},
11
+ groupSectionTitleProps: {},
12
+ groupContentWrapperProps: {},
13
+ groupContentProps: {}
14
+ });
15
+ });
16
+
17
+ it('should return structured data properties for FAQ content', () => {
18
+ const result = getStructuredDataProperties(true);
19
+
20
+ expect(result).toEqual({
21
+ topWrapperProps: {
22
+ itemScope: true,
23
+ itemType: 'https://schema.org/FAQPage'
24
+ },
25
+ groupSectionProps: {
26
+ itemScope: true,
27
+ itemProp: 'mainEntity',
28
+ itemType: 'https://schema.org/Question'
29
+ },
30
+ groupSectionTitleProps: {
31
+ itemProp: 'name'
32
+ },
33
+ groupContentWrapperProps: {
34
+ itemScope: true,
35
+ itemProp: 'acceptedAnswer',
36
+ itemType: 'https://schema.org/Answer'
37
+ },
38
+ groupContentProps: {
39
+ itemProp: 'text'
40
+ }
41
+ });
42
+ });
43
+ });
44
+
45
+ describe('structured data compliance', () => {
46
+ it('should provide valid schema.org URLs for FAQ structured data', () => {
47
+ const result = getStructuredDataProperties(true);
48
+
49
+ expect(result.topWrapperProps.itemType).toBe('https://schema.org/FAQPage');
50
+ expect(result.groupSectionProps.itemType).toBe('https://schema.org/Question');
51
+ expect(result.groupContentWrapperProps.itemType).toBe('https://schema.org/Answer');
52
+ });
53
+
54
+ it('should provide correct microdata properties for FAQ structure', () => {
55
+ const result = getStructuredDataProperties(true);
56
+
57
+ // Check that all itemScope properties are boolean true
58
+ expect(result.topWrapperProps.itemScope).toBe(true);
59
+ expect(result.groupSectionProps.itemScope).toBe(true);
60
+ expect(result.groupContentWrapperProps.itemScope).toBe(true);
61
+
62
+ // Check itemProp values
63
+ expect(result.groupSectionProps.itemProp).toBe('mainEntity');
64
+ expect(result.groupSectionTitleProps.itemProp).toBe('name');
65
+ expect(result.groupContentWrapperProps.itemProp).toBe('acceptedAnswer');
66
+ expect(result.groupContentProps.itemProp).toBe('text');
67
+ });
68
+
69
+ it('should maintain consistent property structure', () => {
70
+ const faqResult = getStructuredDataProperties(true);
71
+ const nonFaqResult = getStructuredDataProperties(false);
72
+
73
+ // Both should have the same property keys
74
+ expect(Object.keys(faqResult)).toEqual(Object.keys(nonFaqResult));
75
+ expect(Object.keys(faqResult)).toEqual([
76
+ 'topWrapperProps',
77
+ 'groupSectionProps',
78
+ 'groupSectionTitleProps',
79
+ 'groupContentWrapperProps',
80
+ 'groupContentProps'
81
+ ]);
82
+ });
83
+ });
84
+
85
+ describe('return value immutability', () => {
86
+ it('should return a new object each time it is called', () => {
87
+ const result1 = getStructuredDataProperties(true);
88
+ const result2 = getStructuredDataProperties(true);
89
+
90
+ expect(result1).not.toBe(result2);
91
+ expect(result1).toEqual(result2);
92
+ });
93
+
94
+ it('should return nested objects that are independent', () => {
95
+ const result1 = getStructuredDataProperties(true);
96
+ const result2 = getStructuredDataProperties(true);
97
+
98
+ expect(result1.topWrapperProps).not.toBe(result2.topWrapperProps);
99
+ expect(result1.groupSectionProps).not.toBe(result2.groupSectionProps);
100
+ expect(result1.groupSectionTitleProps).not.toBe(result2.groupSectionTitleProps);
101
+ expect(result1.groupContentWrapperProps).not.toBe(result2.groupContentWrapperProps);
102
+ expect(result1.groupContentProps).not.toBe(result2.groupContentProps);
103
+ });
104
+ });
105
+ });
@@ -19,11 +19,6 @@ jest.mock('next/router', () => ({
19
19
  useRouter: () => ({ asPath: '/' })
20
20
  }));
21
21
 
22
- // todo: add extra tests to support this util
23
- jest.mock('../../../../../src/components/MenuItem/helpers/has-active-child', () =>
24
- jest.fn(() => false)
25
- );
26
-
27
22
  const componentProps = {
28
23
  id: 'id',
29
24
  name: 'mock name',
@@ -11,22 +11,14 @@ const MENU_ITEM_CHILDREN_CLASS = 'menu--item-children';
11
11
 
12
12
  let mockAsPathValue = '/';
13
13
 
14
- jest.mock('next/router', () => {
15
- const router = { asPath: mockAsPathValue };
16
- return {
17
- useRouter: () => router
18
- };
19
- });
14
+ jest.mock('next/router', () => ({
15
+ useRouter: () => ({ asPath: mockAsPathValue })
16
+ }));
20
17
 
21
18
  jest.mock('@blaze-cms/utils-handlebars', () => ({
22
19
  useStringTemplate: jest.fn((parent, [title]) => ({ loading: false, data: [title] }))
23
20
  }));
24
21
 
25
- // todo: add extra tests to support this util
26
- jest.mock('../../../../../src/components/MenuItem/helpers/has-active-child', () =>
27
- jest.fn(() => false)
28
- );
29
-
30
22
  describe('MenuRender component', () => {
31
23
  it('renders menu item with link when URL is provided', () => {
32
24
  const { getByText } = render(<MenuRender eventType="click" text="Home" url="/home" />);