@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.
|
|
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.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "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.
|
|
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
|
|
286
|
+
if ("openToAll" in access) return isOpenToAllGranted(access);
|
|
286
287
|
return access.roles.some((r) => userRoles.includes(r));
|
|
287
288
|
}
|
|
288
289
|
|