@cosmicdrift/kumiko-renderer-web 0.187.0 → 0.188.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 +4 -4
- package/src/__tests__/config-edit.test.tsx +66 -0
- package/src/__tests__/render-edit.test.tsx +1368 -1
- package/src/__tests__/test-utils.tsx +24 -0
- package/src/__tests__/wizard-form-validation.test.tsx +73 -0
- package/src/app/create-app.tsx +33 -20
- package/src/app/draft-storage.ts +59 -0
- package/src/index.ts +4 -0
- package/src/primitives/__tests__/embedded-list-input.test.tsx +14 -1
- package/src/primitives/embedded-list-input.tsx +3 -2
- package/src/primitives/index.tsx +7 -0
- package/src/primitives/money-input.tsx +5 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.188.0",
|
|
4
4
|
"description": "Web-platform bindings for @cosmicdrift/kumiko-renderer. HTML default-primitives, browser history-based navigation, EventSource-backed live events, and a one-call createKumikoApp that mounts the whole stack via react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"./styles.css": "./src/styles.css"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
19
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.188.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.188.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.188.0",
|
|
22
22
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
23
23
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
24
24
|
"@radix-ui/react-label": "^2.1.8",
|
|
@@ -38,6 +38,28 @@ const settingsScreen: ConfigEditScreenDefinition = {
|
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
// A money-typed field is a legitimate FieldDefinition (screen.fields reuses
|
|
42
|
+
// the entity field-shape, kumiko-framework#1923 render-field.tsx money case)
|
|
43
|
+
// even though ConfigKeyType (write-helpers.ts) never carries "money" — the
|
|
44
|
+
// server side of a config key is always a bare scalar.
|
|
45
|
+
const moneySettingsScreen: ConfigEditScreenDefinition = {
|
|
46
|
+
id: "money-settings",
|
|
47
|
+
type: "configEdit",
|
|
48
|
+
scope: "tenant",
|
|
49
|
+
configKeys: { priceLimit: "demo:config:price-limit" },
|
|
50
|
+
fields: {
|
|
51
|
+
priceLimit: { type: "money" },
|
|
52
|
+
// @cast-boundary inline schema-author shape — FieldDefinition union too narrow
|
|
53
|
+
} as ConfigEditScreenDefinition["fields"],
|
|
54
|
+
layout: { sections: [{ title: "Basics", fields: ["priceLimit"] }] },
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const moneySchema: FeatureSchema = {
|
|
58
|
+
featureName: "demo",
|
|
59
|
+
entities: {},
|
|
60
|
+
screens: [moneySettingsScreen],
|
|
61
|
+
};
|
|
62
|
+
|
|
41
63
|
const schema: FeatureSchema = {
|
|
42
64
|
featureName: "demo",
|
|
43
65
|
entities: {},
|
|
@@ -138,6 +160,50 @@ describe("KumikoScreen / configEdit", () => {
|
|
|
138
160
|
});
|
|
139
161
|
});
|
|
140
162
|
|
|
163
|
+
// kumiko-framework#1923: RenderField's money case now always emits the
|
|
164
|
+
// entityEdit `{amount, currency}` payload shape. A config key's server
|
|
165
|
+
// value is always a bare scalar (ConfigKeyType has no "money" variant) —
|
|
166
|
+
// customSubmit must unwrap back to `.amount` before dispatching, or the
|
|
167
|
+
// batch's config:write:set value fails its `string | number | boolean`
|
|
168
|
+
// schema entirely (regression guard for that unwrap).
|
|
169
|
+
test("money field submit unwraps {amount, currency} back to a bare number", async () => {
|
|
170
|
+
const batchSpy = mock(async (_commands: ReadonlyArray<{ type: string; payload: unknown }>) => ({
|
|
171
|
+
isSuccess: true as const,
|
|
172
|
+
results: [],
|
|
173
|
+
}));
|
|
174
|
+
const dispatcher: Dispatcher = createMockDispatcher({
|
|
175
|
+
query: (async () => ({
|
|
176
|
+
isSuccess: true,
|
|
177
|
+
data: { "demo:config:price-limit": { value: 12.99, scope: "tenant" } },
|
|
178
|
+
})) as unknown as Dispatcher["query"],
|
|
179
|
+
batch: batchSpy as unknown as Dispatcher["batch"],
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const user = userEvent.setup();
|
|
183
|
+
render(
|
|
184
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
185
|
+
<KumikoScreen schema={moneySchema} qn="demo:screen:money-settings" />
|
|
186
|
+
</DispatcherProvider>,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
await waitFor(() => screen.getByTestId("render-edit-form"));
|
|
190
|
+
const priceInput = screen.getByTestId("field-priceLimit").querySelector("input");
|
|
191
|
+
if (!priceInput) throw new Error("expected priceLimit input");
|
|
192
|
+
|
|
193
|
+
await user.clear(priceInput);
|
|
194
|
+
await user.type(priceInput, "15.00");
|
|
195
|
+
await user.click(screen.getByTestId("render-edit-submit"));
|
|
196
|
+
|
|
197
|
+
await waitFor(() => expect(batchSpy).toHaveBeenCalled());
|
|
198
|
+
const commands = batchSpy.mock.calls[0]?.[0];
|
|
199
|
+
if (!commands) throw new Error("batchSpy not called");
|
|
200
|
+
expect(commands).toHaveLength(1);
|
|
201
|
+
expect(commands[0]).toEqual({
|
|
202
|
+
type: "config:write:set",
|
|
203
|
+
payload: { key: "demo:config:price-limit", value: 15, scope: "tenant" },
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
141
207
|
test("save-button disabled after successful submit (rebase fired)", async () => {
|
|
142
208
|
const dispatcher: Dispatcher = createMockDispatcher({
|
|
143
209
|
query: (async () => ({
|