@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer-web",
3
- "version": "0.187.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.187.0",
20
- "@cosmicdrift/kumiko-headless": "0.187.0",
21
- "@cosmicdrift/kumiko-renderer": "0.187.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 () => ({