@cosmicdrift/kumiko-renderer-web 0.140.0 → 0.142.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.142.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.142.0",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.142.0",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.142.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",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// #950 — Input forwardet testId (→ data-testid) + readOnly ans echte <input>,
|
|
2
|
+
// damit App-Tests getByTestId(...) + .readOnly/.disabled assertieren können.
|
|
3
|
+
import { describe, expect, test } from "bun:test";
|
|
4
|
+
import { render, screen } from "@testing-library/react";
|
|
5
|
+
import { defaultPrimitives } from "../index";
|
|
6
|
+
|
|
7
|
+
const { Input } = defaultPrimitives;
|
|
8
|
+
const noop = () => {};
|
|
9
|
+
|
|
10
|
+
describe("DefaultInput testId/readOnly forwarding (#950)", () => {
|
|
11
|
+
test("text: testId → data-testid, readOnly durchgereicht", () => {
|
|
12
|
+
render(
|
|
13
|
+
<Input
|
|
14
|
+
kind="text"
|
|
15
|
+
id="subdomain"
|
|
16
|
+
name="subdomain"
|
|
17
|
+
value="acme"
|
|
18
|
+
onChange={noop}
|
|
19
|
+
testId="url-settings-subdomain"
|
|
20
|
+
readOnly
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
const input = screen.getByTestId("url-settings-subdomain");
|
|
24
|
+
expect(input.tagName).toBe("INPUT");
|
|
25
|
+
expect((input as HTMLInputElement).readOnly).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("readOnly default false (nicht gesetzt = editierbar)", () => {
|
|
29
|
+
render(<Input kind="text" id="x" name="x" value="" onChange={noop} testId="plain" />);
|
|
30
|
+
expect((screen.getByTestId("plain") as HTMLInputElement).readOnly).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("email/password/number: testId erreicht das Element", () => {
|
|
34
|
+
render(
|
|
35
|
+
<>
|
|
36
|
+
<Input kind="email" id="e" name="e" value="" onChange={noop} testId="tid-email" />
|
|
37
|
+
<Input kind="password" id="p" name="p" value="" onChange={noop} testId="tid-pw" />
|
|
38
|
+
<Input kind="number" id="n" name="n" value={1} onChange={noop} testId="tid-num" />
|
|
39
|
+
</>,
|
|
40
|
+
);
|
|
41
|
+
for (const tid of ["tid-email", "tid-pw", "tid-num"]) {
|
|
42
|
+
expect(screen.getByTestId(tid).tagName).toBe("INPUT");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
package/src/primitives/index.tsx
CHANGED
|
@@ -125,14 +125,14 @@ function DefaultButton({
|
|
|
125
125
|
variant = "primary",
|
|
126
126
|
size = "md",
|
|
127
127
|
ariaLabel,
|
|
128
|
-
|
|
128
|
+
width = "auto",
|
|
129
129
|
children,
|
|
130
130
|
testId,
|
|
131
131
|
}: ButtonProps): ReactNode {
|
|
132
132
|
// link-Variant rendert text-artig (Inline-Link im Fließtext/Banner), nicht als
|
|
133
|
-
// gepolsterte Fläche;
|
|
133
|
+
// gepolsterte Fläche; width="full" streckt CTA-Buttons in Karten/Panels.
|
|
134
134
|
const className =
|
|
135
|
-
[variant === "link" ? "h-auto px-0 py-0" : "",
|
|
135
|
+
[variant === "link" ? "h-auto px-0 py-0" : "", width === "full" ? "w-full" : ""]
|
|
136
136
|
.filter(Boolean)
|
|
137
137
|
.join(" ") || undefined;
|
|
138
138
|
return (
|
|
@@ -267,6 +267,8 @@ function DefaultInput(props: InputProps): ReactNode {
|
|
|
267
267
|
<UiInput
|
|
268
268
|
type="text"
|
|
269
269
|
{...common}
|
|
270
|
+
data-testid={props.testId}
|
|
271
|
+
readOnly={props.readOnly}
|
|
270
272
|
value={props.value}
|
|
271
273
|
onChange={(e: ChangeEvent<HTMLInputElement>) => props.onChange(e.target.value)}
|
|
272
274
|
{...(props.placeholder !== undefined && { placeholder: props.placeholder })}
|
|
@@ -278,6 +280,7 @@ function DefaultInput(props: InputProps): ReactNode {
|
|
|
278
280
|
<UiInput
|
|
279
281
|
type="email"
|
|
280
282
|
{...common}
|
|
283
|
+
data-testid={props.testId}
|
|
281
284
|
value={props.value}
|
|
282
285
|
onChange={(e: ChangeEvent<HTMLInputElement>) => props.onChange(e.target.value)}
|
|
283
286
|
{...(props.placeholder !== undefined && { placeholder: props.placeholder })}
|
|
@@ -289,6 +292,7 @@ function DefaultInput(props: InputProps): ReactNode {
|
|
|
289
292
|
<UiInput
|
|
290
293
|
type="password"
|
|
291
294
|
{...common}
|
|
295
|
+
data-testid={props.testId}
|
|
292
296
|
value={props.value}
|
|
293
297
|
onChange={(e: ChangeEvent<HTMLInputElement>) => props.onChange(e.target.value)}
|
|
294
298
|
autoComplete={props.autoComplete ?? "current-password"}
|
|
@@ -299,6 +303,7 @@ function DefaultInput(props: InputProps): ReactNode {
|
|
|
299
303
|
<UiInput
|
|
300
304
|
type="number"
|
|
301
305
|
{...common}
|
|
306
|
+
data-testid={props.testId}
|
|
302
307
|
value={props.value}
|
|
303
308
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
304
309
|
const v = e.target.value;
|
|
@@ -246,11 +246,11 @@ describe("Button size", () => {
|
|
|
246
246
|
expect(screen.getByRole("button", { name: "Entfernen" })).toBeTruthy();
|
|
247
247
|
});
|
|
248
248
|
|
|
249
|
-
test("
|
|
249
|
+
test('width="full" streckt den Button (w-full)', () => {
|
|
250
250
|
function WideBtn(): ReactNode {
|
|
251
251
|
const { Button } = usePrimitives();
|
|
252
252
|
return (
|
|
253
|
-
<Button
|
|
253
|
+
<Button width="full" onClick={() => {}}>
|
|
254
254
|
Upgrade
|
|
255
255
|
</Button>
|
|
256
256
|
);
|
|
@@ -63,15 +63,17 @@ export function ResultTable<Row>({
|
|
|
63
63
|
rows,
|
|
64
64
|
rowKey,
|
|
65
65
|
testId,
|
|
66
|
+
className,
|
|
66
67
|
}: {
|
|
67
68
|
readonly columns: readonly ResultColumn<Row>[];
|
|
68
69
|
readonly rows: readonly Row[];
|
|
69
70
|
readonly rowKey: (row: Row, index: number) => string;
|
|
70
71
|
readonly testId?: string;
|
|
72
|
+
readonly className?: string;
|
|
71
73
|
}): ReactNode {
|
|
72
74
|
return (
|
|
73
75
|
<div className="overflow-x-auto">
|
|
74
|
-
<table data-testid={testId} className="w-full text-sm">
|
|
76
|
+
<table data-testid={testId} className={cn("w-full text-sm", className)}>
|
|
75
77
|
<thead>
|
|
76
78
|
<tr className="border-b text-left text-muted-foreground">
|
|
77
79
|
{columns.map((col) => (
|
|
@@ -120,6 +122,7 @@ export function ComparisonTable<Col>({
|
|
|
120
122
|
metrics,
|
|
121
123
|
metricLabel,
|
|
122
124
|
testId,
|
|
125
|
+
className,
|
|
123
126
|
}: {
|
|
124
127
|
readonly columns: readonly Col[];
|
|
125
128
|
readonly columnHeader: (col: Col, index: number) => string;
|
|
@@ -127,10 +130,11 @@ export function ComparisonTable<Col>({
|
|
|
127
130
|
readonly metrics: readonly ComparisonMetric<Col>[];
|
|
128
131
|
readonly metricLabel: string;
|
|
129
132
|
readonly testId?: string;
|
|
133
|
+
readonly className?: string;
|
|
130
134
|
}): ReactNode {
|
|
131
135
|
return (
|
|
132
136
|
<div className="overflow-x-auto">
|
|
133
|
-
<table data-testid={testId} className="w-full min-w-[24rem] text-sm">
|
|
137
|
+
<table data-testid={testId} className={cn("w-full min-w-[24rem] text-sm", className)}>
|
|
134
138
|
<thead>
|
|
135
139
|
<tr className="border-b text-left text-muted-foreground">
|
|
136
140
|
<th className="py-1.5 font-medium">{metricLabel}</th>
|