@cosmicdrift/kumiko-renderer 0.327.0 → 0.328.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/app/action-form-shim.d.ts +14 -0
- package/{src/app/action-form-shim.ts → dist/app/action-form-shim.js} +26 -44
- package/dist/app/app-features-context.d.ts +8 -0
- package/dist/app/app-features-context.js +16 -0
- package/dist/app/column-renderers.d.ts +30 -0
- package/dist/app/column-renderers.js +34 -0
- package/dist/app/config-edit-shim.d.ts +11 -0
- package/{src/app/config-edit-shim.ts → dist/app/config-edit-shim.js} +12 -25
- package/dist/app/content-editors.d.ts +38 -0
- package/dist/app/content-editors.js +26 -0
- package/dist/app/content-preview.d.ts +11 -0
- package/dist/app/content-preview.js +30 -0
- package/dist/app/custom-screens.d.ts +11 -0
- package/dist/app/custom-screens.js +20 -0
- package/dist/app/dashboard-body.d.ts +16 -0
- package/dist/app/dashboard-body.js +11 -0
- package/dist/app/extension-form-submit.d.ts +32 -0
- package/dist/app/extension-form-submit.js +73 -0
- package/dist/app/extension-sections.d.ts +72 -0
- package/dist/app/extension-sections.js +34 -0
- package/dist/app/feature-schema.d.ts +4 -0
- package/{src/app/feature-schema.ts → dist/app/feature-schema.js} +12 -22
- package/dist/app/form-schema.d.ts +4 -0
- package/dist/app/form-schema.js +85 -0
- package/dist/app/kumiko-screen.d.ts +43 -0
- package/dist/app/kumiko-screen.js +2206 -0
- package/dist/app/layout-fields.d.ts +3 -0
- package/dist/app/layout-fields.js +20 -0
- package/dist/app/list-facets.d.ts +48 -0
- package/dist/app/list-facets.js +185 -0
- package/dist/app/nav.d.ts +61 -0
- package/dist/app/nav.js +139 -0
- package/dist/app/projection-detail-shim.d.ts +8 -0
- package/dist/app/projection-detail-shim.js +71 -0
- package/dist/app/projection-list-shim.d.ts +13 -0
- package/{src/app/projection-list-shim.ts → dist/app/projection-list-shim.js} +26 -38
- package/dist/app/qn.d.ts +7 -0
- package/{src/app/qn.ts → dist/app/qn.js} +9 -11
- package/dist/app/qualify-screen-id.d.ts +2 -0
- package/{src/app/qualify-screen-id.ts → dist/app/qualify-screen-id.js} +5 -6
- package/dist/app/reference-facet-bridge.d.ts +10 -0
- package/dist/app/reference-facet-bridge.js +22 -0
- package/dist/app/return-to.d.ts +31 -0
- package/dist/app/return-to.js +188 -0
- package/dist/app/row-actions.d.ts +77 -0
- package/dist/app/row-actions.js +416 -0
- package/dist/app/screen-access.d.ts +8 -0
- package/dist/app/screen-access.js +48 -0
- package/dist/app/secret-mint-body.d.ts +10 -0
- package/dist/app/secret-mint-body.js +140 -0
- package/dist/app/secrets-edit-body.d.ts +8 -0
- package/dist/app/secrets-edit-body.js +76 -0
- package/dist/app/use-embedded-screen.d.ts +6 -0
- package/dist/app/use-embedded-screen.js +19 -0
- package/dist/app/variable-chips.d.ts +7 -0
- package/dist/app/variable-chips.js +16 -0
- package/dist/app/write-failed-error.d.ts +6 -0
- package/dist/app/write-failed-error.js +21 -0
- package/dist/components/embedded-list-field.d.ts +10 -0
- package/dist/components/embedded-list-field.js +259 -0
- package/dist/components/grid-cell-for-field.d.ts +25 -0
- package/dist/components/grid-cell-for-field.js +31 -0
- package/dist/components/multi-select-checkboxes.d.ts +14 -0
- package/dist/components/multi-select-checkboxes.js +36 -0
- package/dist/components/reference-create-dialog.d.ts +13 -0
- package/dist/components/reference-create-dialog.js +46 -0
- package/dist/components/related-list-section.d.ts +25 -0
- package/dist/components/related-list-section.js +273 -0
- package/dist/components/render-edit-action-button.d.ts +22 -0
- package/dist/components/render-edit-action-button.js +43 -0
- package/dist/components/render-edit-logic.d.ts +6 -0
- package/dist/components/render-edit-logic.js +63 -0
- package/dist/components/render-edit-types.d.ts +209 -0
- package/dist/components/render-edit-types.js +1 -0
- package/dist/components/render-edit.d.ts +6 -0
- package/dist/components/render-edit.js +1169 -0
- package/dist/components/render-field.d.ts +38 -0
- package/dist/components/render-field.js +544 -0
- package/dist/components/render-list.d.ts +117 -0
- package/dist/components/render-list.js +232 -0
- package/dist/components/write-form-section.d.ts +17 -0
- package/dist/components/write-form-section.js +104 -0
- package/dist/context/dispatcher-context.d.ts +10 -0
- package/{src/context/dispatcher-context.tsx → dist/context/dispatcher-context.js} +16 -29
- package/dist/context/draft-storage-context.d.ts +12 -0
- package/dist/context/draft-storage-context.js +18 -0
- package/dist/context/user-roles-context.d.ts +7 -0
- package/{src/context/user-roles-context.tsx → dist/context/user-roles-context.js} +7 -15
- package/dist/format-when.d.ts +1 -0
- package/{src/format-when.ts → dist/format-when.js} +7 -7
- package/dist/hooks/reference-limits.d.ts +3 -0
- package/{src/hooks/reference-limits.ts → dist/hooks/reference-limits.js} +0 -1
- package/dist/hooks/use-ai-text.d.ts +52 -0
- package/dist/hooks/use-ai-text.js +90 -0
- package/dist/hooks/use-disclosure.d.ts +7 -0
- package/dist/hooks/use-disclosure.js +8 -0
- package/dist/hooks/use-form.d.ts +7 -0
- package/dist/hooks/use-form.js +36 -0
- package/dist/hooks/use-list-url-state.d.ts +43 -0
- package/dist/hooks/use-list-url-state.js +131 -0
- package/dist/hooks/use-mutation.d.ts +9 -0
- package/dist/hooks/use-mutation.js +37 -0
- package/dist/hooks/use-query.d.ts +14 -0
- package/dist/hooks/use-query.js +118 -0
- package/dist/hooks/use-reference-lookup.d.ts +14 -0
- package/dist/hooks/use-reference-lookup.js +63 -0
- package/dist/hooks/use-store.d.ts +3 -0
- package/dist/hooks/use-store.js +38 -0
- package/dist/hooks/use-stream-handler.d.ts +14 -0
- package/dist/hooks/use-stream-handler.js +93 -0
- package/dist/i18n-defaults.d.ts +2 -0
- package/dist/i18n-defaults.js +201 -0
- package/dist/i18n.d.ts +64 -0
- package/dist/i18n.js +166 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +55 -0
- package/dist/inside-drawer.d.ts +2 -0
- package/dist/inside-drawer.js +9 -0
- package/dist/page-header-slot.d.ts +2 -0
- package/dist/page-header-slot.js +9 -0
- package/dist/primitives.d.ts +1351 -0
- package/dist/primitives.js +73 -0
- package/dist/screen-fills-height.d.ts +3 -0
- package/dist/screen-fills-height.js +3 -0
- package/dist/share-links.d.ts +4 -0
- package/dist/share-links.js +11 -0
- package/dist/sort-by-accessor.d.ts +5 -0
- package/dist/sort-by-accessor.js +16 -0
- package/dist/sse/live-events.d.ts +23 -0
- package/dist/sse/live-events.js +28 -0
- package/dist/tokens.d.ts +83 -0
- package/dist/tokens.js +80 -0
- package/package.json +16 -9
- package/src/changes.json +52 -0
- package/src/__tests__/error-i18n-defaults.test.ts +0 -82
- package/src/__tests__/format-when.test.ts +0 -12
- package/src/__tests__/i18n.test.tsx +0 -283
- package/src/__tests__/merge-search-params-into-initial.test.ts +0 -525
- package/src/__tests__/qn.test.ts +0 -83
- package/src/__tests__/share-links.test.ts +0 -26
- package/src/__tests__/sort-by-accessor.test.ts +0 -48
- package/src/__tests__/stringify-nav-params.test.ts +0 -33
- package/src/__tests__/use-list-url-state.test.tsx +0 -250
- package/src/app/__tests__/action-form-field-labels.test.tsx +0 -92
- package/src/app/__tests__/action-form-shim.test.ts +0 -137
- package/src/app/__tests__/build-list-query-payload.test.ts +0 -107
- package/src/app/__tests__/config-edit-shim.test.ts +0 -57
- package/src/app/__tests__/content-editors.test.tsx +0 -128
- package/src/app/__tests__/content-preview.test.tsx +0 -184
- package/src/app/__tests__/entity-edit-create-search-params.test.tsx +0 -236
- package/src/app/__tests__/entity-edit-jsonb-embedded-submit.test.tsx +0 -239
- package/src/app/__tests__/entity-list-create-singleton.test.tsx +0 -128
- package/src/app/__tests__/entity-list-row-action-entity-target.test.tsx +0 -424
- package/src/app/__tests__/entity-list-row-action-icon.test.tsx +0 -248
- package/src/app/__tests__/entity-list-row-action-kumiko-actions-view.test.tsx +0 -162
- package/src/app/__tests__/entity-list-row-action-refetch.test.tsx +0 -401
- package/src/app/__tests__/entity-list-screen-padding.test.tsx +0 -111
- package/src/app/__tests__/feature-schema.test.ts +0 -40
- package/src/app/__tests__/form-schema.test.ts +0 -247
- package/src/app/__tests__/list-daterange-facet.test.ts +0 -165
- package/src/app/__tests__/list-filter-facets.test.tsx +0 -521
- package/src/app/__tests__/nav.test.ts +0 -198
- package/src/app/__tests__/projection-detail-actions.test.tsx +0 -719
- package/src/app/__tests__/projection-detail-reference-field.test.tsx +0 -165
- package/src/app/__tests__/projection-detail-shim.test.ts +0 -188
- package/src/app/__tests__/projection-detail-singleton.test.tsx +0 -191
- package/src/app/__tests__/projection-list-bad-shape.test.tsx +0 -152
- package/src/app/__tests__/projection-list-reference-column.test.tsx +0 -219
- package/src/app/__tests__/projection-list-search-sort.test.tsx +0 -212
- package/src/app/__tests__/qualified-names.test.ts +0 -37
- package/src/app/__tests__/return-to.test.ts +0 -396
- package/src/app/__tests__/row-actions-navigate-tab.test.ts +0 -223
- package/src/app/__tests__/row-default-edit-action.test.tsx +0 -324
- package/src/app/__tests__/screen-access-allows.test.ts +0 -34
- package/src/app/__tests__/secret-mint-body.test.tsx +0 -404
- package/src/app/__tests__/variable-chips.test.tsx +0 -70
- package/src/app/app-features-context.tsx +0 -25
- package/src/app/column-renderers.tsx +0 -68
- package/src/app/content-editors.tsx +0 -85
- package/src/app/content-preview.tsx +0 -67
- package/src/app/custom-screens.tsx +0 -29
- package/src/app/dashboard-body.tsx +0 -33
- package/src/app/extension-form-submit.tsx +0 -123
- package/src/app/extension-sections.tsx +0 -111
- package/src/app/form-schema.ts +0 -91
- package/src/app/kumiko-screen.tsx +0 -3614
- package/src/app/layout-fields.ts +0 -31
- package/src/app/list-facets.ts +0 -259
- package/src/app/nav.tsx +0 -258
- package/src/app/projection-detail-shim.ts +0 -85
- package/src/app/reference-facet-bridge.tsx +0 -58
- package/src/app/return-to.tsx +0 -234
- package/src/app/row-actions.ts +0 -639
- package/src/app/screen-access.ts +0 -63
- package/src/app/secret-mint-body.tsx +0 -259
- package/src/app/secrets-edit-body.tsx +0 -194
- package/src/app/use-embedded-screen.ts +0 -27
- package/src/app/variable-chips.tsx +0 -39
- package/src/app/write-failed-error.ts +0 -27
- package/src/components/__tests__/embedded-list-field.test.tsx +0 -531
- package/src/components/__tests__/multi-select-checkboxes.test.tsx +0 -162
- package/src/components/__tests__/related-list-section-empty-state.test.tsx +0 -143
- package/src/components/__tests__/related-list-section.test.tsx +0 -1669
- package/src/components/__tests__/render-edit-action-button.test.tsx +0 -205
- package/src/components/__tests__/render-edit-hidden-section.test.tsx +0 -128
- package/src/components/__tests__/render-edit-logic.test.ts +0 -314
- package/src/components/__tests__/render-edit-screen-description.test.tsx +0 -152
- package/src/components/__tests__/render-edit-submit-actions.test.tsx +0 -460
- package/src/components/__tests__/render-field-app-locale.test.tsx +0 -142
- package/src/components/__tests__/render-field-enum-option.test.tsx +0 -99
- package/src/components/__tests__/render-field-icon-derivation.test.tsx +0 -119
- package/src/components/__tests__/render-field-json-format.test.tsx +0 -164
- package/src/components/__tests__/render-field-money-embedded-list-consistency.test.tsx +0 -212
- package/src/components/__tests__/render-field-money-roundtrip.test.tsx +0 -217
- package/src/components/__tests__/render-field-number-grouping.test.tsx +0 -76
- package/src/components/__tests__/render-field-number-unit.test.tsx +0 -227
- package/src/components/__tests__/render-field-password-format.test.tsx +0 -101
- package/src/components/__tests__/render-field-select-display.test.tsx +0 -134
- package/src/components/__tests__/render-field-unit-format.test.tsx +0 -107
- package/src/components/__tests__/render-field-unsupported-types.test.tsx +0 -195
- package/src/components/__tests__/render-list-toolbar-icon-only.test.tsx +0 -146
- package/src/components/__tests__/write-form-section.test.tsx +0 -375
- package/src/components/embedded-list-field.tsx +0 -354
- package/src/components/grid-cell-for-field.tsx +0 -60
- package/src/components/multi-select-checkboxes.tsx +0 -93
- package/src/components/reference-create-dialog.tsx +0 -91
- package/src/components/related-list-section.tsx +0 -467
- package/src/components/render-edit-action-button.tsx +0 -106
- package/src/components/render-edit-logic.ts +0 -85
- package/src/components/render-edit-types.ts +0 -216
- package/src/components/render-edit.tsx +0 -1700
- package/src/components/render-field.tsx +0 -942
- package/src/components/render-list.tsx +0 -561
- package/src/components/write-form-section.tsx +0 -199
- package/src/context/draft-storage-context.tsx +0 -39
- package/src/hooks/__tests__/use-ai-text.test.tsx +0 -177
- package/src/hooks/__tests__/use-disclosure.test.tsx +0 -25
- package/src/hooks/__tests__/use-form.test.tsx +0 -65
- package/src/hooks/__tests__/use-mutation.test.tsx +0 -77
- package/src/hooks/__tests__/use-stream-handler.test.tsx +0 -107
- package/src/hooks/use-ai-text.ts +0 -172
- package/src/hooks/use-disclosure.ts +0 -20
- package/src/hooks/use-form.ts +0 -78
- package/src/hooks/use-list-url-state.ts +0 -171
- package/src/hooks/use-mutation.ts +0 -61
- package/src/hooks/use-query.ts +0 -132
- package/src/hooks/use-reference-lookup.ts +0 -76
- package/src/hooks/use-store.ts +0 -48
- package/src/hooks/use-stream-handler.ts +0 -134
- package/src/i18n-defaults.ts +0 -220
- package/src/i18n.tsx +0 -286
- package/src/index.ts +0 -272
- package/src/primitives.tsx +0 -1446
- package/src/share-links.ts +0 -12
- package/src/sort-by-accessor.ts +0 -20
- package/src/sse/live-events.tsx +0 -56
- package/src/tokens.tsx +0 -170
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// Regression: RenderField muss das App-Locale (useLocale) an money-/date-
|
|
2
|
-
// Inputs durchreichen — auch ohne explizites field.locale. Vorher fielen
|
|
3
|
-
// die Inputs auf navigator.language (Browser-Sprache) statt der per
|
|
4
|
-
// LocaleProvider gewählten App-Sprache zurück → Separator-Mismatch.
|
|
5
|
-
//
|
|
6
|
-
// Capture-Input statt echter Primitive: hält den Test renderer-intern
|
|
7
|
-
// (relativer Import von RenderField → diese Source), unabhängig davon
|
|
8
|
-
// wie @cosmicdrift/* im Workspace aufgelöst wird.
|
|
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 } from "../../i18n";
|
|
15
|
-
import { type CorePrimitives, type InputProps, PrimitivesProvider } from "../../primitives";
|
|
16
|
-
import { RenderField } from "../render-field";
|
|
17
|
-
|
|
18
|
-
let captured: InputProps | undefined;
|
|
19
|
-
const captureInput: ComponentType<InputProps> = (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: captureInput,
|
|
31
|
-
DataTable: noop,
|
|
32
|
-
Form: noop,
|
|
33
|
-
Section: noop,
|
|
34
|
-
Card: noop,
|
|
35
|
-
Grid: noop,
|
|
36
|
-
GridCell: noop,
|
|
37
|
-
Text: noop,
|
|
38
|
-
Heading: noop,
|
|
39
|
-
Dialog: noop,
|
|
40
|
-
Modal: noop,
|
|
41
|
-
Lightbox: noop,
|
|
42
|
-
ConfigSourceBadge: noop,
|
|
43
|
-
ConfigCascadeView: noop,
|
|
44
|
-
Link: noop,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
function moneyField(): EditFieldViewModel {
|
|
48
|
-
return {
|
|
49
|
-
field: "price",
|
|
50
|
-
label: "Preis",
|
|
51
|
-
type: "money",
|
|
52
|
-
value: 123456,
|
|
53
|
-
visible: true,
|
|
54
|
-
readOnly: false,
|
|
55
|
-
required: false,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function dateField(): EditFieldViewModel {
|
|
60
|
-
return {
|
|
61
|
-
field: "dueAt",
|
|
62
|
-
label: "Fällig",
|
|
63
|
-
type: "date",
|
|
64
|
-
value: "2026-01-15",
|
|
65
|
-
visible: true,
|
|
66
|
-
readOnly: false,
|
|
67
|
-
required: false,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function renderUnderLocale(locale: string, field: EditFieldViewModel = moneyField()): void {
|
|
72
|
-
captured = undefined;
|
|
73
|
-
render(
|
|
74
|
-
<LocaleProvider resolver={createStaticLocaleResolver({ locale })}>
|
|
75
|
-
<PrimitivesProvider value={testPrimitives}>
|
|
76
|
-
<RenderField field={field} onChange={() => {}} />
|
|
77
|
-
</PrimitivesProvider>
|
|
78
|
-
</LocaleProvider>,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
describe("RenderField — App-Locale an money durchreichen", () => {
|
|
83
|
-
test("money ohne field.locale bekommt das App-Locale (de-DE)", () => {
|
|
84
|
-
renderUnderLocale("de-DE");
|
|
85
|
-
expect(captured?.kind).toBe("money");
|
|
86
|
-
if (captured?.kind === "money") expect(captured.locale).toBe("de-DE");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("ein anderes App-Locale wird ebenso durchgereicht (en-US)", () => {
|
|
90
|
-
renderUnderLocale("en-US");
|
|
91
|
-
// Unconditional zuerst — ohne sie wäre der Test bei falschem kind leer.
|
|
92
|
-
expect(captured?.kind).toBe("money");
|
|
93
|
-
if (captured?.kind === "money") expect(captured.locale).toBe("en-US");
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("RenderField — App-Locale an date durchreichen", () => {
|
|
98
|
-
test("date ohne field.locale bekommt das App-Locale (de-DE)", () => {
|
|
99
|
-
renderUnderLocale("de-DE", dateField());
|
|
100
|
-
expect(captured?.kind).toBe("date");
|
|
101
|
-
if (captured?.kind === "date") expect(captured.locale).toBe("de-DE");
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe("RenderField — min/max/dateLocale ans Picker-Input durchreichen (#369)", () => {
|
|
106
|
-
test("date-Feld: min/max/dateLocale landen in den InputProps", () => {
|
|
107
|
-
const field: EditFieldViewModel = {
|
|
108
|
-
...dateField(),
|
|
109
|
-
min: "2020-01-01",
|
|
110
|
-
max: "2026-12-31",
|
|
111
|
-
dateLocale: "en-US",
|
|
112
|
-
};
|
|
113
|
-
renderUnderLocale("de-DE", field);
|
|
114
|
-
expect(captured?.kind).toBe("date");
|
|
115
|
-
if (captured?.kind === "date") {
|
|
116
|
-
expect(captured.min).toBe("2020-01-01");
|
|
117
|
-
expect(captured.max).toBe("2026-12-31");
|
|
118
|
-
// dateLocale (per-Feld) hat Vorrang vor dem App-Locale (de-DE).
|
|
119
|
-
expect(captured.locale).toBe("en-US");
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("timestamp-Feld: min/max landen in den InputProps", () => {
|
|
124
|
-
const field: EditFieldViewModel = {
|
|
125
|
-
field: "at",
|
|
126
|
-
label: "Zeitpunkt",
|
|
127
|
-
type: "timestamp",
|
|
128
|
-
value: "",
|
|
129
|
-
visible: true,
|
|
130
|
-
readOnly: false,
|
|
131
|
-
required: false,
|
|
132
|
-
min: "2026-01-01T00:00:00Z",
|
|
133
|
-
max: "2026-12-31T23:59:59Z",
|
|
134
|
-
};
|
|
135
|
-
renderUnderLocale("de-DE", field);
|
|
136
|
-
expect(captured?.kind).toBe("timestamp");
|
|
137
|
-
if (captured?.kind === "timestamp") {
|
|
138
|
-
expect(captured.min).toBe("2026-01-01T00:00:00Z");
|
|
139
|
-
expect(captured.max).toBe("2026-12-31T23:59:59Z");
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// Fields without a declared icon derive one from the field name — mirrors
|
|
2
|
-
// ACTION_ICON_BY_ID/resolveActionIcon (kumiko-screen.tsx) for fields. Only
|
|
3
|
-
// kind:"text" (single-line) and kind:"number" structurally carry an icon
|
|
4
|
-
// prop, so that's what these tests assert against.
|
|
5
|
-
|
|
6
|
-
import { describe, expect, test } from "bun:test";
|
|
7
|
-
import type { EditFieldViewModel } from "@cosmicdrift/kumiko-headless";
|
|
8
|
-
import { render } from "@testing-library/react";
|
|
9
|
-
import type { ComponentType, ReactNode } from "react";
|
|
10
|
-
import { createStaticLocaleResolver, LocaleProvider } from "../../i18n";
|
|
11
|
-
import { type CorePrimitives, type InputProps, PrimitivesProvider } from "../../primitives";
|
|
12
|
-
import { RenderField } from "../render-field";
|
|
13
|
-
|
|
14
|
-
let captured: InputProps | undefined;
|
|
15
|
-
const captureInput: ComponentType<InputProps> = (props) => {
|
|
16
|
-
captured = props;
|
|
17
|
-
return null;
|
|
18
|
-
};
|
|
19
|
-
const noop = (): ReactNode => null;
|
|
20
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
21
|
-
|
|
22
|
-
const testPrimitives: CorePrimitives = {
|
|
23
|
-
Button: noop,
|
|
24
|
-
Banner: noop,
|
|
25
|
-
Field: passChildren,
|
|
26
|
-
Input: captureInput,
|
|
27
|
-
DataTable: noop,
|
|
28
|
-
Form: noop,
|
|
29
|
-
Section: noop,
|
|
30
|
-
Card: noop,
|
|
31
|
-
Grid: noop,
|
|
32
|
-
GridCell: noop,
|
|
33
|
-
Text: noop,
|
|
34
|
-
Heading: noop,
|
|
35
|
-
Dialog: noop,
|
|
36
|
-
Modal: noop,
|
|
37
|
-
Lightbox: noop,
|
|
38
|
-
ConfigSourceBadge: noop,
|
|
39
|
-
ConfigCascadeView: noop,
|
|
40
|
-
Link: noop,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
function textField(overrides: Partial<EditFieldViewModel> = {}): EditFieldViewModel {
|
|
44
|
-
return {
|
|
45
|
-
field: "name",
|
|
46
|
-
label: "Name",
|
|
47
|
-
type: "text",
|
|
48
|
-
value: "",
|
|
49
|
-
visible: true,
|
|
50
|
-
readOnly: false,
|
|
51
|
-
required: false,
|
|
52
|
-
...overrides,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function renderField(field: EditFieldViewModel): void {
|
|
57
|
-
captured = undefined;
|
|
58
|
-
render(
|
|
59
|
-
<LocaleProvider resolver={createStaticLocaleResolver({ locale: "en-US" })}>
|
|
60
|
-
<PrimitivesProvider value={testPrimitives}>
|
|
61
|
-
<RenderField field={field} onChange={() => {}} />
|
|
62
|
-
</PrimitivesProvider>
|
|
63
|
-
</LocaleProvider>,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe("RenderField — icon derivation from field name/type", () => {
|
|
68
|
-
test("field named 'email' with no declared icon renders the mail icon", () => {
|
|
69
|
-
renderField(textField({ field: "email" }));
|
|
70
|
-
expect(captured?.kind).toBe("text");
|
|
71
|
-
if (captured?.kind === "text") expect(captured.icon).toBe("mail");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("a declared field.icon overrides the derivation", () => {
|
|
75
|
-
renderField(textField({ field: "email", icon: "lock" }));
|
|
76
|
-
expect(captured?.kind).toBe("text");
|
|
77
|
-
if (captured?.kind === "text") expect(captured.icon).toBe("lock");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("a generic text field with no recognizable name gets no icon", () => {
|
|
81
|
-
renderField(textField({ field: "foo" }));
|
|
82
|
-
expect(captured?.kind).toBe("text");
|
|
83
|
-
if (captured?.kind === "text") expect(captured.icon).toBeUndefined();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("a boolean field gets no icon", () => {
|
|
87
|
-
renderField({
|
|
88
|
-
field: "isActive",
|
|
89
|
-
label: "Active",
|
|
90
|
-
type: "boolean",
|
|
91
|
-
value: false,
|
|
92
|
-
visible: true,
|
|
93
|
-
readOnly: false,
|
|
94
|
-
required: false,
|
|
95
|
-
});
|
|
96
|
-
expect(captured?.kind).toBe("boolean");
|
|
97
|
-
if (captured?.kind === "boolean") expect("icon" in captured).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("a multiline field named 'email' gets no icon (textarea has no icon slot)", () => {
|
|
101
|
-
renderField(textField({ field: "email", multiline: true }));
|
|
102
|
-
expect(captured?.kind).toBe("textarea");
|
|
103
|
-
if (captured?.kind === "textarea") expect("icon" in captured).toBe(false);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test("a number field with no recognizable name gets no icon", () => {
|
|
107
|
-
renderField({
|
|
108
|
-
field: "quantity",
|
|
109
|
-
label: "Quantity",
|
|
110
|
-
type: "number",
|
|
111
|
-
value: 1,
|
|
112
|
-
visible: true,
|
|
113
|
-
readOnly: false,
|
|
114
|
-
required: false,
|
|
115
|
-
});
|
|
116
|
-
expect(captured?.kind).toBe("number");
|
|
117
|
-
if (captured?.kind === "number") expect(captured.icon).toBeUndefined();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
// fw#2312: readOnly fields with `field.renderer: { format: "json" }` (audit
|
|
2
|
-
// payload/metadata, job logs) must hand the RAW value to a registered
|
|
3
|
-
// JsonView primitive — applyFormatSpec's already-indented string is only
|
|
4
|
-
// the fallback for a primitives-provider without JsonView (e.g. an app
|
|
5
|
-
// predating this rollout). "children contains \n" is not a discriminating
|
|
6
|
-
// assertion here (applyFormatSpec already returns a real newline string
|
|
7
|
-
// today) — the test instead checks that JsonView is actually invoked and
|
|
8
|
-
// receives the structured value, not a flattened string.
|
|
9
|
-
//
|
|
10
|
-
// Capture-primitives instead of real ones, same pattern as
|
|
11
|
-
// render-field-unit-format.test.tsx.
|
|
12
|
-
|
|
13
|
-
import { describe, expect, test } from "bun:test";
|
|
14
|
-
import type { EditFieldViewModel } from "@cosmicdrift/kumiko-headless";
|
|
15
|
-
import { render } from "@testing-library/react";
|
|
16
|
-
import type { ComponentType, ReactNode } from "react";
|
|
17
|
-
import { createStaticLocaleResolver, LocaleProvider } from "../../i18n";
|
|
18
|
-
import {
|
|
19
|
-
type CorePrimitives,
|
|
20
|
-
type JsonViewProps,
|
|
21
|
-
PrimitivesProvider,
|
|
22
|
-
type TextProps,
|
|
23
|
-
} from "../../primitives";
|
|
24
|
-
import { RenderField } from "../render-field";
|
|
25
|
-
|
|
26
|
-
let capturedJsonView: JsonViewProps | undefined;
|
|
27
|
-
const captureJsonView: ComponentType<JsonViewProps> = (props) => {
|
|
28
|
-
capturedJsonView = props;
|
|
29
|
-
return null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
let capturedText: TextProps | undefined;
|
|
33
|
-
const captureText: ComponentType<TextProps> = (props) => {
|
|
34
|
-
capturedText = props;
|
|
35
|
-
return null;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const noop = (): ReactNode => null;
|
|
39
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
40
|
-
|
|
41
|
-
const basePrimitives: Omit<CorePrimitives, "Text" | "JsonView"> = {
|
|
42
|
-
Button: noop,
|
|
43
|
-
// Passes children through (not noop): the "unsupported jsonb" fallback
|
|
44
|
-
// path wraps its Text/JsonView content in <Banner>, so a Banner that
|
|
45
|
-
// swallows children would silently never mount them, making those
|
|
46
|
-
// captures a no-op regardless of which branch RenderField actually took.
|
|
47
|
-
Banner: passChildren,
|
|
48
|
-
Field: passChildren,
|
|
49
|
-
Input: noop,
|
|
50
|
-
DataTable: noop,
|
|
51
|
-
Form: noop,
|
|
52
|
-
Section: noop,
|
|
53
|
-
Card: noop,
|
|
54
|
-
Grid: noop,
|
|
55
|
-
GridCell: noop,
|
|
56
|
-
Heading: noop,
|
|
57
|
-
Dialog: noop,
|
|
58
|
-
Modal: noop,
|
|
59
|
-
Lightbox: noop,
|
|
60
|
-
ConfigSourceBadge: noop,
|
|
61
|
-
ConfigCascadeView: noop,
|
|
62
|
-
Link: noop,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const primitivesWithJsonView: CorePrimitives = {
|
|
66
|
-
...basePrimitives,
|
|
67
|
-
Text: captureText,
|
|
68
|
-
JsonView: captureJsonView,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const primitivesWithoutJsonView: CorePrimitives = {
|
|
72
|
-
...basePrimitives,
|
|
73
|
-
Text: captureText,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
function payloadField(
|
|
77
|
-
value: unknown,
|
|
78
|
-
renderer: EditFieldViewModel["renderer"] = { format: "json" },
|
|
79
|
-
): EditFieldViewModel {
|
|
80
|
-
return {
|
|
81
|
-
field: "payload",
|
|
82
|
-
label: "Payload",
|
|
83
|
-
type: "jsonb",
|
|
84
|
-
value,
|
|
85
|
-
visible: true,
|
|
86
|
-
readOnly: true,
|
|
87
|
-
required: false,
|
|
88
|
-
renderer,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// No `renderer` key at all (not even `renderer: undefined`, which a default
|
|
93
|
-
// parameter would still override) — this is the "no author-declared
|
|
94
|
-
// FieldRenderer" case that falls through to renderInput's jsonb/embedded/
|
|
95
|
-
// files/images fallback banner instead of FieldRendererOutput.
|
|
96
|
-
function unrenderedJsonbField(value: unknown): EditFieldViewModel {
|
|
97
|
-
return {
|
|
98
|
-
field: "payload",
|
|
99
|
-
label: "Payload",
|
|
100
|
-
type: "jsonb",
|
|
101
|
-
value,
|
|
102
|
-
visible: true,
|
|
103
|
-
readOnly: true,
|
|
104
|
-
required: false,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function renderPayload(primitives: CorePrimitives, field: EditFieldViewModel): void {
|
|
109
|
-
capturedJsonView = undefined;
|
|
110
|
-
capturedText = undefined;
|
|
111
|
-
render(
|
|
112
|
-
<LocaleProvider resolver={createStaticLocaleResolver({ locale: "en-US" })}>
|
|
113
|
-
<PrimitivesProvider value={primitives}>
|
|
114
|
-
<RenderField field={field} onChange={() => {}} />
|
|
115
|
-
</PrimitivesProvider>
|
|
116
|
-
</LocaleProvider>,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
describe("RenderField — format: json (fw#2312)", () => {
|
|
121
|
-
test("mit registriertem JsonView bekommt es den rohen, strukturierten Wert — keinen flachen String", () => {
|
|
122
|
-
const value = { user: { id: 1, roles: ["admin", "billing"] } };
|
|
123
|
-
renderPayload(primitivesWithJsonView, payloadField(value));
|
|
124
|
-
expect(capturedJsonView?.value).toBe(value);
|
|
125
|
-
expect(typeof capturedJsonView?.value).toBe("object");
|
|
126
|
-
expect(capturedText).toBeUndefined();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("renderer.indent wird an JsonView durchgereicht", () => {
|
|
130
|
-
renderPayload(primitivesWithJsonView, payloadField({ a: 1 }, { format: "json", indent: 4 }));
|
|
131
|
-
expect(capturedJsonView?.indent).toBe(4);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("ohne registriertes JsonView fällt es auf Text mit dem von applyFormatSpec formatierten String zurück", () => {
|
|
135
|
-
const value = { a: 1 };
|
|
136
|
-
renderPayload(primitivesWithoutJsonView, payloadField(value));
|
|
137
|
-
expect(capturedJsonView).toBeUndefined();
|
|
138
|
-
expect(capturedText?.children).toBe(JSON.stringify(value, null, 2));
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("ein zirkulärer Wert erreicht JsonView unverändert — die Aufbereitung ist Sache der Primitive-Implementierung, nicht von RenderField", () => {
|
|
142
|
-
const circular: Record<string, unknown> = { name: "job-1" };
|
|
143
|
-
circular["self"] = circular;
|
|
144
|
-
expect(() => renderPayload(primitivesWithJsonView, payloadField(circular))).not.toThrow();
|
|
145
|
-
expect(capturedJsonView?.value).toBe(circular);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe("RenderField — unsupported jsonb/embedded/files/images Fallback-Banner (fw#2312)", () => {
|
|
150
|
-
test("mit registriertem JsonView zeigt der Banner den Wert strukturiert statt als JSON.stringify-Einzeiler", () => {
|
|
151
|
-
const value = { assignee: "user-1", tags: ["a", "b"] };
|
|
152
|
-
renderPayload(primitivesWithJsonView, unrenderedJsonbField(value));
|
|
153
|
-
expect(capturedJsonView?.value).toBe(value);
|
|
154
|
-
expect(capturedText).toBeUndefined();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("ohne registriertes JsonView fällt der Banner auf Text variant=code mit dem alten Einzeiler zurück", () => {
|
|
158
|
-
const value = { assignee: "user-1" };
|
|
159
|
-
renderPayload(primitivesWithoutJsonView, unrenderedJsonbField(value));
|
|
160
|
-
expect(capturedJsonView).toBeUndefined();
|
|
161
|
-
expect(capturedText?.children).toBe(JSON.stringify(value));
|
|
162
|
-
expect(capturedText?.variant).toBe("code");
|
|
163
|
-
});
|
|
164
|
-
});
|