@happyvertical/smrt-ui 0.53.1 → 0.53.2
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/package.json +3 -3
- package/dist/actions/__tests__/ripple.test.js +0 -82
- package/dist/actions/__tests__/swipe-dismiss.test.js +0 -69
- package/dist/components/calendar/__tests__/CalendarView.test.js +0 -226
- package/dist/components/calendar/__tests__/calendar-model.test.js +0 -138
- package/dist/components/chat/__tests__/chat-primitives.test.js +0 -118
- package/dist/components/data/__tests__/CollectionList.test.js +0 -37
- package/dist/components/data/__tests__/DataTable.test.js +0 -807
- package/dist/components/data/__tests__/DataTableConformance.test.js +0 -212
- package/dist/components/data/__tests__/DataTableController.test.js +0 -371
- package/dist/components/data/__tests__/DataTableIdentity.test.js +0 -29
- package/dist/components/data/__tests__/DataTableLayout.test.js +0 -195
- package/dist/components/data/__tests__/DataTablePerformance.test.js +0 -21
- package/dist/components/data/__tests__/DataTableResponsive.test.js +0 -80
- package/dist/components/data/__tests__/DataTableVirtualization.test.js +0 -80
- package/dist/components/data/__tests__/DataTableVirtualizationComponent.test.js +0 -255
- package/dist/components/data/__tests__/SortableHeader.test.js +0 -101
- package/dist/components/data/__tests__/data-surface.test.js +0 -746
- package/dist/components/data/__tests__/data-table-helpers.test.js +0 -36
- package/dist/components/data/__tests__/data-table-surface.test.js +0 -49
- package/dist/components/data/__tests__/link-step-surface.test.js +0 -172
- package/dist/components/data/__tests__/list-sort-state.svelte.test.js +0 -71
- package/dist/components/data/__tests__/list-sort.test.js +0 -91
- package/dist/components/data/__tests__/mounted-data-surface.test.js +0 -794
- package/dist/components/data/__tests__/responsive-table.fixture.svelte +0 -29
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +0 -7
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +0 -1
- package/dist/components/data/__tests__/surface-navigation.test.js +0 -121
- package/dist/components/display/__tests__/ConfidenceBadge.test.js +0 -109
- package/dist/components/display/__tests__/CurrencyDisplay.test.js +0 -304
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte +0 -20
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts +0 -19
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts.map +0 -1
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte +0 -7
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts +0 -19
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts.map +0 -1
- package/dist/components/display/__tests__/DateDisplay.test.js +0 -195
- package/dist/components/display/__tests__/Icon.test.js +0 -223
- package/dist/components/display/__tests__/StatusBadge.test.js +0 -191
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +0 -181
- package/dist/components/feedback/__tests__/LoadingOverlay.test.js +0 -113
- package/dist/components/feedback/__tests__/Modal.test.js +0 -72
- package/dist/components/feedback/__tests__/PhoneSheet.test.js +0 -101
- package/dist/components/feedback/__tests__/ProgressBar.test.js +0 -89
- package/dist/components/feedback/__tests__/WorkingStrip.test.js +0 -172
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +0 -9
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +0 -19
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +0 -1
- package/dist/components/feedback/__tests__/toast.test.js +0 -190
- package/dist/components/forms/__tests__/CameraCapture.test.js +0 -504
- package/dist/components/forms/__tests__/Combobox.test.js +0 -354
- package/dist/components/forms/__tests__/ControlHitArea.test.js +0 -73
- package/dist/components/forms/__tests__/DictationButton.test.js +0 -176
- package/dist/components/forms/__tests__/Fieldset.test.js +0 -37
- package/dist/components/forms/__tests__/Form.test.js +0 -201
- package/dist/components/forms/__tests__/FormActionBar.test.js +0 -22
- package/dist/components/forms/__tests__/FormGroup.test.js +0 -48
- package/dist/components/forms/__tests__/Input.test.js +0 -49
- package/dist/components/forms/__tests__/Listbox.test.js +0 -303
- package/dist/components/forms/__tests__/MultiSelect.test.js +0 -281
- package/dist/components/forms/__tests__/SearchInput.test.js +0 -92
- package/dist/components/forms/__tests__/Select.test.js +0 -37
- package/dist/components/forms/__tests__/SignaturePad.test.js +0 -338
- package/dist/components/forms/__tests__/StagedControlReview.test.js +0 -702
- package/dist/components/forms/__tests__/Textarea.test.js +0 -39
- package/dist/components/forms/__tests__/Toggle.test.js +0 -87
- package/dist/components/forms/__tests__/camera-capture-session.test.js +0 -207
- package/dist/components/forms/__tests__/capture-form-retry.test.js +0 -166
- package/dist/components/forms/__tests__/capture-form.fixture.svelte +0 -38
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +0 -10
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/capture-test-env.d.ts +0 -70
- package/dist/components/forms/__tests__/capture-test-env.d.ts.map +0 -1
- package/dist/components/forms/__tests__/capture-test-env.js +0 -122
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte +0 -15
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +0 -14
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +0 -19
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +0 -11
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte +0 -47
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-controls.test.js +0 -128
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte +0 -41
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/control-interaction.test.js +0 -3421
- package/dist/components/forms/__tests__/control-proposals.test.js +0 -63
- package/dist/components/forms/__tests__/core-controls.fixture.svelte +0 -50
- package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/core-controls.test.js +0 -311
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte +0 -25
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/dictation-recording.test.js +0 -582
- package/dist/components/forms/__tests__/dictation.test.js +0 -486
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte +0 -28
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +0 -11
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte +0 -16
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +0 -31
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-retry-form.test.js +0 -107
- package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +0 -69
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte +0 -27
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/long-press.test.js +0 -175
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +0 -27
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/native-file-field.test.js +0 -177
- package/dist/components/forms/__tests__/place-picker.fixture.svelte +0 -33
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +0 -28
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/segmented-posting.test.js +0 -92
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte +0 -25
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/signature-pad-logic.test.js +0 -54
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte +0 -21
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte +0 -23
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/staged-review.fixture.svelte +0 -56
- package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/touch-density.fixture.svelte +0 -34
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/touch-density.test.js +0 -66
- package/dist/components/forms/form-retry/__tests__/boundaries.test.js +0 -23
- package/dist/components/forms/form-retry/__tests__/content-digest.test.js +0 -70
- package/dist/components/forms/form-retry/__tests__/controller.test.js +0 -802
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +0 -82
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +0 -1
- package/dist/components/forms/form-retry/__tests__/fake-kit.js +0 -180
- package/dist/components/forms/form-retry/__tests__/form-draft.test.js +0 -169
- package/dist/components/forms/form-retry/__tests__/ssr.test.js +0 -37
- package/dist/components/forms/form-retry/__tests__/storage.d.ts +0 -26
- package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +0 -1
- package/dist/components/forms/form-retry/__tests__/storage.js +0 -63
- package/dist/components/forms/form-retry/__tests__/submission-key.test.js +0 -236
- package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +0 -334
- package/dist/components/layout/__tests__/Container.test.js +0 -62
- package/dist/components/layout/__tests__/EmptyState.test.js +0 -83
- package/dist/components/layout/__tests__/Footer.test.js +0 -50
- package/dist/components/layout/__tests__/Grid.test.js +0 -121
- package/dist/components/layout/__tests__/Header.test.js +0 -48
- package/dist/components/layout/__tests__/Masthead.test.js +0 -93
- package/dist/components/layout/__tests__/PageHeader.test.js +0 -166
- package/dist/components/layout/__tests__/SummaryCard.test.js +0 -104
- package/dist/components/nav/__tests__/FilterChips.test.js +0 -94
- package/dist/components/nav/__tests__/LinkTabs.test.js +0 -117
- package/dist/components/nav/__tests__/Tabs.test.js +0 -128
- package/dist/components/roles/__tests__/RoleSelector.test.js +0 -134
- package/dist/components/ui/__tests__/Badge.test.js +0 -76
- package/dist/components/ui/__tests__/Button.test.js +0 -166
- package/dist/components/ui/__tests__/Card.test.js +0 -103
- package/dist/components/ui/__tests__/IconToggle.test.js +0 -59
- package/dist/components/ui/__tests__/IconToggleGroup.test.js +0 -30
- package/dist/components/ui/__tests__/Pagination.test.js +0 -113
- package/dist/components/ui/__tests__/Popover.test.js +0 -44
- package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +0 -125
- package/dist/components/ui/__tests__/gap-primitives-interactive.test.js +0 -112
- package/dist/components/ui/__tests__/gap-primitives.test.js +0 -84
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte +0 -13
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts +0 -19
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts.map +0 -1
- package/dist/components/ui/__tests__/interaction-primitives.test.js +0 -31
- package/dist/components/ui/__tests__/phone-touch-targets.test.js +0 -39
- package/dist/i18n/__tests__/i18n.test.js +0 -74
- package/dist/i18n/__tests__/render-parity.spec.js +0 -37
- package/dist/svelte/__tests__/playground.test.js +0 -20
- package/dist/themes/__tests__/control-boundary-contrast.test.js +0 -112
- package/dist/themes/__tests__/create-theme.test.js +0 -79
- package/dist/themes/__tests__/css-generator.test.js +0 -129
- package/dist/themes/__tests__/happyvertical-theme.test.js +0 -231
- package/dist/themes/__tests__/registry.test.js +0 -43
- package/dist/themes/__tests__/styles-lockstep.test.js +0 -32
- package/dist/themes/__tests__/theme-provider.test.js +0 -147
- package/dist/themes/__tests__/theme-script.test.js +0 -130
- package/dist/themes/__tests__/token-aliases.test.js +0 -178
- package/dist/utils/forms/__tests__/formatters.test.js +0 -27
- package/dist/utils/theme/__tests__/color.test.js +0 -89
- package/dist/utils/theme/__tests__/typography.test.js +0 -11
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/** Native confirmation rendering, actions, focus and instance identity. */
|
|
2
|
-
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
|
3
|
-
import userEvent from '@testing-library/user-event';
|
|
4
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
6
|
-
import ConfirmDialog from '../ConfirmDialog.svelte';
|
|
7
|
-
import RichMessage from './confirm-message.fixture.svelte';
|
|
8
|
-
const baseProps = {
|
|
9
|
-
open: true,
|
|
10
|
-
title: 'Delete item',
|
|
11
|
-
message: 'This action cannot be undone.',
|
|
12
|
-
};
|
|
13
|
-
describe('ConfirmDialog', () => {
|
|
14
|
-
it('renders a labelled dialog with title and message when open', () => {
|
|
15
|
-
render(ConfirmDialog, { props: baseProps });
|
|
16
|
-
const dialog = screen.getByRole('dialog');
|
|
17
|
-
expect(dialog).toHaveAttribute('aria-modal', 'true');
|
|
18
|
-
expect(screen.getByRole('heading', { name: 'Delete item' })).toBeInTheDocument();
|
|
19
|
-
expect(dialog).toHaveAccessibleName('Delete item');
|
|
20
|
-
expect(screen.getByText('This action cannot be undone.')).toBeInTheDocument();
|
|
21
|
-
});
|
|
22
|
-
it('uses native dialog and unique instance description ids', () => {
|
|
23
|
-
render(ConfirmDialog, { props: baseProps });
|
|
24
|
-
render(ConfirmDialog, {
|
|
25
|
-
props: { ...baseProps, title: 'Second', message: 'Other' },
|
|
26
|
-
});
|
|
27
|
-
const dialogs = screen.getAllByRole('dialog');
|
|
28
|
-
expect(dialogs.every((dialog) => dialog instanceof HTMLDialogElement)).toBe(true);
|
|
29
|
-
for (const attribute of ['aria-labelledby', 'aria-describedby']) {
|
|
30
|
-
const ids = dialogs.map((dialog) => dialog.getAttribute(attribute));
|
|
31
|
-
expect(new Set(ids).size).toBe(2);
|
|
32
|
-
for (let index = 0; index < dialogs.length; index++) {
|
|
33
|
-
expect(dialogs[index].contains(document.getElementById(ids[index]))).toBe(true);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
it('handles native cancel without closing outside controlled state', async () => {
|
|
38
|
-
const oncancel = vi.fn();
|
|
39
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
40
|
-
const dialog = screen.getByRole('dialog');
|
|
41
|
-
const event = new Event('cancel', { cancelable: true });
|
|
42
|
-
await fireEvent(dialog, event);
|
|
43
|
-
expect(event.defaultPrevented).toBe(true);
|
|
44
|
-
expect(oncancel).toHaveBeenCalledTimes(1);
|
|
45
|
-
expect(dialog).toHaveAttribute('open');
|
|
46
|
-
});
|
|
47
|
-
it('renders a snippet message as accessible rich content', () => {
|
|
48
|
-
render(RichMessage);
|
|
49
|
-
const dialog = screen.getByRole('dialog', { name: 'Rich confirmation' });
|
|
50
|
-
expect(dialog.querySelector('strong')).toHaveTextContent('Record name');
|
|
51
|
-
expect(screen.getAllByRole('listitem')).toHaveLength(2);
|
|
52
|
-
expect(dialog).toHaveAccessibleDescription('Remove Record name? Notes Attachments');
|
|
53
|
-
});
|
|
54
|
-
it('renders nothing when closed', () => {
|
|
55
|
-
render(ConfirmDialog, { props: { ...baseProps, open: false } });
|
|
56
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
it('uses default Confirm / Cancel button labels', () => {
|
|
59
|
-
render(ConfirmDialog, { props: baseProps });
|
|
60
|
-
expect(screen.getByRole('button', { name: 'Confirm' })).toBeInTheDocument();
|
|
61
|
-
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
|
|
62
|
-
});
|
|
63
|
-
it('honors custom confirm/cancel labels', () => {
|
|
64
|
-
render(ConfirmDialog, {
|
|
65
|
-
props: { ...baseProps, confirmLabel: 'Delete', cancelLabel: 'Keep' },
|
|
66
|
-
});
|
|
67
|
-
expect(screen.getByRole('button', { name: 'Delete' })).toBeInTheDocument();
|
|
68
|
-
expect(screen.getByRole('button', { name: 'Keep' })).toBeInTheDocument();
|
|
69
|
-
});
|
|
70
|
-
it('fires onconfirm when the confirm button is clicked', async () => {
|
|
71
|
-
const onconfirm = vi.fn();
|
|
72
|
-
render(ConfirmDialog, { props: { ...baseProps, onconfirm } });
|
|
73
|
-
await userEvent.click(screen.getByRole('button', { name: 'Confirm' }));
|
|
74
|
-
expect(onconfirm).toHaveBeenCalledTimes(1);
|
|
75
|
-
});
|
|
76
|
-
it('fires oncancel when the cancel button is clicked', async () => {
|
|
77
|
-
const oncancel = vi.fn();
|
|
78
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
79
|
-
await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
80
|
-
expect(oncancel).toHaveBeenCalledTimes(1);
|
|
81
|
-
});
|
|
82
|
-
it('fires oncancel when Escape is pressed', async () => {
|
|
83
|
-
const oncancel = vi.fn();
|
|
84
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
85
|
-
screen.getByRole('dialog').focus();
|
|
86
|
-
await userEvent.keyboard('{Escape}');
|
|
87
|
-
expect(oncancel).toHaveBeenCalledTimes(1);
|
|
88
|
-
});
|
|
89
|
-
it('fires oncancel when the backdrop is clicked', async () => {
|
|
90
|
-
const oncancel = vi.fn();
|
|
91
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
92
|
-
await userEvent.click(screen.getByRole('dialog'));
|
|
93
|
-
expect(oncancel).toHaveBeenCalledTimes(1);
|
|
94
|
-
});
|
|
95
|
-
it('does not fire oncancel when clicking inside the dialog content', async () => {
|
|
96
|
-
const oncancel = vi.fn();
|
|
97
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
98
|
-
await userEvent.click(screen.getByRole('heading', { name: 'Delete item' }));
|
|
99
|
-
expect(oncancel).not.toHaveBeenCalled();
|
|
100
|
-
});
|
|
101
|
-
describe('focus management (#1586)', () => {
|
|
102
|
-
it('moves focus to the confirm button when opened', async () => {
|
|
103
|
-
render(ConfirmDialog, { props: baseProps });
|
|
104
|
-
await waitFor(() => expect(screen.getByRole('button', { name: 'Confirm' })).toHaveFocus());
|
|
105
|
-
});
|
|
106
|
-
it('traps Tab focus within the dialog', async () => {
|
|
107
|
-
render(ConfirmDialog, { props: baseProps });
|
|
108
|
-
const confirm = screen.getByRole('button', { name: 'Confirm' });
|
|
109
|
-
const cancel = screen.getByRole('button', { name: 'Cancel' });
|
|
110
|
-
await waitFor(() => expect(confirm).toHaveFocus());
|
|
111
|
-
// Tab from the last focusable (confirm) wraps to the first (cancel).
|
|
112
|
-
await userEvent.tab();
|
|
113
|
-
expect(cancel).toHaveFocus();
|
|
114
|
-
// Shift+Tab from the first focusable (cancel) wraps back to confirm.
|
|
115
|
-
await userEvent.tab({ shift: true });
|
|
116
|
-
expect(confirm).toHaveFocus();
|
|
117
|
-
});
|
|
118
|
-
it('restores focus to the opener when closed', async () => {
|
|
119
|
-
// An external trigger holds focus before the dialog opens.
|
|
120
|
-
const opener = document.createElement('button');
|
|
121
|
-
opener.textContent = 'Open';
|
|
122
|
-
document.body.appendChild(opener);
|
|
123
|
-
opener.focus();
|
|
124
|
-
expect(opener).toHaveFocus();
|
|
125
|
-
const { rerender } = render(ConfirmDialog, { props: baseProps });
|
|
126
|
-
await waitFor(() => expect(screen.getByRole('button', { name: 'Confirm' })).toHaveFocus());
|
|
127
|
-
await rerender({ ...baseProps, open: false });
|
|
128
|
-
expect(opener).toHaveFocus();
|
|
129
|
-
opener.remove();
|
|
130
|
-
});
|
|
131
|
-
it('does not consume Escape from outside its dialog', async () => {
|
|
132
|
-
const oncancel = vi.fn();
|
|
133
|
-
render(ConfirmDialog, { props: { ...baseProps, oncancel } });
|
|
134
|
-
await fireEvent.keyDown(window, { key: 'Escape' });
|
|
135
|
-
expect(oncancel).not.toHaveBeenCalled();
|
|
136
|
-
});
|
|
137
|
-
it('focuses the dialog container when all controls are disabled (loading)', async () => {
|
|
138
|
-
// Both buttons are disabled while loading, so there is no focusable
|
|
139
|
-
// control; focus falls back to the dialog container (tabindex=-1) to stay
|
|
140
|
-
// contained and keep Escape reachable.
|
|
141
|
-
render(ConfirmDialog, { props: { ...baseProps, loading: true } });
|
|
142
|
-
await waitFor(() => expect(screen.getByRole('dialog')).toHaveFocus());
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
it('disables both buttons while loading', () => {
|
|
146
|
-
render(ConfirmDialog, { props: { ...baseProps, loading: true } });
|
|
147
|
-
expect(screen.getByRole('button', { name: 'Confirm' })).toBeDisabled();
|
|
148
|
-
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled();
|
|
149
|
-
});
|
|
150
|
-
it('does not fire callbacks from disabled buttons while loading', async () => {
|
|
151
|
-
const onconfirm = vi.fn();
|
|
152
|
-
const oncancel = vi.fn();
|
|
153
|
-
render(ConfirmDialog, {
|
|
154
|
-
props: { ...baseProps, loading: true, onconfirm, oncancel },
|
|
155
|
-
});
|
|
156
|
-
await userEvent.click(screen.getByRole('button', { name: 'Confirm' }));
|
|
157
|
-
await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
158
|
-
expect(onconfirm).not.toHaveBeenCalled();
|
|
159
|
-
expect(oncancel).not.toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
it('applies destructive styling to the confirm button', () => {
|
|
162
|
-
render(ConfirmDialog, {
|
|
163
|
-
props: { ...baseProps, destructive: true, confirmLabel: 'Delete' },
|
|
164
|
-
});
|
|
165
|
-
expect(screen.getByRole('button', { name: 'Delete' })).toHaveClass('destructive');
|
|
166
|
-
});
|
|
167
|
-
it('omits destructive styling by default', () => {
|
|
168
|
-
render(ConfirmDialog, { props: baseProps });
|
|
169
|
-
expect(screen.getByRole('button', { name: 'Confirm' })).not.toHaveClass('destructive');
|
|
170
|
-
});
|
|
171
|
-
it('is axe-clean while open', async () => {
|
|
172
|
-
const { container } = render(ConfirmDialog, { props: baseProps });
|
|
173
|
-
await expectNoA11yViolations(container);
|
|
174
|
-
});
|
|
175
|
-
it('is axe-clean in the destructive loading state', async () => {
|
|
176
|
-
const { container } = render(ConfirmDialog, {
|
|
177
|
-
props: { ...baseProps, destructive: true, loading: true },
|
|
178
|
-
});
|
|
179
|
-
await expectNoA11yViolations(container);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Golden test for LoadingOverlay (Sweep S11, #1416).
|
|
3
|
-
*
|
|
4
|
-
* LoadingOverlay renders a full-screen `role="dialog"` busy surface only while
|
|
5
|
-
* `show` is true and not dismissed. It carries `aria-busy` (true unless an
|
|
6
|
-
* error is present), an `aria-labelledby` title from `message`, optional
|
|
7
|
-
* progress/items, an error message, and a dismiss button when `dismissible`.
|
|
8
|
-
*/
|
|
9
|
-
import { render, screen } from '@testing-library/svelte';
|
|
10
|
-
import userEvent from '@testing-library/user-event';
|
|
11
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
13
|
-
import LoadingOverlay from '../LoadingOverlay.svelte';
|
|
14
|
-
describe('LoadingOverlay', () => {
|
|
15
|
-
it('renders nothing when not shown', () => {
|
|
16
|
-
render(LoadingOverlay, { props: { show: false } });
|
|
17
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
18
|
-
});
|
|
19
|
-
it('renders a busy dialog labelled by its message when shown', () => {
|
|
20
|
-
render(LoadingOverlay, {
|
|
21
|
-
props: { show: true, message: 'Importing data' },
|
|
22
|
-
});
|
|
23
|
-
const dialog = screen.getByRole('dialog');
|
|
24
|
-
expect(dialog).toHaveAttribute('aria-busy', 'true');
|
|
25
|
-
expect(dialog).toHaveAccessibleName('Importing data');
|
|
26
|
-
expect(screen.getByRole('heading', { name: 'Importing data' })).toBeInTheDocument();
|
|
27
|
-
});
|
|
28
|
-
it('uses a default message when none is provided', () => {
|
|
29
|
-
render(LoadingOverlay, { props: { show: true } });
|
|
30
|
-
expect(screen.getByRole('heading', { name: 'Loading...' })).toBeInTheDocument();
|
|
31
|
-
});
|
|
32
|
-
it('shows a progress percentage when progress > 0', () => {
|
|
33
|
-
render(LoadingOverlay, { props: { show: true, progress: 60 } });
|
|
34
|
-
expect(screen.getByText('60%')).toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
it('omits the progress readout when progress is 0', () => {
|
|
37
|
-
render(LoadingOverlay, { props: { show: true, progress: 0 } });
|
|
38
|
-
expect(screen.queryByText('0%')).toBeNull();
|
|
39
|
-
});
|
|
40
|
-
it('renders completed items', () => {
|
|
41
|
-
render(LoadingOverlay, {
|
|
42
|
-
props: { show: true, items: ['Profiles', 'Assets'] },
|
|
43
|
-
});
|
|
44
|
-
expect(screen.getByText('Profiles')).toBeInTheDocument();
|
|
45
|
-
expect(screen.getByText('Assets')).toBeInTheDocument();
|
|
46
|
-
});
|
|
47
|
-
it('clears aria-busy and shows the error message in the error state', () => {
|
|
48
|
-
render(LoadingOverlay, {
|
|
49
|
-
props: { show: true, error: { message: 'Import failed' } },
|
|
50
|
-
});
|
|
51
|
-
const dialog = screen.getByRole('dialog');
|
|
52
|
-
expect(dialog).toHaveAttribute('aria-busy', 'false');
|
|
53
|
-
expect(screen.getByText('Import failed')).toBeInTheDocument();
|
|
54
|
-
});
|
|
55
|
-
it('announces the error via an assertive alert region (#1586)', () => {
|
|
56
|
-
// aria-busy flips to false on error, so without a live region the failure
|
|
57
|
-
// would change visually only and never reach a screen reader.
|
|
58
|
-
render(LoadingOverlay, {
|
|
59
|
-
props: { show: true, error: { message: 'Import failed' } },
|
|
60
|
-
});
|
|
61
|
-
const alert = screen.getByRole('alert');
|
|
62
|
-
expect(alert).toHaveTextContent('Import failed');
|
|
63
|
-
expect(alert).toHaveAttribute('aria-live', 'assertive');
|
|
64
|
-
});
|
|
65
|
-
it('exposes no alert region while loading without an error (#1586)', () => {
|
|
66
|
-
render(LoadingOverlay, { props: { show: true, message: 'Working' } });
|
|
67
|
-
expect(screen.queryByRole('alert')).toBeNull();
|
|
68
|
-
});
|
|
69
|
-
it('omits the dismiss button unless dismissible', () => {
|
|
70
|
-
render(LoadingOverlay, { props: { show: true } });
|
|
71
|
-
expect(screen.queryByRole('button', { name: 'Continue' })).toBeNull();
|
|
72
|
-
});
|
|
73
|
-
it('fires ondismiss and hides the overlay when the dismiss button is clicked', async () => {
|
|
74
|
-
const ondismiss = vi.fn();
|
|
75
|
-
render(LoadingOverlay, {
|
|
76
|
-
props: { show: true, dismissible: true, ondismiss },
|
|
77
|
-
});
|
|
78
|
-
await userEvent.click(screen.getByRole('button', { name: 'Continue' }));
|
|
79
|
-
expect(ondismiss).toHaveBeenCalledTimes(1);
|
|
80
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
it('dismisses via the Escape key when dismissible', async () => {
|
|
83
|
-
const ondismiss = vi.fn();
|
|
84
|
-
render(LoadingOverlay, {
|
|
85
|
-
props: { show: true, dismissible: true, ondismiss },
|
|
86
|
-
});
|
|
87
|
-
await userEvent.keyboard('{Escape}');
|
|
88
|
-
expect(ondismiss).toHaveBeenCalledTimes(1);
|
|
89
|
-
expect(screen.queryByRole('dialog')).toBeNull();
|
|
90
|
-
});
|
|
91
|
-
it('ignores Escape when not dismissible', async () => {
|
|
92
|
-
render(LoadingOverlay, { props: { show: true, dismissible: false } });
|
|
93
|
-
await userEvent.keyboard('{Escape}');
|
|
94
|
-
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
95
|
-
});
|
|
96
|
-
it('is axe-clean in the loading state', async () => {
|
|
97
|
-
const { container } = render(LoadingOverlay, {
|
|
98
|
-
props: { show: true, message: 'Loading your workspace', progress: 45 },
|
|
99
|
-
});
|
|
100
|
-
await expectNoA11yViolations(container);
|
|
101
|
-
});
|
|
102
|
-
it('is axe-clean with completed items and a dismiss action', async () => {
|
|
103
|
-
const { container } = render(LoadingOverlay, {
|
|
104
|
-
props: {
|
|
105
|
-
show: true,
|
|
106
|
-
message: 'Almost done',
|
|
107
|
-
items: ['Step one'],
|
|
108
|
-
dismissible: true,
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
await expectNoA11yViolations(container);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Golden test for Modal (Sweep L4, #1423).
|
|
3
|
-
*
|
|
4
|
-
* Modal wraps a native <dialog>; jsdom implements showModal()/close(), so the
|
|
5
|
-
* open/close effect runs. Content is always in the DOM — visibility is driven
|
|
6
|
-
* by the dialog's modal state.
|
|
7
|
-
*/
|
|
8
|
-
import { render, screen } from '@testing-library/svelte';
|
|
9
|
-
import userEvent from '@testing-library/user-event';
|
|
10
|
-
import { createRawSnippet } from 'svelte';
|
|
11
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
13
|
-
import Modal from '../Modal.svelte';
|
|
14
|
-
function bodySnippet(text) {
|
|
15
|
-
return createRawSnippet(() => ({ render: () => `<p>${text}</p>` }));
|
|
16
|
-
}
|
|
17
|
-
describe('Modal', () => {
|
|
18
|
-
it('exposes a dialog labelled by its title, with content', () => {
|
|
19
|
-
render(Modal, {
|
|
20
|
-
props: {
|
|
21
|
-
open: true,
|
|
22
|
-
title: 'Settings',
|
|
23
|
-
children: bodySnippet('Body copy'),
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
const dialog = screen.getByRole('dialog', { hidden: true });
|
|
27
|
-
expect(dialog).toHaveAttribute('aria-label', 'Settings');
|
|
28
|
-
expect(screen.getByRole('heading', { name: 'Settings', hidden: true })).toBeInTheDocument();
|
|
29
|
-
expect(screen.getByText('Body copy')).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
it('invokes onClose from the close button', async () => {
|
|
32
|
-
const onClose = vi.fn();
|
|
33
|
-
render(Modal, { props: { open: true, title: 'X', onClose } });
|
|
34
|
-
await userEvent.click(screen.getByRole('button', { name: 'Close modal', hidden: true }));
|
|
35
|
-
expect(onClose).toHaveBeenCalledTimes(1);
|
|
36
|
-
});
|
|
37
|
-
it('omits the close button when showClose is false', () => {
|
|
38
|
-
render(Modal, { props: { open: true, title: 'X', showClose: false } });
|
|
39
|
-
expect(screen.queryByRole('button', { name: 'Close modal', hidden: true })).toBeNull();
|
|
40
|
-
});
|
|
41
|
-
it('defaults to centered placement', () => {
|
|
42
|
-
render(Modal, { props: { open: true, title: 'X' } });
|
|
43
|
-
expect(screen.getByRole('dialog', { hidden: true })).toHaveClass('modal--center');
|
|
44
|
-
});
|
|
45
|
-
it('applies the end (drawer) placement variant', () => {
|
|
46
|
-
render(Modal, { props: { open: true, title: 'X', placement: 'end' } });
|
|
47
|
-
const dialog = screen.getByRole('dialog', { hidden: true });
|
|
48
|
-
expect(dialog).toHaveClass('modal--end');
|
|
49
|
-
expect(dialog).not.toHaveClass('modal--center');
|
|
50
|
-
});
|
|
51
|
-
it('is axe-clean', async () => {
|
|
52
|
-
const { container } = render(Modal, {
|
|
53
|
-
props: {
|
|
54
|
-
open: true,
|
|
55
|
-
title: 'Accessible dialog',
|
|
56
|
-
children: bodySnippet('content'),
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
await expectNoA11yViolations(container);
|
|
60
|
-
});
|
|
61
|
-
it('is axe-clean with end placement', async () => {
|
|
62
|
-
const { container } = render(Modal, {
|
|
63
|
-
props: {
|
|
64
|
-
open: true,
|
|
65
|
-
title: 'Detail drawer',
|
|
66
|
-
placement: 'end',
|
|
67
|
-
children: bodySnippet('content'),
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
await expectNoA11yViolations(container);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PhoneSheet: a non-modal phone surface (full-screen page or bottom sheet)
|
|
3
|
-
* that stays mounted while closed, closes on its button / Escape / swipe
|
|
4
|
-
* down, and manages focus.
|
|
5
|
-
*/
|
|
6
|
-
import { render, screen } from '@testing-library/svelte';
|
|
7
|
-
import userEvent from '@testing-library/user-event';
|
|
8
|
-
import { createRawSnippet, flushSync, tick } from 'svelte';
|
|
9
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
10
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
11
|
-
import PhoneSheet from '../PhoneSheet.svelte';
|
|
12
|
-
const body = createRawSnippet(() => ({
|
|
13
|
-
render: () => '<p>Sheet body</p>',
|
|
14
|
-
}));
|
|
15
|
-
describe('PhoneSheet', () => {
|
|
16
|
-
it('renders a labelled dialog with a named close button when open', async () => {
|
|
17
|
-
const { container } = render(PhoneSheet, {
|
|
18
|
-
props: {
|
|
19
|
-
open: true,
|
|
20
|
-
title: 'Notifications',
|
|
21
|
-
onclose: () => { },
|
|
22
|
-
children: body,
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
const dialog = screen.getByRole('dialog', { name: 'Notifications' });
|
|
26
|
-
expect(dialog).toHaveAttribute('aria-hidden', 'false');
|
|
27
|
-
expect(screen.getByRole('button', { name: 'Close Notifications' })).toBeInTheDocument();
|
|
28
|
-
expect(screen.getByText('Sheet body')).toBeInTheDocument();
|
|
29
|
-
await expectNoA11yViolations(container);
|
|
30
|
-
});
|
|
31
|
-
it('stays mounted but hidden and inert while closed', () => {
|
|
32
|
-
render(PhoneSheet, {
|
|
33
|
-
props: {
|
|
34
|
-
open: false,
|
|
35
|
-
title: 'Assistant',
|
|
36
|
-
onclose: () => { },
|
|
37
|
-
children: body,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
const sheet = screen.getByTestId('phone-sheet');
|
|
41
|
-
expect(sheet).toHaveAttribute('aria-hidden', 'true');
|
|
42
|
-
// jsdom has no `inert` IDL attribute, so Svelte sets it as a property.
|
|
43
|
-
expect(sheet.hasAttribute('inert') ||
|
|
44
|
-
sheet.inert).toBe(true);
|
|
45
|
-
expect(sheet.classList.contains('open')).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
it('closes on the close button and on Escape', async () => {
|
|
48
|
-
const onclose = vi.fn();
|
|
49
|
-
render(PhoneSheet, {
|
|
50
|
-
props: {
|
|
51
|
-
open: true,
|
|
52
|
-
title: 'Assistant',
|
|
53
|
-
onclose,
|
|
54
|
-
closeLabel: 'Close the assistant',
|
|
55
|
-
children: body,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
await userEvent.click(screen.getByRole('button', { name: 'Close the assistant' }));
|
|
59
|
-
expect(onclose).toHaveBeenCalledTimes(1);
|
|
60
|
-
screen.getByRole('dialog').dispatchEvent(new KeyboardEvent('keydown', {
|
|
61
|
-
key: 'Escape',
|
|
62
|
-
bubbles: true,
|
|
63
|
-
cancelable: true,
|
|
64
|
-
}));
|
|
65
|
-
expect(onclose).toHaveBeenCalledTimes(2);
|
|
66
|
-
});
|
|
67
|
-
it('moves focus in on open and back to the opener on close', async () => {
|
|
68
|
-
const opener = document.createElement('button');
|
|
69
|
-
document.body.appendChild(opener);
|
|
70
|
-
opener.focus();
|
|
71
|
-
const { rerender } = render(PhoneSheet, {
|
|
72
|
-
props: {
|
|
73
|
-
open: false,
|
|
74
|
-
title: 'Assistant',
|
|
75
|
-
onclose: () => { },
|
|
76
|
-
children: body,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
await rerender({ open: true });
|
|
80
|
-
await tick();
|
|
81
|
-
await tick();
|
|
82
|
-
expect(document.activeElement).toBe(screen.getByTestId('phone-sheet'));
|
|
83
|
-
await rerender({ open: false });
|
|
84
|
-
flushSync();
|
|
85
|
-
expect(document.activeElement).toBe(opener);
|
|
86
|
-
opener.remove();
|
|
87
|
-
});
|
|
88
|
-
it('draws a grabber only for the sheet variant', () => {
|
|
89
|
-
const { container } = render(PhoneSheet, {
|
|
90
|
-
props: {
|
|
91
|
-
open: true,
|
|
92
|
-
title: 'Assistant',
|
|
93
|
-
variant: 'sheet',
|
|
94
|
-
onclose: () => { },
|
|
95
|
-
children: body,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
expect(container.querySelector('.phone-sheet__grabber')).not.toBeNull();
|
|
99
|
-
expect(container.querySelector('.phone-sheet--sheet')).not.toBeNull();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Golden test for ProgressBar (Sweep S11, #1416).
|
|
3
|
-
*
|
|
4
|
-
* ProgressBar renders a `role="progressbar"` track carrying
|
|
5
|
-
* `aria-valuenow`/`aria-valuemin`/`aria-valuemax` from its `value`/`max` props.
|
|
6
|
-
* An optional header shows a percentage, a value/max pair, or a custom label,
|
|
7
|
-
* plus an "over by" badge when `value` exceeds `max`.
|
|
8
|
-
*/
|
|
9
|
-
import { render, screen } from '@testing-library/svelte';
|
|
10
|
-
import { describe, expect, it } from 'vitest';
|
|
11
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
12
|
-
import ProgressBar from '../ProgressBar.svelte';
|
|
13
|
-
describe('ProgressBar', () => {
|
|
14
|
-
it('gives the progressbar an accessible name (default and custom label)', async () => {
|
|
15
|
-
const { rerender } = render(ProgressBar, { props: { value: 40 } });
|
|
16
|
-
// Default accessible name comes from the i18n'd "Progress" label.
|
|
17
|
-
expect(screen.getByRole('progressbar', { name: 'Progress' })).toBeInTheDocument();
|
|
18
|
-
// rerender resolves the prop update asynchronously — await before querying.
|
|
19
|
-
await rerender({ value: 40, label: 'Upload' });
|
|
20
|
-
expect(screen.getByRole('progressbar', { name: 'Upload' })).toBeInTheDocument();
|
|
21
|
-
});
|
|
22
|
-
it('falls back to the default name when label is empty or whitespace', () => {
|
|
23
|
-
render(ProgressBar, { props: { value: 40, label: ' ' } });
|
|
24
|
-
expect(screen.getByRole('progressbar', { name: 'Progress' })).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
it('exposes progressbar role with aria-value attributes', () => {
|
|
27
|
-
render(ProgressBar, { props: { value: 40 } });
|
|
28
|
-
const bar = screen.getByRole('progressbar');
|
|
29
|
-
expect(bar).toHaveAttribute('aria-valuenow', '40');
|
|
30
|
-
expect(bar).toHaveAttribute('aria-valuemin', '0');
|
|
31
|
-
expect(bar).toHaveAttribute('aria-valuemax', '100');
|
|
32
|
-
});
|
|
33
|
-
it('reflects a custom max in aria-valuemax', () => {
|
|
34
|
-
render(ProgressBar, { props: { value: 30, max: 200 } });
|
|
35
|
-
const bar = screen.getByRole('progressbar');
|
|
36
|
-
expect(bar).toHaveAttribute('aria-valuenow', '30');
|
|
37
|
-
expect(bar).toHaveAttribute('aria-valuemax', '200');
|
|
38
|
-
});
|
|
39
|
-
it('does not render a label by default', () => {
|
|
40
|
-
render(ProgressBar, { props: { value: 25 } });
|
|
41
|
-
expect(screen.queryByText('25%')).toBeNull();
|
|
42
|
-
});
|
|
43
|
-
it('shows a rounded percentage label when showLabel is set', () => {
|
|
44
|
-
render(ProgressBar, { props: { value: 33, showLabel: true } });
|
|
45
|
-
expect(screen.getByText('33%')).toBeInTheDocument();
|
|
46
|
-
});
|
|
47
|
-
it('shows a value / max label when showValue is set', () => {
|
|
48
|
-
render(ProgressBar, { props: { value: 75, max: 100, showValue: true } });
|
|
49
|
-
expect(screen.getByText('75 / 100')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
it('prefers a custom label over percentage and value', () => {
|
|
52
|
-
render(ProgressBar, {
|
|
53
|
-
props: { value: 50, showLabel: true, label: 'Almost there' },
|
|
54
|
-
});
|
|
55
|
-
expect(screen.getByText('Almost there')).toBeInTheDocument();
|
|
56
|
-
expect(screen.queryByText('50%')).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
it('caps aria-valuenow display semantics but reports the raw over-budget value', () => {
|
|
59
|
-
render(ProgressBar, { props: { value: 120, max: 100, showLabel: true } });
|
|
60
|
-
const bar = screen.getByRole('progressbar');
|
|
61
|
-
expect(bar).toHaveAttribute('aria-valuenow', '120');
|
|
62
|
-
expect(bar).toHaveAttribute('aria-valuemax', '100');
|
|
63
|
-
expect(screen.getByText('Over by 20')).toBeInTheDocument();
|
|
64
|
-
});
|
|
65
|
-
it('is axe-clean at 0%', async () => {
|
|
66
|
-
const { container } = render(ProgressBar, {
|
|
67
|
-
props: { value: 0, showLabel: true },
|
|
68
|
-
});
|
|
69
|
-
await expectNoA11yViolations(container);
|
|
70
|
-
});
|
|
71
|
-
it('is axe-clean mid-progress', async () => {
|
|
72
|
-
const { container } = render(ProgressBar, {
|
|
73
|
-
props: { value: 50, showLabel: true },
|
|
74
|
-
});
|
|
75
|
-
await expectNoA11yViolations(container);
|
|
76
|
-
});
|
|
77
|
-
it('is axe-clean at 100%', async () => {
|
|
78
|
-
const { container } = render(ProgressBar, {
|
|
79
|
-
props: { value: 100, showLabel: true },
|
|
80
|
-
});
|
|
81
|
-
await expectNoA11yViolations(container);
|
|
82
|
-
});
|
|
83
|
-
it('is axe-clean in the over-budget state', async () => {
|
|
84
|
-
const { container } = render(ProgressBar, {
|
|
85
|
-
props: { value: 130, max: 100, showValue: true },
|
|
86
|
-
});
|
|
87
|
-
await expectNoA11yViolations(container);
|
|
88
|
-
});
|
|
89
|
-
});
|