@cosmicdrift/kumiko-renderer-web 0.32.0 → 0.32.1
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.32.
|
|
3
|
+
"version": "0.32.1",
|
|
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>",
|
|
@@ -547,6 +547,127 @@ describe("KumikoScreen", () => {
|
|
|
547
547
|
expect(searchParamUpdates).toEqual([]);
|
|
548
548
|
});
|
|
549
549
|
|
|
550
|
+
// JSON-Schema-Fall (window.__KUMIKO_SCHEMA__): Function-Props wie
|
|
551
|
+
// action.entityId werden beim JSON-Roundtrip silent gedroppt. Zielt
|
|
552
|
+
// die navigate-Action auf einen entityEdit-Screen, MUSS row.id als
|
|
553
|
+
// deklarativer Default greifen — sonst öffnet der Edit im Create-Mode
|
|
554
|
+
// (Prod-e2e-Befund 2026-06-07 nach F1).
|
|
555
|
+
test("entityList rowActions kind=navigate auf entityEdit-Ziel: row.id ist der entityId-Default (JSON-Schema-sicher)", async () => {
|
|
556
|
+
const navigateCalls: { screenId: string; entityId?: string }[] = [];
|
|
557
|
+
const memoryNav = {
|
|
558
|
+
route: { screenId: "task-list" },
|
|
559
|
+
navigate: (target: { screenId: string; entityId?: string }) => navigateCalls.push(target),
|
|
560
|
+
replace: () => undefined,
|
|
561
|
+
hrefFor: (t: { screenId: string }) => `/${t.screenId}`,
|
|
562
|
+
searchParams: {},
|
|
563
|
+
setSearchParams: () => undefined,
|
|
564
|
+
};
|
|
565
|
+
const dispatcher = makeDispatcher({
|
|
566
|
+
query: (async () => ({
|
|
567
|
+
isSuccess: true,
|
|
568
|
+
data: {
|
|
569
|
+
rows: [{ id: "r1", title: "Alpha", count: 1, done: false }],
|
|
570
|
+
nextCursor: null,
|
|
571
|
+
},
|
|
572
|
+
})) as unknown as Dispatcher["query"],
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const screenWithEdit: EntityListScreenDefinition = {
|
|
576
|
+
id: "task-list",
|
|
577
|
+
type: "entityList",
|
|
578
|
+
entity: "task",
|
|
579
|
+
columns: ["title"],
|
|
580
|
+
rowActions: [
|
|
581
|
+
{
|
|
582
|
+
kind: "navigate",
|
|
583
|
+
id: "edit",
|
|
584
|
+
label: "actions.edit",
|
|
585
|
+
screen: "task-edit",
|
|
586
|
+
// entityId-Function ABSICHTLICH gesetzt und dann per
|
|
587
|
+
// JSON-Roundtrip gedroppt — exakt was buildAppSchema +
|
|
588
|
+
// JSON.stringify mit dem Schema im Browser machen.
|
|
589
|
+
entityId: (row) => String(row["id"] ?? ""),
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
};
|
|
593
|
+
const jsonSchema = JSON.parse(
|
|
594
|
+
JSON.stringify({ ...schema, screens: [screenWithEdit, editScreen] }),
|
|
595
|
+
) as FeatureSchema;
|
|
596
|
+
|
|
597
|
+
const { NavProvider } = await import("@cosmicdrift/kumiko-renderer");
|
|
598
|
+
const user = userEvent.setup();
|
|
599
|
+
render(
|
|
600
|
+
<NavProvider value={memoryNav}>
|
|
601
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
602
|
+
<KumikoScreen schema={jsonSchema} qn="tasks:screen:task-list" />
|
|
603
|
+
</DispatcherProvider>
|
|
604
|
+
</NavProvider>,
|
|
605
|
+
);
|
|
606
|
+
await waitFor(() => expect(screen.queryByTestId("kumiko-screen-loading")).toBeNull());
|
|
607
|
+
|
|
608
|
+
await user.click(screen.getByTestId("row-r1-action-edit"));
|
|
609
|
+
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
610
|
+
expect(navigateCalls[0]).toEqual({ screenId: "task-edit", entityId: "r1" });
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("entityList rowActions kind=navigate auf NICHT-entityEdit-Ziel: kein entityId-Default", async () => {
|
|
614
|
+
const navigateCalls: { screenId: string; entityId?: string }[] = [];
|
|
615
|
+
const memoryNav = {
|
|
616
|
+
route: { screenId: "task-list" },
|
|
617
|
+
navigate: (target: { screenId: string; entityId?: string }) => navigateCalls.push(target),
|
|
618
|
+
replace: () => undefined,
|
|
619
|
+
hrefFor: (t: { screenId: string }) => `/${t.screenId}`,
|
|
620
|
+
searchParams: {},
|
|
621
|
+
setSearchParams: () => undefined,
|
|
622
|
+
};
|
|
623
|
+
const dispatcher = makeDispatcher({
|
|
624
|
+
query: (async () => ({
|
|
625
|
+
isSuccess: true,
|
|
626
|
+
data: {
|
|
627
|
+
rows: [{ id: "r1", title: "Alpha", count: 1, done: false }],
|
|
628
|
+
nextCursor: null,
|
|
629
|
+
},
|
|
630
|
+
})) as unknown as Dispatcher["query"],
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const actionScreen: ActionFormScreenDefinition = {
|
|
634
|
+
id: "task-approve",
|
|
635
|
+
type: "actionForm",
|
|
636
|
+
handler: "tasks:write:task:approve",
|
|
637
|
+
fields: { note: { type: "text" } } as ActionFormScreenDefinition["fields"],
|
|
638
|
+
layout: { sections: [{ title: "x", fields: ["note"] }] },
|
|
639
|
+
};
|
|
640
|
+
const screenWithNav: EntityListScreenDefinition = {
|
|
641
|
+
id: "task-list",
|
|
642
|
+
type: "entityList",
|
|
643
|
+
entity: "task",
|
|
644
|
+
columns: ["title"],
|
|
645
|
+
rowActions: [
|
|
646
|
+
{ kind: "navigate", id: "approve", label: "actions.approve", screen: "task-approve" },
|
|
647
|
+
],
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const { NavProvider } = await import("@cosmicdrift/kumiko-renderer");
|
|
651
|
+
const user = userEvent.setup();
|
|
652
|
+
render(
|
|
653
|
+
<NavProvider value={memoryNav}>
|
|
654
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
655
|
+
<KumikoScreen
|
|
656
|
+
schema={{ ...schema, screens: [screenWithNav, actionScreen] }}
|
|
657
|
+
qn="tasks:screen:task-list"
|
|
658
|
+
/>
|
|
659
|
+
</DispatcherProvider>
|
|
660
|
+
</NavProvider>,
|
|
661
|
+
);
|
|
662
|
+
await waitFor(() => expect(screen.queryByTestId("kumiko-screen-loading")).toBeNull());
|
|
663
|
+
|
|
664
|
+
await user.click(screen.getByTestId("row-r1-action-approve"));
|
|
665
|
+
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
666
|
+
// actionForm-Ziel: row-Kontext kommt via params/searchParams, NICHT
|
|
667
|
+
// als Pfad-Segment — kein entityId-Default.
|
|
668
|
+
expect(navigateCalls[0]).toEqual({ screenId: "task-approve" });
|
|
669
|
+
});
|
|
670
|
+
|
|
550
671
|
test("entityList rowActions kind=navigate ohne params: setSearchParams wird NICHT gerufen", async () => {
|
|
551
672
|
const navigateCalls: { screenId: string }[] = [];
|
|
552
673
|
const searchParamUpdates: Record<string, string | null>[] = [];
|