@cosmicdrift/kumiko-renderer-web 0.156.2 → 0.157.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.157.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.157.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.157.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.157.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",
|
|
@@ -432,6 +432,31 @@ describe("KumikoScreen", () => {
|
|
|
432
432
|
expect(screen.queryByTestId("render-edit-form-error")).toBeNull();
|
|
433
433
|
});
|
|
434
434
|
|
|
435
|
+
test("entityEdit update-mode: query-Error zeigt übersetzten Text statt rohem i18nKey (issue #1193)", async () => {
|
|
436
|
+
const dispatcher = makeDispatcher({
|
|
437
|
+
query: (async () => ({
|
|
438
|
+
isSuccess: false,
|
|
439
|
+
error: {
|
|
440
|
+
code: "access_denied",
|
|
441
|
+
httpStatus: 403,
|
|
442
|
+
i18nKey: "errors.access.denied",
|
|
443
|
+
message: "",
|
|
444
|
+
},
|
|
445
|
+
})) as unknown as Dispatcher["query"],
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
render(
|
|
449
|
+
<DispatcherProvider dispatcher={dispatcher}>
|
|
450
|
+
<KumikoScreen schema={schema} qn="tasks:screen:task-edit" entityId="task-1" />
|
|
451
|
+
</DispatcherProvider>,
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
await waitFor(() => expect(screen.queryByTestId("kumiko-screen-error")).toBeTruthy());
|
|
455
|
+
const bannerText = screen.getByTestId("kumiko-screen-error").textContent;
|
|
456
|
+
expect(bannerText).toBe(kumikoDefaultTranslations["en"]?.["errors.access.denied"] ?? "");
|
|
457
|
+
expect(bannerText).not.toBe("errors.access.denied");
|
|
458
|
+
});
|
|
459
|
+
|
|
435
460
|
// RowActions-Mapping (Tier 2.7a Resolution-Layer): pinst dass
|
|
436
461
|
// EntityListBody die Schema-Form (handler-QN, label-i18nKey, payload-
|
|
437
462
|
// builder, visible-Function, confirmLabel) zu DataTableRowAction
|
|
@@ -32,6 +32,29 @@ describe("ResultTable", () => {
|
|
|
32
32
|
expect(container.querySelector(".rounded-lg.border.bg-card")).toBeNull();
|
|
33
33
|
expect(container.querySelector("thead.bg-muted")).toBeNull();
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
test("footer rows render as table rows spanning all-but-last column, value under the last column", () => {
|
|
37
|
+
const { container } = render(
|
|
38
|
+
<ResultTable
|
|
39
|
+
columns={COLUMNS}
|
|
40
|
+
rows={ROWS}
|
|
41
|
+
rowKey={(r) => String(r.year)}
|
|
42
|
+
testId="rt"
|
|
43
|
+
footer={[
|
|
44
|
+
{ label: "Subtotal", value: "300" },
|
|
45
|
+
{ label: "Total", value: "300", emphasize: true },
|
|
46
|
+
]}
|
|
47
|
+
/>,
|
|
48
|
+
);
|
|
49
|
+
const footerRows = container.querySelectorAll("tbody tr");
|
|
50
|
+
// 2 data rows + 2 footer rows.
|
|
51
|
+
expect(footerRows).toHaveLength(4);
|
|
52
|
+
const totalRow = footerRows[3];
|
|
53
|
+
expect(totalRow?.querySelector("td[colspan]")?.textContent).toBe("Total");
|
|
54
|
+
expect(totalRow?.querySelectorAll("td")[1]?.textContent).toBe("300");
|
|
55
|
+
expect(totalRow?.className).toContain("font-semibold");
|
|
56
|
+
expect(screen.getByText("Subtotal")).toBeTruthy();
|
|
57
|
+
});
|
|
35
58
|
});
|
|
36
59
|
|
|
37
60
|
describe("ComparisonTable", () => {
|
|
@@ -54,6 +54,12 @@ export interface ResultColumn<Row> {
|
|
|
54
54
|
readonly cell: (row: Row) => ReactNode;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
export interface ResultTableFooterRow {
|
|
58
|
+
readonly label: string;
|
|
59
|
+
readonly value: ReactNode;
|
|
60
|
+
readonly emphasize?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
/** Statische, prop-getriebene Ergebnistabelle für berechnete Zeilen (Tranchen,
|
|
58
64
|
* Szenarien). Nimmt dem Screen die `<table>`+tabular-nums-Ketten ab.
|
|
59
65
|
* ponytail: bewusst die minimale Read-only-Tabelle — für Sort/Pager/Facets
|
|
@@ -65,6 +71,7 @@ export function ResultTable<Row>({
|
|
|
65
71
|
testId,
|
|
66
72
|
className,
|
|
67
73
|
card,
|
|
74
|
+
footer,
|
|
68
75
|
}: {
|
|
69
76
|
readonly columns: readonly ResultColumn<Row>[];
|
|
70
77
|
readonly rows: readonly Row[];
|
|
@@ -74,6 +81,11 @@ export function ResultTable<Row>({
|
|
|
74
81
|
/** Rahmen-Look wie die CRUD-Liste (DataTable): gerundeter Border-Container +
|
|
75
82
|
* bg-muted-Header-Band. Default = bare (nur die Tabelle, ohne Container). */
|
|
76
83
|
readonly card?: boolean;
|
|
84
|
+
/** Summary rows (Subtotal/Tax/Total) rendered as real `<tr>`s in the same
|
|
85
|
+
* `<table>` instead of a separate flex-div next to it — guarantees
|
|
86
|
+
* column-alignment to the last (typically "Amount") column instead of
|
|
87
|
+
* approximating it with an independent layout. */
|
|
88
|
+
readonly footer?: readonly ResultTableFooterRow[];
|
|
77
89
|
}): ReactNode {
|
|
78
90
|
const table = (
|
|
79
91
|
<div className="overflow-x-auto">
|
|
@@ -111,6 +123,28 @@ export function ResultTable<Row>({
|
|
|
111
123
|
))}
|
|
112
124
|
</tr>
|
|
113
125
|
))}
|
|
126
|
+
{footer?.map((row, i) => (
|
|
127
|
+
<tr
|
|
128
|
+
key={row.label}
|
|
129
|
+
className={cn(i === 0 && "border-t", row.emphasize === true && "font-semibold")}
|
|
130
|
+
>
|
|
131
|
+
<td
|
|
132
|
+
colSpan={Math.max(columns.length - 1, 1)}
|
|
133
|
+
className={cn(
|
|
134
|
+
"py-1.5 text-muted-foreground",
|
|
135
|
+
card === true && "px-3",
|
|
136
|
+
row.emphasize === true && "text-foreground",
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
{row.label}
|
|
140
|
+
</td>
|
|
141
|
+
{columns.length > 1 && (
|
|
142
|
+
<td className={cn("py-1.5 text-right tabular-nums", card === true && "px-3")}>
|
|
143
|
+
{row.value}
|
|
144
|
+
</td>
|
|
145
|
+
)}
|
|
146
|
+
</tr>
|
|
147
|
+
))}
|
|
114
148
|
</tbody>
|
|
115
149
|
</table>
|
|
116
150
|
</div>
|