@svgrid/grid 1.1.2 → 1.2.1
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/dist/SvGrid.svelte +3 -1
- package/dist/SvGridDropdown.svelte +7 -3
- package/dist/cdn/svgrid.js +13504 -0
- package/dist/cdn/svgrid.svelte-external.js +11188 -0
- package/package.json +12 -2
- package/src/SvGrid.svelte +3 -1
- package/src/SvGridDropdown.svelte +7 -3
- package/dist/SvGrid.helpers.test.d.ts +0 -1
- package/dist/SvGrid.helpers.test.js +0 -298
- package/dist/SvGridChart.test.d.ts +0 -1
- package/dist/SvGridChart.test.js +0 -62
- package/dist/a11y.contract.test.d.ts +0 -1
- package/dist/a11y.contract.test.js +0 -39
- package/dist/a11y.test.d.ts +0 -1
- package/dist/a11y.test.js +0 -44
- package/dist/aligned-grids.test.d.ts +0 -1
- package/dist/aligned-grids.test.js +0 -75
- package/dist/build-api.coverage.test.d.ts +0 -20
- package/dist/build-api.coverage.test.js +0 -505
- package/dist/cell-formatting.test.d.ts +0 -1
- package/dist/cell-formatting.test.js +0 -234
- package/dist/cell-render.test.d.ts +0 -1
- package/dist/cell-render.test.js +0 -338
- package/dist/chart-export.test.d.ts +0 -1
- package/dist/chart-export.test.js +0 -302
- package/dist/chart.coverage.test.d.ts +0 -1
- package/dist/chart.coverage.test.js +0 -748
- package/dist/chart.test.d.ts +0 -1
- package/dist/chart.test.js +0 -289
- package/dist/clipboard.test.d.ts +0 -1
- package/dist/clipboard.test.js +0 -700
- package/dist/collaboration.coverage.test.d.ts +0 -1
- package/dist/collaboration.coverage.test.js +0 -200
- package/dist/collaboration.test.d.ts +0 -1
- package/dist/collaboration.test.js +0 -65
- package/dist/column-groups.test.d.ts +0 -1
- package/dist/column-groups.test.js +0 -56
- package/dist/column-types.test.d.ts +0 -1
- package/dist/column-types.test.js +0 -62
- package/dist/columns.test.d.ts +0 -1
- package/dist/columns.test.js +0 -625
- package/dist/conditional-formatting.test.d.ts +0 -1
- package/dist/conditional-formatting.test.js +0 -87
- package/dist/core.coverage.test.d.ts +0 -1
- package/dist/core.coverage.test.js +0 -186
- package/dist/core.performance.test.d.ts +0 -1
- package/dist/core.performance.test.js +0 -29
- package/dist/createGrid.test.d.ts +0 -1
- package/dist/createGrid.test.js +0 -9
- package/dist/editing.test.d.ts +0 -1
- package/dist/editing.test.js +0 -732
- package/dist/editors/cell-editors.coverage.test.d.ts +0 -1
- package/dist/editors/cell-editors.coverage.test.js +0 -139
- package/dist/editors/cell-editors.test.d.ts +0 -1
- package/dist/editors/cell-editors.test.js +0 -75
- package/dist/facet-buckets.test.d.ts +0 -1
- package/dist/facet-buckets.test.js +0 -296
- package/dist/fill-patterns.test.d.ts +0 -1
- package/dist/fill-patterns.test.js +0 -81
- package/dist/filter-operators.test.d.ts +0 -1
- package/dist/filter-operators.test.js +0 -135
- package/dist/filtering/excel-filters.test.d.ts +0 -1
- package/dist/filtering/excel-filters.test.js +0 -116
- package/dist/filtering/locale-filter.test.d.ts +0 -13
- package/dist/filtering/locale-filter.test.js +0 -189
- package/dist/flex-render.test.d.ts +0 -1
- package/dist/flex-render.test.js +0 -142
- package/dist/group-aggregate.test.d.ts +0 -1
- package/dist/group-aggregate.test.js +0 -32
- package/dist/hyperformula-adapter.test.d.ts +0 -1
- package/dist/hyperformula-adapter.test.js +0 -205
- package/dist/keyboard-handlers.coverage.test.d.ts +0 -1
- package/dist/keyboard-handlers.coverage.test.js +0 -495
- package/dist/keyboard.test.d.ts +0 -1
- package/dist/keyboard.test.js +0 -27
- package/dist/menus.test.d.ts +0 -1
- package/dist/menus.test.js +0 -560
- package/dist/named-views.coverage.test.d.ts +0 -1
- package/dist/named-views.coverage.test.js +0 -180
- package/dist/named-views.test.d.ts +0 -1
- package/dist/named-views.test.js +0 -57
- package/dist/new-features.test.d.ts +0 -1
- package/dist/new-features.test.js +0 -183
- package/dist/render-component.test.d.ts +0 -1
- package/dist/render-component.test.js +0 -41
- package/dist/row-drag.test.d.ts +0 -1
- package/dist/row-drag.test.js +0 -142
- package/dist/row-resize.test.d.ts +0 -1
- package/dist/row-resize.test.js +0 -329
- package/dist/scroll-sync.test.d.ts +0 -1
- package/dist/scroll-sync.test.js +0 -290
- package/dist/selection.multi-range.test.d.ts +0 -1
- package/dist/selection.multi-range.test.js +0 -55
- package/dist/selection.test.d.ts +0 -1
- package/dist/selection.test.js +0 -647
- package/dist/server-data-source.coverage.test.d.ts +0 -1
- package/dist/server-data-source.coverage.test.js +0 -154
- package/dist/server-data-source.test.d.ts +0 -1
- package/dist/server-data-source.test.js +0 -81
- package/dist/sparkline.test.d.ts +0 -1
- package/dist/sparkline.test.js +0 -52
- package/dist/spreadsheet.test.d.ts +0 -1
- package/dist/spreadsheet.test.js +0 -446
- package/dist/subscribe.test.d.ts +0 -1
- package/dist/subscribe.test.js +0 -93
- package/dist/svgrid.api-extensions.test.d.ts +0 -14
- package/dist/svgrid.api-extensions.test.js +0 -334
- package/dist/svgrid.api.test.d.ts +0 -15
- package/dist/svgrid.api.test.js +0 -169
- package/dist/svgrid.behavior.test.d.ts +0 -15
- package/dist/svgrid.behavior.test.js +0 -625
- package/dist/svgrid.column-reorder.test.d.ts +0 -15
- package/dist/svgrid.column-reorder.test.js +0 -224
- package/dist/svgrid.comments-autocomplete.test.d.ts +0 -1
- package/dist/svgrid.comments-autocomplete.test.js +0 -96
- package/dist/svgrid.context-menu.test.d.ts +0 -1
- package/dist/svgrid.context-menu.test.js +0 -102
- package/dist/svgrid.features.test.d.ts +0 -1
- package/dist/svgrid.features.test.js +0 -129
- package/dist/svgrid.interaction.test.d.ts +0 -9
- package/dist/svgrid.interaction.test.js +0 -349
- package/dist/svgrid.locale-filtering.test.d.ts +0 -15
- package/dist/svgrid.locale-filtering.test.js +0 -261
- package/dist/svgrid.new-features.wrapper.test.d.ts +0 -1
- package/dist/svgrid.new-features.wrapper.test.js +0 -194
- package/dist/svgrid.row-pinning.test.d.ts +0 -14
- package/dist/svgrid.row-pinning.test.js +0 -250
- package/dist/svgrid.set-filter.test.d.ts +0 -17
- package/dist/svgrid.set-filter.test.js +0 -268
- package/dist/svgrid.wrapper.test.d.ts +0 -1
- package/dist/svgrid.wrapper.test.js +0 -59
- package/dist/test-setup.d.ts +0 -1
- package/dist/test-setup.js +0 -35
- package/dist/transaction.test.d.ts +0 -1
- package/dist/transaction.test.js +0 -89
- package/dist/virtualization/column-virtualizer.test.d.ts +0 -1
- package/dist/virtualization/column-virtualizer.test.js +0 -25
- package/dist/virtualization/scroll-scaling.test.d.ts +0 -1
- package/dist/virtualization/scroll-scaling.test.js +0 -127
- package/dist/virtualization/virtualizer.test.d.ts +0 -1
- package/dist/virtualization/virtualizer.test.js +0 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "SvGrid - a headless-first, Svelte 5-native data grid engine and render component.",
|
|
5
5
|
"author": "jQWidgets <boikom@jqwidgets.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,12 +21,22 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"module": "dist/index.js",
|
|
23
23
|
"svelte": "./dist/index.js",
|
|
24
|
+
"unpkg": "./dist/cdn/svgrid.js",
|
|
25
|
+
"jsdelivr": "./dist/cdn/svgrid.js",
|
|
24
26
|
"exports": {
|
|
25
27
|
".": {
|
|
26
28
|
"types": "./dist/index.d.ts",
|
|
27
29
|
"svelte": "./dist/index.js",
|
|
28
30
|
"import": "./dist/index.js"
|
|
29
31
|
},
|
|
32
|
+
"./cdn": {
|
|
33
|
+
"import": "./dist/cdn/svgrid.js",
|
|
34
|
+
"default": "./dist/cdn/svgrid.js"
|
|
35
|
+
},
|
|
36
|
+
"./cdn/svelte-external": {
|
|
37
|
+
"import": "./dist/cdn/svgrid.svelte-external.js",
|
|
38
|
+
"default": "./dist/cdn/svgrid.svelte-external.js"
|
|
39
|
+
},
|
|
30
40
|
"./static-functions": {
|
|
31
41
|
"types": "./dist/static-functions.d.ts",
|
|
32
42
|
"svelte": "./dist/static-functions.js",
|
|
@@ -52,7 +62,7 @@
|
|
|
52
62
|
"test:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
53
63
|
"test:lib": "vitest run --coverage",
|
|
54
64
|
"test:build": "publint --strict",
|
|
55
|
-
"build": "svelte-package --input ./src --output ./dist"
|
|
65
|
+
"build": "svelte-package --input ./src --output ./dist && node ../../tools/strip-dist-tests.mjs ./dist && node ./scripts/build-cdn.mjs"
|
|
56
66
|
},
|
|
57
67
|
"devDependencies": {
|
|
58
68
|
"@sveltejs/package": "^2.5.7",
|
package/src/SvGrid.svelte
CHANGED
|
@@ -196,7 +196,6 @@
|
|
|
196
196
|
function flashClassFor(
|
|
197
197
|
cfg: boolean | { className?: string } | undefined,
|
|
198
198
|
): string {
|
|
199
|
-
console.log("flash cell");
|
|
200
199
|
if (cfg && typeof cfg === "object" && cfg.className) return cfg.className;
|
|
201
200
|
return "sv-grid-cell-flash";
|
|
202
201
|
}
|
|
@@ -929,6 +928,7 @@
|
|
|
929
928
|
{:else}
|
|
930
929
|
<!-- Free-form chips: typed tags. Enter / comma commits a chip,
|
|
931
930
|
Backspace on empty input removes the last chip, blur saves. -->
|
|
931
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
932
932
|
<div
|
|
933
933
|
class="sv-grid-cell-editor sv-grid-cell-editor-chips"
|
|
934
934
|
role="group"
|
|
@@ -1885,10 +1885,12 @@
|
|
|
1885
1885
|
cells on release. Rendered inside the bottom-
|
|
1886
1886
|
right cell of the selection range (or active
|
|
1887
1887
|
cell if there's no range). -->
|
|
1888
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1888
1889
|
<div
|
|
1889
1890
|
class="sv-grid-fill-handle"
|
|
1890
1891
|
role="button"
|
|
1891
1892
|
aria-label="Fill handle"
|
|
1893
|
+
tabindex={-1}
|
|
1892
1894
|
onpointerdown={(event) =>
|
|
1893
1895
|
startFillDrag(event, rowIndex, colIndex)}
|
|
1894
1896
|
></div>
|
|
@@ -334,14 +334,18 @@
|
|
|
334
334
|
style={opt?.color ? colorfulChipStyleInline(opt.color) : ''}
|
|
335
335
|
>
|
|
336
336
|
{opt ? opt.label : String(v)}
|
|
337
|
-
<button
|
|
338
|
-
|
|
337
|
+
<!-- A <button> can't be nested inside the trigger <button>; this is a
|
|
338
|
+
pointer-only affordance (tabindex -1), so a role="button" span is
|
|
339
|
+
valid HTML and preserves identical behaviour. -->
|
|
340
|
+
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
|
341
|
+
<span
|
|
339
342
|
class="sv-grid-chip-remove"
|
|
343
|
+
role="button"
|
|
340
344
|
aria-label="Remove {opt ? opt.label : String(v)}"
|
|
341
345
|
tabindex={-1}
|
|
342
346
|
onmousedown={(event) => event.preventDefault()}
|
|
343
347
|
onclick={(event) => removeChip(v, event)}
|
|
344
|
-
>×</
|
|
348
|
+
>×</span>
|
|
345
349
|
</span>
|
|
346
350
|
{/each}
|
|
347
351
|
</span>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { cfTextStyle, fmtStat, getCellKey, resolveClassList, toDateInputValue, toDateTimeLocalInputValue, getEditableInputValue, getEditorInputType, toValueArray, getOptionLabel, getOptionColor, colorfulChipStyle, getEditorClass, asDate, clampMenuX, cssEscape, rawToNumber, formatFacetNumber, formatFacetDate, } from './SvGrid.helpers';
|
|
3
|
-
describe('cfTextStyle', () => {
|
|
4
|
-
it('returns empty string when no color or fontWeight', () => {
|
|
5
|
-
expect(cfTextStyle({})).toBe('');
|
|
6
|
-
});
|
|
7
|
-
it('emits a color declaration', () => {
|
|
8
|
-
expect(cfTextStyle({ color: 'red' })).toBe('color:red;');
|
|
9
|
-
});
|
|
10
|
-
it('emits a font-weight declaration including 0 (not null)', () => {
|
|
11
|
-
expect(cfTextStyle({ fontWeight: 700 })).toBe('font-weight:700;');
|
|
12
|
-
});
|
|
13
|
-
it('combines color and fontWeight', () => {
|
|
14
|
-
expect(cfTextStyle({ color: '#fff', fontWeight: 'bold' })).toBe('color:#fff;font-weight:bold;');
|
|
15
|
-
});
|
|
16
|
-
it('omits font-weight when it is null', () => {
|
|
17
|
-
expect(cfTextStyle({ color: 'blue', fontWeight: null })).toBe('color:blue;');
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
describe('fmtStat', () => {
|
|
21
|
-
it('formats integers with locale grouping', () => {
|
|
22
|
-
expect(fmtStat(1000)).toBe((1000).toLocaleString());
|
|
23
|
-
});
|
|
24
|
-
it('rounds floats to at most 2 fraction digits', () => {
|
|
25
|
-
expect(fmtStat(1.23456)).toBe((1.23456).toLocaleString(undefined, { maximumFractionDigits: 2 }));
|
|
26
|
-
});
|
|
27
|
-
it('treats whole-number floats as integers', () => {
|
|
28
|
-
expect(fmtStat(5.0)).toBe((5).toLocaleString());
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('getCellKey', () => {
|
|
32
|
-
it('joins rowId and columnId with a colon', () => {
|
|
33
|
-
expect(getCellKey('r1', 'colA')).toBe('r1:colA');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
describe('resolveClassList', () => {
|
|
37
|
-
it('returns empty for null/undefined/empty', () => {
|
|
38
|
-
expect(resolveClassList(null)).toBe('');
|
|
39
|
-
expect(resolveClassList(undefined)).toBe('');
|
|
40
|
-
expect(resolveClassList('')).toBe('');
|
|
41
|
-
});
|
|
42
|
-
it('passes a plain string through unchanged', () => {
|
|
43
|
-
expect(resolveClassList('a b')).toBe('a b');
|
|
44
|
-
});
|
|
45
|
-
it('joins arrays and drops falsy entries', () => {
|
|
46
|
-
expect(resolveClassList(['a', '', 'b'])).toBe('a b');
|
|
47
|
-
});
|
|
48
|
-
it('emits only truthy keys of a record', () => {
|
|
49
|
-
expect(resolveClassList({ a: true, b: false, c: true })).toBe('a c');
|
|
50
|
-
});
|
|
51
|
-
it('returns empty string for an all-false record', () => {
|
|
52
|
-
expect(resolveClassList({ a: false })).toBe('');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe('toDateInputValue', () => {
|
|
56
|
-
it('returns empty for null/undefined/empty', () => {
|
|
57
|
-
expect(toDateInputValue(null)).toBe('');
|
|
58
|
-
expect(toDateInputValue(undefined)).toBe('');
|
|
59
|
-
expect(toDateInputValue('')).toBe('');
|
|
60
|
-
});
|
|
61
|
-
it('passes an already-ISO date string through', () => {
|
|
62
|
-
expect(toDateInputValue('2024-01-15')).toBe('2024-01-15');
|
|
63
|
-
});
|
|
64
|
-
it('formats a Date instance to yyyy-mm-dd', () => {
|
|
65
|
-
expect(toDateInputValue(new Date(Date.UTC(2024, 0, 15)))).toBe('2024-01-15');
|
|
66
|
-
});
|
|
67
|
-
it('parses a numeric timestamp', () => {
|
|
68
|
-
const ts = Date.UTC(2020, 5, 1);
|
|
69
|
-
expect(toDateInputValue(ts)).toBe('2020-06-01');
|
|
70
|
-
});
|
|
71
|
-
it('returns empty for an unparseable value', () => {
|
|
72
|
-
expect(toDateInputValue('not-a-date')).toBe('');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe('toDateTimeLocalInputValue', () => {
|
|
76
|
-
it('returns empty for null/empty', () => {
|
|
77
|
-
expect(toDateTimeLocalInputValue(null)).toBe('');
|
|
78
|
-
expect(toDateTimeLocalInputValue('')).toBe('');
|
|
79
|
-
});
|
|
80
|
-
it('passes a matching datetime-local string through', () => {
|
|
81
|
-
expect(toDateTimeLocalInputValue('2024-01-15T13:45')).toBe('2024-01-15T13:45');
|
|
82
|
-
});
|
|
83
|
-
it('formats a Date instance to yyyy-mm-ddThh:mm', () => {
|
|
84
|
-
expect(toDateTimeLocalInputValue(new Date(Date.UTC(2024, 0, 15, 8, 30)))).toBe('2024-01-15T08:30');
|
|
85
|
-
});
|
|
86
|
-
it('returns empty for an invalid value', () => {
|
|
87
|
-
expect(toDateTimeLocalInputValue('garbage')).toBe('');
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe('getEditableInputValue', () => {
|
|
91
|
-
it('delegates to the date formatter for date editors', () => {
|
|
92
|
-
expect(getEditableInputValue('date', '2024-01-15')).toBe('2024-01-15');
|
|
93
|
-
});
|
|
94
|
-
it('delegates to the datetime formatter for datetime editors', () => {
|
|
95
|
-
expect(getEditableInputValue('datetime', '2024-01-15T10:00')).toBe('2024-01-15T10:00');
|
|
96
|
-
});
|
|
97
|
-
it('stringifies other editor types', () => {
|
|
98
|
-
expect(getEditableInputValue('text', 42)).toBe('42');
|
|
99
|
-
});
|
|
100
|
-
it('returns empty string for nullish values', () => {
|
|
101
|
-
expect(getEditableInputValue('text', null)).toBe('');
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
describe('getEditorInputType', () => {
|
|
105
|
-
it('maps each known editor type to its input type', () => {
|
|
106
|
-
expect(getEditorInputType('number')).toBe('number');
|
|
107
|
-
expect(getEditorInputType('date')).toBe('date');
|
|
108
|
-
expect(getEditorInputType('datetime')).toBe('datetime-local');
|
|
109
|
-
expect(getEditorInputType('time')).toBe('time');
|
|
110
|
-
expect(getEditorInputType('password')).toBe('password');
|
|
111
|
-
expect(getEditorInputType('color')).toBe('color');
|
|
112
|
-
});
|
|
113
|
-
it('falls back to text for unknown editor types', () => {
|
|
114
|
-
expect(getEditorInputType('select')).toBe('text');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe('toValueArray', () => {
|
|
118
|
-
it('returns empty for null/empty', () => {
|
|
119
|
-
expect(toValueArray(null)).toEqual([]);
|
|
120
|
-
expect(toValueArray('')).toEqual([]);
|
|
121
|
-
});
|
|
122
|
-
it('filters nullish/empty entries from an array', () => {
|
|
123
|
-
expect(toValueArray([1, null, '', 2, undefined])).toEqual([1, 2]);
|
|
124
|
-
});
|
|
125
|
-
it('wraps a scalar in an array', () => {
|
|
126
|
-
expect(toValueArray('x')).toEqual(['x']);
|
|
127
|
-
expect(toValueArray(7)).toEqual([7]);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
describe('getOptionLabel', () => {
|
|
131
|
-
const options = [
|
|
132
|
-
{ value: 1, label: 'One' },
|
|
133
|
-
{ value: 'two', label: 'Two' },
|
|
134
|
-
];
|
|
135
|
-
it('finds the label by strict value match', () => {
|
|
136
|
-
expect(getOptionLabel(options, 1)).toBe('One');
|
|
137
|
-
});
|
|
138
|
-
it('finds the label by stringified value match', () => {
|
|
139
|
-
expect(getOptionLabel(options, '1')).toBe('One');
|
|
140
|
-
});
|
|
141
|
-
it('falls back to the stringified value when no option matches', () => {
|
|
142
|
-
expect(getOptionLabel(options, 99)).toBe('99');
|
|
143
|
-
});
|
|
144
|
-
it('returns empty string for a nullish unmatched value', () => {
|
|
145
|
-
expect(getOptionLabel(options, null)).toBe('');
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe('getOptionColor', () => {
|
|
149
|
-
const options = [
|
|
150
|
-
{ value: 'a', label: 'A', color: '#f00' },
|
|
151
|
-
{ value: 'b', label: 'B' },
|
|
152
|
-
];
|
|
153
|
-
it('returns the configured color', () => {
|
|
154
|
-
expect(getOptionColor(options, 'a')).toBe('#f00');
|
|
155
|
-
});
|
|
156
|
-
it('returns undefined when matched option has no color', () => {
|
|
157
|
-
expect(getOptionColor(options, 'b')).toBeUndefined();
|
|
158
|
-
});
|
|
159
|
-
it('returns undefined when no option matches', () => {
|
|
160
|
-
expect(getOptionColor(options, 'z')).toBeUndefined();
|
|
161
|
-
});
|
|
162
|
-
it('matches by stringified value', () => {
|
|
163
|
-
const opts = [{ value: 5, label: 'Five', color: 'green' }];
|
|
164
|
-
expect(getOptionColor(opts, '5')).toBe('green');
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
describe('colorfulChipStyle', () => {
|
|
168
|
-
it('returns empty for no color', () => {
|
|
169
|
-
expect(colorfulChipStyle(undefined)).toBe('');
|
|
170
|
-
});
|
|
171
|
-
it('builds a color-mix based style with the given color', () => {
|
|
172
|
-
const style = colorfulChipStyle('#abc');
|
|
173
|
-
expect(style).toContain('background: color-mix(in srgb, #abc 22%, transparent);');
|
|
174
|
-
expect(style).toContain('border-color: color-mix(in srgb, #abc 45%, transparent);');
|
|
175
|
-
expect(style).toContain('color: color-mix(in srgb, #abc 80%, var(--sg-fg, #0f172a));');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
describe('getEditorClass', () => {
|
|
179
|
-
it('returns the modifier class for each editor type', () => {
|
|
180
|
-
expect(getEditorClass('number')).toBe('sv-grid-cell-editor sv-grid-cell-editor-number');
|
|
181
|
-
expect(getEditorClass('date')).toBe('sv-grid-cell-editor sv-grid-cell-editor-date');
|
|
182
|
-
expect(getEditorClass('datetime')).toBe('sv-grid-cell-editor sv-grid-cell-editor-datetime');
|
|
183
|
-
expect(getEditorClass('color')).toBe('sv-grid-cell-editor sv-grid-cell-editor-color');
|
|
184
|
-
});
|
|
185
|
-
it('returns the base class for other editor types', () => {
|
|
186
|
-
expect(getEditorClass('text')).toBe('sv-grid-cell-editor');
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
describe('asDate', () => {
|
|
190
|
-
it('returns null for null/empty', () => {
|
|
191
|
-
expect(asDate(null)).toBeNull();
|
|
192
|
-
expect(asDate('')).toBeNull();
|
|
193
|
-
});
|
|
194
|
-
it('returns the same Date instance for a Date', () => {
|
|
195
|
-
const d = new Date(2024, 0, 1);
|
|
196
|
-
expect(asDate(d)).toBe(d);
|
|
197
|
-
});
|
|
198
|
-
it('parses a date string', () => {
|
|
199
|
-
const result = asDate('2024-01-15');
|
|
200
|
-
expect(result).toBeInstanceOf(Date);
|
|
201
|
-
expect(result?.getUTCFullYear()).toBe(2024);
|
|
202
|
-
});
|
|
203
|
-
it('returns null for an invalid date', () => {
|
|
204
|
-
expect(asDate('nope')).toBeNull();
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
describe('clampMenuX', () => {
|
|
208
|
-
it('clamps to the left minimum of 8', () => {
|
|
209
|
-
expect(clampMenuX(-50, 100)).toBe(8);
|
|
210
|
-
});
|
|
211
|
-
it('clamps against the right edge', () => {
|
|
212
|
-
const max = window.innerWidth - 100 - 8;
|
|
213
|
-
expect(clampMenuX(window.innerWidth, 100)).toBe(max);
|
|
214
|
-
});
|
|
215
|
-
it('returns the value unchanged when it fits', () => {
|
|
216
|
-
expect(clampMenuX(20, 50)).toBe(20);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
describe('cssEscape', () => {
|
|
220
|
-
const hasCssEscape = typeof CSS !== 'undefined' && typeof CSS.escape === 'function';
|
|
221
|
-
it('delegates to CSS.escape when available, otherwise escapes quotes', () => {
|
|
222
|
-
if (hasCssEscape) {
|
|
223
|
-
expect(cssEscape('1abc')).toBe(CSS.escape('1abc'));
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
// Fallback only escapes double quotes.
|
|
227
|
-
expect(cssEscape('a"b')).toBe('a\\"b');
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
it('uses the quote-escaping fallback path when CSS is unavailable', () => {
|
|
231
|
-
const original = globalThis.CSS;
|
|
232
|
-
globalThis.CSS = undefined;
|
|
233
|
-
try {
|
|
234
|
-
expect(cssEscape('say "hi"')).toBe('say \\"hi\\"');
|
|
235
|
-
expect(cssEscape('plain')).toBe('plain');
|
|
236
|
-
}
|
|
237
|
-
finally {
|
|
238
|
-
;
|
|
239
|
-
globalThis.CSS = original;
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
it('leaves a plain identifier intact', () => {
|
|
243
|
-
expect(cssEscape('abc')).toBe('abc');
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
describe('rawToNumber', () => {
|
|
247
|
-
it('returns NaN for null/empty', () => {
|
|
248
|
-
expect(rawToNumber(null, false)).toBeNaN();
|
|
249
|
-
expect(rawToNumber('', false)).toBeNaN();
|
|
250
|
-
});
|
|
251
|
-
it('returns a numeric value as-is', () => {
|
|
252
|
-
expect(rawToNumber(42, false)).toBe(42);
|
|
253
|
-
});
|
|
254
|
-
it('coerces a numeric string', () => {
|
|
255
|
-
expect(rawToNumber('3.5', false)).toBe(3.5);
|
|
256
|
-
});
|
|
257
|
-
it('returns NaN for a non-numeric string', () => {
|
|
258
|
-
expect(rawToNumber('abc', false)).toBeNaN();
|
|
259
|
-
});
|
|
260
|
-
it('returns the timestamp for a Date when isDate is true', () => {
|
|
261
|
-
const d = new Date(Date.UTC(2024, 0, 1));
|
|
262
|
-
expect(rawToNumber(d, true)).toBe(d.getTime());
|
|
263
|
-
});
|
|
264
|
-
it('parses a date string to a timestamp when isDate is true', () => {
|
|
265
|
-
expect(rawToNumber('2024-01-01', true)).toBe(new Date('2024-01-01').getTime());
|
|
266
|
-
});
|
|
267
|
-
it('returns NaN for an invalid date when isDate is true', () => {
|
|
268
|
-
expect(rawToNumber('not-a-date', true)).toBeNaN();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
describe('formatFacetNumber', () => {
|
|
272
|
-
it('uses 0 fraction digits for magnitudes >= 1000', () => {
|
|
273
|
-
expect(formatFacetNumber(1234.56)).toBe((1234.56).toLocaleString(undefined, { maximumFractionDigits: 0 }));
|
|
274
|
-
});
|
|
275
|
-
it('uses 1 fraction digit for magnitudes >= 100', () => {
|
|
276
|
-
expect(formatFacetNumber(123.456)).toBe((123.456).toLocaleString(undefined, { maximumFractionDigits: 1 }));
|
|
277
|
-
});
|
|
278
|
-
it('uses 2 fraction digits for magnitudes >= 1', () => {
|
|
279
|
-
expect(formatFacetNumber(1.2345)).toBe((1.2345).toLocaleString(undefined, { maximumFractionDigits: 2 }));
|
|
280
|
-
});
|
|
281
|
-
it('uses 4 fraction digits for small magnitudes', () => {
|
|
282
|
-
expect(formatFacetNumber(0.123456)).toBe((0.123456).toLocaleString(undefined, { maximumFractionDigits: 4 }));
|
|
283
|
-
});
|
|
284
|
-
it('handles negative numbers via absolute magnitude', () => {
|
|
285
|
-
expect(formatFacetNumber(-2000)).toBe((-2000).toLocaleString(undefined, { maximumFractionDigits: 0 }));
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
describe('formatFacetDate', () => {
|
|
289
|
-
it('formats a timestamp into a short localized date', () => {
|
|
290
|
-
const ts = Date.UTC(2024, 0, 15);
|
|
291
|
-
const expected = new Date(ts).toLocaleDateString(undefined, {
|
|
292
|
-
year: 'numeric',
|
|
293
|
-
month: 'short',
|
|
294
|
-
day: 'numeric',
|
|
295
|
-
});
|
|
296
|
-
expect(formatFacetDate(ts)).toBe(expected);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/SvGridChart.test.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { mount, unmount } from 'svelte';
|
|
3
|
-
import SvGridChart from './SvGridChart.svelte';
|
|
4
|
-
let app = null;
|
|
5
|
-
let target = null;
|
|
6
|
-
function render(spec, extra = {}) {
|
|
7
|
-
target = document.createElement('div');
|
|
8
|
-
document.body.appendChild(target);
|
|
9
|
-
app = mount(SvGridChart, { target, props: { spec, ...extra } });
|
|
10
|
-
return target;
|
|
11
|
-
}
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
if (app)
|
|
14
|
-
unmount(app);
|
|
15
|
-
if (target)
|
|
16
|
-
target.remove();
|
|
17
|
-
app = null;
|
|
18
|
-
target = null;
|
|
19
|
-
});
|
|
20
|
-
describe('SvGridChart legend overflow', () => {
|
|
21
|
-
const many = {
|
|
22
|
-
type: 'bar',
|
|
23
|
-
categories: ['A', 'B'],
|
|
24
|
-
series: Array.from({ length: 14 }, (_, i) => ({ label: `S${i}`, values: [i, i + 1] })),
|
|
25
|
-
};
|
|
26
|
-
it('collapses to the first 10 chips with a "+N more" toggle', () => {
|
|
27
|
-
const el = render(many);
|
|
28
|
-
const chips = el.querySelectorAll('.sv-grid-chart-legend-item');
|
|
29
|
-
expect(chips.length).toBe(10);
|
|
30
|
-
const more = el.querySelector('.sv-grid-chart-legend-more');
|
|
31
|
-
expect(more).toBeTruthy();
|
|
32
|
-
expect(more.textContent).toContain('+4 more');
|
|
33
|
-
});
|
|
34
|
-
it('expands to show every series when "+N more" is clicked', async () => {
|
|
35
|
-
const el = render(many);
|
|
36
|
-
const more = el.querySelector('.sv-grid-chart-legend-more');
|
|
37
|
-
more.click();
|
|
38
|
-
await Promise.resolve();
|
|
39
|
-
expect(el.querySelectorAll('.sv-grid-chart-legend-item').length).toBe(14);
|
|
40
|
-
expect(el.querySelector('.sv-grid-chart-legend-more').textContent).toContain('Show less');
|
|
41
|
-
});
|
|
42
|
-
it('shows no overflow toggle for <= 10 series', () => {
|
|
43
|
-
const el = render({ type: 'bar', categories: ['A'], series: [{ label: 's', values: [1] }] });
|
|
44
|
-
expect(el.querySelector('.sv-grid-chart-legend-more')).toBeNull();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('SvGridChart horizontal bars', () => {
|
|
48
|
-
it('renders left category labels and a bottom value axis', () => {
|
|
49
|
-
const el = render({
|
|
50
|
-
type: 'bar',
|
|
51
|
-
orientation: 'horizontal',
|
|
52
|
-
categories: ['Ada', 'Grace'],
|
|
53
|
-
series: [{ label: 'rev', values: [10, 30] }],
|
|
54
|
-
});
|
|
55
|
-
const bars = el.querySelectorAll('.sv-grid-chart-bar');
|
|
56
|
-
expect(bars.length).toBe(2);
|
|
57
|
-
// category labels appear as axis text
|
|
58
|
-
const axisText = [...el.querySelectorAll('.sv-grid-chart-axis')].map((n) => n.textContent);
|
|
59
|
-
expect(axisText).toContain('Ada');
|
|
60
|
-
expect(axisText).toContain('Grace');
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { getGridCellA11yProps, getGridHeaderA11yProps, getGridRootA11yProps, getGridRowA11yProps, } from './a11y';
|
|
3
|
-
function attrsToHtml(attrs) {
|
|
4
|
-
return Object.entries(attrs)
|
|
5
|
-
.map(([key, value]) => `${key}="${String(value)}"`)
|
|
6
|
-
.join(' ');
|
|
7
|
-
}
|
|
8
|
-
describe('a11y contract rendering', () => {
|
|
9
|
-
it('builds expected headless markup attributes', () => {
|
|
10
|
-
const root = getGridRootA11yProps();
|
|
11
|
-
const header = getGridHeaderA11yProps({
|
|
12
|
-
sortable: true,
|
|
13
|
-
sortDirection: 'descending',
|
|
14
|
-
});
|
|
15
|
-
const row = getGridRowA11yProps(1);
|
|
16
|
-
const cell = getGridCellA11yProps({
|
|
17
|
-
rowIndex: 1,
|
|
18
|
-
colIndex: 2,
|
|
19
|
-
selected: true,
|
|
20
|
-
});
|
|
21
|
-
const html = [
|
|
22
|
-
`<table ${attrsToHtml(root)}><thead>`,
|
|
23
|
-
`<tr ${attrsToHtml(row)}>`,
|
|
24
|
-
`<th ${attrsToHtml(header)}>Age</th>`,
|
|
25
|
-
`</tr></thead><tbody>`,
|
|
26
|
-
`<tr ${attrsToHtml(row)}><td ${attrsToHtml(cell)}>42</td></tr>`,
|
|
27
|
-
`</tbody></table>`,
|
|
28
|
-
].join('');
|
|
29
|
-
expect(html).toContain('role="grid"');
|
|
30
|
-
expect(html).toContain('role="columnheader"');
|
|
31
|
-
expect(html).toContain('aria-sort="descending"');
|
|
32
|
-
expect(html).toContain('aria-selected="true"');
|
|
33
|
-
expect(html).toContain('aria-colindex="2"');
|
|
34
|
-
});
|
|
35
|
-
it('creates stable active descendant ids', async () => {
|
|
36
|
-
const { getGridCellDomId } = await import('./a11y');
|
|
37
|
-
expect(getGridCellDomId('svgrid', 3, 4)).toBe('svgrid_cell_3_4');
|
|
38
|
-
});
|
|
39
|
-
});
|
package/dist/a11y.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/a11y.test.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { getGridCellA11yProps, getGridHeaderA11yProps, getGridRootA11yProps, getGridRowA11yProps, } from './a11y';
|
|
3
|
-
describe('a11y helpers', () => {
|
|
4
|
-
it('returns grid role props', () => {
|
|
5
|
-
expect(getGridRootA11yProps({
|
|
6
|
-
activeDescendantId: 'svgrid_cell_0_0',
|
|
7
|
-
rowCount: 10,
|
|
8
|
-
colCount: 5,
|
|
9
|
-
})).toEqual({
|
|
10
|
-
role: 'grid',
|
|
11
|
-
tabindex: 0,
|
|
12
|
-
'aria-activedescendant': 'svgrid_cell_0_0',
|
|
13
|
-
'aria-rowcount': 10,
|
|
14
|
-
'aria-colcount': 5,
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
it('returns header sort semantics', () => {
|
|
18
|
-
expect(getGridHeaderA11yProps({
|
|
19
|
-
sortable: true,
|
|
20
|
-
sortDirection: 'ascending',
|
|
21
|
-
})).toEqual({
|
|
22
|
-
role: 'columnheader',
|
|
23
|
-
'aria-sort': 'ascending',
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
it('returns row and cell indices for accessibility metadata', () => {
|
|
27
|
-
expect(getGridRowA11yProps(2)).toEqual({
|
|
28
|
-
role: 'row',
|
|
29
|
-
'aria-rowindex': 2,
|
|
30
|
-
});
|
|
31
|
-
expect(getGridCellA11yProps({
|
|
32
|
-
id: 'svgrid_cell_2_3',
|
|
33
|
-
rowIndex: 2,
|
|
34
|
-
colIndex: 3,
|
|
35
|
-
selected: true,
|
|
36
|
-
})).toEqual({
|
|
37
|
-
role: 'gridcell',
|
|
38
|
-
id: 'svgrid_cell_2_3',
|
|
39
|
-
'aria-colindex': 3,
|
|
40
|
-
'aria-rowindex': 2,
|
|
41
|
-
'aria-selected': true,
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for aligned grids: two members sharing a group mirror horizontal
|
|
3
|
-
* scroll and column-resize widths to each other. Driven with minimal fake ctxs.
|
|
4
|
-
*/
|
|
5
|
-
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import { createAlignedGrids } from './aligned-grids';
|
|
7
|
-
function makeCtx(group) {
|
|
8
|
-
return {
|
|
9
|
-
props: { alignedGridGroup: group },
|
|
10
|
-
scrollContainer: { scrollLeft: 0 },
|
|
11
|
-
columnWidths: {},
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
describe('aligned grids', () => {
|
|
15
|
-
it('mirrors horizontal scroll to peers in the same group', () => {
|
|
16
|
-
const a = makeCtx('g');
|
|
17
|
-
const b = makeCtx('g');
|
|
18
|
-
const ha = createAlignedGrids(a);
|
|
19
|
-
const hb = createAlignedGrids(b);
|
|
20
|
-
const un1 = ha.register();
|
|
21
|
-
const un2 = hb.register();
|
|
22
|
-
ha.broadcastScroll(240);
|
|
23
|
-
expect(b.scrollContainer.scrollLeft).toBe(240);
|
|
24
|
-
// and the other way
|
|
25
|
-
hb.broadcastScroll(80);
|
|
26
|
-
expect(a.scrollContainer.scrollLeft).toBe(80);
|
|
27
|
-
un1();
|
|
28
|
-
un2();
|
|
29
|
-
});
|
|
30
|
-
it('mirrors column-resize widths to peers (matched by column id)', () => {
|
|
31
|
-
const a = makeCtx('g');
|
|
32
|
-
const b = makeCtx('g');
|
|
33
|
-
const ha = createAlignedGrids(a);
|
|
34
|
-
const hb = createAlignedGrids(b);
|
|
35
|
-
ha.register();
|
|
36
|
-
hb.register();
|
|
37
|
-
a.columnWidths = { Feb: 180 };
|
|
38
|
-
ha.broadcastWidths();
|
|
39
|
-
expect(b.columnWidths.Feb).toBe(180);
|
|
40
|
-
});
|
|
41
|
-
it('does NOT mirror across different groups', () => {
|
|
42
|
-
const a = makeCtx('one');
|
|
43
|
-
const b = makeCtx('two');
|
|
44
|
-
const ha = createAlignedGrids(a);
|
|
45
|
-
const hb = createAlignedGrids(b);
|
|
46
|
-
ha.register();
|
|
47
|
-
hb.register();
|
|
48
|
-
ha.broadcastScroll(200);
|
|
49
|
-
expect(b.scrollContainer.scrollLeft).toBe(0);
|
|
50
|
-
a.columnWidths = { Feb: 999 };
|
|
51
|
-
ha.broadcastWidths();
|
|
52
|
-
expect(b.columnWidths.Feb).toBeUndefined();
|
|
53
|
-
});
|
|
54
|
-
it('is inert with no group set, and after unregister', () => {
|
|
55
|
-
const a = makeCtx(undefined);
|
|
56
|
-
const b = makeCtx(undefined);
|
|
57
|
-
const ha = createAlignedGrids(a);
|
|
58
|
-
const hb = createAlignedGrids(b);
|
|
59
|
-
ha.register();
|
|
60
|
-
hb.register();
|
|
61
|
-
ha.broadcastScroll(120);
|
|
62
|
-
expect(b.scrollContainer.scrollLeft).toBe(0);
|
|
63
|
-
// Grouped, then unregistered -> no longer receives updates.
|
|
64
|
-
const c = makeCtx('gg');
|
|
65
|
-
const d = makeCtx('gg');
|
|
66
|
-
const hc = createAlignedGrids(c);
|
|
67
|
-
const hd = createAlignedGrids(d);
|
|
68
|
-
const unc = hc.register();
|
|
69
|
-
const und = hd.register();
|
|
70
|
-
und(); // d leaves the group
|
|
71
|
-
hc.broadcastScroll(55);
|
|
72
|
-
expect(d.scrollContainer.scrollLeft).toBe(0);
|
|
73
|
-
unc();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage-focused behavioral tests for the SvGridApi assembled in
|
|
3
|
-
* `build-api.ts`. The existing `svgrid.api.test.ts` / `svgrid.api-extensions
|
|
4
|
-
* .test.ts` cover filters, row selection, pagination, navigation, and
|
|
5
|
-
* getState/setState. This file exercises the remaining surface:
|
|
6
|
-
* - getCellValue / setCellValue
|
|
7
|
-
* - selectCells / getSelected
|
|
8
|
-
* - addRow(s) / removeRow(s) / applyTransaction
|
|
9
|
-
* - addColumn(s) / removeColumn / setColumnVisible / getColumns
|
|
10
|
-
* - setColumnWidth / setColumnPinning / setColumnOrder
|
|
11
|
-
* - setFacetFilter
|
|
12
|
-
* - undo / redo / canUndo / canRedo / clearHistory
|
|
13
|
-
* - find (open/close/query/hits)
|
|
14
|
-
* - grouping (setGroupBy / expandAllGroups / collapseAllGroups)
|
|
15
|
-
* - autosizeColumn / refresh
|
|
16
|
-
*
|
|
17
|
-
* All tests mount the real <SvGrid /> in jsdom and drive the api handed back
|
|
18
|
-
* from onApiReady.
|
|
19
|
-
*/
|
|
20
|
-
export {};
|