@svgrid/grid 2.2.26 → 2.2.27
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 +199 -145
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +179 -179
- package/dist/GridMenus.svelte +734 -728
- package/dist/SvAutoComplete.svelte +165 -165
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +101 -101
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +275 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +160 -160
- package/dist/SvDrawer.svelte +250 -250
- package/dist/SvDropDownList.svelte +374 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.css +2723 -2719
- package/dist/SvGrid.svelte +3333 -3333
- package/dist/SvGridChart.svelte +1724 -1724
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +696 -696
- package/dist/SvGridSelect.svelte +259 -259
- package/dist/SvGroupCell.svelte +109 -109
- package/dist/SvListBox.svelte +287 -287
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +281 -281
- package/dist/SvNumberInput.svelte +168 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +131 -131
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +200 -200
- package/dist/SvSwitchButton.svelte +107 -107
- package/dist/SvTagsInput.svelte +112 -112
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +204 -204
- package/dist/SvTree.svelte +437 -437
- package/dist/SvTreeSelect.svelte +233 -233
- package/dist/a11y/dismissable.d.ts +11 -0
- package/dist/a11y/dismissable.js +21 -0
- package/dist/ai.js +3 -3
- package/dist/cdn/GridMenus-BzxEYNtW.js +421 -0
- package/dist/cdn/GridMenus-Dr-nGEXn.js +417 -0
- package/dist/cdn/{SvDateTimePicker-CYC7gRER.js → SvDateTimePicker-B1sDc9Pz.js} +451 -444
- package/dist/cdn/{SvDateTimePicker-DtQKd4Ns.js → SvDateTimePicker-Bh0FTs1E.js} +114 -107
- package/dist/cdn/{SvGridChart-BhnFdLd0.js → SvGridChart-BKQsO5fZ.js} +1 -1
- package/dist/cdn/{SvGridChart-BcLCYvw9.js → SvGridChart-Bgk4hDs8.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DvjwxLvK.js → SvGridChartPanel-DSkHIQDd.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-Cmx9DHAS.js → SvGridChartPanel-DkCc2Z7b.js} +1 -1
- package/dist/cdn/{SvGridChartView-B40DTJSX.js → SvGridChartView-BrbVaELm.js} +1 -1
- package/dist/cdn/{SvGridChartView-PwqI4s02.js → SvGridChartView-i9rWvSKg.js} +1 -1
- package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-CGxxopQ4.js} +1 -1
- package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D08sVmAy.js} +1 -1
- package/dist/cdn/{src-DysW9qbk.js → src-Cw8d9U8j.js} +5753 -5753
- package/dist/cdn/{src-bYfr_7aR.js → src-cgs1wcUS.js} +8102 -8102
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +9 -9
- package/dist/chart-export.js +8 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +30 -13
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +179 -179
- package/src/GridMenus.svelte +734 -728
- package/src/SvAutoComplete.svelte +165 -165
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +101 -101
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +275 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +160 -160
- package/src/SvDrawer.svelte +250 -250
- package/src/SvDropDownList.svelte +374 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +3239 -3239
- package/src/SvGrid.css +2723 -2719
- package/src/SvGrid.svelte +3333 -3333
- package/src/SvGrid.types.ts +1613 -1613
- package/src/SvGridChart.svelte +1724 -1724
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +696 -696
- package/src/SvGridSelect.svelte +259 -259
- package/src/SvGroupCell.svelte +109 -109
- package/src/SvListBox.svelte +287 -287
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +281 -281
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +131 -131
- package/src/SvPopover.svelte +197 -197
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +200 -200
- package/src/SvSwitchButton.svelte +107 -107
- package/src/SvTagsInput.svelte +112 -112
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +204 -204
- package/src/SvTree.svelte +437 -437
- package/src/SvTreeSelect.svelte +233 -233
- package/src/a11y/dismissable.test.ts +119 -95
- package/src/a11y/dismissable.ts +114 -94
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1391 -1391
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +895 -895
- package/src/builtin-editors.grid.test.ts +85 -85
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/chart-export.ts +201 -201
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2296 -2296
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1111 -1111
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createMenu.svelte.ts +225 -225
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +319 -319
- package/src/editing.test.ts +859 -859
- package/src/editing.ts +675 -675
- package/src/editor-contract.ts +157 -157
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/filter-operators.ts +157 -157
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/index.ts +713 -712
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +172 -172
- package/src/menus.test.ts +700 -700
- package/src/menus.ts +652 -652
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +872 -872
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +600 -600
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +489 -489
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +524 -524
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-menu-scroll.test.ts +102 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +215 -215
- package/src/toast-store.svelte.ts +246 -246
- package/src/toast-store.test.ts +147 -147
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
- package/themes/ember.css +71 -71
- package/dist/cdn/GridMenus-GfCAHgkZ.js +0 -420
- package/dist/cdn/GridMenus-GiVNSeDU.js +0 -416
package/src/merge-objects.ts
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Merges objects together while keeping their getters alive.
|
|
3
|
-
* Taken from SolidJS:
|
|
4
|
-
* https://github.com/solidjs/solid/blob/24abc825c0996fd2bc8c1de1491efe9a7e743aff/packages/solid/src/server/rendering.ts#L82-L115
|
|
5
|
-
*/
|
|
6
|
-
export function mergeObjects<T>(source: T): T
|
|
7
|
-
export function mergeObjects<T, U>(source: T, source1: U): T & U
|
|
8
|
-
export function mergeObjects<T, U, V>(
|
|
9
|
-
source: T,
|
|
10
|
-
source1: U,
|
|
11
|
-
source2: V,
|
|
12
|
-
): T & U & V
|
|
13
|
-
export function mergeObjects<T, U, V, W>(
|
|
14
|
-
source: T,
|
|
15
|
-
source1: U,
|
|
16
|
-
source2: V,
|
|
17
|
-
source3: W,
|
|
18
|
-
): T & U & V & W
|
|
19
|
-
export function mergeObjects(...sources: any): any {
|
|
20
|
-
const target = {}
|
|
21
|
-
|
|
22
|
-
for (let source of sources) {
|
|
23
|
-
if (typeof source === 'function') source = source()
|
|
24
|
-
|
|
25
|
-
if (source) {
|
|
26
|
-
const descriptors = Object.getOwnPropertyDescriptors(source)
|
|
27
|
-
|
|
28
|
-
for (const key in descriptors) {
|
|
29
|
-
if (key in target) continue
|
|
30
|
-
|
|
31
|
-
Object.defineProperty(target, key, {
|
|
32
|
-
enumerable: true,
|
|
33
|
-
get() {
|
|
34
|
-
for (let i = sources.length - 1; i >= 0; i--) {
|
|
35
|
-
let scopedSource = sources[i]
|
|
36
|
-
if (typeof scopedSource === 'function') scopedSource = scopedSource()
|
|
37
|
-
|
|
38
|
-
const value = (scopedSource || {})[key]
|
|
39
|
-
if (value !== undefined) return value
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return target
|
|
48
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Merges objects together while keeping their getters alive.
|
|
3
|
+
* Taken from SolidJS:
|
|
4
|
+
* https://github.com/solidjs/solid/blob/24abc825c0996fd2bc8c1de1491efe9a7e743aff/packages/solid/src/server/rendering.ts#L82-L115
|
|
5
|
+
*/
|
|
6
|
+
export function mergeObjects<T>(source: T): T
|
|
7
|
+
export function mergeObjects<T, U>(source: T, source1: U): T & U
|
|
8
|
+
export function mergeObjects<T, U, V>(
|
|
9
|
+
source: T,
|
|
10
|
+
source1: U,
|
|
11
|
+
source2: V,
|
|
12
|
+
): T & U & V
|
|
13
|
+
export function mergeObjects<T, U, V, W>(
|
|
14
|
+
source: T,
|
|
15
|
+
source1: U,
|
|
16
|
+
source2: V,
|
|
17
|
+
source3: W,
|
|
18
|
+
): T & U & V & W
|
|
19
|
+
export function mergeObjects(...sources: any): any {
|
|
20
|
+
const target = {}
|
|
21
|
+
|
|
22
|
+
for (let source of sources) {
|
|
23
|
+
if (typeof source === 'function') source = source()
|
|
24
|
+
|
|
25
|
+
if (source) {
|
|
26
|
+
const descriptors = Object.getOwnPropertyDescriptors(source)
|
|
27
|
+
|
|
28
|
+
for (const key in descriptors) {
|
|
29
|
+
if (key in target) continue
|
|
30
|
+
|
|
31
|
+
Object.defineProperty(target, key, {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get() {
|
|
34
|
+
for (let i = sources.length - 1; i >= 0; i--) {
|
|
35
|
+
let scopedSource = sources[i]
|
|
36
|
+
if (typeof scopedSource === 'function') scopedSource = scopedSource()
|
|
37
|
+
|
|
38
|
+
const value = (scopedSource || {})[key]
|
|
39
|
+
if (value !== undefined) return value
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return target
|
|
48
|
+
}
|
package/src/overlays.test.ts
CHANGED
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component tests for the overlay trio: SvModal (dialog semantics, Escape /
|
|
3
|
-
* backdrop close), SvPopover (click open + outside close) and SvTooltip
|
|
4
|
-
* (focus-triggered, aria-describedby wiring).
|
|
5
|
-
*/
|
|
6
|
-
import { describe, expect, it, vi, afterEach } from 'vitest'
|
|
7
|
-
import { mount, unmount, flushSync, createRawSnippet } from 'svelte'
|
|
8
|
-
import SvModal from './SvModal.svelte'
|
|
9
|
-
import SvPopover from './SvPopover.svelte'
|
|
10
|
-
import SvTooltip from './SvTooltip.svelte'
|
|
11
|
-
|
|
12
|
-
const body = createRawSnippet(() => ({ render: () => `<p class="body">hello</p>` }))
|
|
13
|
-
const anchorBtn = createRawSnippet(() => ({ render: () => `<button class="anchor">open</button>` }))
|
|
14
|
-
|
|
15
|
-
function mountC(Comp: any, props: Record<string, unknown>) {
|
|
16
|
-
const target = document.createElement('div')
|
|
17
|
-
document.body.appendChild(target)
|
|
18
|
-
const app = mount(Comp, { target, props: props as any })
|
|
19
|
-
flushSync()
|
|
20
|
-
return { target, destroy: () => { unmount(app); target.remove() } }
|
|
21
|
-
}
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
document.querySelectorAll('.sv-modal__backdrop, .sv-pop__panel, .sv-tip').forEach((n) => n.remove())
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
describe('SvModal', () => {
|
|
27
|
-
it('renders a labelled modal dialog when open', () => {
|
|
28
|
-
const { destroy } = mountC(SvModal, { open: true, title: 'Edit', children: body })
|
|
29
|
-
try {
|
|
30
|
-
const dialog = document.querySelector<HTMLElement>('.sv-modal')!
|
|
31
|
-
expect(dialog.getAttribute('role')).toBe('dialog')
|
|
32
|
-
expect(dialog.getAttribute('aria-modal')).toBe('true')
|
|
33
|
-
const titleId = dialog.getAttribute('aria-labelledby')!
|
|
34
|
-
expect(document.getElementById(titleId)!.textContent).toBe('Edit')
|
|
35
|
-
expect(document.querySelector('.sv-modal .body')!.textContent).toBe('hello')
|
|
36
|
-
} finally { destroy() }
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('Escape and the close button both fire onClose', () => {
|
|
40
|
-
let closed = 0
|
|
41
|
-
const { destroy } = mountC(SvModal, { open: true, title: 'X', onClose: () => (closed++), children: body })
|
|
42
|
-
try {
|
|
43
|
-
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
44
|
-
flushSync()
|
|
45
|
-
expect(closed).toBe(1)
|
|
46
|
-
} finally { destroy() }
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
describe('SvPopover', () => {
|
|
51
|
-
it('opens the panel on anchor click and closes on outside pointerdown', () => {
|
|
52
|
-
const { target, destroy } = mountC(SvPopover, { anchor: anchorBtn, children: body })
|
|
53
|
-
try {
|
|
54
|
-
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
55
|
-
target.querySelector<HTMLElement>('.anchor')!.click()
|
|
56
|
-
flushSync()
|
|
57
|
-
const panel = document.querySelector<HTMLElement>('.sv-pop__panel')!
|
|
58
|
-
expect(panel).not.toBeNull()
|
|
59
|
-
expect(panel.getAttribute('role')).toBe('dialog')
|
|
60
|
-
// Outside click closes.
|
|
61
|
-
document.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
62
|
-
flushSync()
|
|
63
|
-
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
64
|
-
} finally { destroy() }
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe('SvTooltip', () => {
|
|
69
|
-
it('shows after the delay on focus and wires aria-describedby', () => {
|
|
70
|
-
vi.useFakeTimers()
|
|
71
|
-
try {
|
|
72
|
-
const child = createRawSnippet(() => ({ render: () => `<button class="t">i</button>` }))
|
|
73
|
-
const { target, destroy } = mountC(SvTooltip, { text: 'Tip text', delay: 200, children: child })
|
|
74
|
-
const anchor = target.querySelector<HTMLElement>('.sv-tip__anchor')!
|
|
75
|
-
anchor.dispatchEvent(new FocusEvent('focusin', { bubbles: true }))
|
|
76
|
-
vi.advanceTimersByTime(210)
|
|
77
|
-
flushSync()
|
|
78
|
-
const tip = document.querySelector<HTMLElement>('.sv-tip')!
|
|
79
|
-
expect(tip).not.toBeNull()
|
|
80
|
-
expect(tip.getAttribute('role')).toBe('tooltip')
|
|
81
|
-
expect(anchor.getAttribute('aria-describedby')).toBe(tip.id)
|
|
82
|
-
// The wrapper span is never itself focusable, so AT users only ever land
|
|
83
|
-
// on the real interactive child below - it must carry the description
|
|
84
|
-
// too, or it's never announced on focus (see SvTooltip.svelte effect).
|
|
85
|
-
const focusEl = target.querySelector<HTMLElement>('.t')!
|
|
86
|
-
expect(focusEl.getAttribute('aria-describedby')).toBe(tip.id)
|
|
87
|
-
destroy()
|
|
88
|
-
} finally { vi.useRealTimers() }
|
|
89
|
-
})
|
|
90
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* Component tests for the overlay trio: SvModal (dialog semantics, Escape /
|
|
3
|
+
* backdrop close), SvPopover (click open + outside close) and SvTooltip
|
|
4
|
+
* (focus-triggered, aria-describedby wiring).
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it, vi, afterEach } from 'vitest'
|
|
7
|
+
import { mount, unmount, flushSync, createRawSnippet } from 'svelte'
|
|
8
|
+
import SvModal from './SvModal.svelte'
|
|
9
|
+
import SvPopover from './SvPopover.svelte'
|
|
10
|
+
import SvTooltip from './SvTooltip.svelte'
|
|
11
|
+
|
|
12
|
+
const body = createRawSnippet(() => ({ render: () => `<p class="body">hello</p>` }))
|
|
13
|
+
const anchorBtn = createRawSnippet(() => ({ render: () => `<button class="anchor">open</button>` }))
|
|
14
|
+
|
|
15
|
+
function mountC(Comp: any, props: Record<string, unknown>) {
|
|
16
|
+
const target = document.createElement('div')
|
|
17
|
+
document.body.appendChild(target)
|
|
18
|
+
const app = mount(Comp, { target, props: props as any })
|
|
19
|
+
flushSync()
|
|
20
|
+
return { target, destroy: () => { unmount(app); target.remove() } }
|
|
21
|
+
}
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.querySelectorAll('.sv-modal__backdrop, .sv-pop__panel, .sv-tip').forEach((n) => n.remove())
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('SvModal', () => {
|
|
27
|
+
it('renders a labelled modal dialog when open', () => {
|
|
28
|
+
const { destroy } = mountC(SvModal, { open: true, title: 'Edit', children: body })
|
|
29
|
+
try {
|
|
30
|
+
const dialog = document.querySelector<HTMLElement>('.sv-modal')!
|
|
31
|
+
expect(dialog.getAttribute('role')).toBe('dialog')
|
|
32
|
+
expect(dialog.getAttribute('aria-modal')).toBe('true')
|
|
33
|
+
const titleId = dialog.getAttribute('aria-labelledby')!
|
|
34
|
+
expect(document.getElementById(titleId)!.textContent).toBe('Edit')
|
|
35
|
+
expect(document.querySelector('.sv-modal .body')!.textContent).toBe('hello')
|
|
36
|
+
} finally { destroy() }
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('Escape and the close button both fire onClose', () => {
|
|
40
|
+
let closed = 0
|
|
41
|
+
const { destroy } = mountC(SvModal, { open: true, title: 'X', onClose: () => (closed++), children: body })
|
|
42
|
+
try {
|
|
43
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
|
|
44
|
+
flushSync()
|
|
45
|
+
expect(closed).toBe(1)
|
|
46
|
+
} finally { destroy() }
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('SvPopover', () => {
|
|
51
|
+
it('opens the panel on anchor click and closes on outside pointerdown', () => {
|
|
52
|
+
const { target, destroy } = mountC(SvPopover, { anchor: anchorBtn, children: body })
|
|
53
|
+
try {
|
|
54
|
+
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
55
|
+
target.querySelector<HTMLElement>('.anchor')!.click()
|
|
56
|
+
flushSync()
|
|
57
|
+
const panel = document.querySelector<HTMLElement>('.sv-pop__panel')!
|
|
58
|
+
expect(panel).not.toBeNull()
|
|
59
|
+
expect(panel.getAttribute('role')).toBe('dialog')
|
|
60
|
+
// Outside click closes.
|
|
61
|
+
document.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))
|
|
62
|
+
flushSync()
|
|
63
|
+
expect(document.querySelector('.sv-pop__panel')).toBeNull()
|
|
64
|
+
} finally { destroy() }
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('SvTooltip', () => {
|
|
69
|
+
it('shows after the delay on focus and wires aria-describedby', () => {
|
|
70
|
+
vi.useFakeTimers()
|
|
71
|
+
try {
|
|
72
|
+
const child = createRawSnippet(() => ({ render: () => `<button class="t">i</button>` }))
|
|
73
|
+
const { target, destroy } = mountC(SvTooltip, { text: 'Tip text', delay: 200, children: child })
|
|
74
|
+
const anchor = target.querySelector<HTMLElement>('.sv-tip__anchor')!
|
|
75
|
+
anchor.dispatchEvent(new FocusEvent('focusin', { bubbles: true }))
|
|
76
|
+
vi.advanceTimersByTime(210)
|
|
77
|
+
flushSync()
|
|
78
|
+
const tip = document.querySelector<HTMLElement>('.sv-tip')!
|
|
79
|
+
expect(tip).not.toBeNull()
|
|
80
|
+
expect(tip.getAttribute('role')).toBe('tooltip')
|
|
81
|
+
expect(anchor.getAttribute('aria-describedby')).toBe(tip.id)
|
|
82
|
+
// The wrapper span is never itself focusable, so AT users only ever land
|
|
83
|
+
// on the real interactive child below - it must carry the description
|
|
84
|
+
// too, or it's never announced on focus (see SvTooltip.svelte effect).
|
|
85
|
+
const focusEl = target.querySelector<HTMLElement>('.t')!
|
|
86
|
+
expect(focusEl.getAttribute('aria-describedby')).toBe(tip.id)
|
|
87
|
+
destroy()
|
|
88
|
+
} finally { vi.useRealTimers() }
|
|
89
|
+
})
|
|
90
|
+
})
|
package/src/render-component.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import type { Component, ComponentProps, Snippet } from 'svelte'
|
|
2
|
-
|
|
3
|
-
export class RenderComponentConfig<TComponent extends Component> {
|
|
4
|
-
constructor(
|
|
5
|
-
public component: TComponent,
|
|
6
|
-
public props?: ComponentProps<TComponent> | Record<string, never>,
|
|
7
|
-
) {}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class RenderSnippetConfig<TProps> {
|
|
11
|
-
constructor(
|
|
12
|
-
public snippet: Snippet<[TProps]>,
|
|
13
|
-
public params?: TProps,
|
|
14
|
-
) {}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const renderComponent = <
|
|
18
|
-
TComponent extends Component<any>,
|
|
19
|
-
TProps extends ComponentProps<TComponent>,
|
|
20
|
-
>(
|
|
21
|
-
component: TComponent,
|
|
22
|
-
props?: TProps,
|
|
23
|
-
) => new RenderComponentConfig(component, props)
|
|
24
|
-
|
|
25
|
-
export const renderSnippet = <TProps>(
|
|
26
|
-
snippet: Snippet<[TProps]>,
|
|
27
|
-
params?: TProps,
|
|
28
|
-
) => new RenderSnippetConfig(snippet, params)
|
|
1
|
+
import type { Component, ComponentProps, Snippet } from 'svelte'
|
|
2
|
+
|
|
3
|
+
export class RenderComponentConfig<TComponent extends Component> {
|
|
4
|
+
constructor(
|
|
5
|
+
public component: TComponent,
|
|
6
|
+
public props?: ComponentProps<TComponent> | Record<string, never>,
|
|
7
|
+
) {}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class RenderSnippetConfig<TProps> {
|
|
11
|
+
constructor(
|
|
12
|
+
public snippet: Snippet<[TProps]>,
|
|
13
|
+
public params?: TProps,
|
|
14
|
+
) {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const renderComponent = <
|
|
18
|
+
TComponent extends Component<any>,
|
|
19
|
+
TProps extends ComponentProps<TComponent>,
|
|
20
|
+
>(
|
|
21
|
+
component: TComponent,
|
|
22
|
+
props?: TProps,
|
|
23
|
+
) => new RenderComponentConfig(component, props)
|
|
24
|
+
|
|
25
|
+
export const renderSnippet = <TProps>(
|
|
26
|
+
snippet: Snippet<[TProps]>,
|
|
27
|
+
params?: TProps,
|
|
28
|
+
) => new RenderSnippetConfig(snippet, params)
|