@svgrid/grid 2.2.16 → 2.2.18
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/README.md +1 -1
- package/dist/GridFooter.svelte +12 -11
- package/dist/SvAnchor.svelte +65 -0
- package/dist/SvAnchor.svelte.d.ts +24 -0
- package/dist/SvAspectRatio.svelte +38 -0
- package/dist/SvAspectRatio.svelte.d.ts +16 -0
- package/dist/SvAutoComplete.svelte +52 -7
- package/dist/SvAutoComplete.svelte.d.ts +4 -1
- package/dist/SvBlockquote.svelte +43 -0
- package/dist/SvBlockquote.svelte.d.ts +16 -0
- package/dist/SvCalendar.svelte +33 -2
- package/dist/SvCalendar.svelte.d.ts +8 -1
- package/dist/SvCheckBox.svelte +6 -2
- package/dist/SvCode.svelte +52 -0
- package/dist/SvCode.svelte.d.ts +19 -0
- package/dist/SvCollapsible.svelte +75 -0
- package/dist/SvCollapsible.svelte.d.ts +27 -0
- package/dist/SvColorInput.svelte +11 -17
- package/dist/SvColorInput.svelte.d.ts +2 -0
- package/dist/SvComboBox.svelte +85 -8
- package/dist/SvComboBox.svelte.d.ts +14 -0
- package/dist/SvCountryInput.svelte +2 -2
- package/dist/SvCountryInput.svelte.d.ts +1 -1
- package/dist/SvDropDownList.svelte +154 -51
- package/dist/SvDropDownList.svelte.d.ts +17 -3
- package/dist/SvDurationInput.svelte +59 -41
- package/dist/SvDurationInput.svelte.d.ts +18 -0
- package/dist/SvField.svelte +203 -12
- package/dist/SvField.svelte.d.ts +45 -8
- package/dist/SvForm.svelte +195 -21
- package/dist/SvForm.svelte.d.ts +23 -0
- package/dist/SvGrid.controller.svelte.d.ts +12 -1
- package/dist/SvGrid.controller.svelte.js +87 -7
- package/dist/SvGrid.svelte +383 -181
- package/dist/SvGrid.types.d.ts +123 -2
- package/dist/SvGridChartView.svelte +70 -0
- package/dist/SvGridChartView.svelte.d.ts +11 -0
- package/dist/SvGridDropdown.svelte +9 -2
- package/dist/SvGridSelect.svelte +5 -2
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroup.svelte +41 -0
- package/dist/SvGroup.svelte.d.ts +27 -0
- package/dist/SvHoverCard.svelte +70 -0
- package/dist/SvHoverCard.svelte.d.ts +41 -0
- package/dist/SvKbd.svelte +51 -0
- package/dist/SvKbd.svelte.d.ts +18 -0
- package/dist/SvList.svelte +55 -0
- package/dist/SvList.svelte.d.ts +23 -0
- package/dist/SvListBox.svelte +166 -77
- package/dist/SvListBox.svelte.d.ts +17 -4
- package/dist/SvLoadingOverlay.svelte +53 -0
- package/dist/SvLoadingOverlay.svelte.d.ts +28 -0
- package/dist/SvMark.svelte +36 -0
- package/dist/SvMark.svelte.d.ts +14 -0
- package/dist/SvMaskedInput.svelte +50 -35
- package/dist/SvMaskedInput.svelte.d.ts +17 -2
- package/dist/SvMenuList.svelte +32 -3
- package/dist/SvMenubar.svelte +200 -0
- package/dist/SvMenubar.svelte.d.ts +18 -0
- package/dist/SvMultiSelect.svelte +7 -4
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +79 -42
- package/dist/SvNumberInput.svelte.d.ts +25 -0
- package/dist/SvOtpInput.svelte +2 -2
- package/dist/SvPasswordInput.svelte +51 -31
- package/dist/SvPasswordInput.svelte.d.ts +17 -0
- package/dist/SvPhoneInput.svelte +39 -28
- package/dist/SvPhoneInput.svelte.d.ts +4 -0
- package/dist/SvPopconfirm.svelte +79 -0
- package/dist/SvPopconfirm.svelte.d.ts +42 -0
- package/dist/SvPopover.svelte +87 -29
- package/dist/SvPopover.svelte.d.ts +18 -4
- package/dist/SvRadioGroup.svelte +5 -1
- package/dist/SvResult.svelte +73 -0
- package/dist/SvResult.svelte.d.ts +28 -0
- package/dist/SvSegmented.svelte +114 -0
- package/dist/SvSegmented.svelte.d.ts +29 -0
- package/dist/SvSimpleGrid.svelte +41 -0
- package/dist/SvSimpleGrid.svelte.d.ts +25 -0
- package/dist/SvSlider.svelte +23 -10
- package/dist/SvSpinner.svelte +43 -0
- package/dist/SvSpinner.svelte.d.ts +20 -0
- package/dist/SvStack.svelte +37 -0
- package/dist/SvStack.svelte.d.ts +24 -0
- package/dist/SvSwitchButton.svelte +6 -1
- package/dist/SvTagsInput.svelte +9 -5
- package/dist/SvText.svelte +76 -0
- package/dist/SvText.svelte.d.ts +31 -0
- package/dist/SvTextInput.svelte +75 -43
- package/dist/SvTextInput.svelte.d.ts +28 -1
- package/dist/SvTimePicker.svelte +32 -2
- package/dist/SvTimePicker.svelte.d.ts +8 -9
- package/dist/SvTitle.svelte +57 -0
- package/dist/SvTitle.svelte.d.ts +24 -0
- package/dist/SvToaster.svelte +41 -6
- package/dist/SvToggleButton.svelte +4 -1
- package/dist/SvTooltip.svelte +68 -34
- package/dist/SvTooltip.svelte.d.ts +14 -2
- package/dist/SvTree.svelte +81 -21
- package/dist/SvTree.svelte.d.ts +3 -2
- package/dist/SvTreeSelect.svelte +5 -2
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/SvVisuallyHidden.svelte +49 -0
- package/dist/SvVisuallyHidden.svelte.d.ts +19 -0
- package/dist/ai.d.ts +289 -0
- package/dist/ai.js +1013 -0
- package/dist/board-view.svelte.d.ts +22 -0
- package/dist/board-view.svelte.js +13 -0
- package/dist/build-api.js +36 -6
- package/dist/cdn/GridMenus-0AK9O7qG.js +420 -0
- package/dist/cdn/GridMenus-BjUoCXoD.js +416 -0
- package/dist/cdn/SvDateTimePicker-CYC7gRER.js +2176 -0
- package/dist/cdn/SvDateTimePicker-DtQKd4Ns.js +2198 -0
- package/dist/cdn/SvGridChart-BcLCYvw9.js +1195 -0
- package/dist/cdn/SvGridChart-BhnFdLd0.js +1203 -0
- package/dist/cdn/SvGridChartPanel-Cmx9DHAS.js +561 -0
- package/dist/cdn/SvGridChartPanel-DvjwxLvK.js +577 -0
- package/dist/cdn/SvGridChartView-B40DTJSX.js +54 -0
- package/dist/cdn/SvGridChartView-PwqI4s02.js +53 -0
- package/dist/cdn/SvGridDropdown-DjALc_4f.js +255 -0
- package/dist/cdn/SvGridDropdown-hpnHvOGt.js +254 -0
- package/dist/cdn/chart-T1usDN0o.js +1212 -0
- package/dist/cdn/disclose-version-DoD9AFD_.js +2958 -0
- package/dist/cdn/export-format-JT5pWVT8.js +561 -0
- package/dist/cdn/popover-P-9P2YYn.js +69 -0
- package/dist/cdn/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/cdn/src-BLhdLmoN.js +25707 -0
- package/dist/cdn/src-BwnUVj3T.js +25623 -0
- package/dist/cdn/svgrid.js +9 -32897
- package/dist/cdn/svgrid.svelte-external.js +9 -30052
- package/dist/cell-formatting.d.ts +2 -2
- package/dist/cell-render.js +4 -4
- package/dist/chart-view.svelte.d.ts +25 -0
- package/dist/chart-view.svelte.js +14 -0
- package/dist/createCombobox.svelte.d.ts +5 -0
- package/dist/createCombobox.svelte.js +21 -5
- package/dist/createDropdownList.svelte.d.ts +3 -0
- package/dist/createDropdownList.svelte.js +8 -2
- package/dist/createForm.svelte.d.ts +18 -0
- package/dist/createForm.svelte.js +51 -6
- package/dist/createMenu.svelte.js +27 -0
- package/dist/createPopoverSelect.svelte.d.ts +3 -0
- package/dist/createPopoverSelect.svelte.js +9 -1
- package/dist/createSlider.svelte.d.ts +8 -21
- package/dist/createSlider.svelte.js +34 -0
- package/dist/createTooltip.svelte.d.ts +12 -0
- package/dist/createTooltip.svelte.js +72 -9
- package/dist/editor-contract.d.ts +15 -0
- package/dist/export-provider.d.ts +27 -0
- package/dist/export-provider.js +9 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +33 -0
- package/dist/form-field.d.ts +45 -2
- package/dist/grid-messages.d.ts +61 -0
- package/dist/grid-messages.js +72 -0
- package/dist/group-display.d.ts +62 -0
- package/dist/group-display.js +78 -0
- package/dist/index.d.ts +36 -4
- package/dist/index.js +43 -2
- package/dist/js-scroller.svelte.d.ts +35 -0
- package/dist/js-scroller.svelte.js +154 -0
- package/dist/list-option.d.ts +46 -0
- package/dist/list-option.js +53 -0
- package/dist/menus.js +25 -11
- package/dist/pivot-view.svelte.d.ts +72 -0
- package/dist/pivot-view.svelte.js +13 -0
- package/dist/positioning.d.ts +111 -0
- package/dist/positioning.js +191 -0
- package/dist/scheduler-model.d.ts +3 -0
- package/dist/scheduler-model.js +22 -5
- package/dist/svgrid-wrapper.types.d.ts +18 -0
- package/dist/toast-store.svelte.d.ts +54 -0
- package/dist/toast-store.svelte.js +65 -15
- package/package.json +9 -1
- package/src/GridFooter.svelte +12 -11
- package/src/SvAnchor.svelte +65 -0
- package/src/SvAspectRatio.svelte +38 -0
- package/src/SvAutoComplete.svelte +52 -7
- package/src/SvBlockquote.svelte +43 -0
- package/src/SvCalendar.svelte +33 -2
- package/src/SvCalendar.test.ts +15 -0
- package/src/SvCheckBox.svelte +6 -2
- package/src/SvCode.svelte +52 -0
- package/src/SvCollapsible.svelte +75 -0
- package/src/SvColorInput.svelte +11 -17
- package/src/SvComboBox.svelte +85 -8
- package/src/SvCountryInput.svelte +2 -2
- package/src/SvDropDownList.svelte +154 -51
- package/src/SvDurationInput.svelte +59 -41
- package/src/SvField.svelte +203 -12
- package/src/SvForm.svelte +195 -21
- package/src/SvForm.test.ts +112 -0
- package/src/SvGrid.controller.svelte.ts +88 -7
- package/src/SvGrid.svelte +383 -181
- package/src/SvGrid.types.ts +125 -1
- package/src/SvGridChartView.svelte +70 -0
- package/src/SvGridDropdown.svelte +9 -2
- package/src/SvGridSelect.svelte +5 -2
- package/src/SvGroup.svelte +41 -0
- package/src/SvHoverCard.svelte +70 -0
- package/src/SvKbd.svelte +51 -0
- package/src/SvList.svelte +55 -0
- package/src/SvListBox.svelte +166 -77
- package/src/SvLoadingOverlay.svelte +53 -0
- package/src/SvMark.svelte +36 -0
- package/src/SvMaskedInput.svelte +50 -35
- package/src/SvMenuList.svelte +32 -3
- package/src/SvMenubar.svelte +200 -0
- package/src/SvMultiSelect.svelte +7 -4
- package/src/SvNumberInput.svelte +79 -42
- package/src/SvOtpInput.svelte +2 -2
- package/src/SvPasswordInput.svelte +51 -31
- package/src/SvPhoneInput.svelte +39 -28
- package/src/SvPopconfirm.svelte +79 -0
- package/src/SvPopover.svelte +87 -29
- package/src/SvRadioGroup.svelte +5 -1
- package/src/SvResult.svelte +73 -0
- package/src/SvSegmented.svelte +114 -0
- package/src/SvSimpleGrid.svelte +41 -0
- package/src/SvSlider.svelte +23 -10
- package/src/SvSpinner.svelte +43 -0
- package/src/SvStack.svelte +37 -0
- package/src/SvSwitchButton.svelte +6 -1
- package/src/SvTagsInput.svelte +9 -5
- package/src/SvText.svelte +76 -0
- package/src/SvTextInput.svelte +75 -43
- package/src/SvTimePicker.svelte +32 -2
- package/src/SvTitle.svelte +57 -0
- package/src/SvToaster.svelte +41 -6
- package/src/SvToaster.test.ts +32 -0
- package/src/SvToggleButton.svelte +4 -1
- package/src/SvTooltip.svelte +68 -34
- package/src/SvTree.svelte +81 -21
- package/src/SvTreeSelect.svelte +5 -2
- package/src/SvVisuallyHidden.svelte +49 -0
- package/src/ai.test.ts +502 -0
- package/src/ai.ts +1391 -0
- package/src/board-view.svelte.ts +33 -0
- package/src/build-api.coverage.test.ts +101 -0
- package/src/build-api.ts +43 -18
- package/src/builtin-editors.grid.test.ts +1 -1
- package/src/cell-formatting.ts +5 -5
- package/src/cell-render.ts +4 -4
- package/src/chart-view.svelte.ts +36 -0
- package/src/createCombobox.svelte.ts +17 -5
- package/src/createDropdownList.svelte.ts +8 -2
- package/src/createForm.svelte.ts +59 -6
- package/src/createMenu.svelte.ts +23 -0
- package/src/createPopoverSelect.svelte.ts +8 -1
- package/src/createSlider.svelte.ts +20 -0
- package/src/createTooltip.svelte.ts +67 -9
- package/src/editor-contract.ts +16 -0
- package/src/export-provider.ts +40 -0
- package/src/filter-operators.ts +37 -0
- package/src/form-field.ts +42 -2
- package/src/grid-messages.test.ts +24 -0
- package/src/grid-messages.ts +137 -0
- package/src/group-display.test.ts +77 -0
- package/src/group-display.ts +117 -0
- package/src/i18n.grid.test.ts +73 -0
- package/src/index.ts +91 -2
- package/src/js-scroller.svelte.ts +173 -0
- package/src/list-option.test.ts +56 -0
- package/src/list-option.ts +81 -0
- package/src/menus.test.ts +32 -0
- package/src/menus.ts +25 -12
- package/src/pivot-view.svelte.ts +90 -0
- package/src/pivot-view.test.ts +38 -0
- package/src/pivot.grid.test.ts +102 -0
- package/src/positioning.test.ts +141 -0
- package/src/positioning.ts +266 -0
- package/src/scheduler-model.test.ts +16 -0
- package/src/scheduler-model.ts +24 -5
- package/src/svgrid-wrapper.types.ts +23 -0
- package/src/svgrid.charting.test.ts +8 -1
- package/src/svgrid.comments-autocomplete.test.ts +7 -2
- package/src/svgrid.context-menu.test.ts +8 -3
- package/src/toast-store.svelte.ts +98 -0
- package/src/toast-store.test.ts +62 -1
- package/src/ui-breadth.test.ts +138 -0
- package/src/ui-buttons.test.ts +39 -0
- package/src/ui-inputs.test.ts +2 -2
- package/src/ui-range.test.ts +9 -0
- package/src/ui-selection.test.ts +36 -0
- package/src/ui-tier1.test.ts +1 -1
- package/src/virtual.test.ts +20 -6
- package/dist/SvGridBoard.svelte +0 -2339
- package/dist/SvGridBoard.svelte.d.ts +0 -31
- package/src/SvGridBoard.svelte +0 -2339
- package/src/board.test.ts +0 -927
package/src/toast-store.test.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* manual dismiss, sticky toasts, and the SR announcement side effect.
|
|
4
4
|
*/
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
-
import { toast, dismissToast, clearToasts, pauseToast, resumeToast, toastStore } from './toast-store.svelte'
|
|
6
|
+
import { toast, dismissToast, updateToast, clearToasts, pauseToast, resumeToast, toastStore } from './toast-store.svelte'
|
|
7
7
|
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
vi.useFakeTimers()
|
|
@@ -83,4 +83,65 @@ describe('toast store', () => {
|
|
|
83
83
|
clearToasts()
|
|
84
84
|
expect(toastStore.toasts).toHaveLength(0)
|
|
85
85
|
})
|
|
86
|
+
|
|
87
|
+
it('update patches a toast in place (same id) and restarts its timer', () => {
|
|
88
|
+
const id = toast('working', { duration: 0 })
|
|
89
|
+
updateToast(id, { message: 'done', variant: 'success', duration: 3000 })
|
|
90
|
+
expect(toastStore.toasts).toHaveLength(1)
|
|
91
|
+
const t = toastStore.toasts[0]!
|
|
92
|
+
expect(t.id).toBe(id)
|
|
93
|
+
expect(t.message).toBe('done')
|
|
94
|
+
expect(t.variant).toBe('success')
|
|
95
|
+
// The new duration now applies.
|
|
96
|
+
vi.advanceTimersByTime(3000)
|
|
97
|
+
expect(toastStore.toasts).toHaveLength(0)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('update is a no-op for an unknown id', () => {
|
|
101
|
+
updateToast(999, { message: 'nope' })
|
|
102
|
+
expect(toastStore.toasts).toHaveLength(0)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('promise shows a sticky loader, then updates in place to success', async () => {
|
|
106
|
+
vi.useRealTimers() // real microtask flush for the promise
|
|
107
|
+
let resolve!: (v: string) => void
|
|
108
|
+
const p = new Promise<string>((r) => (resolve = r))
|
|
109
|
+
const ret = toast.promise(p, { loading: 'Saving...', success: (v) => `Saved ${v}`, error: 'Failed' })
|
|
110
|
+
expect(ret).toBe(p) // returns the original promise
|
|
111
|
+
expect(toastStore.toasts).toHaveLength(1)
|
|
112
|
+
const id = toastStore.toasts[0]!.id
|
|
113
|
+
expect(toastStore.toasts[0]!.message).toBe('Saving...')
|
|
114
|
+
expect(toastStore.toasts[0]!.variant).toBe('info')
|
|
115
|
+
resolve('now')
|
|
116
|
+
await p
|
|
117
|
+
await Promise.resolve()
|
|
118
|
+
const t = toastStore.toasts.find((x) => x.id === id)!
|
|
119
|
+
expect(t.message).toBe('Saved now')
|
|
120
|
+
expect(t.variant).toBe('success')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('promise updates in place to error on rejection', async () => {
|
|
124
|
+
vi.useRealTimers()
|
|
125
|
+
const p = Promise.reject(new Error('x'))
|
|
126
|
+
toast.promise(p, { loading: 'Load', success: 'ok', error: (e) => `Err: ${(e as Error).message}` })
|
|
127
|
+
await p.catch(() => {})
|
|
128
|
+
await Promise.resolve()
|
|
129
|
+
const t = toastStore.toasts[0]!
|
|
130
|
+
expect(t.variant).toBe('error')
|
|
131
|
+
expect(t.message).toBe('Err: x')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('action + cancel are stored on the toast', () => {
|
|
135
|
+
const onClick = vi.fn()
|
|
136
|
+
toast('Undo?', { action: { label: 'Undo', onClick }, cancel: { label: 'Dismiss' } })
|
|
137
|
+
const t = toastStore.toasts[0]!
|
|
138
|
+
expect(t.action?.label).toBe('Undo')
|
|
139
|
+
expect(t.cancel?.label).toBe('Dismiss')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('custom carries a render snippet', () => {
|
|
143
|
+
const render = (() => {}) as never
|
|
144
|
+
const id = toast.custom(render, { duration: 0 })
|
|
145
|
+
expect(toastStore.toasts.find((x) => x.id === id)!.render).toBe(render)
|
|
146
|
+
})
|
|
86
147
|
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Pillar C breadth components: SvCollapsible (accessible toggle),
|
|
3
|
+
* SvResult (status), SvLoadingOverlay (scrim), and the layout primitives.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it } from 'vitest'
|
|
6
|
+
import { mount, unmount, flushSync } from 'svelte'
|
|
7
|
+
import SvCollapsible from './SvCollapsible.svelte'
|
|
8
|
+
import SvResult from './SvResult.svelte'
|
|
9
|
+
import SvLoadingOverlay from './SvLoadingOverlay.svelte'
|
|
10
|
+
import SvSimpleGrid from './SvSimpleGrid.svelte'
|
|
11
|
+
import SvTitle from './SvTitle.svelte'
|
|
12
|
+
import SvText from './SvText.svelte'
|
|
13
|
+
import SvAnchor from './SvAnchor.svelte'
|
|
14
|
+
import SvKbd from './SvKbd.svelte'
|
|
15
|
+
import SvCode from './SvCode.svelte'
|
|
16
|
+
import SvVisuallyHidden from './SvVisuallyHidden.svelte'
|
|
17
|
+
|
|
18
|
+
function mnt(Comp: any, props: Record<string, unknown>) {
|
|
19
|
+
const target = document.createElement('div')
|
|
20
|
+
document.body.appendChild(target)
|
|
21
|
+
const app = mount(Comp, { target, props: props as any })
|
|
22
|
+
flushSync()
|
|
23
|
+
return { target, destroy: () => { unmount(app); target.remove() } }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('SvCollapsible', () => {
|
|
27
|
+
it('toggles aria-expanded and wires the region', () => {
|
|
28
|
+
let open: boolean | undefined
|
|
29
|
+
const { target, destroy } = mnt(SvCollapsible, { title: 'More', onOpenChange: (o: boolean) => (open = o) })
|
|
30
|
+
try {
|
|
31
|
+
const trigger = target.querySelector<HTMLButtonElement>('.sv-collapse__trigger')!
|
|
32
|
+
expect(trigger.getAttribute('aria-expanded')).toBe('false')
|
|
33
|
+
const region = target.querySelector('.sv-collapse__region')!
|
|
34
|
+
expect(trigger.getAttribute('aria-controls')).toBe(region.id)
|
|
35
|
+
trigger.click(); flushSync()
|
|
36
|
+
expect(trigger.getAttribute('aria-expanded')).toBe('true')
|
|
37
|
+
expect(open).toBe(true)
|
|
38
|
+
} finally { destroy() }
|
|
39
|
+
})
|
|
40
|
+
it('disabled does not toggle', () => {
|
|
41
|
+
const { target, destroy } = mnt(SvCollapsible, { title: 'X', disabled: true })
|
|
42
|
+
try {
|
|
43
|
+
const trigger = target.querySelector<HTMLButtonElement>('.sv-collapse__trigger')!
|
|
44
|
+
trigger.click(); flushSync()
|
|
45
|
+
expect(trigger.getAttribute('aria-expanded')).toBe('false')
|
|
46
|
+
} finally { destroy() }
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('SvResult', () => {
|
|
51
|
+
it('renders the status class, title and description', () => {
|
|
52
|
+
const { target, destroy } = mnt(SvResult, { status: 'success', title: 'Done', description: 'All good' })
|
|
53
|
+
try {
|
|
54
|
+
expect(target.querySelector('.sv-result--success')).not.toBeNull()
|
|
55
|
+
expect(target.querySelector('.sv-result__title')?.textContent).toBe('Done')
|
|
56
|
+
expect(target.querySelector('.sv-result__desc')?.textContent).toBe('All good')
|
|
57
|
+
} finally { destroy() }
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe('SvLoadingOverlay', () => {
|
|
62
|
+
it('renders only when visible, with a polite status role', () => {
|
|
63
|
+
const hidden = mnt(SvLoadingOverlay, { visible: false })
|
|
64
|
+
try { expect(hidden.target.querySelector('.sv-loading')).toBeNull() } finally { hidden.destroy() }
|
|
65
|
+
const shown = mnt(SvLoadingOverlay, { visible: true, label: 'Saving' })
|
|
66
|
+
try {
|
|
67
|
+
const el = shown.target.querySelector('.sv-loading')!
|
|
68
|
+
expect(el).not.toBeNull()
|
|
69
|
+
expect(el.getAttribute('role')).toBe('status')
|
|
70
|
+
expect(el.getAttribute('aria-label')).toBe('Saving')
|
|
71
|
+
} finally { shown.destroy() }
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('SvSimpleGrid', () => {
|
|
76
|
+
it('uses a fixed column template when cols is set', () => {
|
|
77
|
+
const { target, destroy } = mnt(SvSimpleGrid, { cols: 3 })
|
|
78
|
+
try {
|
|
79
|
+
const el = target.querySelector<HTMLElement>('.sv-simplegrid')!
|
|
80
|
+
expect(el.style.gridTemplateColumns).toContain('repeat(3')
|
|
81
|
+
} finally { destroy() }
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('SvTitle', () => {
|
|
86
|
+
it('renders the heading tag for its order', () => {
|
|
87
|
+
const { target, destroy } = mnt(SvTitle, { order: 3, children: undefined })
|
|
88
|
+
try { expect(target.querySelector('h3')).not.toBeNull() } finally { destroy() }
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('SvText', () => {
|
|
93
|
+
it('applies the line clamp when clamp is set', () => {
|
|
94
|
+
const { target, destroy } = mnt(SvText, { clamp: 2 })
|
|
95
|
+
try {
|
|
96
|
+
const el = target.querySelector<HTMLElement>('.sv-text')!
|
|
97
|
+
expect(el.classList.contains('is-clamp')).toBe(true)
|
|
98
|
+
expect(el.style.getPropertyValue('--sv-text-clamp')).toBe('2')
|
|
99
|
+
} finally { destroy() }
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('SvAnchor', () => {
|
|
104
|
+
it('adds safe rel and target for external links', () => {
|
|
105
|
+
const { target, destroy } = mnt(SvAnchor, { href: 'https://svelte.dev', external: true })
|
|
106
|
+
try {
|
|
107
|
+
const a = target.querySelector('a')!
|
|
108
|
+
expect(a.getAttribute('target')).toBe('_blank')
|
|
109
|
+
expect(a.getAttribute('rel')).toBe('noopener noreferrer')
|
|
110
|
+
} finally { destroy() }
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('SvKbd', () => {
|
|
115
|
+
it('renders one <kbd> per key in a combo', () => {
|
|
116
|
+
const { target, destroy } = mnt(SvKbd, { keys: ['Ctrl', 'K'] })
|
|
117
|
+
try { expect(target.querySelectorAll('kbd').length).toBe(2) } finally { destroy() }
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
describe('SvCode', () => {
|
|
122
|
+
it('renders a <pre> in block mode and inline <code> otherwise', () => {
|
|
123
|
+
const block = mnt(SvCode, { block: true, code: 'a' })
|
|
124
|
+
try { expect(block.target.querySelector('pre.sv-code--block')).not.toBeNull() } finally { block.destroy() }
|
|
125
|
+
const inline = mnt(SvCode, { code: 'a' })
|
|
126
|
+
try {
|
|
127
|
+
expect(inline.target.querySelector('pre')).toBeNull()
|
|
128
|
+
expect(inline.target.querySelector('code.sv-code--inline')).not.toBeNull()
|
|
129
|
+
} finally { inline.destroy() }
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe('SvVisuallyHidden', () => {
|
|
134
|
+
it('renders content in an off-screen element', () => {
|
|
135
|
+
const { target, destroy } = mnt(SvVisuallyHidden, { children: undefined })
|
|
136
|
+
try { expect(target.querySelector('.sv-vh')).not.toBeNull() } finally { destroy() }
|
|
137
|
+
})
|
|
138
|
+
})
|
package/src/ui-buttons.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import SvSwitchButton from './SvSwitchButton.svelte'
|
|
|
9
9
|
import SvCheckBox from './SvCheckBox.svelte'
|
|
10
10
|
import SvRadioGroup from './SvRadioGroup.svelte'
|
|
11
11
|
import SvRating from './SvRating.svelte'
|
|
12
|
+
import SvSegmented from './SvSegmented.svelte'
|
|
12
13
|
|
|
13
14
|
function mnt(Comp: any, props: Record<string, unknown>) {
|
|
14
15
|
const target = document.createElement('div')
|
|
@@ -52,6 +53,17 @@ describe('SvSwitchButton', () => {
|
|
|
52
53
|
expect(states.at(-1)).toBe(true)
|
|
53
54
|
} finally { destroy() }
|
|
54
55
|
})
|
|
56
|
+
it('readonly blocks the change but stays focusable + aria-readonly', () => {
|
|
57
|
+
let got: boolean | undefined
|
|
58
|
+
const { target, destroy } = mnt(SvSwitchButton, { checked: false, readonly: true, onChange: (v: boolean) => (got = v) })
|
|
59
|
+
try {
|
|
60
|
+
const sw = target.querySelector<HTMLButtonElement>('[role="switch"]')!
|
|
61
|
+
expect(sw.getAttribute('aria-readonly')).toBe('true')
|
|
62
|
+
expect(sw.hasAttribute('disabled')).toBe(false) // focusable, unlike disabled
|
|
63
|
+
sw.click(); flushSync()
|
|
64
|
+
expect(got).toBeUndefined() // no change emitted
|
|
65
|
+
} finally { destroy() }
|
|
66
|
+
})
|
|
55
67
|
})
|
|
56
68
|
|
|
57
69
|
describe('SvCheckBox', () => {
|
|
@@ -68,6 +80,16 @@ describe('SvCheckBox', () => {
|
|
|
68
80
|
expect(b.target.querySelector('[role="checkbox"]')!.getAttribute('aria-checked')).toBe('mixed')
|
|
69
81
|
} finally { b.destroy() }
|
|
70
82
|
})
|
|
83
|
+
it('readonly blocks the toggle', () => {
|
|
84
|
+
let got: boolean | undefined
|
|
85
|
+
const { target, destroy } = mnt(SvCheckBox, { checked: false, readonly: true, onChange: (v: boolean) => (got = v) })
|
|
86
|
+
try {
|
|
87
|
+
const box = target.querySelector<HTMLButtonElement>('[role="checkbox"]')!
|
|
88
|
+
expect(box.getAttribute('aria-readonly')).toBe('true')
|
|
89
|
+
box.click(); flushSync()
|
|
90
|
+
expect(got).toBeUndefined()
|
|
91
|
+
} finally { destroy() }
|
|
92
|
+
})
|
|
71
93
|
})
|
|
72
94
|
|
|
73
95
|
describe('SvRadioGroup', () => {
|
|
@@ -103,3 +125,20 @@ describe('SvRating', () => {
|
|
|
103
125
|
} finally { destroy() }
|
|
104
126
|
})
|
|
105
127
|
})
|
|
128
|
+
|
|
129
|
+
describe('SvSegmented', () => {
|
|
130
|
+
const opts = [{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }, { value: 'map', label: 'Map' }]
|
|
131
|
+
it('is a radiogroup; clicking selects and reports the value', () => {
|
|
132
|
+
let got: unknown
|
|
133
|
+
const { target, destroy } = mnt(SvSegmented, { options: opts, value: 'list', onChange: (v: unknown) => (got = v) })
|
|
134
|
+
try {
|
|
135
|
+
expect(target.querySelector('[role="radiogroup"]')).not.toBeNull()
|
|
136
|
+
const radios = target.querySelectorAll<HTMLButtonElement>('[role="radio"]')
|
|
137
|
+
expect(radios.length).toBe(3)
|
|
138
|
+
expect(radios[0]!.getAttribute('aria-checked')).toBe('true')
|
|
139
|
+
radios[2]!.click(); flushSync()
|
|
140
|
+
expect(got).toBe('map')
|
|
141
|
+
expect(radios[2]!.getAttribute('aria-checked')).toBe('true')
|
|
142
|
+
} finally { destroy() }
|
|
143
|
+
})
|
|
144
|
+
})
|
package/src/ui-inputs.test.ts
CHANGED
|
@@ -25,7 +25,7 @@ describe('SvNumberInput validation a11y (editor contract)', () => {
|
|
|
25
25
|
const err = target.querySelector('#qty__error')!
|
|
26
26
|
expect(err.getAttribute('role')).toBe('alert')
|
|
27
27
|
expect(err.textContent).toBe('Too big')
|
|
28
|
-
expect(target.querySelector('.sv-
|
|
28
|
+
expect(target.querySelector('.sv-field__control.is-invalid')).toBeTruthy()
|
|
29
29
|
} finally { destroy() }
|
|
30
30
|
})
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ describe('SvNumberInput validation a11y (editor contract)', () => {
|
|
|
35
35
|
const input = target.querySelector<HTMLInputElement>('.sv-num__input')!
|
|
36
36
|
expect(input.getAttribute('aria-invalid')).toBeNull()
|
|
37
37
|
expect(input.getAttribute('aria-describedby')).toBeNull()
|
|
38
|
-
expect(target.querySelector('.sv-
|
|
38
|
+
expect(target.querySelector('.sv-field__control.is-invalid')).toBeNull()
|
|
39
39
|
} finally { destroy() }
|
|
40
40
|
})
|
|
41
41
|
})
|
package/src/ui-range.test.ts
CHANGED
|
@@ -38,6 +38,15 @@ describe('SvSlider', () => {
|
|
|
38
38
|
expect(got).toEqual([30, 80])
|
|
39
39
|
} finally { destroy() }
|
|
40
40
|
})
|
|
41
|
+
it('wires validation ARIA onto the thumb (aria-invalid + describedby)', () => {
|
|
42
|
+
const { target, destroy } = mnt(SvSlider, { value: 50, invalid: true, error: 'Out of range', id: 'vol', required: true })
|
|
43
|
+
try {
|
|
44
|
+
const thumb = target.querySelector<HTMLElement>('[role="slider"]')!
|
|
45
|
+
expect(thumb.getAttribute('aria-invalid')).toBe('true')
|
|
46
|
+
expect(thumb.getAttribute('aria-required')).toBe('true')
|
|
47
|
+
expect(thumb.getAttribute('aria-describedby')).toContain('vol__error')
|
|
48
|
+
} finally { destroy() }
|
|
49
|
+
})
|
|
41
50
|
})
|
|
42
51
|
|
|
43
52
|
describe('SvGauge', () => {
|
package/src/ui-selection.test.ts
CHANGED
|
@@ -67,6 +67,31 @@ describe('SvDropDownList', () => {
|
|
|
67
67
|
expect(document.querySelector('.sv-ddl__panel')).toBeNull() // closed
|
|
68
68
|
} finally { destroy() }
|
|
69
69
|
})
|
|
70
|
+
it('readonly does not open the panel and marks aria-readonly', () => {
|
|
71
|
+
let got: any
|
|
72
|
+
const { target, destroy } = mnt(SvDropDownList, { options: OPTS, value: 'a', readonly: true, onChange: (v: any) => (got = v) })
|
|
73
|
+
try {
|
|
74
|
+
const trigger = target.querySelector<HTMLButtonElement>('.sv-ddl')!
|
|
75
|
+
expect(trigger.getAttribute('aria-readonly')).toBe('true')
|
|
76
|
+
expect(trigger.hasAttribute('disabled')).toBe(false) // still focusable
|
|
77
|
+
trigger.click(); flushSync()
|
|
78
|
+
expect(document.querySelector('.sv-ddl__panel')).toBeNull() // stayed closed
|
|
79
|
+
expect(got).toBeUndefined()
|
|
80
|
+
} finally { destroy() }
|
|
81
|
+
})
|
|
82
|
+
it('renders a resize grip only when resizable and the panel opens downward', () => {
|
|
83
|
+
const off = mnt(SvDropDownList, { options: OPTS, value: null })
|
|
84
|
+
try {
|
|
85
|
+
off.target.querySelector<HTMLButtonElement>('.sv-ddl')!.click(); flushSync()
|
|
86
|
+
expect(document.querySelector('.sv-ddl__grip')).toBeNull() // no grip by default
|
|
87
|
+
} finally { off.destroy() }
|
|
88
|
+
const on = mnt(SvDropDownList, { options: OPTS, value: null, resizable: true })
|
|
89
|
+
try {
|
|
90
|
+
on.target.querySelector<HTMLButtonElement>('.sv-ddl')!.click(); flushSync()
|
|
91
|
+
expect(document.querySelector('.sv-ddl__panel.is-resizable')).not.toBeNull()
|
|
92
|
+
expect(document.querySelector('.sv-ddl__grip')).not.toBeNull()
|
|
93
|
+
} finally { on.destroy() }
|
|
94
|
+
})
|
|
70
95
|
})
|
|
71
96
|
|
|
72
97
|
describe('SvComboBox', () => {
|
|
@@ -84,6 +109,17 @@ describe('SvComboBox', () => {
|
|
|
84
109
|
expect(got).toBe('b')
|
|
85
110
|
} finally { destroy() }
|
|
86
111
|
})
|
|
112
|
+
it('shows a resize grip on the open panel when resizable', () => {
|
|
113
|
+
const { target, destroy } = mnt(SvComboBox, { options: OPTS, value: null, resizable: true })
|
|
114
|
+
try {
|
|
115
|
+
const input = target.querySelector<HTMLInputElement>('.sv-combo__input')!
|
|
116
|
+
input.dispatchEvent(new FocusEvent('focus')); flushSync()
|
|
117
|
+
input.value = 'b'
|
|
118
|
+
input.dispatchEvent(new Event('input', { bubbles: true })); flushSync()
|
|
119
|
+
expect(document.querySelector('.sv-ddl__panel.is-resizable')).not.toBeNull()
|
|
120
|
+
expect(document.querySelector('.sv-ddl__grip')).not.toBeNull()
|
|
121
|
+
} finally { destroy() }
|
|
122
|
+
})
|
|
87
123
|
})
|
|
88
124
|
|
|
89
125
|
describe('SvTagsInput', () => {
|
package/src/ui-tier1.test.ts
CHANGED
|
@@ -46,7 +46,7 @@ describe('SvTextInput', () => {
|
|
|
46
46
|
const onChange = vi.fn()
|
|
47
47
|
const { target, destroy } = mountOn(SvTextInput, { value: 'x', clearable: true, onChange })
|
|
48
48
|
try {
|
|
49
|
-
const clear = target.querySelector<HTMLButtonElement>('.sv-
|
|
49
|
+
const clear = target.querySelector<HTMLButtonElement>('.sv-field__clear')!
|
|
50
50
|
expect(clear).not.toBeNull()
|
|
51
51
|
clear.click()
|
|
52
52
|
expect(onChange).toHaveBeenCalledWith('')
|
package/src/virtual.test.ts
CHANGED
|
@@ -48,12 +48,26 @@ describe('SvListBox virtual', () => {
|
|
|
48
48
|
const rendered = target.querySelectorAll('.sv-listbox__opt').length
|
|
49
49
|
expect(rendered).toBeGreaterThan(0)
|
|
50
50
|
expect(rendered).toBeLessThan(60) // nowhere near 5000
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
expect(
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
// Virtual mode is a JS-driven scroller: one clipped viewport holds the
|
|
52
|
+
// windowed rows (moved by `scrollOffset`, not native scroll) plus a custom
|
|
53
|
+
// scrollbar - so there is no native async-scroll gap to flash blank.
|
|
54
|
+
expect(target.querySelector('.sv-listbox__vp')).not.toBeNull()
|
|
55
|
+
expect(target.querySelector('.sv-listbox__sb')).not.toBeNull()
|
|
56
|
+
} finally { unmount(app); target.remove() }
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('windows a GROUPED list too (group headings render inside the window)', () => {
|
|
60
|
+
const roles = ['Eng', 'Design', 'Ops']
|
|
61
|
+
const options = Array.from({ length: 3000 }, (_, i) => ({ value: i, label: `User ${i}`, group: roles[i % 3] }))
|
|
62
|
+
const target = document.createElement('div')
|
|
63
|
+
document.body.appendChild(target)
|
|
64
|
+
const app = mount(SvListBox, { target, props: { options, virtual: true } as any })
|
|
65
|
+
flushSync()
|
|
66
|
+
try {
|
|
67
|
+
const rendered = target.querySelectorAll('.sv-listbox__opt').length
|
|
68
|
+
expect(rendered).toBeGreaterThan(0)
|
|
69
|
+
expect(rendered).toBeLessThan(60) // grouped list is windowed, not fully rendered
|
|
70
|
+
expect(target.querySelector('.sv-listbox__vp')).not.toBeNull()
|
|
57
71
|
} finally { unmount(app); target.remove() }
|
|
58
72
|
})
|
|
59
73
|
|