@cosmicdrift/kumiko-renderer-web 0.235.4 → 0.236.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.
|
|
3
|
+
"version": "0.236.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>",
|
|
@@ -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.236.1",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.236.1",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.236.1",
|
|
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",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@types/react-dom": "^19.2.3",
|
|
65
65
|
"jsdom": "^29.1.1",
|
|
66
66
|
"tailwindcss": "^4.3.0",
|
|
67
|
-
"@cosmicdrift/kumiko-locale-de": "0.
|
|
67
|
+
"@cosmicdrift/kumiko-locale-de": "0.236.1"
|
|
68
68
|
},
|
|
69
69
|
"repository": {
|
|
70
70
|
"type": "git",
|
|
@@ -203,9 +203,18 @@ describe("KumikoScreen: singleton entityEdit", () => {
|
|
|
203
203
|
</DispatcherProvider>,
|
|
204
204
|
);
|
|
205
205
|
await waitFor(() => expect(screen.queryByTestId("kumiko-screen-loading")).toBeNull());
|
|
206
|
+
await waitFor(() => expect(screen.getByTestId("field-title")).toBeTruthy());
|
|
206
207
|
|
|
207
208
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
208
209
|
fireEvent.change(titleInput, { target: { value: "Created title" } });
|
|
210
|
+
// Re-query (not a held reference, which a re-render could have detached)
|
|
211
|
+
// and confirm the button is actually clickable before clicking it — a
|
|
212
|
+
// disabled native button swallows fireEvent.click silently, which reads
|
|
213
|
+
// identically to "the handler never ran" at the write-count assertion below.
|
|
214
|
+
await waitFor(() => {
|
|
215
|
+
const button = screen.getByTestId("render-edit-submit") as HTMLButtonElement;
|
|
216
|
+
expect(button.disabled).toBe(false);
|
|
217
|
+
});
|
|
209
218
|
fireEvent.click(screen.getByTestId("render-edit-submit"));
|
|
210
219
|
|
|
211
220
|
await waitFor(() => expect(write).toHaveBeenCalledTimes(1));
|
|
@@ -228,6 +237,10 @@ describe("KumikoScreen: singleton entityEdit", () => {
|
|
|
228
237
|
fireEvent.change(screen.getByTestId("field-title").querySelector("input") as HTMLInputElement, {
|
|
229
238
|
target: { value: "Edited again" },
|
|
230
239
|
});
|
|
240
|
+
await waitFor(() => {
|
|
241
|
+
const button = screen.getByTestId("render-edit-submit") as HTMLButtonElement;
|
|
242
|
+
expect(button.disabled).toBe(false);
|
|
243
|
+
});
|
|
231
244
|
fireEvent.click(screen.getByTestId("render-edit-submit"));
|
|
232
245
|
// Exactly one create (the first submit) followed by an update, never a
|
|
233
246
|
// second create — the singleton wrapper must have flipped branches.
|
|
@@ -58,6 +58,17 @@ const schema: FeatureSchema = {
|
|
|
58
58
|
screens: [editScreen, listScreen, restrictedListScreen],
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// Re-queries the button (a held reference may have been detached by a
|
|
62
|
+
// re-render) and waits for it to actually be clickable before firing the
|
|
63
|
+
// click — a disabled native button swallows fireEvent.click silently.
|
|
64
|
+
async function clickSubmitOnceEnabled(): Promise<void> {
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
const button = screen.getByTestId("render-edit-submit") as HTMLButtonElement;
|
|
67
|
+
expect(button.disabled).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
fireEvent.click(screen.getByTestId("render-edit-submit"));
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
function makeDispatcher(overrides: Partial<Dispatcher> = {}): Dispatcher {
|
|
62
73
|
const base = createMockDispatcher({
|
|
63
74
|
query: (async () => ({
|
|
@@ -276,7 +287,7 @@ describe("KumikoScreen", () => {
|
|
|
276
287
|
|
|
277
288
|
// Edit + submit → write command trägt { id, version, changes: {title} }
|
|
278
289
|
fireEvent.change(titleInput, { target: { value: "edited-title" } });
|
|
279
|
-
|
|
290
|
+
await clickSubmitOnceEnabled();
|
|
280
291
|
|
|
281
292
|
await waitFor(() => expect(writeCalls.length).toBe(1));
|
|
282
293
|
const [call] = writeCalls;
|
|
@@ -538,7 +549,7 @@ describe("KumikoScreen", () => {
|
|
|
538
549
|
|
|
539
550
|
// Dirty machen damit der Submit überhaupt feuert.
|
|
540
551
|
fireEvent.change(titleInput, { target: { value: "edited" } });
|
|
541
|
-
|
|
552
|
+
await clickSubmitOnceEnabled();
|
|
542
553
|
|
|
543
554
|
// Banner zeigt die ÜBERSETZTE version-conflict-Message (kein roher i18nKey)
|
|
544
555
|
// und bietet einen Reload-Button. Gegen das Default-Bundle assertet, damit
|
|
@@ -1341,7 +1352,7 @@ describe("KumikoScreen", () => {
|
|
|
1341
1352
|
const noteInput = screen.getByTestId("field-note").querySelector("input");
|
|
1342
1353
|
if (noteInput === null) throw new Error("expected an <input> inside field-note");
|
|
1343
1354
|
fireEvent.change(noteInput, { target: { value: "hello" } });
|
|
1344
|
-
|
|
1355
|
+
await clickSubmitOnceEnabled();
|
|
1345
1356
|
|
|
1346
1357
|
await waitFor(() => expect(writeCalls.length).toBe(1));
|
|
1347
1358
|
expect(writeCalls[0]?.type).toBe("tasks:write:task:note");
|
|
@@ -1566,7 +1577,7 @@ describe("KumikoScreen", () => {
|
|
|
1566
1577
|
expect(titleInput).toBeTruthy();
|
|
1567
1578
|
fireEvent.change(titleInput, { target: { value: "New Task" } });
|
|
1568
1579
|
|
|
1569
|
-
|
|
1580
|
+
await clickSubmitOnceEnabled();
|
|
1570
1581
|
await waitFor(() => expect(writeCalls.length).toBe(1));
|
|
1571
1582
|
expect(writeCalls[0]?.type).toBe("tasks:write:task:quick-add");
|
|
1572
1583
|
// payloadMode="values" — alle Form-Werte landen im Payload, nicht
|
|
@@ -1615,7 +1626,7 @@ describe("KumikoScreen", () => {
|
|
|
1615
1626
|
|
|
1616
1627
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
1617
1628
|
fireEvent.change(titleInput, { target: { value: "go" } });
|
|
1618
|
-
|
|
1629
|
+
await clickSubmitOnceEnabled();
|
|
1619
1630
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1620
1631
|
// #2419: entityList targets ignore entityId — do not attach a handler id.
|
|
1621
1632
|
expect(navigateCalls[0]).toEqual({ screenId: "task-list" });
|
|
@@ -1674,7 +1685,7 @@ describe("KumikoScreen", () => {
|
|
|
1674
1685
|
|
|
1675
1686
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
1676
1687
|
fireEvent.change(titleInput, { target: { value: "go" } });
|
|
1677
|
-
|
|
1688
|
+
await clickSubmitOnceEnabled();
|
|
1678
1689
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1679
1690
|
// #2485: the cross-feature target now resolves (over all mounted
|
|
1680
1691
|
// features) — it just happens to be an entityList, which never carries
|
|
@@ -1739,7 +1750,7 @@ describe("KumikoScreen", () => {
|
|
|
1739
1750
|
|
|
1740
1751
|
const vinInput = screen.getByTestId("field-vin").querySelector("input") as HTMLInputElement;
|
|
1741
1752
|
fireEvent.change(vinInput, { target: { value: "1HGCM82633A004352" } });
|
|
1742
|
-
|
|
1753
|
+
await clickSubmitOnceEnabled();
|
|
1743
1754
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1744
1755
|
// #2485: redirect crosses a feature boundary (vehicle-vin-decode →
|
|
1745
1756
|
// vehicles) — the target is only found by searching all mounted
|
|
@@ -1819,7 +1830,7 @@ describe("KumikoScreen", () => {
|
|
|
1819
1830
|
|
|
1820
1831
|
const vinInput = screen.getByTestId("field-vin").querySelector("input") as HTMLInputElement;
|
|
1821
1832
|
fireEvent.change(vinInput, { target: { value: "1HGCM82633A004352" } });
|
|
1822
|
-
|
|
1833
|
+
await clickSubmitOnceEnabled();
|
|
1823
1834
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1824
1835
|
// The QN names "vehicles" explicitly — the decoy feature's same-id
|
|
1825
1836
|
// screen (mounted first) must not win just because it comes first.
|
|
@@ -1873,7 +1884,7 @@ describe("KumikoScreen", () => {
|
|
|
1873
1884
|
|
|
1874
1885
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
1875
1886
|
fireEvent.change(titleInput, { target: { value: "go" } });
|
|
1876
|
-
|
|
1887
|
+
await clickSubmitOnceEnabled();
|
|
1877
1888
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1878
1889
|
expect(navigateCalls[0]).toEqual({ screenId: "task-edit", entityId: "x" });
|
|
1879
1890
|
});
|
|
@@ -1924,7 +1935,7 @@ describe("KumikoScreen", () => {
|
|
|
1924
1935
|
|
|
1925
1936
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
1926
1937
|
fireEvent.change(titleInput, { target: { value: "go" } });
|
|
1927
|
-
|
|
1938
|
+
await clickSubmitOnceEnabled();
|
|
1928
1939
|
await waitFor(() => expect(navigateCalls.length).toBe(1));
|
|
1929
1940
|
expect(navigateCalls[0]).toEqual({ screenId: "task-list" });
|
|
1930
1941
|
expect(Object.hasOwn(navigateCalls[0] ?? {}, "entityId")).toBe(false);
|
|
@@ -2186,7 +2197,7 @@ describe("KumikoScreen", () => {
|
|
|
2186
2197
|
|
|
2187
2198
|
const titleInput = screen.getByTestId("field-title").querySelector("input") as HTMLInputElement;
|
|
2188
2199
|
fireEvent.change(titleInput, { target: { value: "stay" } });
|
|
2189
|
-
|
|
2200
|
+
await clickSubmitOnceEnabled();
|
|
2190
2201
|
// Auf den Write-Call warten — sonst racet der Test gegen den
|
|
2191
2202
|
// async submit und prüft navigate-Calls bevor handleSubmitted
|
|
2192
2203
|
// überhaupt gerufen wurde (waitFor auf "render-edit-form" wäre
|