@cosmicdrift/kumiko-renderer-web 0.260.0 → 0.262.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.260.0",
3
+ "version": "0.262.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.260.0",
20
- "@cosmicdrift/kumiko-headless": "0.260.0",
21
- "@cosmicdrift/kumiko-renderer": "0.260.0",
19
+ "@cosmicdrift/kumiko-dispatcher-live": "0.262.0",
20
+ "@cosmicdrift/kumiko-headless": "0.262.0",
21
+ "@cosmicdrift/kumiko-renderer": "0.262.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",
@@ -66,7 +66,7 @@
66
66
  "@types/react-dom": "^19.2.3",
67
67
  "jsdom": "^29.1.1",
68
68
  "tailwindcss": "^4.3.0",
69
- "@cosmicdrift/kumiko-locale-de": "0.260.0"
69
+ "@cosmicdrift/kumiko-locale-de": "0.262.0"
70
70
  },
71
71
  "repository": {
72
72
  "type": "git",
@@ -58,7 +58,7 @@ function buildParentRedirectSchema(): FeatureSchema {
58
58
  id: "product-edit",
59
59
  type: "entityEdit",
60
60
  entity: "product",
61
- layout: { sections: [{ fields: ["name"] }] },
61
+ layout: { sections: [{ fields: ["name", "parentId"] }] },
62
62
  redirect: { screen: "parent-detail", idFrom: "parentId" },
63
63
  };
64
64
  const parentDetailScreen: ScreenDefinition = {
@@ -464,4 +464,72 @@ describe("entityEdit redirect (#1942)", () => {
464
464
  expect(navigated).toEqual([{ screenId: "parent-detail", entityId: "parent-1" }]),
465
465
  );
466
466
  });
467
+
468
+ // Create's write-handler success payload nests the new record under `data`
469
+ // (`{ kind, id, data, … }`) rather than exposing fields like a parent FK
470
+ // flatly — the object-form redirect falls back to the values submitted to
471
+ // the write handler, matching entityEdit's update-path fallback.
472
+ test("create: redirect object form with idFrom falls back to the submitted form values when the write payload lacks the field", async () => {
473
+ const navigated: NavTarget[] = [];
474
+ const dispatcher = createMockDispatcher({
475
+ write: (async () => ({
476
+ isSuccess: true,
477
+ data: { kind: "created", id: "new-1", data: { id: "new-1", parentId: "parent-1" } },
478
+ })) as unknown as Dispatcher["write"],
479
+ });
480
+ render(
481
+ <DispatcherProvider dispatcher={dispatcher}>
482
+ <NavProvider
483
+ value={{
484
+ route: { screenId: "shop:screen:product-edit" },
485
+ navigate: (target) => navigated.push(target),
486
+ replace: () => {},
487
+ hrefFor: () => "",
488
+ searchParams: { parentId: "parent-1" },
489
+ setSearchParams: () => {},
490
+ }}
491
+ >
492
+ <KumikoScreen schema={buildParentRedirectSchema()} qn="shop:screen:product-edit" />
493
+ </NavProvider>
494
+ </DispatcherProvider>,
495
+ );
496
+
497
+ fillNameAndSubmit();
498
+
499
+ await waitFor(() =>
500
+ expect(navigated).toEqual([{ screenId: "parent-detail", entityId: "parent-1" }]),
501
+ );
502
+ });
503
+
504
+ test("create: redirect object form with idFrom prefers the write handler's payload over the submitted form values", async () => {
505
+ const navigated: NavTarget[] = [];
506
+ const dispatcher = createMockDispatcher({
507
+ write: (async () => ({
508
+ isSuccess: true,
509
+ data: { id: "new-1", parentId: "payload-parent" },
510
+ })) as unknown as Dispatcher["write"],
511
+ });
512
+ render(
513
+ <DispatcherProvider dispatcher={dispatcher}>
514
+ <NavProvider
515
+ value={{
516
+ route: { screenId: "shop:screen:product-edit" },
517
+ navigate: (target) => navigated.push(target),
518
+ replace: () => {},
519
+ hrefFor: () => "",
520
+ searchParams: { parentId: "form-parent" },
521
+ setSearchParams: () => {},
522
+ }}
523
+ >
524
+ <KumikoScreen schema={buildParentRedirectSchema()} qn="shop:screen:product-edit" />
525
+ </NavProvider>
526
+ </DispatcherProvider>,
527
+ );
528
+
529
+ fillNameAndSubmit();
530
+
531
+ await waitFor(() =>
532
+ expect(navigated).toEqual([{ screenId: "parent-detail", entityId: "payload-parent" }]),
533
+ );
534
+ });
467
535
  });
@@ -30,6 +30,7 @@
30
30
  // rule that NavDefinition.access follows)
31
31
 
32
32
  import type { AccessRule } from "@cosmicdrift/kumiko-framework/ui-types";
33
+ import { isOpenToAllGranted } from "@cosmicdrift/kumiko-framework/ui-types";
33
34
  import type { AppSchema, FeatureSchema, WorkspaceSchema } from "@cosmicdrift/kumiko-renderer";
34
35
  import { qualifyNavId, toAppSchema, UserRolesProvider, useNav } from "@cosmicdrift/kumiko-renderer";
35
36
  import { type ReactNode, useCallback, useLayoutEffect, useMemo } from "react";
@@ -282,7 +283,7 @@ export function filterByAccess(
282
283
 
283
284
  function userMatchesAccess(access: AccessRule | undefined, userRoles: readonly string[]): boolean {
284
285
  if (access === undefined) return true;
285
- if ("openToAll" in access) return access.openToAll;
286
+ if ("openToAll" in access) return isOpenToAllGranted(access);
286
287
  return access.roles.some((r) => userRoles.includes(r));
287
288
  }
288
289