@happyvertical/smrt-ui 0.51.3 → 0.51.5
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.
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DATA_TABLE_SURFACE_CONTROL_IDS, dataTableCommandFromDataSurfaceCommand, } from '../data-table-surface.js';
|
|
3
|
+
const identity = { surfaceId: 'pin', kind: 'table' };
|
|
4
|
+
function command(controlId, payload) {
|
|
5
|
+
return {
|
|
6
|
+
version: 1,
|
|
7
|
+
commandId: 'pin',
|
|
8
|
+
identity,
|
|
9
|
+
expectedRevision: 0,
|
|
10
|
+
controlId,
|
|
11
|
+
...(payload === undefined ? {} : { payload }),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A minimal, valid payload for each canonical table controlId — proof that
|
|
16
|
+
* `DATA_TABLE_SURFACE_CONTROL_IDS` names exactly the ids
|
|
17
|
+
* `dataTableCommandFromDataSurfaceCommand` translates, not a superset or
|
|
18
|
+
* subset. Consumers (e.g. `@happyvertical/smrt-svelte`'s
|
|
19
|
+
* `mountListDataSurface`) rely on this enumeration to distinguish "a table
|
|
20
|
+
* command whose payload failed to translate" from "a genuinely unknown
|
|
21
|
+
* control" — drift here would silently break that distinction.
|
|
22
|
+
*/
|
|
23
|
+
const VALID_PAYLOADS = {
|
|
24
|
+
'set-search': { search: 'x' },
|
|
25
|
+
'set-filters': { filters: [] },
|
|
26
|
+
'set-sorting': { sorting: [] },
|
|
27
|
+
'toggle-sorting': { columnId: 'title' },
|
|
28
|
+
'set-page': { page: 1 },
|
|
29
|
+
'set-page-size': { pageSize: 25 },
|
|
30
|
+
'set-column-order': { columnIds: ['title'] },
|
|
31
|
+
'set-column-visibility': { columns: [] },
|
|
32
|
+
'set-selected-rows': { rowIds: [] },
|
|
33
|
+
'toggle-row-selection': { rowId: 'a' },
|
|
34
|
+
'set-expanded-rows': { rowIds: [] },
|
|
35
|
+
'toggle-row-expansion': { rowId: 'a' },
|
|
36
|
+
reset: undefined,
|
|
37
|
+
};
|
|
38
|
+
describe('DATA_TABLE_SURFACE_CONTROL_IDS', () => {
|
|
39
|
+
it('names exactly the ids dataTableCommandFromDataSurfaceCommand translates', () => {
|
|
40
|
+
for (const controlId of DATA_TABLE_SURFACE_CONTROL_IDS) {
|
|
41
|
+
expect(dataTableCommandFromDataSurfaceCommand(command(controlId, VALID_PAYLOADS[controlId])), `expected controlId "${controlId}" to translate with its documented valid payload`).not.toBeNull();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
it('does not include a genuinely unknown/custom controlId', () => {
|
|
45
|
+
expect(DATA_TABLE_SURFACE_CONTROL_IDS).not.toContain('refresh');
|
|
46
|
+
expect(DATA_TABLE_SURFACE_CONTROL_IDS).not.toContain('focus');
|
|
47
|
+
expect(dataTableCommandFromDataSurfaceCommand(command('a-bespoke-control'))).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/** Maps declared DataSurface controls onto the existing DataTable command model. */
|
|
2
2
|
import type { DataTableCommand } from './DataTableController.js';
|
|
3
3
|
import type { DataSurfaceVisibleCommand } from './data-surface.js';
|
|
4
|
+
/**
|
|
5
|
+
* Every `controlId` {@link dataTableCommandFromDataSurfaceCommand} below
|
|
6
|
+
* understands as a table command (as opposed to a component-local control it
|
|
7
|
+
* returns `null` for). Declared once, here, as the sole source of truth: the
|
|
8
|
+
* `Record` below is a mapped type over this union, so the compiler — not a
|
|
9
|
+
* comment — rejects both a missing handler for a listed id and a handler
|
|
10
|
+
* keyed by an id not listed here. There is no second list and no `switch` to
|
|
11
|
+
* drift out of sync with it.
|
|
12
|
+
*/
|
|
13
|
+
export type DataTableSurfaceControlId = 'set-search' | 'set-filters' | 'set-sorting' | 'toggle-sorting' | 'set-page' | 'set-page-size' | 'set-column-order' | 'set-column-visibility' | 'set-selected-rows' | 'toggle-row-selection' | 'set-expanded-rows' | 'toggle-row-expansion' | 'reset';
|
|
14
|
+
/**
|
|
15
|
+
* Mechanically derived from {@link dataTableSurfaceControlHandlers} — never
|
|
16
|
+
* hand-copy this list. A consumer that must distinguish "this controlId
|
|
17
|
+
* names a table command whose payload failed to translate" from "this is a
|
|
18
|
+
* genuinely unknown/custom control" (for example to deny the former outright
|
|
19
|
+
* rather than forwarding it to a generic escape hatch) imports this.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DATA_TABLE_SURFACE_CONTROL_IDS: DataTableSurfaceControlId[];
|
|
4
22
|
/**
|
|
5
23
|
* Returns `null` for a component-local control (focus, reveal, refresh, …) or
|
|
6
24
|
* an invalid table command. The mounted component owns those local controls.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table-surface.d.ts","sourceRoot":"","sources":["../../../src/components/data/data-table-surface.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAEpF,OAAO,KAAK,EAEV,gBAAgB,EAKjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAEV,yBAAyB,EAC1B,MAAM,mBAAmB,CAAC;AAqJ3B;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,yBAAyB,GACjC,gBAAgB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"data-table-surface.d.ts","sourceRoot":"","sources":["../../../src/components/data/data-table-surface.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAEpF,OAAO,KAAK,EAEV,gBAAgB,EAKjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAEV,yBAAyB,EAC1B,MAAM,mBAAmB,CAAC;AAqJ3B;;;;;;;;GAQG;AACH,MAAM,MAAM,yBAAyB,GACjC,YAAY,GACZ,aAAa,GACb,aAAa,GACb,gBAAgB,GAChB,UAAU,GACV,eAAe,GACf,kBAAkB,GAClB,uBAAuB,GACvB,mBAAmB,GACnB,sBAAsB,GACtB,mBAAmB,GACnB,sBAAsB,GACtB,OAAO,CAAC;AAmFZ;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,EAEtC,yBAAyB,EAAE,CAAC;AAQjC;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,yBAAyB,GACjC,gBAAgB,GAAG,IAAI,CAIzB"}
|
|
@@ -106,90 +106,101 @@ function dataTableRowIds(value) {
|
|
|
106
106
|
? values
|
|
107
107
|
: undefined;
|
|
108
108
|
}
|
|
109
|
+
const dataTableSurfaceControlHandlers = {
|
|
110
|
+
'set-search': (payload) => {
|
|
111
|
+
const search = stringValue(payload?.search);
|
|
112
|
+
return search === undefined ? null : { type: 'setSearch', search };
|
|
113
|
+
},
|
|
114
|
+
'set-filters': (payload) => {
|
|
115
|
+
const filters = dataTableFilters(payload?.filters);
|
|
116
|
+
return filters === undefined ? null : { type: 'setFilters', filters };
|
|
117
|
+
},
|
|
118
|
+
'set-sorting': (payload) => {
|
|
119
|
+
const sorting = dataTableSorting(payload?.sorting);
|
|
120
|
+
return sorting === undefined ? null : { type: 'setSorting', sorting };
|
|
121
|
+
},
|
|
122
|
+
'toggle-sorting': (payload) => {
|
|
123
|
+
const columnId = stringValue(payload?.columnId);
|
|
124
|
+
if (!columnId)
|
|
125
|
+
return null;
|
|
126
|
+
const multi = booleanValue(payload?.multi);
|
|
127
|
+
return {
|
|
128
|
+
type: 'toggleSorting',
|
|
129
|
+
columnId,
|
|
130
|
+
...(multi === undefined ? {} : { multi }),
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
'set-page': (payload) => {
|
|
134
|
+
const page = numberValue(payload?.page);
|
|
135
|
+
return page === undefined ? null : { type: 'setPage', page };
|
|
136
|
+
},
|
|
137
|
+
'set-page-size': (payload) => {
|
|
138
|
+
const pageSize = payload?.pageSize;
|
|
139
|
+
if (pageSize === null)
|
|
140
|
+
return { type: 'setPageSize', pageSize: null };
|
|
141
|
+
const numericPageSize = numberValue(pageSize);
|
|
142
|
+
return numericPageSize === undefined
|
|
143
|
+
? null
|
|
144
|
+
: { type: 'setPageSize', pageSize: numericPageSize };
|
|
145
|
+
},
|
|
146
|
+
'set-column-order': (payload) => {
|
|
147
|
+
const columnIds = arrayValue(payload?.columnIds);
|
|
148
|
+
if (!columnIds?.every((value) => typeof value === 'string'))
|
|
149
|
+
return null;
|
|
150
|
+
const ids = [];
|
|
151
|
+
for (const value of columnIds) {
|
|
152
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
153
|
+
return null;
|
|
154
|
+
ids.push(value);
|
|
155
|
+
}
|
|
156
|
+
return { type: 'setColumnOrder', columnIds: ids };
|
|
157
|
+
},
|
|
158
|
+
'set-column-visibility': (payload) => {
|
|
159
|
+
const columns = dataTableVisibilities(payload?.columns);
|
|
160
|
+
return columns === undefined
|
|
161
|
+
? null
|
|
162
|
+
: { type: 'setColumnVisibility', columns };
|
|
163
|
+
},
|
|
164
|
+
'set-selected-rows': (payload) => {
|
|
165
|
+
const rowIds = dataTableRowIds(payload?.rowIds);
|
|
166
|
+
return rowIds === undefined ? null : { type: 'setSelectedRows', rowIds };
|
|
167
|
+
},
|
|
168
|
+
'toggle-row-selection': (payload) => {
|
|
169
|
+
const rowId = payload?.rowId;
|
|
170
|
+
return typeof rowId === 'string' || typeof rowId === 'number'
|
|
171
|
+
? { type: 'toggleRowSelection', rowId }
|
|
172
|
+
: null;
|
|
173
|
+
},
|
|
174
|
+
'set-expanded-rows': (payload) => {
|
|
175
|
+
const rowIds = dataTableRowIds(payload?.rowIds);
|
|
176
|
+
return rowIds === undefined ? null : { type: 'setExpandedRows', rowIds };
|
|
177
|
+
},
|
|
178
|
+
'toggle-row-expansion': (payload) => {
|
|
179
|
+
const rowId = payload?.rowId;
|
|
180
|
+
return typeof rowId === 'string' || typeof rowId === 'number'
|
|
181
|
+
? { type: 'toggleRowExpansion', rowId }
|
|
182
|
+
: null;
|
|
183
|
+
},
|
|
184
|
+
reset: () => ({ type: 'reset' }),
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Mechanically derived from {@link dataTableSurfaceControlHandlers} — never
|
|
188
|
+
* hand-copy this list. A consumer that must distinguish "this controlId
|
|
189
|
+
* names a table command whose payload failed to translate" from "this is a
|
|
190
|
+
* genuinely unknown/custom control" (for example to deny the former outright
|
|
191
|
+
* rather than forwarding it to a generic escape hatch) imports this.
|
|
192
|
+
*/
|
|
193
|
+
export const DATA_TABLE_SURFACE_CONTROL_IDS = Object.keys(dataTableSurfaceControlHandlers);
|
|
194
|
+
function isDataTableSurfaceControlId(value) {
|
|
195
|
+
return Object.hasOwn(dataTableSurfaceControlHandlers, value);
|
|
196
|
+
}
|
|
109
197
|
/**
|
|
110
198
|
* Returns `null` for a component-local control (focus, reveal, refresh, …) or
|
|
111
199
|
* an invalid table command. The mounted component owns those local controls.
|
|
112
200
|
*/
|
|
113
201
|
export function dataTableCommandFromDataSurfaceCommand(command) {
|
|
202
|
+
if (!isDataTableSurfaceControlId(command.controlId))
|
|
203
|
+
return null;
|
|
114
204
|
const payload = payloadObject(command.payload);
|
|
115
|
-
|
|
116
|
-
case 'set-search': {
|
|
117
|
-
const search = stringValue(payload?.search);
|
|
118
|
-
return search === undefined ? null : { type: 'setSearch', search };
|
|
119
|
-
}
|
|
120
|
-
case 'set-filters': {
|
|
121
|
-
const filters = dataTableFilters(payload?.filters);
|
|
122
|
-
return filters === undefined ? null : { type: 'setFilters', filters };
|
|
123
|
-
}
|
|
124
|
-
case 'set-sorting': {
|
|
125
|
-
const sorting = dataTableSorting(payload?.sorting);
|
|
126
|
-
return sorting === undefined ? null : { type: 'setSorting', sorting };
|
|
127
|
-
}
|
|
128
|
-
case 'toggle-sorting': {
|
|
129
|
-
const columnId = stringValue(payload?.columnId);
|
|
130
|
-
if (!columnId)
|
|
131
|
-
return null;
|
|
132
|
-
const multi = booleanValue(payload?.multi);
|
|
133
|
-
return {
|
|
134
|
-
type: 'toggleSorting',
|
|
135
|
-
columnId,
|
|
136
|
-
...(multi === undefined ? {} : { multi }),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
case 'set-page': {
|
|
140
|
-
const page = numberValue(payload?.page);
|
|
141
|
-
return page === undefined ? null : { type: 'setPage', page };
|
|
142
|
-
}
|
|
143
|
-
case 'set-page-size': {
|
|
144
|
-
const pageSize = payload?.pageSize;
|
|
145
|
-
if (pageSize === null)
|
|
146
|
-
return { type: 'setPageSize', pageSize: null };
|
|
147
|
-
const numericPageSize = numberValue(pageSize);
|
|
148
|
-
return numericPageSize === undefined
|
|
149
|
-
? null
|
|
150
|
-
: { type: 'setPageSize', pageSize: numericPageSize };
|
|
151
|
-
}
|
|
152
|
-
case 'set-column-order': {
|
|
153
|
-
const columnIds = arrayValue(payload?.columnIds);
|
|
154
|
-
if (!columnIds?.every((value) => typeof value === 'string'))
|
|
155
|
-
return null;
|
|
156
|
-
const ids = [];
|
|
157
|
-
for (const value of columnIds) {
|
|
158
|
-
if (typeof value !== 'string' || value.length === 0)
|
|
159
|
-
return null;
|
|
160
|
-
ids.push(value);
|
|
161
|
-
}
|
|
162
|
-
return { type: 'setColumnOrder', columnIds: ids };
|
|
163
|
-
}
|
|
164
|
-
case 'set-column-visibility': {
|
|
165
|
-
const columns = dataTableVisibilities(payload?.columns);
|
|
166
|
-
return columns === undefined
|
|
167
|
-
? null
|
|
168
|
-
: { type: 'setColumnVisibility', columns };
|
|
169
|
-
}
|
|
170
|
-
case 'set-selected-rows': {
|
|
171
|
-
const rowIds = dataTableRowIds(payload?.rowIds);
|
|
172
|
-
return rowIds === undefined ? null : { type: 'setSelectedRows', rowIds };
|
|
173
|
-
}
|
|
174
|
-
case 'toggle-row-selection': {
|
|
175
|
-
const rowId = payload?.rowId;
|
|
176
|
-
return typeof rowId === 'string' || typeof rowId === 'number'
|
|
177
|
-
? { type: 'toggleRowSelection', rowId }
|
|
178
|
-
: null;
|
|
179
|
-
}
|
|
180
|
-
case 'set-expanded-rows': {
|
|
181
|
-
const rowIds = dataTableRowIds(payload?.rowIds);
|
|
182
|
-
return rowIds === undefined ? null : { type: 'setExpandedRows', rowIds };
|
|
183
|
-
}
|
|
184
|
-
case 'toggle-row-expansion': {
|
|
185
|
-
const rowId = payload?.rowId;
|
|
186
|
-
return typeof rowId === 'string' || typeof rowId === 'number'
|
|
187
|
-
? { type: 'toggleRowExpansion', rowId }
|
|
188
|
-
: null;
|
|
189
|
-
}
|
|
190
|
-
case 'reset':
|
|
191
|
-
return { type: 'reset' };
|
|
192
|
-
default:
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
205
|
+
return dataTableSurfaceControlHandlers[command.controlId](payload);
|
|
195
206
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-ui",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.5",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Domain-agnostic Svelte 5 UI runtime for SMRT: primitives, i18n client, theme system, and module UI registry",
|
|
6
6
|
"type": "module",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"access": "public"
|
|
130
130
|
},
|
|
131
131
|
"dependencies": {
|
|
132
|
-
"@happyvertical/smrt-types": "0.51.
|
|
132
|
+
"@happyvertical/smrt-types": "0.51.5",
|
|
133
133
|
"esm-env": "^1.2.2"
|
|
134
134
|
},
|
|
135
135
|
"peerDependencies": {
|