@cosmicdrift/kumiko-headless 0.234.0 → 0.235.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.235.1",
|
|
4
4
|
"description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
39
|
+
"@cosmicdrift/kumiko-framework": "0.235.1",
|
|
40
40
|
"temporal-polyfill": "^0.3.2",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
EntityDefinition,
|
|
4
4
|
EntityListScreenDefinition,
|
|
5
5
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
|
+
import { LIST_ROW_META_COLUMNS } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
7
|
import { computeListViewModel } from "../list";
|
|
7
8
|
|
|
8
9
|
// Minimal EntityDefinition-shape. ui-core's view-model only reads
|
|
@@ -264,6 +265,52 @@ describe("computeListViewModel", () => {
|
|
|
264
265
|
});
|
|
265
266
|
});
|
|
266
267
|
|
|
268
|
+
// Table-driven over LIST_ROW_META_COLUMNS itself (not one-off cases) — a
|
|
269
|
+
// row-meta column (id/tenantId/version/insertedAt/...) is a base-table
|
|
270
|
+
// column, never a declared entity field (a SystemAdmin cross-tenant list
|
|
271
|
+
// picks tenantId as a column this way). Together with boot-validator.test.ts
|
|
272
|
+
// (both boot validators accept the same set) and list-row-meta-drift.test.ts
|
|
273
|
+
// (LIST_ROW_META_COLUMNS key-set drift guard), this pins "whatever boot
|
|
274
|
+
// accepts as a row-meta column, the renderer can actually draw" — a #2601
|
|
275
|
+
// review found the two sides had drifted apart.
|
|
276
|
+
test("every row-meta column resolves to its declared type, sortable, no throw", () => {
|
|
277
|
+
for (const [field, expectedType] of Object.entries(LIST_ROW_META_COLUMNS)) {
|
|
278
|
+
const vm = computeListViewModel({
|
|
279
|
+
screen: listScreen(["title", field]),
|
|
280
|
+
entity: taskEntity,
|
|
281
|
+
rows: [],
|
|
282
|
+
translate,
|
|
283
|
+
featureName: "tasks",
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
expect(vm.columns[1]).toEqual({
|
|
287
|
+
field,
|
|
288
|
+
label: `tasks:entity:task:field:${field}`,
|
|
289
|
+
type: expectedType,
|
|
290
|
+
sortable: true,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("row-meta column: explicit label + renderer are passed through unchanged", () => {
|
|
296
|
+
const fmt = { format: "currency" as const, symbol: "€" };
|
|
297
|
+
const vm = computeListViewModel({
|
|
298
|
+
screen: listScreen([{ field: "insertedAt", label: "custom.created", renderer: fmt }]),
|
|
299
|
+
entity: taskEntity,
|
|
300
|
+
rows: [],
|
|
301
|
+
translate,
|
|
302
|
+
featureName: "tasks",
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
expect(vm.columns[0]).toEqual({
|
|
306
|
+
field: "insertedAt",
|
|
307
|
+
label: "custom.created",
|
|
308
|
+
type: "timestamp",
|
|
309
|
+
sortable: true,
|
|
310
|
+
renderer: fmt,
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
267
314
|
test("slots pass through unchanged for the renderer to mount", () => {
|
|
268
315
|
const slots = { header: { react: { component: "HeaderRef" } } };
|
|
269
316
|
const vm = computeListViewModel({
|
package/src/view-model/list.ts
CHANGED
|
@@ -3,7 +3,11 @@ import type {
|
|
|
3
3
|
EntityListScreenDefinition,
|
|
4
4
|
FieldDefinition,
|
|
5
5
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
LIST_ROW_META_COLUMNS,
|
|
8
|
+
normalizeListColumn,
|
|
9
|
+
parseRefTarget,
|
|
10
|
+
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
7
11
|
import type { ListColumnViewModel, ListRowViewModel, ListViewModel, Translate } from "./types";
|
|
8
12
|
|
|
9
13
|
export type ComputeListViewModelInput = {
|
|
@@ -42,6 +46,24 @@ export function computeListViewModel(input: ComputeListViewModelInput): ListView
|
|
|
42
46
|
// columns carry no reference/select metadata and never server-sort.
|
|
43
47
|
const derivedDef = entity.derivedFields?.[normalized.field];
|
|
44
48
|
if (!derivedDef) {
|
|
49
|
+
// Row-meta column (id/tenantId/version/insertedAt/modifiedAt/...) —
|
|
50
|
+
// a real base-table column that's never a declared entity field, e.g.
|
|
51
|
+
// a SystemAdmin cross-tenant list exposing tenantId. Unlike the
|
|
52
|
+
// derivedFields branch above, these are actual DB columns, so
|
|
53
|
+
// server-side ORDER BY works — sortable stays true.
|
|
54
|
+
const rowMetaType = LIST_ROW_META_COLUMNS[normalized.field];
|
|
55
|
+
if (rowMetaType !== undefined) {
|
|
56
|
+
columns.push({
|
|
57
|
+
field: normalized.field,
|
|
58
|
+
label: translate(
|
|
59
|
+
normalized.label ?? fieldLabelKey(featureName, screen.entity, normalized.field),
|
|
60
|
+
),
|
|
61
|
+
type: rowMetaType,
|
|
62
|
+
sortable: true,
|
|
63
|
+
...(normalized.renderer !== undefined && { renderer: normalized.renderer }),
|
|
64
|
+
});
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
45
67
|
// A virtual presentational column — drawn entirely by a columnRenderer
|
|
46
68
|
// component from the row (e.g. tag chips); value is undefined and it
|
|
47
69
|
// never server-sorts. `field` is just the column key. It needs BOTH a
|