@spectral_labs/ui 1.7.0 → 1.8.0-next.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/LICENSE +21 -0
- package/THIRD_PARTY_NOTICES.md +201 -0
- package/package.json +8 -2
- package/dist/components/__tests__/overlay-scrim-source.test.js +0 -134
- package/dist/components/accordion/__tests__/AccordionTest.svelte +0 -31
- package/dist/components/accordion/__tests__/accordion.test.svelte.js +0 -139
- package/dist/components/alert-dialog/__tests__/AlertDialogTest.svelte +0 -28
- package/dist/components/alert-dialog/__tests__/alert-dialog.a11y.test.js +0 -42
- package/dist/components/alert-dialog/__tests__/alert-dialog.integration.test.svelte.js +0 -51
- package/dist/components/alert-dialog/__tests__/alert-dialog.test.svelte.js +0 -113
- package/dist/components/app-bar/__tests__/app-bar.test.svelte.js +0 -30
- package/dist/components/aspect-ratio/__tests__/aspect-ratio.test.svelte.js +0 -22
- package/dist/components/avatar/__tests__/avatar.test.svelte.js +0 -26
- package/dist/components/badge/__tests__/badge.test.svelte.js +0 -30
- package/dist/components/bottom-sheet/__tests__/BottomSheetTest.svelte +0 -22
- package/dist/components/bottom-sheet/__tests__/bottom-sheet.test.svelte.js +0 -83
- package/dist/components/button/__tests__/button.test.svelte.js +0 -63
- package/dist/components/calendar/__tests__/CalendarTest.svelte +0 -56
- package/dist/components/calendar/__tests__/calendar.test.svelte.js +0 -190
- package/dist/components/card/__tests__/card.test.svelte.js +0 -30
- package/dist/components/checkbox/__tests__/checkbox.test.svelte.js +0 -44
- package/dist/components/chips/__tests__/chips.test.svelte.js +0 -26
- package/dist/components/collapsible/__tests__/CollapsibleTest.svelte +0 -18
- package/dist/components/collapsible/__tests__/collapsible.test.svelte.js +0 -125
- package/dist/components/combobox/__tests__/ComboboxTest.svelte +0 -40
- package/dist/components/combobox/__tests__/combobox.test.svelte.js +0 -255
- package/dist/components/command/__tests__/CommandInlineDirectTest.svelte +0 -29
- package/dist/components/command/__tests__/CommandInlineTest.svelte +0 -89
- package/dist/components/command/__tests__/CommandTest.svelte +0 -77
- package/dist/components/command/__tests__/command-inline.test.svelte.js +0 -392
- package/dist/components/command/__tests__/command-item-passthrough.test.svelte.js +0 -284
- package/dist/components/command/__tests__/command.a11y.test.js +0 -30
- package/dist/components/command/__tests__/command.integration.test.svelte.js +0 -68
- package/dist/components/command/__tests__/command.test.svelte.js +0 -230
- package/dist/components/context-menu/__tests__/ContextMenuNonInteractiveTest.svelte +0 -32
- package/dist/components/context-menu/__tests__/ContextMenuTest.svelte +0 -25
- package/dist/components/context-menu/__tests__/context-menu.test.svelte.js +0 -141
- package/dist/components/date-field/__tests__/DateFieldTest.svelte +0 -20
- package/dist/components/date-field/__tests__/date-field.test.svelte.js +0 -139
- package/dist/components/date-picker/__tests__/DatePickerTest.svelte +0 -20
- package/dist/components/date-picker/__tests__/date-picker.test.svelte.js +0 -190
- package/dist/components/date-range-field/__tests__/DateRangeFieldTest.svelte +0 -20
- package/dist/components/date-range-field/__tests__/date-range-field.test.svelte.js +0 -130
- package/dist/components/date-range-picker/__tests__/DateRangePickerTest.svelte +0 -18
- package/dist/components/date-range-picker/__tests__/date-range-picker.test.svelte.js +0 -156
- package/dist/components/dialog/__tests__/DialogTest.svelte +0 -34
- package/dist/components/dialog/__tests__/dialog.test.svelte.js +0 -184
- package/dist/components/drop-zone/__tests__/drop-zone.test.svelte.js +0 -26
- package/dist/components/dropdown-menu/__tests__/DropdownMenuRadioTest.svelte +0 -90
- package/dist/components/dropdown-menu/__tests__/DropdownMenuTest.svelte +0 -71
- package/dist/components/dropdown-menu/__tests__/dropdown-menu-radio-group.test.svelte.js +0 -306
- package/dist/components/dropdown-menu/__tests__/dropdown-menu.test.svelte.js +0 -567
- package/dist/components/empty-state/__tests__/empty-state.test.svelte.js +0 -26
- package/dist/components/fab/__tests__/fab.test.svelte.js +0 -30
- package/dist/components/field/__tests__/FieldTest.svelte +0 -26
- package/dist/components/field/__tests__/field.test.svelte.js +0 -74
- package/dist/components/grid-list/__tests__/GridListTest.svelte +0 -46
- package/dist/components/grid-list/__tests__/grid-list.test.svelte.js +0 -231
- package/dist/components/hover-card/__tests__/HoverCardTest.svelte +0 -30
- package/dist/components/hover-card/__tests__/hover-card.test.svelte.js +0 -236
- package/dist/components/indicator/__tests__/indicator.test.svelte.js +0 -30
- package/dist/components/input/__tests__/input.test.svelte.js +0 -67
- package/dist/components/label/__tests__/label.test.svelte.js +0 -22
- package/dist/components/language-select/__tests__/language-select.test.svelte.js +0 -199
- package/dist/components/language-select/__tests__/languages.test.js +0 -87
- package/dist/components/link-preview/__tests__/LinkPreviewTest.svelte +0 -37
- package/dist/components/link-preview/__tests__/link-preview.a11y.test.js +0 -28
- package/dist/components/link-preview/__tests__/link-preview.integration.test.svelte.js +0 -54
- package/dist/components/link-preview/__tests__/link-preview.test.svelte.js +0 -215
- package/dist/components/list/__tests__/list.test.svelte.js +0 -22
- package/dist/components/menubar/__tests__/MenubarTest.svelte +0 -52
- package/dist/components/menubar/__tests__/menubar.a11y.test.js +0 -28
- package/dist/components/menubar/__tests__/menubar.integration.test.svelte.js +0 -57
- package/dist/components/menubar/__tests__/menubar.test.svelte.js +0 -328
- package/dist/components/meter/__tests__/meter.test.svelte.js +0 -26
- package/dist/components/nav-bar/__tests__/nav-bar.test.svelte.js +0 -23
- package/dist/components/nav-drawer/__tests__/nav-drawer.test.svelte.js +0 -28
- package/dist/components/nav-rail/__tests__/nav-rail.test.svelte.js +0 -28
- package/dist/components/pagination/__tests__/pagination.test.svelte.js +0 -26
- package/dist/components/pin-input/__tests__/pin-input.test.svelte.js +0 -71
- package/dist/components/popover/__tests__/PopoverTest.svelte +0 -27
- package/dist/components/popover/__tests__/popover.test.svelte.js +0 -196
- package/dist/components/progress/__tests__/progress.test.svelte.js +0 -32
- package/dist/components/radio-group/__tests__/radio-group.test.svelte.js +0 -22
- package/dist/components/range-calendar/__tests__/RangeCalendarTest.svelte +0 -23
- package/dist/components/range-calendar/__tests__/range-calendar.test.svelte.js +0 -105
- package/dist/components/rating-group/__tests__/rating-group.test.svelte.js +0 -26
- package/dist/components/scroll-area/__tests__/scroll-area.test.svelte.js +0 -34
- package/dist/components/search-field/__tests__/SearchFieldTest.svelte +0 -34
- package/dist/components/search-field/__tests__/search-field.test.svelte.js +0 -118
- package/dist/components/select/__tests__/SelectLabelPersistenceTest.svelte +0 -31
- package/dist/components/select/__tests__/SelectTest.svelte +0 -33
- package/dist/components/select/__tests__/select.test.svelte.js +0 -221
- package/dist/components/separator/__tests__/separator.test.svelte.js +0 -39
- package/dist/components/sheet/__tests__/SheetTest.svelte +0 -34
- package/dist/components/sheet/__tests__/sheet.test.svelte.js +0 -193
- package/dist/components/side-sheet/__tests__/side-sheet.test.svelte.js +0 -138
- package/dist/components/sidebar/__tests__/SidebarFullTest.svelte +0 -45
- package/dist/components/sidebar/__tests__/SidebarTest.svelte +0 -9
- package/dist/components/sidebar/__tests__/sidebar.test.svelte.js +0 -186
- package/dist/components/simple-dialog/__tests__/SimpleDialogTest.svelte +0 -40
- package/dist/components/simple-dialog/__tests__/simple-dialog.test.svelte.js +0 -50
- package/dist/components/simple-tooltip/__tests__/SimpleTooltipTest.svelte +0 -19
- package/dist/components/simple-tooltip/__tests__/simple-tooltip.test.svelte.js +0 -61
- package/dist/components/slider/__tests__/slider.test.svelte.js +0 -42
- package/dist/components/sortable-list/__tests__/NestedSortableListTest.svelte +0 -49
- package/dist/components/sortable-list/__tests__/SortableListTest.svelte +0 -42
- package/dist/components/sortable-list/__tests__/sortable-list.nested.test.svelte.js +0 -112
- package/dist/components/sortable-list/__tests__/sortable-list.test.svelte.js +0 -175
- package/dist/components/splitter/__tests__/splitter.test.svelte.js +0 -26
- package/dist/components/switch/__tests__/switch.test.svelte.js +0 -44
- package/dist/components/table/__tests__/table.test.svelte.js +0 -22
- package/dist/components/tabs/__tests__/TabsTest.svelte +0 -32
- package/dist/components/tabs/__tests__/tabs.test.svelte.js +0 -227
- package/dist/components/textarea/__tests__/textarea.test.svelte.js +0 -55
- package/dist/components/time-field/__tests__/TimeFieldTest.svelte +0 -20
- package/dist/components/time-field/__tests__/time-field.test.svelte.js +0 -100
- package/dist/components/time-range-field/__tests__/TimeRangeFieldTest.svelte +0 -20
- package/dist/components/time-range-field/__tests__/time-range-field.test.svelte.js +0 -124
- package/dist/components/toast/__tests__/toast.test.svelte.js +0 -46
- package/dist/components/toggle/__tests__/toggle.test.svelte.js +0 -38
- package/dist/components/toggle-group/__tests__/toggle-group.test.svelte.js +0 -22
- package/dist/components/toolbar/__tests__/ToolbarCompositionTest.svelte +0 -18
- package/dist/components/toolbar/__tests__/ToolbarTabstopTest.svelte +0 -10
- package/dist/components/toolbar/__tests__/toolbar.test.svelte.js +0 -63
- package/dist/components/tooltip/__tests__/TooltipTest.svelte +0 -22
- package/dist/components/tooltip/__tests__/tooltip.test.svelte.js +0 -225
- package/dist/components/tree/__tests__/TreeLabelContentTest.svelte +0 -21
- package/dist/components/tree/__tests__/TreeTest.svelte +0 -24
- package/dist/components/tree/__tests__/tree.test.svelte.js +0 -229
- package/dist/components/virtualizer/__tests__/waterfall-layout.test.js +0 -82
- package/dist/internal/announcer/__tests__/announcer.test.js +0 -42
- package/dist/internal/dismissible-layer/__tests__/dismissible-layer.test.js +0 -196
- package/dist/internal/drag-drop/__tests__/auto-scroll.test.js +0 -104
- package/dist/internal/drag-drop/__tests__/drag-session.test.svelte.js +0 -803
- package/dist/internal/drag-drop/__tests__/drag-source.test.svelte.js +0 -130
- package/dist/internal/drag-drop/__tests__/drop-delegate.test.js +0 -223
- package/dist/internal/drag-drop/__tests__/types.test.js +0 -20
- package/dist/internal/escape-layer/__tests__/escape-layer.test.js +0 -113
- package/dist/internal/focus-scope/__tests__/focus-scope.test.js +0 -225
- package/dist/internal/grace-area/__tests__/grace-area.test.js +0 -67
- package/dist/internal/helpers/__tests__/helpers.test.js +0 -123
- package/dist/internal/portal/__tests__/PortalTest.svelte +0 -21
- package/dist/internal/portal/__tests__/portal.test.js +0 -29
- package/dist/internal/portal/__tests__/portal.test.svelte.js +0 -57
- package/dist/internal/scroll-lock/__tests__/scroll-lock.test.js +0 -160
- package/dist/internal/typeahead/__tests__/typeahead.test.js +0 -61
- package/src/themes/__tests__/carbon-dark-contrast.test.ts +0 -170
- package/src/themes/__tests__/generated-css-drift.test.ts +0 -53
- package/src/themes/__tests__/scrim-opaque.test.ts +0 -52
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import TimeFieldTest from './TimeFieldTest.svelte';
|
|
4
|
-
describe('timeField', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(TimeFieldTest, { target, props });
|
|
11
|
-
flushSync();
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
instance = undefined;
|
|
17
|
-
target?.remove();
|
|
18
|
-
});
|
|
19
|
-
// 1. Renders group container
|
|
20
|
-
it('renders group container', () => {
|
|
21
|
-
setup();
|
|
22
|
-
expect(target.querySelector('[role="group"]')).toBeTruthy();
|
|
23
|
-
});
|
|
24
|
-
// 2. Renders at least 2 spinbutton segments with default granularity (minute = hour + minute)
|
|
25
|
-
it('renders hour + minute segments (default granularity)', () => {
|
|
26
|
-
setup();
|
|
27
|
-
expect(target.querySelectorAll('[role="spinbutton"]').length).toBeGreaterThanOrEqual(2);
|
|
28
|
-
});
|
|
29
|
-
// 3. Segments include hour and minute labels
|
|
30
|
-
it('segments include hour and minute labels', () => {
|
|
31
|
-
setup();
|
|
32
|
-
const labels = Array.from(target.querySelectorAll('[role="spinbutton"]')).map((s) => s.getAttribute('aria-label')?.toLowerCase() ?? '');
|
|
33
|
-
expect(labels.some((l) => l && (l.includes('hour') || l.includes('heure')))).toBe(true);
|
|
34
|
-
expect(labels.some((l) => l && l.includes('minute'))).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
// 4. Renders value when provided (minute field shows 30)
|
|
37
|
-
it('renders value when provided', () => {
|
|
38
|
-
setup({ value: { hour: 14, minute: 30, second: 0 } });
|
|
39
|
-
expect(target.textContent ?? '').toMatch(/30/);
|
|
40
|
-
});
|
|
41
|
-
// 5. hourCycle=12 adds AM/PM segment
|
|
42
|
-
it('hourCycle=12 adds dayPeriod segment (AM/PM)', () => {
|
|
43
|
-
setup({ hourCycle: 12, value: { hour: 14, minute: 30, second: 0 } });
|
|
44
|
-
const text = (target.textContent ?? '').toUpperCase();
|
|
45
|
-
expect(text.includes('AM') || text.includes('PM')).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
// 6. hourCycle=24 does not show AM/PM
|
|
48
|
-
it('hourCycle=24 does not add dayPeriod', () => {
|
|
49
|
-
setup({ hourCycle: 24, value: { hour: 14, minute: 30, second: 0 } });
|
|
50
|
-
const text = (target.textContent ?? '').toUpperCase();
|
|
51
|
-
expect(text).not.toMatch(/\bAM\b|\bPM\b/);
|
|
52
|
-
});
|
|
53
|
-
// 7. ArrowUp increments hour segment (24h to avoid 12h display offset)
|
|
54
|
-
it('arrowUp increments hour', () => {
|
|
55
|
-
setup({ hourCycle: 24, value: { hour: 14, minute: 30, second: 0 } });
|
|
56
|
-
const hour = target.querySelector('[role="spinbutton"][aria-label="hour"]');
|
|
57
|
-
expect(hour).toBeTruthy();
|
|
58
|
-
hour.focus();
|
|
59
|
-
hour.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }));
|
|
60
|
-
flushSync();
|
|
61
|
-
const val = hour.getAttribute('aria-valuenow') ?? hour.textContent ?? '';
|
|
62
|
-
expect(String(val)).toMatch(/15/);
|
|
63
|
-
});
|
|
64
|
-
// 8. ArrowDown decrements minute segment
|
|
65
|
-
it('arrowDown decrements minute', () => {
|
|
66
|
-
setup({ value: { hour: 14, minute: 30, second: 0 } });
|
|
67
|
-
const minute = target.querySelector('[role="spinbutton"][aria-label="minute"]');
|
|
68
|
-
expect(minute).toBeTruthy();
|
|
69
|
-
minute.focus();
|
|
70
|
-
minute.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
|
|
71
|
-
flushSync();
|
|
72
|
-
const val = minute.getAttribute('aria-valuenow') ?? minute.textContent ?? '';
|
|
73
|
-
expect(String(val)).toMatch(/29/);
|
|
74
|
-
});
|
|
75
|
-
// 9. granularity="second" adds seconds segment
|
|
76
|
-
it('granularity="second" adds seconds segment', () => {
|
|
77
|
-
setup({ granularity: 'second', value: { hour: 14, minute: 30, second: 45 } });
|
|
78
|
-
const labels = Array.from(target.querySelectorAll('[role="spinbutton"]')).map((s) => s.getAttribute('aria-label')?.toLowerCase() ?? '');
|
|
79
|
-
expect(labels.some((l) => l && l.includes('second'))).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
// 10. disabled marks group with data-disabled
|
|
82
|
-
it('disabled marks group as disabled', () => {
|
|
83
|
-
setup({ disabled: true });
|
|
84
|
-
const group = target.querySelector('[role="group"]');
|
|
85
|
-
expect(group?.hasAttribute('data-disabled')).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
// 11. readonly marks segments with aria-readonly
|
|
88
|
-
it('readonly marks segments as readonly', () => {
|
|
89
|
-
setup({ readonly: true });
|
|
90
|
-
const segments = target.querySelectorAll('[role="spinbutton"]');
|
|
91
|
-
const readonlySegments = Array.from(segments).filter((s) => s.getAttribute('aria-readonly') === 'true');
|
|
92
|
-
expect(readonlySegments.length).toBeGreaterThanOrEqual(2);
|
|
93
|
-
});
|
|
94
|
-
// 12. group has aria-label
|
|
95
|
-
it('group has aria-label', () => {
|
|
96
|
-
setup();
|
|
97
|
-
const group = target.querySelector('[role="group"]');
|
|
98
|
-
expect(group?.getAttribute('aria-label')).toBeTruthy();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { TimeRange } from '../types.js'
|
|
3
|
-
import TimeRangeFieldRoot from '../components/time-range-field-root.svelte'
|
|
4
|
-
|
|
5
|
-
let {
|
|
6
|
-
value = $bindable<TimeRange>({}),
|
|
7
|
-
disabled = false,
|
|
8
|
-
readonly: isReadonly = false,
|
|
9
|
-
required = false,
|
|
10
|
-
onValueChange,
|
|
11
|
-
}: {
|
|
12
|
-
value?: TimeRange
|
|
13
|
-
disabled?: boolean
|
|
14
|
-
readonly?: boolean
|
|
15
|
-
required?: boolean
|
|
16
|
-
onValueChange?: (v: TimeRange) => void
|
|
17
|
-
} = $props()
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<TimeRangeFieldRoot bind:value {disabled} readonly={isReadonly} {required} {onValueChange} />
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { Time } from '@internationalized/date';
|
|
2
|
-
import { flushSync, mount, unmount } from 'svelte';
|
|
3
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
4
|
-
import TimeRangeFieldTest from './TimeRangeFieldTest.svelte';
|
|
5
|
-
describe('timeRangeField', () => {
|
|
6
|
-
let target;
|
|
7
|
-
let instance;
|
|
8
|
-
function setup(props = {}) {
|
|
9
|
-
target = document.createElement('div');
|
|
10
|
-
document.body.appendChild(target);
|
|
11
|
-
instance = mount(TimeRangeFieldTest, { target, props });
|
|
12
|
-
flushSync();
|
|
13
|
-
}
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
if (instance)
|
|
16
|
-
unmount(instance);
|
|
17
|
-
instance = undefined;
|
|
18
|
-
target?.remove();
|
|
19
|
-
});
|
|
20
|
-
// T-C-2-1: smoke — renders two inner TimeField groups (start + end)
|
|
21
|
-
it('renders two time field groups (start + end)', () => {
|
|
22
|
-
setup();
|
|
23
|
-
// The outer wrapper is role=group (aria-label="Time range")
|
|
24
|
-
// Each TimeField.Root also renders role=group — at least 3 groups expected
|
|
25
|
-
const allGroups = target.querySelectorAll('[role="group"]');
|
|
26
|
-
expect(allGroups.length).toBeGreaterThanOrEqual(3);
|
|
27
|
-
// Inner groups have aria-label "Start time" and "End time"
|
|
28
|
-
const startGroup = target.querySelector('[aria-label="Start time"]');
|
|
29
|
-
const endGroup = target.querySelector('[aria-label="End time"]');
|
|
30
|
-
expect(startGroup).not.toBeNull();
|
|
31
|
-
expect(endGroup).not.toBeNull();
|
|
32
|
-
});
|
|
33
|
-
// T-C-2-2: accepts an initial TimeRange value and renders spinbutton segments
|
|
34
|
-
it('accepts an initial TimeRange value and renders spinbutton segments', () => {
|
|
35
|
-
const initialRange = {
|
|
36
|
-
start: new Time(9, 0),
|
|
37
|
-
end: new Time(17, 30),
|
|
38
|
-
};
|
|
39
|
-
setup({ value: initialRange });
|
|
40
|
-
// Each TimeField renders spinbutton segments (hour, minute, optionally dayPeriod)
|
|
41
|
-
const segments = target.querySelectorAll('[role="spinbutton"]');
|
|
42
|
-
// At least 2 fields × 2 segments (hour + minute) = 4 spinbuttons
|
|
43
|
-
expect(segments.length).toBeGreaterThanOrEqual(4);
|
|
44
|
-
// Verify hour values are present in aria-valuenow attributes
|
|
45
|
-
const hourSegments = Array.from(segments).filter((s) => s.getAttribute('aria-label') === 'hour');
|
|
46
|
-
expect(hourSegments.length).toBeGreaterThanOrEqual(2);
|
|
47
|
-
});
|
|
48
|
-
// T-C-2-3: applies data-disabled to inner TimeField groups when disabled=true
|
|
49
|
-
it('applies data-disabled to inner TimeField groups when disabled=true', () => {
|
|
50
|
-
setup({ disabled: true });
|
|
51
|
-
// TimeField.Root sets data-disabled on its own group element
|
|
52
|
-
const disabledGroups = target.querySelectorAll('[role="group"][data-disabled]');
|
|
53
|
-
expect(disabledGroups.length).toBeGreaterThanOrEqual(2);
|
|
54
|
-
});
|
|
55
|
-
// T-C-2-4: applies aria-readonly to segments when readonly=true
|
|
56
|
-
it('applies aria-readonly to segments when readonly=true', () => {
|
|
57
|
-
setup({ readonly: true });
|
|
58
|
-
// TimeField renders aria-readonly on each spinbutton segment
|
|
59
|
-
const readonlySegments = target.querySelectorAll('[role="spinbutton"][aria-readonly="true"]');
|
|
60
|
-
expect(readonlySegments.length).toBeGreaterThanOrEqual(2);
|
|
61
|
-
});
|
|
62
|
-
// T-C-2-5: renders a visible en-dash separator between start and end
|
|
63
|
-
it('renders a visible en-dash separator between start and end fields', () => {
|
|
64
|
-
setup();
|
|
65
|
-
const separator = target.querySelector('.sp-time-range-separator');
|
|
66
|
-
expect(separator).not.toBeNull();
|
|
67
|
-
expect(separator?.textContent).toBe('–');
|
|
68
|
-
});
|
|
69
|
-
// T-C-2-extra-1: onStartChange fires when start TimeField segment value changes via ArrowUp
|
|
70
|
-
it('fires onValueChange when start time segment is incremented via ArrowUp', () => {
|
|
71
|
-
const changes = [];
|
|
72
|
-
setup({
|
|
73
|
-
value: { start: new Time(9, 0), end: new Time(17, 30) },
|
|
74
|
-
onValueChange: (v) => changes.push(v),
|
|
75
|
-
});
|
|
76
|
-
// Get the first spinbutton in the start TimeField
|
|
77
|
-
const startGroup = target.querySelector('[aria-label="Start time"]');
|
|
78
|
-
expect(startGroup).not.toBeNull();
|
|
79
|
-
const segments = startGroup.querySelectorAll('[role="spinbutton"]');
|
|
80
|
-
expect(segments.length).toBeGreaterThan(0);
|
|
81
|
-
// Fire ArrowUp to increment — triggers onStartChange → onValueChange
|
|
82
|
-
const firstSeg = segments[0];
|
|
83
|
-
firstSeg.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }));
|
|
84
|
-
flushSync();
|
|
85
|
-
expect(changes.length).toBeGreaterThan(0);
|
|
86
|
-
});
|
|
87
|
-
// T-C-2-extra-2: onEndChange fires when end TimeField segment value changes via ArrowUp
|
|
88
|
-
it('fires onValueChange when end time segment is incremented via ArrowUp', () => {
|
|
89
|
-
const changes = [];
|
|
90
|
-
setup({
|
|
91
|
-
value: { start: new Time(9, 0), end: new Time(17, 30) },
|
|
92
|
-
onValueChange: (v) => changes.push(v),
|
|
93
|
-
});
|
|
94
|
-
// Get the first spinbutton in the end TimeField
|
|
95
|
-
const endGroup = target.querySelector('[aria-label="End time"]');
|
|
96
|
-
expect(endGroup).not.toBeNull();
|
|
97
|
-
const segments = endGroup.querySelectorAll('[role="spinbutton"]');
|
|
98
|
-
expect(segments.length).toBeGreaterThan(0);
|
|
99
|
-
// Fire ArrowUp to increment — triggers onEndChange → onValueChange
|
|
100
|
-
const firstSeg = segments[0];
|
|
101
|
-
firstSeg.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }));
|
|
102
|
-
flushSync();
|
|
103
|
-
expect(changes.length).toBeGreaterThan(0);
|
|
104
|
-
});
|
|
105
|
-
// T-C-2-6: re-mounting with a new value reflects updated bindable prop
|
|
106
|
-
it('updates rendered segments when remounted with new TimeRange value', () => {
|
|
107
|
-
setup();
|
|
108
|
-
// Initial state: no value → segments show placeholder (no aria-valuenow)
|
|
109
|
-
const initialHourSegments = Array.from(target.querySelectorAll('[role="spinbutton"]')).filter((s) => s.getAttribute('aria-label') === 'hour');
|
|
110
|
-
expect(initialHourSegments.length).toBeGreaterThanOrEqual(2);
|
|
111
|
-
expect(initialHourSegments[0].getAttribute('aria-valuenow')).toBeNull();
|
|
112
|
-
// Remount with concrete value
|
|
113
|
-
if (instance)
|
|
114
|
-
unmount(instance);
|
|
115
|
-
const newRange = { start: new Time(14, 0), end: new Time(16, 45) };
|
|
116
|
-
instance = mount(TimeRangeFieldTest, { target, props: { value: newRange } });
|
|
117
|
-
flushSync();
|
|
118
|
-
const updatedHourSegments = Array.from(target.querySelectorAll('[role="spinbutton"]')).filter((s) => s.getAttribute('aria-label') === 'hour');
|
|
119
|
-
expect(updatedHourSegments.length).toBeGreaterThanOrEqual(2);
|
|
120
|
-
// At least one hour segment should have a valuenow (14 or its 12h equivalent)
|
|
121
|
-
const hasValue = updatedHourSegments.some((s) => s.getAttribute('aria-valuenow') !== null);
|
|
122
|
-
expect(hasValue).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Toast from '../toast.svelte';
|
|
4
|
-
describe('toast', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Toast, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role="status"', () => {
|
|
19
|
-
setup({ title: 'Test' });
|
|
20
|
-
expect(target.querySelector('[role="status"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has aria-live="polite"', () => {
|
|
23
|
-
setup({ title: 'Test' });
|
|
24
|
-
expect(target.querySelector('[aria-live="polite"]')).toBeTruthy();
|
|
25
|
-
});
|
|
26
|
-
it('has aria-atomic="true"', () => {
|
|
27
|
-
setup({ title: 'Test' });
|
|
28
|
-
expect(target.querySelector('[aria-atomic="true"]')).toBeTruthy();
|
|
29
|
-
});
|
|
30
|
-
it('renders title', () => {
|
|
31
|
-
setup({ title: 'Saved' });
|
|
32
|
-
expect(target.querySelector('.sp-toast-title')?.textContent).toBe('Saved');
|
|
33
|
-
});
|
|
34
|
-
it('applies variant class', () => {
|
|
35
|
-
setup({ title: 'Err', variant: 'error' });
|
|
36
|
-
expect(target.querySelector('.sp-toast')?.className).toContain('error');
|
|
37
|
-
});
|
|
38
|
-
it('renders dismiss button when dismissible', () => {
|
|
39
|
-
setup({ title: 'Test', dismissible: true });
|
|
40
|
-
expect(target.querySelector('.sp-toast-dismiss')).toBeTruthy();
|
|
41
|
-
});
|
|
42
|
-
it('hides dismiss button when not dismissible', () => {
|
|
43
|
-
setup({ title: 'Test', dismissible: false });
|
|
44
|
-
expect(target.querySelector('.sp-toast-dismiss')).toBeFalsy();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Toggle from '../toggle.svelte';
|
|
4
|
-
describe('toggle', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(Toggle, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders as button', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('button')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
it('has aria-pressed="false" by default', () => {
|
|
23
|
-
setup();
|
|
24
|
-
expect(target.querySelector('button')?.getAttribute('aria-pressed')).toBe('false');
|
|
25
|
-
});
|
|
26
|
-
it('has data-state="off" by default', () => {
|
|
27
|
-
setup();
|
|
28
|
-
expect(target.querySelector('button')?.getAttribute('data-state')).toBe('off');
|
|
29
|
-
});
|
|
30
|
-
it('has aria-pressed="true" when pressed', () => {
|
|
31
|
-
setup({ pressed: true });
|
|
32
|
-
expect(target.querySelector('button')?.getAttribute('aria-pressed')).toBe('true');
|
|
33
|
-
});
|
|
34
|
-
it('renders disabled', () => {
|
|
35
|
-
setup({ disabled: true });
|
|
36
|
-
expect(target.querySelector('button')?.hasAttribute('disabled')).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import ToggleGroupRoot from '../components/toggle-group-root.svelte';
|
|
4
|
-
describe('toggle-group', () => {
|
|
5
|
-
let target;
|
|
6
|
-
let instance;
|
|
7
|
-
function setup(props = {}) {
|
|
8
|
-
target = document.createElement('div');
|
|
9
|
-
document.body.appendChild(target);
|
|
10
|
-
instance = mount(ToggleGroupRoot, { target, props });
|
|
11
|
-
return target;
|
|
12
|
-
}
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (instance)
|
|
15
|
-
unmount(instance);
|
|
16
|
-
target?.remove();
|
|
17
|
-
});
|
|
18
|
-
it('renders with role=toolbar', () => {
|
|
19
|
-
setup();
|
|
20
|
-
expect(target.querySelector('[role="toolbar"]')).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { ToolbarOrientation } from '../types.js'
|
|
3
|
-
import ToolbarGroup from '../toolbar-group.svelte'
|
|
4
|
-
import ToolbarSeparator from '../toolbar-separator.svelte'
|
|
5
|
-
import Toolbar from '../toolbar.svelte'
|
|
6
|
-
|
|
7
|
-
const { orientation = 'horizontal' }: { orientation?: ToolbarOrientation } = $props()
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<Toolbar {orientation} aria-label="Formatting">
|
|
11
|
-
<ToolbarGroup aria-label="History">
|
|
12
|
-
<button type="button">Undo</button>
|
|
13
|
-
</ToolbarGroup>
|
|
14
|
-
<ToolbarSeparator data-testid="sep" />
|
|
15
|
-
<ToolbarGroup aria-label="Actions">
|
|
16
|
-
<button type="button">Redo</button>
|
|
17
|
-
</ToolbarGroup>
|
|
18
|
-
</Toolbar>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import Toolbar from '../toolbar.svelte'
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<Toolbar aria-label="Tabstop test">
|
|
6
|
-
<button type="button" data-testid="first">First</button>
|
|
7
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
8
|
-
<div tabindex="0" data-testid="container">Container</div>
|
|
9
|
-
<button type="button" data-testid="second">Second</button>
|
|
10
|
-
</Toolbar>
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from 'svelte';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import Toolbar from '../toolbar.svelte';
|
|
4
|
-
import ToolbarCompositionTest from './ToolbarCompositionTest.svelte';
|
|
5
|
-
import ToolbarTabstopTest from './ToolbarTabstopTest.svelte';
|
|
6
|
-
describe('toolbar', () => {
|
|
7
|
-
let target;
|
|
8
|
-
let instance;
|
|
9
|
-
function setup(props = {}) {
|
|
10
|
-
target = document.createElement('div');
|
|
11
|
-
document.body.appendChild(target);
|
|
12
|
-
instance = mount(Toolbar, { target, props });
|
|
13
|
-
return target;
|
|
14
|
-
}
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
if (instance)
|
|
17
|
-
unmount(instance);
|
|
18
|
-
target?.remove();
|
|
19
|
-
});
|
|
20
|
-
it('renders with role=toolbar', () => {
|
|
21
|
-
setup();
|
|
22
|
-
expect(target.querySelector('[role="toolbar"]')).toBeTruthy();
|
|
23
|
-
});
|
|
24
|
-
it('has aria-orientation', () => {
|
|
25
|
-
setup();
|
|
26
|
-
expect(target.querySelector('[aria-orientation]')).toBeTruthy();
|
|
27
|
-
});
|
|
28
|
-
it('separator defaults to vertical in horizontal toolbar', () => {
|
|
29
|
-
target = document.createElement('div');
|
|
30
|
-
document.body.appendChild(target);
|
|
31
|
-
instance = mount(ToolbarCompositionTest, { target, props: { orientation: 'horizontal' } });
|
|
32
|
-
const sep = target.querySelector('[data-testid="sep"]');
|
|
33
|
-
expect(sep?.getAttribute('data-orientation')).toBe('vertical');
|
|
34
|
-
});
|
|
35
|
-
it('separator defaults to horizontal in vertical toolbar', () => {
|
|
36
|
-
target = document.createElement('div');
|
|
37
|
-
document.body.appendChild(target);
|
|
38
|
-
instance = mount(ToolbarCompositionTest, { target, props: { orientation: 'vertical' } });
|
|
39
|
-
const sep = target.querySelector('[data-testid="sep"]');
|
|
40
|
-
expect(sep?.getAttribute('data-orientation')).toBe('horizontal');
|
|
41
|
-
});
|
|
42
|
-
it('arrow navigation moves focus across groups', () => {
|
|
43
|
-
target = document.createElement('div');
|
|
44
|
-
document.body.appendChild(target);
|
|
45
|
-
instance = mount(ToolbarCompositionTest, { target, props: { orientation: 'horizontal' } });
|
|
46
|
-
const buttons = Array.from(target.querySelectorAll('button'));
|
|
47
|
-
buttons[0]?.focus();
|
|
48
|
-
buttons[0]?.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
49
|
-
expect(document.activeElement).toBe(buttons[1]);
|
|
50
|
-
});
|
|
51
|
-
it('ignores non-control tabindex containers when moving focus', () => {
|
|
52
|
-
target = document.createElement('div');
|
|
53
|
-
document.body.appendChild(target);
|
|
54
|
-
instance = mount(ToolbarTabstopTest, { target });
|
|
55
|
-
const first = target.querySelector('[data-testid="first"]');
|
|
56
|
-
const second = target.querySelector('[data-testid="second"]');
|
|
57
|
-
const container = target.querySelector('[data-testid="container"]');
|
|
58
|
-
second?.focus();
|
|
59
|
-
second?.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
|
|
60
|
-
expect(document.activeElement).toBe(first);
|
|
61
|
-
expect(document.activeElement).not.toBe(container);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import TooltipContent from '../components/tooltip-content.svelte'
|
|
3
|
-
import TooltipRoot from '../components/tooltip-root.svelte'
|
|
4
|
-
import TooltipTrigger from '../components/tooltip-trigger.svelte'
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
openDelay = 0,
|
|
8
|
-
closeDelay = 0,
|
|
9
|
-
portalTo,
|
|
10
|
-
closeOnOutsideClick,
|
|
11
|
-
}: {
|
|
12
|
-
openDelay?: number
|
|
13
|
-
closeDelay?: number
|
|
14
|
-
portalTo?: HTMLElement | string
|
|
15
|
-
closeOnOutsideClick?: boolean
|
|
16
|
-
} = $props()
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<TooltipRoot {openDelay} {closeDelay}>
|
|
20
|
-
<TooltipTrigger>Hover me</TooltipTrigger>
|
|
21
|
-
<TooltipContent {portalTo} {closeOnOutsideClick}>Tooltip text</TooltipContent>
|
|
22
|
-
</TooltipRoot>
|