@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,281 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/svelte';
|
|
2
|
-
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import { tick } from 'svelte';
|
|
4
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { createControlInteractionRegistry, executeLocalControlCommand, } from '../control-interaction.js';
|
|
6
|
-
import MultiSelect from '../MultiSelect.svelte';
|
|
7
|
-
import Fixture from './multiselect-form.fixture.svelte';
|
|
8
|
-
function form() {
|
|
9
|
-
return screen.getByRole('form', { name: 'Roles form' });
|
|
10
|
-
}
|
|
11
|
-
function data() {
|
|
12
|
-
return new FormData(form());
|
|
13
|
-
}
|
|
14
|
-
async function open() {
|
|
15
|
-
await userEvent.click(screen.getByRole('button', { name: /Roles/ }));
|
|
16
|
-
}
|
|
17
|
-
describe('MultiSelect native form values', () => {
|
|
18
|
-
it('posts repeated values in selection order and removes deselected inputs', async () => {
|
|
19
|
-
render(Fixture);
|
|
20
|
-
await open();
|
|
21
|
-
await userEvent.click(screen.getByRole('option', { name: 'Administrator' }));
|
|
22
|
-
await userEvent.click(screen.getByRole('option', { name: 'Operator' }));
|
|
23
|
-
expect(data().getAll('roles')).toEqual(['admin', '42']);
|
|
24
|
-
await userEvent.click(screen.getByRole('option', { name: 'Administrator' }));
|
|
25
|
-
expect(data().getAll('roles')).toEqual(['42']);
|
|
26
|
-
await userEvent.click(screen.getByRole('option', { name: 'Disabled role' }));
|
|
27
|
-
expect(data().getAll('roles')).toEqual(['42']);
|
|
28
|
-
});
|
|
29
|
-
it('posts canonical numeric selections from legacy strings', () => {
|
|
30
|
-
render(Fixture, { props: { values: ['42'] } });
|
|
31
|
-
expect(data().getAll('roles')).toEqual(['42']);
|
|
32
|
-
});
|
|
33
|
-
it('omits empty selections, empty names and unnamed controls', async () => {
|
|
34
|
-
const { rerender } = render(Fixture);
|
|
35
|
-
expect([...data()]).toEqual([]);
|
|
36
|
-
await rerender({ values: [42], name: '' });
|
|
37
|
-
expect([...data()]).toEqual([]);
|
|
38
|
-
await rerender({ values: [42], unnamed: true });
|
|
39
|
-
expect([...data()]).toEqual([]);
|
|
40
|
-
});
|
|
41
|
-
it.each([
|
|
42
|
-
{ disabled: true },
|
|
43
|
-
{ fieldsetDisabled: true },
|
|
44
|
-
])('omits disabled selections %j', (props) => {
|
|
45
|
-
render(Fixture, { props: { ...props, values: [42, 'admin'] } });
|
|
46
|
-
expect([...data()]).toEqual([]);
|
|
47
|
-
});
|
|
48
|
-
it('restores initial selected values on native reset', async () => {
|
|
49
|
-
render(Fixture, { props: { values: [42] } });
|
|
50
|
-
await open();
|
|
51
|
-
await userEvent.click(screen.getByRole('option', { name: 'Administrator' }));
|
|
52
|
-
form().reset();
|
|
53
|
-
await settleReset();
|
|
54
|
-
expect(data().getAll('roles')).toEqual(['42']);
|
|
55
|
-
await open();
|
|
56
|
-
expect(screen.getByRole('option', { name: 'Operator' })).toHaveAttribute('aria-selected', 'true');
|
|
57
|
-
expect(screen.getByRole('option', { name: 'Administrator' })).toHaveAttribute('aria-selected', 'false');
|
|
58
|
-
});
|
|
59
|
-
it('preserves changed values when reset is canceled', async () => {
|
|
60
|
-
render(Fixture);
|
|
61
|
-
await open();
|
|
62
|
-
await userEvent.click(screen.getByRole('option', { name: 'Operator' }));
|
|
63
|
-
form().addEventListener('reset', (event) => event.preventDefault());
|
|
64
|
-
form().reset();
|
|
65
|
-
await settleReset();
|
|
66
|
-
expect(data().getAll('roles')).toEqual(['42']);
|
|
67
|
-
});
|
|
68
|
-
it('preserves registry identity and posts only applied canonical values', async () => {
|
|
69
|
-
const registry = createControlInteractionRegistry({
|
|
70
|
-
isLocalGesture: () => true,
|
|
71
|
-
});
|
|
72
|
-
render(Fixture, { props: { registry } });
|
|
73
|
-
const identity = { formId: 'roles-form', controlId: 'roles' };
|
|
74
|
-
await registry.execute({ action: 'stage', identity, value: ['42', 'admin'] }, { source: 'agent' });
|
|
75
|
-
expect([...data()]).toEqual([]);
|
|
76
|
-
await userEvent.click(screen.getByRole('button', { name: 'Apply valid changes' }));
|
|
77
|
-
expect(data().getAll('roles')).toEqual(['42', 'admin']);
|
|
78
|
-
expect(registry.get(identity)?.state.value).toEqual([42, 'admin']);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
async function settleReset() {
|
|
82
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
83
|
-
await tick();
|
|
84
|
-
}
|
|
85
|
-
describe('MultiSelect deferred native reset', () => {
|
|
86
|
-
it('preserves a newer programmatic value before reset completion', async () => {
|
|
87
|
-
const target = document.createElement('form');
|
|
88
|
-
document.body.append(target);
|
|
89
|
-
const view = render(MultiSelect, {
|
|
90
|
-
target,
|
|
91
|
-
props: {
|
|
92
|
-
options: [
|
|
93
|
-
{ value: 42, label: 'Initial' },
|
|
94
|
-
{ value: 'other', label: 'Other' },
|
|
95
|
-
{ value: 'changed', label: 'Changed' },
|
|
96
|
-
],
|
|
97
|
-
label: 'Choice',
|
|
98
|
-
name: 'choice',
|
|
99
|
-
values: [42],
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
await view.rerender({ values: ['changed'] });
|
|
103
|
-
target.reset();
|
|
104
|
-
await view.rerender({ values: ['other'] });
|
|
105
|
-
await settleReset();
|
|
106
|
-
expect(new FormData(target).getAll('choice')).toEqual(['other']);
|
|
107
|
-
await view.unmount();
|
|
108
|
-
target.remove();
|
|
109
|
-
});
|
|
110
|
-
it('cancels pending completion when unmounted', async () => {
|
|
111
|
-
const target = document.createElement('form');
|
|
112
|
-
document.body.append(target);
|
|
113
|
-
const onChange = vi.fn();
|
|
114
|
-
const view = render(MultiSelect, {
|
|
115
|
-
target,
|
|
116
|
-
props: {
|
|
117
|
-
options: [
|
|
118
|
-
{ value: 42, label: 'Initial' },
|
|
119
|
-
{ value: 'other', label: 'Other' },
|
|
120
|
-
{ value: 'changed', label: 'Changed' },
|
|
121
|
-
],
|
|
122
|
-
label: 'Choice',
|
|
123
|
-
name: 'choice',
|
|
124
|
-
values: [42],
|
|
125
|
-
onvalueschange: onChange,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
target.reset();
|
|
129
|
-
await view.unmount();
|
|
130
|
-
await settleReset();
|
|
131
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
132
|
-
target.remove();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
describe('MultiSelect selected disabled option posting', () => {
|
|
136
|
-
it('omits initially disabled selections and responds to dynamic disable without losing state', async () => {
|
|
137
|
-
const target = document.createElement('form');
|
|
138
|
-
document.body.append(target);
|
|
139
|
-
const options = [
|
|
140
|
-
{ value: 42, label: 'Selected', disabled: true },
|
|
141
|
-
{ value: 'other', label: 'Other' },
|
|
142
|
-
];
|
|
143
|
-
const view = render(MultiSelect, {
|
|
144
|
-
target,
|
|
145
|
-
props: {
|
|
146
|
-
options,
|
|
147
|
-
label: 'Choice',
|
|
148
|
-
name: 'choice',
|
|
149
|
-
values: [42, 'other'],
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
expect(new FormData(target).getAll('choice')).toEqual(['other']);
|
|
153
|
-
await view.rerender({
|
|
154
|
-
options: options.map((option) => ({ ...option, disabled: false })),
|
|
155
|
-
});
|
|
156
|
-
expect(new FormData(target).getAll('choice')).toEqual(['42', 'other']);
|
|
157
|
-
await view.rerender({ options });
|
|
158
|
-
expect(new FormData(target).getAll('choice')).toEqual(['other']);
|
|
159
|
-
await view.rerender({
|
|
160
|
-
options: options.map((option) => ({ ...option, disabled: false })),
|
|
161
|
-
});
|
|
162
|
-
expect(new FormData(target).getAll('choice')).toEqual(['42', 'other']);
|
|
163
|
-
await view.unmount();
|
|
164
|
-
target.remove();
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
describe('MultiSelect reset listener ordering', () => {
|
|
168
|
-
it('honors cancellation from a later listener microtask', async () => {
|
|
169
|
-
const target = document.createElement('form');
|
|
170
|
-
document.body.append(target);
|
|
171
|
-
const onChange = vi.fn();
|
|
172
|
-
const view = render(MultiSelect, {
|
|
173
|
-
target,
|
|
174
|
-
props: {
|
|
175
|
-
options: [
|
|
176
|
-
{ value: 42, label: 'Initial' },
|
|
177
|
-
{ value: 'changed', label: 'Changed' },
|
|
178
|
-
],
|
|
179
|
-
label: 'Choice',
|
|
180
|
-
name: 'choice',
|
|
181
|
-
values: [42],
|
|
182
|
-
onvalueschange: onChange,
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
await view.rerender({ values: ['changed'] });
|
|
186
|
-
target.addEventListener('reset', (event) => {
|
|
187
|
-
queueMicrotask(() => event.preventDefault());
|
|
188
|
-
});
|
|
189
|
-
target.reset();
|
|
190
|
-
await settleReset();
|
|
191
|
-
expect(new FormData(target).getAll('choice')).toEqual(['changed']);
|
|
192
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
193
|
-
await view.unmount();
|
|
194
|
-
target.remove();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
function dispatchLocalCommand(registry, command) {
|
|
198
|
-
const target = new EventTarget();
|
|
199
|
-
let pending;
|
|
200
|
-
target.addEventListener('click', (event) => {
|
|
201
|
-
pending = executeLocalControlCommand(registry, command, event);
|
|
202
|
-
}, { once: true });
|
|
203
|
-
target.dispatchEvent(new Event('click'));
|
|
204
|
-
if (!pending)
|
|
205
|
-
throw new Error('local command handler did not run');
|
|
206
|
-
return pending;
|
|
207
|
-
}
|
|
208
|
-
describe('MultiSelect reset registry currency', () => {
|
|
209
|
-
it.each([
|
|
210
|
-
false,
|
|
211
|
-
true,
|
|
212
|
-
])('keeps staged proposal currency correct when cancellation is %s', async (cancel) => {
|
|
213
|
-
const registry = createControlInteractionRegistry({
|
|
214
|
-
isLocalGesture: () => true,
|
|
215
|
-
});
|
|
216
|
-
render(Fixture, { props: { ...{ values: [42] }, registry } });
|
|
217
|
-
const identity = { formId: 'roles-form', controlId: 'roles' };
|
|
218
|
-
const target = form();
|
|
219
|
-
await open();
|
|
220
|
-
await userEvent.click(screen.getByRole('option', { name: 'Administrator' }));
|
|
221
|
-
expect((await registry.execute({ action: 'stage', identity, value: ['admin'] }, { source: 'agent' })).ok).toBe(true);
|
|
222
|
-
if (cancel)
|
|
223
|
-
target.addEventListener('reset', (event) => event.preventDefault());
|
|
224
|
-
target.reset();
|
|
225
|
-
await settleReset();
|
|
226
|
-
expect(registry.get(identity)?.state.staged?.stale).toBe(!cancel);
|
|
227
|
-
const applied = await dispatchLocalCommand(registry, {
|
|
228
|
-
action: 'apply',
|
|
229
|
-
identity,
|
|
230
|
-
});
|
|
231
|
-
expect(applied.ok).toBe(cancel);
|
|
232
|
-
if (!cancel) {
|
|
233
|
-
expect(applied.reason).toBe('staged_value_stale');
|
|
234
|
-
expect(new FormData(target).getAll('roles')).toEqual(['42']);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
it.each([
|
|
238
|
-
false,
|
|
239
|
-
true,
|
|
240
|
-
])('preserves reset over an async local clear when cancellation is %s', async (cancel) => {
|
|
241
|
-
let releasePolicy;
|
|
242
|
-
let policyStarted;
|
|
243
|
-
const blocked = new Promise((resolve) => {
|
|
244
|
-
releasePolicy = resolve;
|
|
245
|
-
});
|
|
246
|
-
const started = new Promise((resolve) => {
|
|
247
|
-
policyStarted = resolve;
|
|
248
|
-
});
|
|
249
|
-
const registry = createControlInteractionRegistry({
|
|
250
|
-
isLocalGesture: () => true,
|
|
251
|
-
policy: async (command) => {
|
|
252
|
-
if (command.action === 'clear') {
|
|
253
|
-
policyStarted();
|
|
254
|
-
await blocked;
|
|
255
|
-
}
|
|
256
|
-
return { allowed: true };
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
render(Fixture, { props: { ...{ values: [42] }, registry } });
|
|
260
|
-
const identity = { formId: 'roles-form', controlId: 'roles' };
|
|
261
|
-
const target = form();
|
|
262
|
-
await open();
|
|
263
|
-
await userEvent.click(screen.getByRole('option', { name: 'Administrator' }));
|
|
264
|
-
const clearing = dispatchLocalCommand(registry, {
|
|
265
|
-
action: 'clear',
|
|
266
|
-
identity,
|
|
267
|
-
});
|
|
268
|
-
await started;
|
|
269
|
-
if (cancel)
|
|
270
|
-
target.addEventListener('reset', (event) => event.preventDefault());
|
|
271
|
-
target.reset();
|
|
272
|
-
await settleReset();
|
|
273
|
-
releasePolicy();
|
|
274
|
-
const result = await clearing;
|
|
275
|
-
await tick();
|
|
276
|
-
expect(result.ok).toBe(cancel);
|
|
277
|
-
if (!cancel)
|
|
278
|
-
expect(result.reason).toBe('staged_value_stale');
|
|
279
|
-
expect(new FormData(target).getAll('roles')).toEqual(cancel ? [] : ['42']);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/svelte';
|
|
2
|
-
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
5
|
-
import SearchInput from '../SearchInput.svelte';
|
|
6
|
-
describe('SearchInput', () => {
|
|
7
|
-
beforeEach(() => vi.useFakeTimers({ shouldAdvanceTime: true }));
|
|
8
|
-
afterEach(() => vi.useRealTimers());
|
|
9
|
-
it('is a labelled search field in a search landmark', () => {
|
|
10
|
-
render(SearchInput, {
|
|
11
|
-
props: { value: '', onsearch: vi.fn(), placeholder: 'Search by title' },
|
|
12
|
-
});
|
|
13
|
-
expect(screen.getByRole('search')).toBeInTheDocument();
|
|
14
|
-
expect(screen.getByRole('searchbox', { name: 'Search' })).toHaveAttribute('placeholder', 'Search by title');
|
|
15
|
-
});
|
|
16
|
-
it('searches once, trimmed and single-line, after the last key', async () => {
|
|
17
|
-
const onsearch = vi.fn();
|
|
18
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
19
|
-
render(SearchInput, { props: { value: '', onsearch, debounceMs: 300 } });
|
|
20
|
-
await user.type(screen.getByRole('searchbox'), ' council hall ');
|
|
21
|
-
expect(onsearch).not.toHaveBeenCalled();
|
|
22
|
-
vi.advanceTimersByTime(300);
|
|
23
|
-
expect(onsearch).toHaveBeenCalledTimes(1);
|
|
24
|
-
expect(onsearch).toHaveBeenCalledWith('council hall');
|
|
25
|
-
});
|
|
26
|
-
it('searches at once on Enter', async () => {
|
|
27
|
-
const onsearch = vi.fn();
|
|
28
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
29
|
-
render(SearchInput, { props: { value: '', onsearch } });
|
|
30
|
-
await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
|
|
31
|
-
expect(onsearch).toHaveBeenCalledWith('roads');
|
|
32
|
-
});
|
|
33
|
-
it('does not search for the search it is already showing', async () => {
|
|
34
|
-
const onsearch = vi.fn();
|
|
35
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
36
|
-
render(SearchInput, { props: { value: 'roads', onsearch } });
|
|
37
|
-
await user.type(screen.getByRole('searchbox'), '{Enter}');
|
|
38
|
-
expect(onsearch).not.toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
it('clears with the x and focuses the field', async () => {
|
|
41
|
-
const onsearch = vi.fn();
|
|
42
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
43
|
-
render(SearchInput, { props: { value: 'roads', onsearch } });
|
|
44
|
-
const field = screen.getByRole('searchbox');
|
|
45
|
-
expect(field).toHaveValue('roads');
|
|
46
|
-
await user.click(screen.getByRole('button', { name: 'Clear search' }));
|
|
47
|
-
expect(onsearch).toHaveBeenCalledWith('');
|
|
48
|
-
expect(field).toHaveValue('');
|
|
49
|
-
expect(field).toHaveFocus();
|
|
50
|
-
expect(screen.queryByRole('button', { name: 'Clear search' })).toBeNull();
|
|
51
|
-
});
|
|
52
|
-
it('follows the applied search when it changes from outside', async () => {
|
|
53
|
-
const { rerender } = render(SearchInput, {
|
|
54
|
-
props: { value: 'roads', onsearch: vi.fn() },
|
|
55
|
-
});
|
|
56
|
-
await rerender({ value: 'parks', onsearch: vi.fn() });
|
|
57
|
-
expect(screen.getByRole('searchbox')).toHaveValue('parks');
|
|
58
|
-
});
|
|
59
|
-
it('inside a host form, Enter searches and never submits the host form', async () => {
|
|
60
|
-
const onsearch = vi.fn();
|
|
61
|
-
const onsubmit = vi.fn((event) => event.preventDefault());
|
|
62
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
63
|
-
const host = document.createElement('form');
|
|
64
|
-
host.addEventListener('submit', onsubmit);
|
|
65
|
-
document.body.append(host);
|
|
66
|
-
render(SearchInput, { target: host, props: { value: '', onsearch } });
|
|
67
|
-
expect(host.querySelector('form')).toBeNull();
|
|
68
|
-
await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
|
|
69
|
-
expect(onsearch).toHaveBeenCalledWith('roads');
|
|
70
|
-
expect(onsubmit).not.toHaveBeenCalled();
|
|
71
|
-
host.remove();
|
|
72
|
-
});
|
|
73
|
-
it('a value the host normalized differently does not swallow a later one', async () => {
|
|
74
|
-
const onsearch = vi.fn();
|
|
75
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
76
|
-
const view = render(SearchInput, { props: { value: '', onsearch } });
|
|
77
|
-
await user.type(screen.getByRole('searchbox'), 'Roads{Enter}');
|
|
78
|
-
expect(onsearch).toHaveBeenCalledWith('Roads');
|
|
79
|
-
// The host applies its own normalization, then later navigates to the
|
|
80
|
-
// exact text the box once sent: the box must follow both.
|
|
81
|
-
await view.rerender({ value: 'roads', onsearch });
|
|
82
|
-
expect(screen.getByRole('searchbox')).toHaveValue('roads');
|
|
83
|
-
await view.rerender({ value: 'Roads', onsearch });
|
|
84
|
-
expect(screen.getByRole('searchbox')).toHaveValue('Roads');
|
|
85
|
-
});
|
|
86
|
-
it('is axe-clean', async () => {
|
|
87
|
-
const { container } = render(SearchInput, {
|
|
88
|
-
props: { value: 'roads', onsearch: vi.fn() },
|
|
89
|
-
});
|
|
90
|
-
await expectNoA11yViolations(container);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Golden test for Select (Sweep L1, #1420).
|
|
3
|
-
*
|
|
4
|
-
* Select forwards aria attributes (so it can be labelled / error-associated)
|
|
5
|
-
* and inherits id / describedby / invalid from a wrapping FormGroup via context.
|
|
6
|
-
*/
|
|
7
|
-
import { render, screen } from '@testing-library/svelte';
|
|
8
|
-
import { createRawSnippet } from 'svelte';
|
|
9
|
-
import { describe, expect, it } from 'vitest';
|
|
10
|
-
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
11
|
-
import Select from '../Select.svelte';
|
|
12
|
-
function options() {
|
|
13
|
-
return createRawSnippet(() => ({
|
|
14
|
-
render: () => `<option value="a">A</option><option value="b">B</option>`,
|
|
15
|
-
}));
|
|
16
|
-
}
|
|
17
|
-
describe('Select', () => {
|
|
18
|
-
it('forwards aria-label / aria-describedby / aria-invalid', () => {
|
|
19
|
-
render(Select, {
|
|
20
|
-
props: {
|
|
21
|
-
'aria-label': 'Country',
|
|
22
|
-
'aria-describedby': 'country-hint',
|
|
23
|
-
'aria-invalid': 'true',
|
|
24
|
-
children: options(),
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
const select = screen.getByRole('combobox', { name: 'Country' });
|
|
28
|
-
expect(select).toHaveAttribute('aria-describedby', 'country-hint');
|
|
29
|
-
expect(select).toHaveAttribute('aria-invalid', 'true');
|
|
30
|
-
});
|
|
31
|
-
it('is axe-clean when labelled', async () => {
|
|
32
|
-
const { container } = render(Select, {
|
|
33
|
-
props: { 'aria-label': 'Country', children: options() },
|
|
34
|
-
});
|
|
35
|
-
await expectNoA11yViolations(container);
|
|
36
|
-
});
|
|
37
|
-
});
|