@svgrid/grid 2.2.27 → 2.2.28
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 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +179 -179
- package/dist/GridMenus.svelte +734 -734
- 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 +254 -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 -2723
- 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 +133 -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/cdn/{GridMenus-BzxEYNtW.js → GridMenus-BbzEvTYO.js} +2 -2
- package/dist/cdn/{GridMenus-Dr-nGEXn.js → GridMenus-E220X2kM.js} +2 -2
- package/dist/cdn/{SvDateTimePicker-B1sDc9Pz.js → SvDateTimePicker-B8GopjhC.js} +2 -2
- package/dist/cdn/{SvDateTimePicker-Bh0FTs1E.js → SvDateTimePicker-DB0aIEk8.js} +2 -2
- package/dist/cdn/{SvGridChart-Bgk4hDs8.js → SvGridChart-BcLCYvw9.js} +1 -1
- package/dist/cdn/{SvGridChart-BKQsO5fZ.js → SvGridChart-BhnFdLd0.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DkCc2Z7b.js → SvGridChartPanel-Cmx9DHAS.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DSkHIQDd.js → SvGridChartPanel-DvjwxLvK.js} +1 -1
- package/dist/cdn/{SvGridChartView-BrbVaELm.js → SvGridChartView-B40DTJSX.js} +1 -1
- package/dist/cdn/{SvGridChartView-i9rWvSKg.js → SvGridChartView-PwqI4s02.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D08sVmAy.js → SvGridDropdown-DjALc_4f.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CGxxopQ4.js → SvGridDropdown-hpnHvOGt.js} +1 -1
- package/dist/cdn/{src-cgs1wcUS.js → src-BxNEslEo.js} +25 -25
- package/dist/cdn/{src-Cw8d9U8j.js → src-C4yKQZ5t.js} +26 -26
- package/dist/cdn/svgrid.js +5 -5
- package/dist/cdn/svgrid.svelte-external.js +5 -5
- package/dist/chart-export.js +8 -8
- package/package.json +10 -10
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +179 -179
- package/src/GridMenus.svelte +734 -734
- 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 +254 -250
- package/src/SvDrawer.test.ts +18 -1
- 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 -2723
- 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/SvNavPane.test.ts +15 -4
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -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 -119
- package/src/a11y/dismissable.ts +114 -114
- 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 -713
- 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.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
|
@@ -1,251 +1,251 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Source-level smoke tests for the wrapper additions: `getRowId` prop,
|
|
3
|
-
* `cellClass` / `rowClass` rendering, the column-layout API surface
|
|
4
|
-
* (`setColumnWidth` / `getColumnWidths` / `setColumnPinning` /
|
|
5
|
-
* `getColumnPinning`), the `between` operator UI, and the multi-cell
|
|
6
|
-
* paste / cut keyboard plumbing.
|
|
7
|
-
*
|
|
8
|
-
* These are deliberately source-string assertions, not jsdom mounts -
|
|
9
|
-
* Svelte 5 mount inside vitest is brittle (the existing
|
|
10
|
-
* `svgrid.wrapper.test.ts` follows the same pattern). The behaviour
|
|
11
|
-
* itself is covered by integration probes in the gallery (see the
|
|
12
|
-
* pivot expansion + paste/cut probes that were run during build) and
|
|
13
|
-
* the upstream unit tests of the headless engine + Pro pivot.
|
|
14
|
-
*/
|
|
15
|
-
import { readFileSync } from 'node:fs'
|
|
16
|
-
import { resolve } from 'node:path'
|
|
17
|
-
import { describe, expect, it } from 'vitest'
|
|
18
|
-
|
|
19
|
-
// The wrapper implementation spans SvGrid.svelte plus its extracted
|
|
20
|
-
// sibling modules (types + pure helpers). Concatenate them so these
|
|
21
|
-
// "the wrapper declares/exposes X" assertions hold wherever the code
|
|
22
|
-
// physically lives.
|
|
23
|
-
const source = [
|
|
24
|
-
'./SvGrid.svelte',
|
|
25
|
-
'./SvGrid.controller.svelte.ts',
|
|
26
|
-
'./GridMenus.svelte',
|
|
27
|
-
'./GridFooter.svelte',
|
|
28
|
-
'./SvGrid.types.ts',
|
|
29
|
-
'./SvGrid.helpers.ts',
|
|
30
|
-
'./filter-operators.ts',
|
|
31
|
-
'./facet-buckets.ts',
|
|
32
|
-
'./cell-values.ts',
|
|
33
|
-
'./clipboard.ts',
|
|
34
|
-
'./build-api.ts',
|
|
35
|
-
'./columns.ts',
|
|
36
|
-
'./selection.ts',
|
|
37
|
-
'./editing.ts',
|
|
38
|
-
'./cell-render.ts',
|
|
39
|
-
'./menus.ts',
|
|
40
|
-
'./summaries.ts',
|
|
41
|
-
'./keyboard-handlers.ts',
|
|
42
|
-
'./scroll-sync.ts',
|
|
43
|
-
'./features.ts',
|
|
44
|
-
]
|
|
45
|
-
.map((p) => readFileSync(resolve(__dirname, p), 'utf-8'))
|
|
46
|
-
.join('\n')
|
|
47
|
-
const typesPath = resolve(__dirname, './svgrid-wrapper.types.ts')
|
|
48
|
-
const types = readFileSync(typesPath, 'utf-8')
|
|
49
|
-
|
|
50
|
-
describe('SvGrid wrapper - getRowId prop', () => {
|
|
51
|
-
it('declares the getRowId prop on the Props type', () => {
|
|
52
|
-
expect(source).toMatch(/getRowId\?:\s*\(row:\s*TData,\s*index:\s*number\)\s*=>\s*string/)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('forwards the prop into createSvGrid options', () => {
|
|
56
|
-
expect(source).toMatch(/get getRowId\(\)[\s\S]*?return props\.getRowId/)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('SvGrid wrapper - cellClass + rowClass', () => {
|
|
61
|
-
it('declares the rowClass prop on the Props type', () => {
|
|
62
|
-
expect(source).toMatch(/rowClass\?:\s*\(ctx:\s*\{/)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('exposes a resolveClassList helper that normalises array / record / string forms', () => {
|
|
66
|
-
expect(source).toMatch(/function resolveClassList\(/)
|
|
67
|
-
expect(source).toMatch(/Array\.isArray\(value\)/)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('computes the row-level class once per render row and threads it onto the <tr>', () => {
|
|
71
|
-
expect(source).toMatch(/function computeRowClass/)
|
|
72
|
-
expect(source).toMatch(/userRowClass = computeRowClass\(row, rowIndex\)/)
|
|
73
|
-
expect(source).toMatch(/class=\{`sv-grid-row \$\{userRowClass\}/)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('computes the cell-level class per column and threads it onto the <td>', () => {
|
|
77
|
-
expect(source).toMatch(/function computeCellClass/)
|
|
78
|
-
expect(source).toMatch(/userCellClass = computeCellClass\(\s*row,\s*rendered\.column,?\s*\)/)
|
|
79
|
-
expect(source).toMatch(/class=\{`sv-grid-cell \$\{userCellClass\}`\}/)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('SvGrid wrapper - declarative cell validation (validate hook)', () => {
|
|
84
|
-
it('reads the column validate hook with the documented return union', () => {
|
|
85
|
-
expect(source).toMatch(/column\.columnDef\.validate/)
|
|
86
|
-
expect(source).toMatch(/=>\s*string\s*\|\s*boolean\s*\|\s*null\s*\|\s*undefined/)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('computes per-cell validity in the render layer (runs for every rendered cell)', () => {
|
|
90
|
-
expect(source).toMatch(/function computeCellValidity/)
|
|
91
|
-
expect(source).toMatch(/columnDef\.validate/)
|
|
92
|
-
// Valid when null / undefined / true; invalid on false or a message.
|
|
93
|
-
expect(source).toMatch(/if \(out == null \|\| out === true\)/)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('threads the invalid class + message tooltip onto both <td> render paths', () => {
|
|
97
|
-
const hits = source.match(/class:sv-grid-cell-invalid=\{cellValidity\.invalid\}/g) ?? []
|
|
98
|
-
expect(hits.length).toBeGreaterThanOrEqual(2)
|
|
99
|
-
// Message wins over the plain column tooltip when the cell is invalid.
|
|
100
|
-
expect(source).toMatch(/cellValidity\.invalid && cellValidity\.message/)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('forwards computeCellValidity through the controller so the wrapper can read it', () => {
|
|
104
|
-
expect(source).toMatch(/get computeCellValidity\(\)\s*\{\s*return computeCellValidity;/)
|
|
105
|
-
expect(source).toMatch(/computeCellValidity\s*=\s*\$derived\(ctrl\.computeCellValidity\)/)
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
describe('SvGrid wrapper - column-layout API', () => {
|
|
110
|
-
it('declares setColumnWidth + getColumnWidths in the public type', () => {
|
|
111
|
-
expect(types).toContain('setColumnWidth(columnId: string, width: number): void')
|
|
112
|
-
expect(types).toContain('getColumnWidths(): Record<string, number>')
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('declares setColumnPinning + getColumnPinning in the public type', () => {
|
|
116
|
-
expect(types).toMatch(/setColumnPinning\(pinning:\s*\{/)
|
|
117
|
-
expect(types).toContain('getColumnPinning(): { left: string[]; right: string[] }')
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('implements all four methods in the api object', () => {
|
|
121
|
-
expect(source).toMatch(/setColumnWidth\(columnId: string, width: number\)/)
|
|
122
|
-
expect(source).toMatch(/getColumnWidths\(\)/)
|
|
123
|
-
expect(source).toMatch(/setColumnPinning\(pinning\)/)
|
|
124
|
-
expect(source).toMatch(/getColumnPinning\(\)/)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
it('clamps setColumnWidth to MIN_COLUMN_WIDTH', () => {
|
|
128
|
-
// Confirm the clamp is in the code path so a caller can't drop a
|
|
129
|
-
// column below the minimum by passing 0 or a negative number.
|
|
130
|
-
expect(source).toMatch(/Math\.max\(MIN_COLUMN_WIDTH/)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('defensive-copies setColumnPinning inputs so callers cannot mutate state', () => {
|
|
134
|
-
expect(source).toMatch(/Array\.from\(new Set\(pinning\.left/)
|
|
135
|
-
expect(source).toMatch(/Array\.from\(new Set\(pinning\.right/)
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
describe('SvGrid wrapper - between filter operator', () => {
|
|
140
|
-
it("includes 'between' in the FilterOperator type", () => {
|
|
141
|
-
expect(source).toMatch(/"between"/)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it("includes 'between' in NUMBER_OPERATORS + DATE_OPERATORS but NOT in TEXT_OPERATORS", () => {
|
|
145
|
-
// The list literals appear in order, so we slice each block out
|
|
146
|
-
// separately and check membership.
|
|
147
|
-
const numOps = source.match(/const NUMBER_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
148
|
-
expect(numOps).toContain('"between"')
|
|
149
|
-
const dateOps = source.match(/const DATE_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
150
|
-
expect(dateOps).toContain('"between"')
|
|
151
|
-
const textOps = source.match(/const TEXT_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
152
|
-
expect(textOps).not.toContain('"between"')
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('renders a second input ("To") when the operator is between', () => {
|
|
156
|
-
expect(source).toMatch(/menuActiveOperator === "between"/)
|
|
157
|
-
expect(source).toMatch(/placeholder="To"/)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('treats the filter as inactive until BOTH endpoints are non-empty', () => {
|
|
161
|
-
// The active-filters filter() should require value AND valueTo.
|
|
162
|
-
expect(source).toMatch(/if \(f\.operator === "between"\) \{[^}]*f\.value\.trim\(\)\.length > 0[^}]*f\.valueTo[^}]*\.trim\(\)\.length > 0/)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('forwards valueTo through applyExcelFilter', () => {
|
|
166
|
-
expect(source).toMatch(/valueTo:\s*(filter\.operator|op) === "between" \? (filter\.valueTo|valueTo) : undefined/)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
it('publishes valueTo in the onFiltersChange payload only for between clauses', () => {
|
|
170
|
-
expect(source).toMatch(/f\.operator === "between" && f\.valueTo\s*\?\s*\{ valueTo: f\.valueTo \}/)
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('exposes valueTo on the imperative setFilter input + getFilters output', () => {
|
|
174
|
-
expect(types).toMatch(/setFilter\(\s*columnId: string,[\s\S]*?valueTo\?: string/)
|
|
175
|
-
expect(types).toMatch(/getFilters\(\)[\s\S]*?valueTo\?: string/)
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
describe('SvGrid wrapper - multi-cell paste + cut + delete', () => {
|
|
180
|
-
it('fills the selection range when the clipboard payload is 1x1', () => {
|
|
181
|
-
expect(source).toMatch(/clipboardIsSingleCell\s*=\s*\n?\s*lines\.length === 1 && \(lines\[0\]\?\.split\("\\t"\)\.length \?\? 0\) === 1/)
|
|
182
|
-
expect(source).toMatch(/selectionIsRange = startRow !== endRow \|\| startCol !== endCol/)
|
|
183
|
-
expect(source).toMatch(/fillRange = clipboardIsSingleCell && selectionIsRange/)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('walks the entire selection rect when filling', () => {
|
|
187
|
-
expect(source).toMatch(/const rowSpan = fillRange \? endRow - startRow \+ 1 : lines\.length/)
|
|
188
|
-
expect(source).toMatch(/const colSpan = fillRange/)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('implements clearSelectedCells() that touches every editable cell in the range', () => {
|
|
192
|
-
expect(source).toMatch(/function clearSelectedCells\(\): boolean/)
|
|
193
|
-
expect(source).toMatch(/parseEditorValue\(editorType, ""\)/)
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('wires Ctrl/Cmd+X to cutSelectionToClipboard', () => {
|
|
197
|
-
expect(source).toMatch(/lower === "x"/)
|
|
198
|
-
expect(source).toMatch(/void (?:ctx\.)?cutSelectionToClipboard\(\)/)
|
|
199
|
-
expect(source).toMatch(/async function cutSelectionToClipboard/)
|
|
200
|
-
// Confirm both calls live inside the cutSelectionToClipboard body
|
|
201
|
-
// (copy first, then clear). A comment between them is fine.
|
|
202
|
-
const body = source.match(
|
|
203
|
-
/async function cutSelectionToClipboard\(\)[^{]*\{([\s\S]*?)^\s\s\}/m,
|
|
204
|
-
)?.[1] ?? ''
|
|
205
|
-
expect(body).toContain('copySelectionToClipboard();')
|
|
206
|
-
expect(body).toContain('clearSelectedCells();')
|
|
207
|
-
expect(body.indexOf('copySelectionToClipboard()')).toBeLessThan(
|
|
208
|
-
body.indexOf('clearSelectedCells()'),
|
|
209
|
-
)
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('wires Delete + Backspace to clearSelectedCells (without clipboard interaction)', () => {
|
|
213
|
-
expect(source).toMatch(/event\.key === "Delete" \|\| event\.key === "Backspace"/)
|
|
214
|
-
expect(source).toMatch(/if \((?:ctx\.)?clearSelectedCells\(\)\)\s*\{/)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
// Regression: the controller's ctx is an object literal of getters, and the
|
|
218
|
-
// copy/cut/paste handlers in keyboard-handlers + menus reach the functions
|
|
219
|
-
// via `ctx.<name>()`. A refactor once shipped the `ctx.cutSelectionToClipboard`
|
|
220
|
-
// / `ctx.pasteFromClipboard` CALLS but dropped their getters, so those threw
|
|
221
|
-
// "is not a function" at runtime - copy worked, cut + paste were dead. Assert
|
|
222
|
-
// every clipboard function called through ctx has a matching getter.
|
|
223
|
-
it('exposes a ctx getter for every clipboard command it calls through ctx', () => {
|
|
224
|
-
for (const fn of [
|
|
225
|
-
'copySelectionToClipboard',
|
|
226
|
-
'cutSelectionToClipboard',
|
|
227
|
-
'pasteFromClipboard',
|
|
228
|
-
'onGridPaste',
|
|
229
|
-
'clearSelectedCells',
|
|
230
|
-
]) {
|
|
231
|
-
expect(source).toMatch(new RegExp(`get ${fn}\\(\\)\\s*\\{\\s*return ${fn};?\\s*\\}`))
|
|
232
|
-
}
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('copy/cut fall back to execCommand when navigator.clipboard is absent (insecure context)', () => {
|
|
236
|
-
expect(source).toMatch(/function writeClipboardText\(/)
|
|
237
|
-
expect(source).toMatch(/document\.execCommand\("copy"\)/)
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('paste falls back to a native paste event when the async API is unavailable', () => {
|
|
241
|
-
// keydown only preventDefaults (and uses the async API) in a secure context;
|
|
242
|
-
// otherwise it lets the browser deliver a native paste event to onGridPaste.
|
|
243
|
-
// Accept either the positive branch or the inverted early-return guard
|
|
244
|
-
// (`if (!navigator.clipboard?.readText) return`) - both gate on the async API.
|
|
245
|
-
expect(source).toMatch(/if \(!?navigator\.clipboard\?\.readText\)/)
|
|
246
|
-
expect(source).toMatch(/function onGridPaste\(event: ClipboardEvent\)/)
|
|
247
|
-
expect(source).toMatch(/clipboardData\?\.getData\("text\/plain"\)/)
|
|
248
|
-
// The grid root wires the handler.
|
|
249
|
-
expect(source).toMatch(/onpaste=\{onGridPaste\}/)
|
|
250
|
-
})
|
|
251
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* Source-level smoke tests for the wrapper additions: `getRowId` prop,
|
|
3
|
+
* `cellClass` / `rowClass` rendering, the column-layout API surface
|
|
4
|
+
* (`setColumnWidth` / `getColumnWidths` / `setColumnPinning` /
|
|
5
|
+
* `getColumnPinning`), the `between` operator UI, and the multi-cell
|
|
6
|
+
* paste / cut keyboard plumbing.
|
|
7
|
+
*
|
|
8
|
+
* These are deliberately source-string assertions, not jsdom mounts -
|
|
9
|
+
* Svelte 5 mount inside vitest is brittle (the existing
|
|
10
|
+
* `svgrid.wrapper.test.ts` follows the same pattern). The behaviour
|
|
11
|
+
* itself is covered by integration probes in the gallery (see the
|
|
12
|
+
* pivot expansion + paste/cut probes that were run during build) and
|
|
13
|
+
* the upstream unit tests of the headless engine + Pro pivot.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs'
|
|
16
|
+
import { resolve } from 'node:path'
|
|
17
|
+
import { describe, expect, it } from 'vitest'
|
|
18
|
+
|
|
19
|
+
// The wrapper implementation spans SvGrid.svelte plus its extracted
|
|
20
|
+
// sibling modules (types + pure helpers). Concatenate them so these
|
|
21
|
+
// "the wrapper declares/exposes X" assertions hold wherever the code
|
|
22
|
+
// physically lives.
|
|
23
|
+
const source = [
|
|
24
|
+
'./SvGrid.svelte',
|
|
25
|
+
'./SvGrid.controller.svelte.ts',
|
|
26
|
+
'./GridMenus.svelte',
|
|
27
|
+
'./GridFooter.svelte',
|
|
28
|
+
'./SvGrid.types.ts',
|
|
29
|
+
'./SvGrid.helpers.ts',
|
|
30
|
+
'./filter-operators.ts',
|
|
31
|
+
'./facet-buckets.ts',
|
|
32
|
+
'./cell-values.ts',
|
|
33
|
+
'./clipboard.ts',
|
|
34
|
+
'./build-api.ts',
|
|
35
|
+
'./columns.ts',
|
|
36
|
+
'./selection.ts',
|
|
37
|
+
'./editing.ts',
|
|
38
|
+
'./cell-render.ts',
|
|
39
|
+
'./menus.ts',
|
|
40
|
+
'./summaries.ts',
|
|
41
|
+
'./keyboard-handlers.ts',
|
|
42
|
+
'./scroll-sync.ts',
|
|
43
|
+
'./features.ts',
|
|
44
|
+
]
|
|
45
|
+
.map((p) => readFileSync(resolve(__dirname, p), 'utf-8'))
|
|
46
|
+
.join('\n')
|
|
47
|
+
const typesPath = resolve(__dirname, './svgrid-wrapper.types.ts')
|
|
48
|
+
const types = readFileSync(typesPath, 'utf-8')
|
|
49
|
+
|
|
50
|
+
describe('SvGrid wrapper - getRowId prop', () => {
|
|
51
|
+
it('declares the getRowId prop on the Props type', () => {
|
|
52
|
+
expect(source).toMatch(/getRowId\?:\s*\(row:\s*TData,\s*index:\s*number\)\s*=>\s*string/)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('forwards the prop into createSvGrid options', () => {
|
|
56
|
+
expect(source).toMatch(/get getRowId\(\)[\s\S]*?return props\.getRowId/)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('SvGrid wrapper - cellClass + rowClass', () => {
|
|
61
|
+
it('declares the rowClass prop on the Props type', () => {
|
|
62
|
+
expect(source).toMatch(/rowClass\?:\s*\(ctx:\s*\{/)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('exposes a resolveClassList helper that normalises array / record / string forms', () => {
|
|
66
|
+
expect(source).toMatch(/function resolveClassList\(/)
|
|
67
|
+
expect(source).toMatch(/Array\.isArray\(value\)/)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('computes the row-level class once per render row and threads it onto the <tr>', () => {
|
|
71
|
+
expect(source).toMatch(/function computeRowClass/)
|
|
72
|
+
expect(source).toMatch(/userRowClass = computeRowClass\(row, rowIndex\)/)
|
|
73
|
+
expect(source).toMatch(/class=\{`sv-grid-row \$\{userRowClass\}/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('computes the cell-level class per column and threads it onto the <td>', () => {
|
|
77
|
+
expect(source).toMatch(/function computeCellClass/)
|
|
78
|
+
expect(source).toMatch(/userCellClass = computeCellClass\(\s*row,\s*rendered\.column,?\s*\)/)
|
|
79
|
+
expect(source).toMatch(/class=\{`sv-grid-cell \$\{userCellClass\}`\}/)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('SvGrid wrapper - declarative cell validation (validate hook)', () => {
|
|
84
|
+
it('reads the column validate hook with the documented return union', () => {
|
|
85
|
+
expect(source).toMatch(/column\.columnDef\.validate/)
|
|
86
|
+
expect(source).toMatch(/=>\s*string\s*\|\s*boolean\s*\|\s*null\s*\|\s*undefined/)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('computes per-cell validity in the render layer (runs for every rendered cell)', () => {
|
|
90
|
+
expect(source).toMatch(/function computeCellValidity/)
|
|
91
|
+
expect(source).toMatch(/columnDef\.validate/)
|
|
92
|
+
// Valid when null / undefined / true; invalid on false or a message.
|
|
93
|
+
expect(source).toMatch(/if \(out == null \|\| out === true\)/)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('threads the invalid class + message tooltip onto both <td> render paths', () => {
|
|
97
|
+
const hits = source.match(/class:sv-grid-cell-invalid=\{cellValidity\.invalid\}/g) ?? []
|
|
98
|
+
expect(hits.length).toBeGreaterThanOrEqual(2)
|
|
99
|
+
// Message wins over the plain column tooltip when the cell is invalid.
|
|
100
|
+
expect(source).toMatch(/cellValidity\.invalid && cellValidity\.message/)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('forwards computeCellValidity through the controller so the wrapper can read it', () => {
|
|
104
|
+
expect(source).toMatch(/get computeCellValidity\(\)\s*\{\s*return computeCellValidity;/)
|
|
105
|
+
expect(source).toMatch(/computeCellValidity\s*=\s*\$derived\(ctrl\.computeCellValidity\)/)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('SvGrid wrapper - column-layout API', () => {
|
|
110
|
+
it('declares setColumnWidth + getColumnWidths in the public type', () => {
|
|
111
|
+
expect(types).toContain('setColumnWidth(columnId: string, width: number): void')
|
|
112
|
+
expect(types).toContain('getColumnWidths(): Record<string, number>')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('declares setColumnPinning + getColumnPinning in the public type', () => {
|
|
116
|
+
expect(types).toMatch(/setColumnPinning\(pinning:\s*\{/)
|
|
117
|
+
expect(types).toContain('getColumnPinning(): { left: string[]; right: string[] }')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('implements all four methods in the api object', () => {
|
|
121
|
+
expect(source).toMatch(/setColumnWidth\(columnId: string, width: number\)/)
|
|
122
|
+
expect(source).toMatch(/getColumnWidths\(\)/)
|
|
123
|
+
expect(source).toMatch(/setColumnPinning\(pinning\)/)
|
|
124
|
+
expect(source).toMatch(/getColumnPinning\(\)/)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('clamps setColumnWidth to MIN_COLUMN_WIDTH', () => {
|
|
128
|
+
// Confirm the clamp is in the code path so a caller can't drop a
|
|
129
|
+
// column below the minimum by passing 0 or a negative number.
|
|
130
|
+
expect(source).toMatch(/Math\.max\(MIN_COLUMN_WIDTH/)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('defensive-copies setColumnPinning inputs so callers cannot mutate state', () => {
|
|
134
|
+
expect(source).toMatch(/Array\.from\(new Set\(pinning\.left/)
|
|
135
|
+
expect(source).toMatch(/Array\.from\(new Set\(pinning\.right/)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe('SvGrid wrapper - between filter operator', () => {
|
|
140
|
+
it("includes 'between' in the FilterOperator type", () => {
|
|
141
|
+
expect(source).toMatch(/"between"/)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it("includes 'between' in NUMBER_OPERATORS + DATE_OPERATORS but NOT in TEXT_OPERATORS", () => {
|
|
145
|
+
// The list literals appear in order, so we slice each block out
|
|
146
|
+
// separately and check membership.
|
|
147
|
+
const numOps = source.match(/const NUMBER_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
148
|
+
expect(numOps).toContain('"between"')
|
|
149
|
+
const dateOps = source.match(/const DATE_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
150
|
+
expect(dateOps).toContain('"between"')
|
|
151
|
+
const textOps = source.match(/const TEXT_OPERATORS[^]*?\];/)?.[0] ?? ''
|
|
152
|
+
expect(textOps).not.toContain('"between"')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('renders a second input ("To") when the operator is between', () => {
|
|
156
|
+
expect(source).toMatch(/menuActiveOperator === "between"/)
|
|
157
|
+
expect(source).toMatch(/placeholder="To"/)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('treats the filter as inactive until BOTH endpoints are non-empty', () => {
|
|
161
|
+
// The active-filters filter() should require value AND valueTo.
|
|
162
|
+
expect(source).toMatch(/if \(f\.operator === "between"\) \{[^}]*f\.value\.trim\(\)\.length > 0[^}]*f\.valueTo[^}]*\.trim\(\)\.length > 0/)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('forwards valueTo through applyExcelFilter', () => {
|
|
166
|
+
expect(source).toMatch(/valueTo:\s*(filter\.operator|op) === "between" \? (filter\.valueTo|valueTo) : undefined/)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('publishes valueTo in the onFiltersChange payload only for between clauses', () => {
|
|
170
|
+
expect(source).toMatch(/f\.operator === "between" && f\.valueTo\s*\?\s*\{ valueTo: f\.valueTo \}/)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('exposes valueTo on the imperative setFilter input + getFilters output', () => {
|
|
174
|
+
expect(types).toMatch(/setFilter\(\s*columnId: string,[\s\S]*?valueTo\?: string/)
|
|
175
|
+
expect(types).toMatch(/getFilters\(\)[\s\S]*?valueTo\?: string/)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
describe('SvGrid wrapper - multi-cell paste + cut + delete', () => {
|
|
180
|
+
it('fills the selection range when the clipboard payload is 1x1', () => {
|
|
181
|
+
expect(source).toMatch(/clipboardIsSingleCell\s*=\s*\n?\s*lines\.length === 1 && \(lines\[0\]\?\.split\("\\t"\)\.length \?\? 0\) === 1/)
|
|
182
|
+
expect(source).toMatch(/selectionIsRange = startRow !== endRow \|\| startCol !== endCol/)
|
|
183
|
+
expect(source).toMatch(/fillRange = clipboardIsSingleCell && selectionIsRange/)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('walks the entire selection rect when filling', () => {
|
|
187
|
+
expect(source).toMatch(/const rowSpan = fillRange \? endRow - startRow \+ 1 : lines\.length/)
|
|
188
|
+
expect(source).toMatch(/const colSpan = fillRange/)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('implements clearSelectedCells() that touches every editable cell in the range', () => {
|
|
192
|
+
expect(source).toMatch(/function clearSelectedCells\(\): boolean/)
|
|
193
|
+
expect(source).toMatch(/parseEditorValue\(editorType, ""\)/)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('wires Ctrl/Cmd+X to cutSelectionToClipboard', () => {
|
|
197
|
+
expect(source).toMatch(/lower === "x"/)
|
|
198
|
+
expect(source).toMatch(/void (?:ctx\.)?cutSelectionToClipboard\(\)/)
|
|
199
|
+
expect(source).toMatch(/async function cutSelectionToClipboard/)
|
|
200
|
+
// Confirm both calls live inside the cutSelectionToClipboard body
|
|
201
|
+
// (copy first, then clear). A comment between them is fine.
|
|
202
|
+
const body = source.match(
|
|
203
|
+
/async function cutSelectionToClipboard\(\)[^{]*\{([\s\S]*?)^\s\s\}/m,
|
|
204
|
+
)?.[1] ?? ''
|
|
205
|
+
expect(body).toContain('copySelectionToClipboard();')
|
|
206
|
+
expect(body).toContain('clearSelectedCells();')
|
|
207
|
+
expect(body.indexOf('copySelectionToClipboard()')).toBeLessThan(
|
|
208
|
+
body.indexOf('clearSelectedCells()'),
|
|
209
|
+
)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('wires Delete + Backspace to clearSelectedCells (without clipboard interaction)', () => {
|
|
213
|
+
expect(source).toMatch(/event\.key === "Delete" \|\| event\.key === "Backspace"/)
|
|
214
|
+
expect(source).toMatch(/if \((?:ctx\.)?clearSelectedCells\(\)\)\s*\{/)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// Regression: the controller's ctx is an object literal of getters, and the
|
|
218
|
+
// copy/cut/paste handlers in keyboard-handlers + menus reach the functions
|
|
219
|
+
// via `ctx.<name>()`. A refactor once shipped the `ctx.cutSelectionToClipboard`
|
|
220
|
+
// / `ctx.pasteFromClipboard` CALLS but dropped their getters, so those threw
|
|
221
|
+
// "is not a function" at runtime - copy worked, cut + paste were dead. Assert
|
|
222
|
+
// every clipboard function called through ctx has a matching getter.
|
|
223
|
+
it('exposes a ctx getter for every clipboard command it calls through ctx', () => {
|
|
224
|
+
for (const fn of [
|
|
225
|
+
'copySelectionToClipboard',
|
|
226
|
+
'cutSelectionToClipboard',
|
|
227
|
+
'pasteFromClipboard',
|
|
228
|
+
'onGridPaste',
|
|
229
|
+
'clearSelectedCells',
|
|
230
|
+
]) {
|
|
231
|
+
expect(source).toMatch(new RegExp(`get ${fn}\\(\\)\\s*\\{\\s*return ${fn};?\\s*\\}`))
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('copy/cut fall back to execCommand when navigator.clipboard is absent (insecure context)', () => {
|
|
236
|
+
expect(source).toMatch(/function writeClipboardText\(/)
|
|
237
|
+
expect(source).toMatch(/document\.execCommand\("copy"\)/)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('paste falls back to a native paste event when the async API is unavailable', () => {
|
|
241
|
+
// keydown only preventDefaults (and uses the async API) in a secure context;
|
|
242
|
+
// otherwise it lets the browser deliver a native paste event to onGridPaste.
|
|
243
|
+
// Accept either the positive branch or the inverted early-return guard
|
|
244
|
+
// (`if (!navigator.clipboard?.readText) return`) - both gate on the async API.
|
|
245
|
+
expect(source).toMatch(/if \(!?navigator\.clipboard\?\.readText\)/)
|
|
246
|
+
expect(source).toMatch(/function onGridPaste\(event: ClipboardEvent\)/)
|
|
247
|
+
expect(source).toMatch(/clipboardData\?\.getData\("text\/plain"\)/)
|
|
248
|
+
// The grid root wires the handler.
|
|
249
|
+
expect(source).toMatch(/onpaste=\{onGridPaste\}/)
|
|
250
|
+
})
|
|
251
|
+
})
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
import SvGrid from './SvGrid.svelte'
|
|
5
|
-
|
|
6
|
-
describe('SvGrid wrapper', () => {
|
|
7
|
-
it('exports a production wrapper component', () => {
|
|
8
|
-
expect(SvGrid).toBeDefined()
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('contains full-wrapper controls and aria-activedescendant wiring', () => {
|
|
12
|
-
// The wrapper spans SvGrid.svelte plus its extracted sibling modules
|
|
13
|
-
// (types + pure helpers); read them together so these "contains X"
|
|
14
|
-
// assertions hold wherever the code physically lives.
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
import SvGrid from './SvGrid.svelte'
|
|
5
|
+
|
|
6
|
+
describe('SvGrid wrapper', () => {
|
|
7
|
+
it('exports a production wrapper component', () => {
|
|
8
|
+
expect(SvGrid).toBeDefined()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('contains full-wrapper controls and aria-activedescendant wiring', () => {
|
|
12
|
+
// The wrapper spans SvGrid.svelte plus its extracted sibling modules
|
|
13
|
+
// (types + pure helpers); read them together so these "contains X"
|
|
14
|
+
// assertions hold wherever the code physically lives.
|
|
15
15
|
const source = [
|
|
16
16
|
'./SvGrid.svelte',
|
|
17
17
|
'./SvGrid.controller.svelte.ts',
|
|
@@ -33,29 +33,29 @@ describe('SvGrid wrapper', () => {
|
|
|
33
33
|
'./keyboard-handlers.ts',
|
|
34
34
|
'./scroll-sync.ts',
|
|
35
35
|
'./features.ts',
|
|
36
|
-
]
|
|
37
|
-
.map((p) => readFileSync(resolve(__dirname, p), 'utf-8'))
|
|
38
|
-
.join('\n')
|
|
39
|
-
expect(source).toContain('Filter all rows')
|
|
40
|
-
expect(source).toContain('Previous')
|
|
41
|
-
expect(source).toContain('role="status"')
|
|
42
|
-
expect(source).toContain('role="alert"')
|
|
43
|
-
expect(source).toContain('activeDescendantId')
|
|
44
|
-
expect(source).toContain('getGridRootA11yProps')
|
|
45
|
-
expect(source).toContain('data-svgrid-header-col')
|
|
46
|
-
expect(source).toContain('data-svgrid-row')
|
|
47
|
-
expect(source).toContain('createSvelteVirtualizer')
|
|
48
|
-
expect(source).toContain('props.virtualization ?? true')
|
|
49
|
-
expect(source).toContain('virtualizer.getVirtualItems()')
|
|
50
|
-
expect(source).toContain('createColumnVirtualizer')
|
|
51
|
-
expect(source).toContain('data-selected-range')
|
|
52
|
-
expect(source).toContain('enableInlineEditing')
|
|
53
|
-
expect(source).toContain('showFilterMenu')
|
|
54
|
-
expect(source).toContain('<tfoot')
|
|
55
|
-
expect(source).toContain('editorType === "number"')
|
|
56
|
-
expect(source).toContain('editorType === "date"')
|
|
57
|
-
expect(source).toContain('editorType === "checkbox"')
|
|
58
|
-
expect(source).toContain('applyExcelFilter')
|
|
59
|
-
expect(source).toContain('showFilterRow')
|
|
60
|
-
})
|
|
61
|
-
})
|
|
36
|
+
]
|
|
37
|
+
.map((p) => readFileSync(resolve(__dirname, p), 'utf-8'))
|
|
38
|
+
.join('\n')
|
|
39
|
+
expect(source).toContain('Filter all rows')
|
|
40
|
+
expect(source).toContain('Previous')
|
|
41
|
+
expect(source).toContain('role="status"')
|
|
42
|
+
expect(source).toContain('role="alert"')
|
|
43
|
+
expect(source).toContain('activeDescendantId')
|
|
44
|
+
expect(source).toContain('getGridRootA11yProps')
|
|
45
|
+
expect(source).toContain('data-svgrid-header-col')
|
|
46
|
+
expect(source).toContain('data-svgrid-row')
|
|
47
|
+
expect(source).toContain('createSvelteVirtualizer')
|
|
48
|
+
expect(source).toContain('props.virtualization ?? true')
|
|
49
|
+
expect(source).toContain('virtualizer.getVirtualItems()')
|
|
50
|
+
expect(source).toContain('createColumnVirtualizer')
|
|
51
|
+
expect(source).toContain('data-selected-range')
|
|
52
|
+
expect(source).toContain('enableInlineEditing')
|
|
53
|
+
expect(source).toContain('showFilterMenu')
|
|
54
|
+
expect(source).toContain('<tfoot')
|
|
55
|
+
expect(source).toContain('editorType === "number"')
|
|
56
|
+
expect(source).toContain('editorType === "date"')
|
|
57
|
+
expect(source).toContain('editorType === "checkbox"')
|
|
58
|
+
expect(source).toContain('applyExcelFilter')
|
|
59
|
+
expect(source).toContain('showFilterRow')
|
|
60
|
+
})
|
|
61
|
+
})
|