@justeattakeaway/pie-modal 0.13.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +6 -0
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +47 -0
- package/README.md +17 -15
- package/dist/index.js +1070 -200
- package/dist/react.js +0 -0
- package/dist/types/index.d.ts +0 -0
- package/dist/types/packages/components/pie-modal/src/defs.d.ts +50 -8
- package/dist/types/packages/components/pie-modal/src/defs.d.ts.map +1 -1
- package/dist/types/packages/components/pie-modal/src/index.d.ts +36 -3
- package/dist/types/packages/components/pie-modal/src/index.d.ts.map +1 -1
- package/dist/types/packages/components/pie-modal/src/react.d.ts +0 -0
- package/dist/types/packages/components/pie-modal/src/react.d.ts.map +0 -0
- package/dist/types/react.d.ts +0 -0
- package/package.json +3 -1
- package/playwright/index.ts +1 -1
- package/playwright-lit-visual.config.ts +0 -1
- package/playwright-lit.config.ts +1 -2
- package/src/defs.ts +62 -7
- package/src/index.ts +145 -37
- package/src/modal.scss +107 -19
- package/test/component/pie-modal.spec.ts +273 -12
- package/test/helpers/index.ts +2 -0
- package/test/visual/pie-modal.spec.ts +376 -85
- package/tsconfig.json +1 -1
package/src/modal.scss
CHANGED
|
@@ -33,10 +33,24 @@
|
|
|
33
33
|
--modal-bg-color: var(--dt-color-container-default);
|
|
34
34
|
--modal-elevation: var(--dt-elevation-04);
|
|
35
35
|
|
|
36
|
+
// TODO: This should be moved into global CSS typography setting
|
|
37
|
+
// This should be imported by consuming apps and set on the application body
|
|
38
|
+
text-rendering: optimizelegibility;
|
|
39
|
+
-webkit-font-smoothing: antialiased;
|
|
40
|
+
-moz-font-smoothing: antialiased;
|
|
41
|
+
|
|
36
42
|
&:focus-visible {
|
|
37
43
|
outline: none;
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
// We need to override the icon sizes at different screen sizes regardless of size prop passed in
|
|
47
|
+
pie-icon-button {
|
|
48
|
+
@media (max-width: $breakpoint-wide) {
|
|
49
|
+
--btn-dimension: 40px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
40
54
|
&[open] {
|
|
41
55
|
// We only apply this when the modal is open,
|
|
42
56
|
// otherwise it interferes with the native
|
|
@@ -110,6 +124,16 @@
|
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
126
|
|
|
127
|
+
&[position='top'] {
|
|
128
|
+
margin-block-start: var(--dt-spacing-j);
|
|
129
|
+
|
|
130
|
+
&[isfullwidthbelowmid] {
|
|
131
|
+
@media (max-width: calc($breakpoint-wide - 1px)) {
|
|
132
|
+
margin-block-start: var(--dt-spacing-none);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
113
137
|
// We need to pull in the token directly here because the
|
|
114
138
|
// pseudo element `::backdrop` doesn't seem to pick up custom css properties.
|
|
115
139
|
&::backdrop {
|
|
@@ -131,24 +155,51 @@
|
|
|
131
155
|
}
|
|
132
156
|
}
|
|
133
157
|
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
// When hasStackedActions is set
|
|
159
|
+
// change the direction of the footer flex container so buttons are full width of modal
|
|
160
|
+
&[hasstackedactions] {
|
|
161
|
+
& .c-modal-footer {
|
|
162
|
+
// TODO: Move breakpoint sizes into shared CSS component utilities
|
|
163
|
+
@media (max-width: calc($breakpoint-wide - 1px)) {
|
|
164
|
+
flex-direction: column;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
136
168
|
|
|
137
|
-
|
|
138
|
-
padding-
|
|
169
|
+
& .c-modal-header {
|
|
170
|
+
padding-inline: var(--dt-spacing-d);
|
|
171
|
+
padding-block: 14px; // This is deliberately not a custom property
|
|
172
|
+
display: grid;
|
|
173
|
+
grid-template-areas:
|
|
174
|
+
'back heading close'
|
|
175
|
+
'. heading .';
|
|
176
|
+
grid-template-columns: minmax(0, max-content) minmax(0, 1fr) minmax(0, max-content);
|
|
139
177
|
align-items: center;
|
|
140
|
-
|
|
178
|
+
|
|
179
|
+
@media (min-width: $breakpoint-wide) {
|
|
180
|
+
padding-inline: var(--dt-spacing-e);
|
|
181
|
+
padding-block: 20px; // This is deliberately not a custom property
|
|
182
|
+
}
|
|
141
183
|
}
|
|
142
184
|
|
|
143
185
|
&[hasbackbutton] .c-modal-header {
|
|
144
|
-
padding-block: var(--dt-spacing-
|
|
145
|
-
padding-inline-start: var(--dt-spacing-
|
|
186
|
+
padding-block: var(--dt-spacing-b);
|
|
187
|
+
padding-inline-start: var(--dt-spacing-b);
|
|
188
|
+
|
|
189
|
+
@media (min-width: $breakpoint-wide) {
|
|
190
|
+
padding-block: var(--dt-spacing-c);
|
|
191
|
+
padding-inline-start: var(--dt-spacing-c);
|
|
192
|
+
}
|
|
146
193
|
}
|
|
147
194
|
|
|
148
195
|
&[isdismissible] .c-modal-header {
|
|
149
|
-
|
|
150
|
-
padding-
|
|
151
|
-
|
|
196
|
+
padding-block: var(--dt-spacing-b);
|
|
197
|
+
padding-inline-end: var(--dt-spacing-b);
|
|
198
|
+
|
|
199
|
+
@media (min-width: $breakpoint-wide) {
|
|
200
|
+
padding-block: var(--dt-spacing-c);
|
|
201
|
+
padding-inline-end: var(--dt-spacing-c);
|
|
202
|
+
}
|
|
152
203
|
}
|
|
153
204
|
|
|
154
205
|
& .c-modal-heading {
|
|
@@ -160,16 +211,59 @@
|
|
|
160
211
|
line-height: var(--modal-header-font-line-height);
|
|
161
212
|
font-weight: var(--modal-header-font-weight);
|
|
162
213
|
margin: 0;
|
|
214
|
+
grid-area: heading;
|
|
163
215
|
}
|
|
164
216
|
|
|
165
217
|
// Ensure correct padding when there is a back button in front of the heading
|
|
166
218
|
&[hasbackbutton] .c-modal-heading {
|
|
167
|
-
|
|
219
|
+
padding-inline-start: var(--dt-spacing-b);
|
|
220
|
+
|
|
221
|
+
@media (min-width: $breakpoint-wide) {
|
|
222
|
+
padding-inline-start: var(--dt-spacing-c);
|
|
223
|
+
}
|
|
168
224
|
}
|
|
169
225
|
|
|
170
226
|
// Ensure correct padding when there is a close button behind the heading
|
|
171
227
|
&[isdismissible] .c-modal-heading {
|
|
172
|
-
|
|
228
|
+
padding-inline-end: var(--dt-spacing-d);
|
|
229
|
+
|
|
230
|
+
@media (min-width: $breakpoint-wide) {
|
|
231
|
+
padding-inline-end: var(--dt-spacing-e);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
& .c-modal-backBtn {
|
|
236
|
+
grid-area: back;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
& .c-modal-closeBtn {
|
|
240
|
+
grid-area: close;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
&[isfooterpinned] .c-modal-content,
|
|
244
|
+
& .c-modal-scrollContainer {
|
|
245
|
+
overflow-y: auto;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
& .c-modal-scrollContainer {
|
|
249
|
+
// These are the shadows used to indicate scrolling above and below content
|
|
250
|
+
--bg-scroll-start: linear-gradient(var(--dt-color-container-default) 30%, rgba(255, 255, 255, 0));
|
|
251
|
+
--bg-scroll-end: linear-gradient(rgba(255, 255, 255, 0), var(--dt-color-container-default) 70%) 0 100%;
|
|
252
|
+
--bg-scroll-top: radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0));
|
|
253
|
+
--bg-scroll-bottom: radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0)) 0 100%;
|
|
254
|
+
|
|
255
|
+
// Sizes of the scroll shadows
|
|
256
|
+
--bg-size-scroll-start: 100% 40px;
|
|
257
|
+
--bg-size-scroll-end: 100% 40px;
|
|
258
|
+
--bg-size-scroll-top: 100% 16px;
|
|
259
|
+
--bg-size-scroll-bottom: 100% 16px;
|
|
260
|
+
|
|
261
|
+
background: var(--bg-scroll-start), var(--bg-scroll-end), var(--bg-scroll-top), var(--bg-scroll-bottom);
|
|
262
|
+
background-repeat: no-repeat;
|
|
263
|
+
background-color: var(--dt-color-container-default);
|
|
264
|
+
background-size: var(--bg-size-scroll-start), var(--bg-size-scroll-end), var(--bg-size-scroll-top), var(--bg-size-scroll-bottom);
|
|
265
|
+
|
|
266
|
+
background-attachment: local, local, scroll, scroll;
|
|
173
267
|
}
|
|
174
268
|
|
|
175
269
|
& .c-modal-content {
|
|
@@ -198,7 +292,7 @@
|
|
|
198
292
|
--spinner-animation-iteration-count: infinite;
|
|
199
293
|
|
|
200
294
|
position: relative;
|
|
201
|
-
min-block-size:
|
|
295
|
+
min-block-size: var(--dt-spacing-j);
|
|
202
296
|
|
|
203
297
|
font-size: var(--modal-content-font-size);
|
|
204
298
|
line-height: var(--modal-content-line-height);
|
|
@@ -207,8 +301,6 @@
|
|
|
207
301
|
padding-inline: var(--modal-content-padding);
|
|
208
302
|
padding-block: var(--modal-content-padding-block);
|
|
209
303
|
|
|
210
|
-
overflow-y: auto;
|
|
211
|
-
|
|
212
304
|
&--scrollable {
|
|
213
305
|
background:
|
|
214
306
|
// Scroll shadow cover
|
|
@@ -258,8 +350,4 @@
|
|
|
258
350
|
opacity: 0;
|
|
259
351
|
}
|
|
260
352
|
}
|
|
261
|
-
|
|
262
|
-
& .c-modal-closeBtn {
|
|
263
|
-
margin-inline-start: auto;
|
|
264
|
-
}
|
|
265
353
|
}
|
|
@@ -5,7 +5,7 @@ import { PieIconButton } from '@justeattakeaway/pie-icon-button';
|
|
|
5
5
|
import {
|
|
6
6
|
WebComponentTestWrapper,
|
|
7
7
|
} from '@justeattakeaway/pie-webc-testing/src/helpers/components/web-component-test-wrapper/WebComponentTestWrapper.ts';
|
|
8
|
-
import { renderTestPieModal } from '../helpers/index.ts';
|
|
8
|
+
import { createScrollablePageHTML, renderTestPieModal } from '../helpers/index.ts';
|
|
9
9
|
|
|
10
10
|
import { PieModal } from '@/index';
|
|
11
11
|
import {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
headingLevels,
|
|
15
15
|
} from '@/defs';
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const componentSelector = '[data-test-id="pie-modal"]';
|
|
18
18
|
const backButtonSelector = '[data-test-id="modal-back-button"]';
|
|
19
19
|
const closeButtonSelector = '[data-test-id="modal-close-button"]';
|
|
20
20
|
|
|
@@ -38,7 +38,7 @@ test.describe('modal', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
// Act
|
|
41
|
-
const modal = page.locator(
|
|
41
|
+
const modal = page.locator(componentSelector);
|
|
42
42
|
|
|
43
43
|
// Assert
|
|
44
44
|
expect(modal).toBeVisible();
|
|
@@ -109,7 +109,7 @@ test.describe('When modal is closed', () => {
|
|
|
109
109
|
},
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
const modal = page.locator(
|
|
112
|
+
const modal = page.locator(componentSelector);
|
|
113
113
|
|
|
114
114
|
// Act
|
|
115
115
|
await page.click(closeButtonSelector);
|
|
@@ -159,7 +159,7 @@ test.describe('When modal is closed', () => {
|
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
// Act
|
|
162
|
-
await page.click(
|
|
162
|
+
await page.click(componentSelector, { position: { x: -10, y: -10 } }); // Click outside dialog
|
|
163
163
|
|
|
164
164
|
// Assert
|
|
165
165
|
expect(events).toHaveLength(1); // TODO - Event object is null for this test
|
|
@@ -174,7 +174,7 @@ test.describe('When modal is closed', () => {
|
|
|
174
174
|
},
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
-
const modal = await page.locator(
|
|
177
|
+
const modal = await page.locator(componentSelector);
|
|
178
178
|
|
|
179
179
|
// Act
|
|
180
180
|
await modal.click({ position: { x: -10, y: -10 } }); // Click outside dialog
|
|
@@ -355,7 +355,7 @@ test.describe('`isDismissible` prop', () => {
|
|
|
355
355
|
// Act
|
|
356
356
|
await page.click('body');
|
|
357
357
|
|
|
358
|
-
const element = await page.locator(
|
|
358
|
+
const element = await page.locator(componentSelector);
|
|
359
359
|
|
|
360
360
|
const styles = await element.evaluate((modal) => {
|
|
361
361
|
const computedStyles = window.getComputedStyle(modal);
|
|
@@ -377,7 +377,7 @@ test.describe('`isDismissible` prop', () => {
|
|
|
377
377
|
},
|
|
378
378
|
});
|
|
379
379
|
|
|
380
|
-
const modal = await page.locator(
|
|
380
|
+
const modal = await page.locator(componentSelector);
|
|
381
381
|
|
|
382
382
|
// Act
|
|
383
383
|
await page.keyboard.press('Escape');
|
|
@@ -419,7 +419,7 @@ test.describe('`isDismissible` prop', () => {
|
|
|
419
419
|
// Act
|
|
420
420
|
await page.locator('body').click();
|
|
421
421
|
|
|
422
|
-
const element = await page.locator(
|
|
422
|
+
const element = await page.locator(componentSelector);
|
|
423
423
|
|
|
424
424
|
const styles = await element.evaluate((modal) => {
|
|
425
425
|
const computedStyles = window.getComputedStyle(modal);
|
|
@@ -443,7 +443,7 @@ test.describe('`isDismissible` prop', () => {
|
|
|
443
443
|
|
|
444
444
|
// Act
|
|
445
445
|
await page.keyboard.press('Escape');
|
|
446
|
-
const modal = await page.locator(
|
|
446
|
+
const modal = await page.locator(componentSelector);
|
|
447
447
|
|
|
448
448
|
// Assert
|
|
449
449
|
await expect(modal).toBeVisible();
|
|
@@ -451,6 +451,94 @@ test.describe('`isDismissible` prop', () => {
|
|
|
451
451
|
});
|
|
452
452
|
});
|
|
453
453
|
|
|
454
|
+
test.describe('isOpen prop', () => {
|
|
455
|
+
test('should not render open when isOpen = false', async ({ mount, page }) => {
|
|
456
|
+
// Arrange
|
|
457
|
+
await mount(PieModal, {
|
|
458
|
+
props: {
|
|
459
|
+
isOpen: false,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Assert
|
|
464
|
+
await expect(page.locator(componentSelector)).not.toBeVisible();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('should render open when isOpen = true', async ({ mount, page }) => {
|
|
468
|
+
// Arrange
|
|
469
|
+
await mount(PieModal, {
|
|
470
|
+
props: {
|
|
471
|
+
isOpen: true,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Assert
|
|
476
|
+
await expect(page.locator(componentSelector)).toBeVisible();
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test.describe('scrolling logic', () => {
|
|
481
|
+
test('Should not be able to scroll when isOpen = true', async ({ page, mount }) => {
|
|
482
|
+
// Arrange
|
|
483
|
+
const modalComponent = renderTestPieModal();
|
|
484
|
+
|
|
485
|
+
await mount(
|
|
486
|
+
WebComponentTestWrapper,
|
|
487
|
+
{
|
|
488
|
+
props: {
|
|
489
|
+
pageMode: true,
|
|
490
|
+
},
|
|
491
|
+
slots: {
|
|
492
|
+
component: modalComponent,
|
|
493
|
+
pageMarkup: createScrollablePageHTML(),
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// Act
|
|
499
|
+
// Scroll 800 pixels down the page
|
|
500
|
+
await page.mouse.wheel(0, 5000);
|
|
501
|
+
|
|
502
|
+
// The mouse.wheel function causes scrolling, but doesn't wait for the scroll to finish before returning.
|
|
503
|
+
await page.waitForTimeout(3000);
|
|
504
|
+
|
|
505
|
+
// Assert
|
|
506
|
+
await expect.soft(page.getByText('Top of page copy')).toBeInViewport();
|
|
507
|
+
await expect(page.getByText('Bottom of page copy')).not.toBeInViewport();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test('Should scroll to the bottom when Pie Modal is closed', async ({ page, mount }) => {
|
|
511
|
+
// Arrange
|
|
512
|
+
const modalComponent = renderTestPieModal();
|
|
513
|
+
|
|
514
|
+
await mount(
|
|
515
|
+
WebComponentTestWrapper,
|
|
516
|
+
{
|
|
517
|
+
props: {
|
|
518
|
+
pageMode: true,
|
|
519
|
+
},
|
|
520
|
+
slots: {
|
|
521
|
+
component: modalComponent,
|
|
522
|
+
pageMarkup: createScrollablePageHTML(),
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
// Act
|
|
528
|
+
await page.locator('[data-test-id="modal-close-button"]').click();
|
|
529
|
+
|
|
530
|
+
// Scroll 800 pixels down the page
|
|
531
|
+
await page.mouse.wheel(0, 5000);
|
|
532
|
+
|
|
533
|
+
// The mouse.wheel function causes scrolling, but doesn't wait for the scroll to finish before returning.
|
|
534
|
+
await page.waitForTimeout(3000);
|
|
535
|
+
|
|
536
|
+
// Assert
|
|
537
|
+
await expect.soft(page.getByText('Top of page copy')).not.toBeInViewport();
|
|
538
|
+
await expect(page.getByText('Bottom of page copy')).toBeInViewport();
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
454
542
|
test.describe('`hasBackButton` prop', () => {
|
|
455
543
|
test.describe('when `true`', () => {
|
|
456
544
|
test('should make the modal contain a back button', async ({ mount }) => {
|
|
@@ -519,10 +607,20 @@ test.describe('actions', () => {
|
|
|
519
607
|
props: {
|
|
520
608
|
heading: 'Modal Header',
|
|
521
609
|
isOpen: true,
|
|
610
|
+
leadingAction: {
|
|
611
|
+
text: 'Confirm',
|
|
612
|
+
variant: 'primary',
|
|
613
|
+
ariaLabel: 'Descriptive message',
|
|
614
|
+
},
|
|
615
|
+
supportingAction: {
|
|
616
|
+
text: 'Cancel',
|
|
617
|
+
variant: 'ghost',
|
|
618
|
+
ariaLabel: 'Descriptive message',
|
|
619
|
+
},
|
|
522
620
|
},
|
|
523
621
|
});
|
|
524
622
|
|
|
525
|
-
const modal = await page.locator(
|
|
623
|
+
const modal = await page.locator(componentSelector);
|
|
526
624
|
|
|
527
625
|
// Act
|
|
528
626
|
await page.click(buttonSelector);
|
|
@@ -537,13 +635,23 @@ test.describe('actions', () => {
|
|
|
537
635
|
props: {
|
|
538
636
|
heading: 'Modal Header',
|
|
539
637
|
isOpen: true,
|
|
638
|
+
leadingAction: {
|
|
639
|
+
text: 'Confirm',
|
|
640
|
+
variant: 'primary',
|
|
641
|
+
ariaLabel: 'Descriptive message',
|
|
642
|
+
},
|
|
643
|
+
supportingAction: {
|
|
644
|
+
text: 'Cancel',
|
|
645
|
+
variant: 'ghost',
|
|
646
|
+
ariaLabel: 'Descriptive message',
|
|
647
|
+
},
|
|
540
648
|
},
|
|
541
649
|
});
|
|
542
650
|
|
|
543
651
|
// Act
|
|
544
652
|
await page.click(buttonSelector);
|
|
545
653
|
const returnValue = await page.$eval(
|
|
546
|
-
|
|
654
|
+
componentSelector,
|
|
547
655
|
(dialog : HTMLDialogElement) => dialog.returnValue,
|
|
548
656
|
);
|
|
549
657
|
|
|
@@ -553,3 +661,156 @@ test.describe('actions', () => {
|
|
|
553
661
|
});
|
|
554
662
|
});
|
|
555
663
|
});
|
|
664
|
+
|
|
665
|
+
test.describe('Props: `aria`', () => {
|
|
666
|
+
test.describe('when aria exist', () => {
|
|
667
|
+
test('should render component elements with the correct aria-labels', async ({ mount }) => {
|
|
668
|
+
// Arrange
|
|
669
|
+
const component = await mount(PieModal, {
|
|
670
|
+
props: {
|
|
671
|
+
isOpen: true,
|
|
672
|
+
isDismissible: true,
|
|
673
|
+
isLoading: true,
|
|
674
|
+
hasBackButton: true,
|
|
675
|
+
aria: {
|
|
676
|
+
close: 'Close label info',
|
|
677
|
+
back: 'Back label info',
|
|
678
|
+
loading: 'Loading label info',
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// Act
|
|
684
|
+
// Close button
|
|
685
|
+
const closeButton = await component.locator(closeButtonSelector);
|
|
686
|
+
const ariaCloseLabel = await closeButton.getAttribute('aria-label');
|
|
687
|
+
|
|
688
|
+
// Back button
|
|
689
|
+
const backButton = await component.locator(backButtonSelector);
|
|
690
|
+
const ariaBackLabel = await backButton.getAttribute('aria-label');
|
|
691
|
+
|
|
692
|
+
// Assert
|
|
693
|
+
await expect(ariaCloseLabel).toBe('Close label info');
|
|
694
|
+
await expect(ariaBackLabel).toBe('Back label info');
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
test.describe('when modal `isloading` is true', () => {
|
|
698
|
+
test('should render component with the correct aria values: `aria-label` & `aria-busy`', async ({ mount }) => {
|
|
699
|
+
// Arrange
|
|
700
|
+
const component = await mount(PieModal, {
|
|
701
|
+
props: {
|
|
702
|
+
isOpen: true,
|
|
703
|
+
isLoading: true,
|
|
704
|
+
aria: {
|
|
705
|
+
loading: 'Loading label info',
|
|
706
|
+
},
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// Loading state
|
|
711
|
+
const pieModalComponent = await component.locator(componentSelector);
|
|
712
|
+
const ariaLoadingLabel = await pieModalComponent.getAttribute('aria-label');
|
|
713
|
+
const ariaLoadingBusy = await pieModalComponent.getAttribute('aria-busy');
|
|
714
|
+
|
|
715
|
+
// Assert
|
|
716
|
+
await expect(ariaLoadingLabel).toBe('Loading label info');
|
|
717
|
+
await expect(ariaLoadingBusy).toBe('true');
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
test.describe('when modal `isLoading` is dynamically changing from `isLoading: true` to `isLoading: false`', () => {
|
|
722
|
+
test('should dynamically add, remove, and update `arial-label` & `aria-busy` labels', async ({ mount }) => {
|
|
723
|
+
// Arrange
|
|
724
|
+
const component = await mount(PieModal, {
|
|
725
|
+
props: {
|
|
726
|
+
isOpen: true,
|
|
727
|
+
isLoading: true,
|
|
728
|
+
aria: {
|
|
729
|
+
loading: 'Loading label info',
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
const pieModalComponent = await component.locator(componentSelector);
|
|
735
|
+
let ariaLoadingLabel = await pieModalComponent.getAttribute('aria-label');
|
|
736
|
+
let ariaLoadingBusy = await pieModalComponent.getAttribute('aria-busy');
|
|
737
|
+
|
|
738
|
+
// Assert: When `isLoading: true`
|
|
739
|
+
await expect(ariaLoadingLabel).toBe('Loading label info');
|
|
740
|
+
await expect(ariaLoadingBusy).toBe('true');
|
|
741
|
+
|
|
742
|
+
await component.update({ props: { isLoading: false } });
|
|
743
|
+
|
|
744
|
+
ariaLoadingLabel = await pieModalComponent.getAttribute('aria-label');
|
|
745
|
+
ariaLoadingBusy = await pieModalComponent.getAttribute('aria-busy');
|
|
746
|
+
|
|
747
|
+
// Assert: When `isLoading: false`
|
|
748
|
+
await expect(ariaLoadingLabel).toBeNull();
|
|
749
|
+
await expect(ariaLoadingBusy).toBe('false');
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
test.describe('when aria does not exist', () => {
|
|
755
|
+
test('should not render the aria-labels', async ({ mount }) => {
|
|
756
|
+
// Arrange
|
|
757
|
+
const component = await mount(PieModal, {
|
|
758
|
+
props: {
|
|
759
|
+
isOpen: true,
|
|
760
|
+
isDismissible: true,
|
|
761
|
+
hasBackButton: true,
|
|
762
|
+
},
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
// Act
|
|
766
|
+
// Close button
|
|
767
|
+
const closeButton = await component.locator(closeButtonSelector);
|
|
768
|
+
const ariaCloseLabel = await closeButton.getAttribute('aria-label');
|
|
769
|
+
|
|
770
|
+
// Back button
|
|
771
|
+
const backButton = await component.locator(backButtonSelector);
|
|
772
|
+
const ariaBackLabel = await backButton.getAttribute('aria-label');
|
|
773
|
+
|
|
774
|
+
// Assert
|
|
775
|
+
await expect(ariaCloseLabel).toBe(null);
|
|
776
|
+
await expect(ariaBackLabel).toBe(null);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
test.describe('when modal `isloading` is false', () => {
|
|
781
|
+
test('should not render aria-label', async ({ mount }) => {
|
|
782
|
+
// Arrange
|
|
783
|
+
const component = await mount(PieModal, {
|
|
784
|
+
props: {
|
|
785
|
+
isOpen: true,
|
|
786
|
+
isLoading: false,
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// Loading state
|
|
791
|
+
const pieModalComponent = await component.locator(componentSelector);
|
|
792
|
+
const ariaLoadingLabel = await pieModalComponent.getAttribute('aria-label');
|
|
793
|
+
|
|
794
|
+
// Assert
|
|
795
|
+
await expect(ariaLoadingLabel).toBe(null);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
test('should set `aria-busy` to `false`', async ({ mount }) => {
|
|
799
|
+
// Arrange
|
|
800
|
+
const component = await mount(PieModal, {
|
|
801
|
+
props: {
|
|
802
|
+
isOpen: true,
|
|
803
|
+
isLoading: false,
|
|
804
|
+
},
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
// Loading state
|
|
808
|
+
const pieModalComponent = await component.locator(componentSelector);
|
|
809
|
+
const ariaLoadingBusy = await pieModalComponent.getAttribute('aria-busy');
|
|
810
|
+
|
|
811
|
+
// Assert
|
|
812
|
+
await expect(ariaLoadingBusy).toBe('false');
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
});
|
|
816
|
+
|
package/test/helpers/index.ts
CHANGED
|
@@ -22,8 +22,10 @@ export const renderTestPieModal = ({
|
|
|
22
22
|
// Creates some test page markup to test scroll locking
|
|
23
23
|
export const createScrollablePageHTML = () => `<div>
|
|
24
24
|
<h1>Test Page</h1>
|
|
25
|
+
<p>Top of page copy</p>
|
|
25
26
|
<p> Test copy </p>
|
|
26
27
|
<ol>
|
|
27
28
|
${'<li>List item</li>'.repeat(200)}
|
|
29
|
+
<li>Bottom of page copy</li>
|
|
28
30
|
</ol>
|
|
29
31
|
</div>`;
|