@cosmicdrift/kumiko-renderer-web 0.70.0 → 0.72.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.70.0",
3
+ "version": "0.72.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.70.0",
20
- "@cosmicdrift/kumiko-headless": "0.70.0",
21
- "@cosmicdrift/kumiko-renderer": "0.70.0",
19
+ "@cosmicdrift/kumiko-dispatcher-live": "0.72.0",
20
+ "@cosmicdrift/kumiko-headless": "0.72.0",
21
+ "@cosmicdrift/kumiko-renderer": "0.72.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",
@@ -297,4 +297,76 @@ describe("KumikoScreen / configEdit", () => {
297
297
  });
298
298
  expect(queryCalls["config:query:values"] ?? 0).toBeGreaterThan(valuesBefore);
299
299
  });
300
+
301
+ // #432/1: der #429 Slot-Split rendert die Cascade in zwei Slots — den
302
+ // Trigger (Toggle) im labelAppendix, das Panel im fieldAppendix, beide über
303
+ // einen geteilten expandedFields-State. Der open/close-Zyklus war nur
304
+ // indirekt (Render-Snapshot) gedeckt; hier wird er als Interaktion gepinnt.
305
+ test("#432/1: cascade trigger toggles the panel slot open and closed", async () => {
306
+ const dispatcher: Dispatcher = createMockDispatcher({
307
+ query: (async (qn: string) => {
308
+ if (qn === "config:query:cascade") {
309
+ return {
310
+ isSuccess: true,
311
+ data: {
312
+ "demo:config:site-name": {
313
+ value: "Acme",
314
+ source: "tenant-row",
315
+ levels: [
316
+ {
317
+ source: "tenant-row",
318
+ label: "tenant-row",
319
+ value: "Acme",
320
+ isActive: true,
321
+ hasValue: true,
322
+ },
323
+ {
324
+ source: "default",
325
+ label: "default",
326
+ value: "fallback",
327
+ isActive: false,
328
+ hasValue: true,
329
+ },
330
+ ],
331
+ },
332
+ },
333
+ };
334
+ }
335
+ return {
336
+ isSuccess: true,
337
+ data: {
338
+ "demo:config:site-name": { value: "Acme", scope: "tenant", source: "tenant-row" },
339
+ },
340
+ };
341
+ }) as unknown as Dispatcher["query"],
342
+ });
343
+
344
+ const user = userEvent.setup();
345
+ render(
346
+ <DispatcherProvider dispatcher={dispatcher}>
347
+ <KumikoScreen schema={schema} qn="demo:screen:settings" />
348
+ </DispatcherProvider>,
349
+ );
350
+
351
+ await waitFor(() => screen.getByTestId("render-edit-form"));
352
+ const field = screen.getByTestId("field-siteName");
353
+ await waitFor(() =>
354
+ expect(field.querySelector('[data-testid="config-cascade"]')).not.toBeNull(),
355
+ );
356
+
357
+ // collapsed: panel slot is absent → the inactive default level ("fallback")
358
+ // is not in the DOM (the trigger only shows the active value "Acme").
359
+ expect(screen.queryByText("fallback")).toBeNull();
360
+
361
+ const toggle = field.querySelector('[data-testid="config-cascade"]')?.querySelector("button");
362
+ if (!toggle) throw new Error("cascade trigger toggle not found");
363
+
364
+ // open → panel slot renders all levels incl. the inactive default value
365
+ await user.click(toggle);
366
+ await waitFor(() => expect(screen.getByText("fallback")).toBeTruthy());
367
+
368
+ // close → panel slot gone again (shared expandedFields state cleared)
369
+ await user.click(toggle);
370
+ await waitFor(() => expect(screen.queryByText("fallback")).toBeNull());
371
+ });
300
372
  });
@@ -66,6 +66,9 @@ export function FileUploadInput({
66
66
  setError("upload_failed");
67
67
  } finally {
68
68
  setUploading(false);
69
+ // Clear the input so re-picking the SAME file still fires change — the
70
+ // browser suppresses the event otherwise (no-op in jsdom, real-browser).
71
+ if (inputRef.current) inputRef.current.value = "";
69
72
  }
70
73
  }
71
74