@cosmicdrift/kumiko-renderer 0.213.0 → 0.215.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-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.215.0",
|
|
4
4
|
"description": "Platform-agnostic React renderer for Kumiko screens. Contains the shared logic — primitives-contract, hooks, KumikoScreen, navigation & SSE abstractions — that any platform-specific renderer (web, native) composes. No DOM, no EventSource, no react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
19
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
18
|
+
"@cosmicdrift/kumiko-framework": "0.215.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.215.0",
|
|
20
20
|
"react": "^19.2.6",
|
|
21
21
|
"temporal-polyfill": "^0.3.2",
|
|
22
22
|
"zod": "^4.4.3"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@testing-library/react": "^16.3.2",
|
|
26
26
|
"@types/react": "^19.2.14",
|
|
27
27
|
"jsdom": "^29.1.1",
|
|
28
|
-
"@cosmicdrift/kumiko-locale-de": "0.
|
|
28
|
+
"@cosmicdrift/kumiko-locale-de": "0.215.0"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// fw#2315: a projectionDetail field with `renderer: { format: "enumOption",
|
|
2
|
+
// keyPrefix }` must resolve the raw enum value through useTranslation() —
|
|
3
|
+
// the same key convention buildOptionLabels uses for entityList select
|
|
4
|
+
// columns (`<feature>:entity:<entity>:field:<field>:option:<value>`), but
|
|
5
|
+
// for the FieldRendererOutput (readOnly + declared FormatSpec) path.
|
|
6
|
+
//
|
|
7
|
+
// DoD-Test: rendering the SAME field under two locales must produce two
|
|
8
|
+
// DIFFERENT labels — not just prove the format key exists.
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
import type { EditFieldViewModel } from "@cosmicdrift/kumiko-headless";
|
|
12
|
+
import { render } from "@testing-library/react";
|
|
13
|
+
import type { ComponentType, ReactNode } from "react";
|
|
14
|
+
import { createStaticLocaleResolver, LocaleProvider, type TranslationsByLocale } from "../../i18n";
|
|
15
|
+
import { type CorePrimitives, PrimitivesProvider, type TextProps } from "../../primitives";
|
|
16
|
+
import { RenderField } from "../render-field";
|
|
17
|
+
|
|
18
|
+
let captured: TextProps | undefined;
|
|
19
|
+
const captureText: ComponentType<TextProps> = (props) => {
|
|
20
|
+
captured = props;
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
const noop = (): ReactNode => null;
|
|
24
|
+
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
25
|
+
|
|
26
|
+
const testPrimitives: CorePrimitives = {
|
|
27
|
+
Button: noop,
|
|
28
|
+
Banner: noop,
|
|
29
|
+
Field: passChildren,
|
|
30
|
+
Input: noop,
|
|
31
|
+
DataTable: noop,
|
|
32
|
+
Form: noop,
|
|
33
|
+
Section: noop,
|
|
34
|
+
Card: noop,
|
|
35
|
+
Grid: noop,
|
|
36
|
+
GridCell: noop,
|
|
37
|
+
Text: captureText,
|
|
38
|
+
Heading: noop,
|
|
39
|
+
Dialog: noop,
|
|
40
|
+
Modal: noop,
|
|
41
|
+
Lightbox: noop,
|
|
42
|
+
ConfigSourceBadge: noop,
|
|
43
|
+
ConfigCascadeView: noop,
|
|
44
|
+
Link: noop,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const STATUS_KEY_PREFIX = "contact:entity:contact:field:status:option:";
|
|
48
|
+
|
|
49
|
+
const STATUS_BUNDLES: TranslationsByLocale = {
|
|
50
|
+
de: { [`${STATUS_KEY_PREFIX}active`]: "Aktiv" },
|
|
51
|
+
en: { [`${STATUS_KEY_PREFIX}active`]: "Active" },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function statusField(value: unknown = "active"): EditFieldViewModel {
|
|
55
|
+
return {
|
|
56
|
+
field: "status",
|
|
57
|
+
label: "Status",
|
|
58
|
+
type: "text",
|
|
59
|
+
value,
|
|
60
|
+
visible: true,
|
|
61
|
+
readOnly: true,
|
|
62
|
+
required: false,
|
|
63
|
+
renderer: { format: "enumOption", keyPrefix: STATUS_KEY_PREFIX },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderUnderLocale(locale: string, field: EditFieldViewModel): void {
|
|
68
|
+
captured = undefined;
|
|
69
|
+
render(
|
|
70
|
+
<LocaleProvider
|
|
71
|
+
resolver={createStaticLocaleResolver({ locale })}
|
|
72
|
+
fallbackBundles={[STATUS_BUNDLES]}
|
|
73
|
+
>
|
|
74
|
+
<PrimitivesProvider value={testPrimitives}>
|
|
75
|
+
<RenderField field={field} onChange={() => {}} />
|
|
76
|
+
</PrimitivesProvider>
|
|
77
|
+
</LocaleProvider>,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe("RenderField — enumOption-FormatSpec (fw#2315)", () => {
|
|
82
|
+
test("dieselbe Maske unter zwei Locales rendert zwei verschiedene Beschriftungen", () => {
|
|
83
|
+
renderUnderLocale("de", statusField());
|
|
84
|
+
expect(captured?.children).toBe("Aktiv");
|
|
85
|
+
|
|
86
|
+
renderUnderLocale("en", statusField());
|
|
87
|
+
expect(captured?.children).toBe("Active");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("unbekannter Options-Key fällt auf den Rohwert zurück, nicht auf den i18n-Key", () => {
|
|
91
|
+
renderUnderLocale("de", statusField("archived"));
|
|
92
|
+
expect(captured?.children).toBe("archived");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("leerer Wert rendert leer", () => {
|
|
96
|
+
renderUnderLocale("de", statusField(null));
|
|
97
|
+
expect(captured?.children).toBe("");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -349,6 +349,7 @@ function FieldRendererOutput({
|
|
|
349
349
|
readonly appLocale: string;
|
|
350
350
|
}): ReactNode {
|
|
351
351
|
const { Text } = usePrimitives();
|
|
352
|
+
const t = useTranslation();
|
|
352
353
|
const componentName =
|
|
353
354
|
!isFormatSpec(renderer) && typeof renderer === "object" && renderer !== null
|
|
354
355
|
? extensionSectionName(renderer)
|
|
@@ -363,7 +364,7 @@ function FieldRendererOutput({
|
|
|
363
364
|
// further below in readOnlyDisplayText.
|
|
364
365
|
return (
|
|
365
366
|
<Text testId={`field-value-${field.field}`}>
|
|
366
|
-
{applyFormatSpec({ locale: appLocale, ...renderer }, field.value)}
|
|
367
|
+
{applyFormatSpec({ locale: appLocale, ...renderer }, field.value, t)}
|
|
367
368
|
</Text>
|
|
368
369
|
);
|
|
369
370
|
}
|