@cosmicdrift/kumiko-headless 0.225.0 → 0.226.0

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.225.0",
3
+ "version": "0.226.0",
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.225.0",
39
+ "@cosmicdrift/kumiko-framework": "0.226.0",
40
40
  "temporal-polyfill": "^0.3.2",
41
41
  "zod": "^4.4.3"
42
42
  },
@@ -14,6 +14,8 @@ const taskEntity = {
14
14
  title: { type: "text", required: true, sortable: true },
15
15
  done: { type: "boolean" },
16
16
  priority: { type: "number", sortable: true },
17
+ role: { type: "select", options: ["admin", "member"] },
18
+ labels: { type: "multiSelect", options: ["urgent", "low-priority"] },
17
19
  },
18
20
  derivedFields: {
19
21
  // Read-time computed (value appended by the list-query handler). The
@@ -232,6 +234,36 @@ describe("computeListViewModel", () => {
232
234
  expect(vm.columns[0]?.renderer).toEqual(fmt);
233
235
  });
234
236
 
237
+ test("select field column includes translated optionLabels", () => {
238
+ const vm = computeListViewModel({
239
+ screen: listScreen(["role"]),
240
+ entity: taskEntity,
241
+ rows: [],
242
+ translate: (key) => (key === "tasks:entity:task:field:role:option:admin" ? "Admin" : key),
243
+ featureName: "tasks",
244
+ });
245
+
246
+ expect(vm.columns[0]?.optionLabels).toEqual({
247
+ admin: "Admin",
248
+ member: "member",
249
+ });
250
+ });
251
+
252
+ test("multiSelect field column includes translated optionLabels (fw#2491)", () => {
253
+ const vm = computeListViewModel({
254
+ screen: listScreen(["labels"]),
255
+ entity: taskEntity,
256
+ rows: [],
257
+ translate: (key) => (key === "tasks:entity:task:field:labels:option:urgent" ? "Urgent" : key),
258
+ featureName: "tasks",
259
+ });
260
+
261
+ expect(vm.columns[0]?.optionLabels).toEqual({
262
+ urgent: "Urgent",
263
+ "low-priority": "low-priority",
264
+ });
265
+ });
266
+
235
267
  test("slots pass through unchanged for the renderer to mount", () => {
236
268
  const slots = { header: { react: { component: "HeaderRef" } } };
237
269
  const vm = computeListViewModel({
@@ -95,12 +95,12 @@ export function computeListViewModel(input: ComputeListViewModelInput): ListView
95
95
  fieldDef.type === "reference"
96
96
  ? ((fieldDef as unknown as { labelField?: string }).labelField ?? "id")
97
97
  : undefined;
98
- // Bei select-Feldern: translated Option-Labels einbacken. Convention
99
- // matcht den Form-Patheine Translation-Quelle für List + Form.
100
- // Missing-Key returnt convention-gemäß den Key zurück; Renderer hat
101
- // dann humanizeSlug-Fallback.
98
+ // For select and multiSelect fields: bake in translated option labels.
99
+ // Convention matches the form path one translation source for list +
100
+ // form. A missing key returns the key itself per convention; the
101
+ // renderer then falls back to humanizeSlug.
102
102
  const optionLabels =
103
- fieldDef.type === "select"
103
+ fieldDef.type === "select" || fieldDef.type === "multiSelect"
104
104
  ? buildOptionLabels(
105
105
  translate,
106
106
  (value) => fieldOptionLabelKey(featureName, screen.entity, normalized.field, value),
@@ -43,9 +43,10 @@ export type ListColumnViewModel = {
43
43
  readonly type: string; // field-type ("text", "number", "money", ...)
44
44
  readonly renderer?: FieldRenderer | RuntimeRenderer;
45
45
  readonly sortable: boolean;
46
- /** Nur bei `type: "select"` translated Option-Labels keyed nach raw
47
- * value. Renderer rendert `optionLabels[value]` statt humanizeSlug
48
- * wenn vorhanden. Convention-Key: `<feature>:entity:<entity>:field:<field>:option:<value>`. */
46
+ /** Only for `type: "select"` or `type: "multiSelect"` translated option
47
+ * labels keyed by raw value. The renderer renders `optionLabels[value]`
48
+ * instead of humanizeSlug when present. Convention key:
49
+ * `<feature>:entity:<entity>:field:<field>:option:<value>`. */
49
50
  readonly optionLabels?: Readonly<Record<string, string>>;
50
51
  /** Nur bei `type: "reference"` — referenced Entity-Name für Bulk-
51
52
  * Lookup im Renderer (`<refFeature>:query:<refEntity>:list`). */