@xenide-io/the-old-ui-theme 0.9.18 → 0.10.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/dist/{Chip-BzuNgJIa.d.mts → Chip-DKT-82Lf.d.mts} +5 -3
- package/dist/{Chip-BzuNgJIa.d.ts → Chip-DKT-82Lf.d.ts} +5 -3
- package/dist/{chunk-6SVJAU5K.mjs → chunk-LHE4ROON.mjs} +977 -521
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +138 -99
- package/dist/index.mjs +1 -1
- package/dist/suite.d.mts +23 -1
- package/dist/suite.d.ts +23 -1
- package/dist/suite.js +4248 -4039
- package/dist/suite.mjs +1006 -793
- package/dist/tailwind-preset.js +10 -1
- package/dist/tailwind-preset.mjs +10 -1
- package/dist/ui.d.mts +33 -4
- package/dist/ui.d.ts +33 -4
- package/dist/ui.js +1024 -566
- package/dist/ui.mjs +13 -1
- package/docs/macos-identity-audit.md +279 -0
- package/package.json +1 -1
- package/src/app/globals.css +334 -44
- package/src/components/ui/Accordion.tsx +4 -7
- package/src/components/ui/Avatar.tsx +5 -5
- package/src/components/ui/Calendar.tsx +7 -7
- package/src/components/ui/CodeBlock.tsx +3 -10
- package/src/components/ui/CollapsibleSection.tsx +4 -4
- package/src/components/ui/CommandPalette.tsx +7 -10
- package/src/components/ui/EmptyState.tsx +2 -2
- package/src/components/ui/Input.tsx +1 -1
- package/src/components/ui/Marketing.tsx +128 -0
- package/src/components/ui/MarketingIllustrations.tsx +287 -0
- package/src/components/ui/Panel.tsx +1 -1
- package/src/components/ui/SegmentedControl.tsx +59 -13
- package/src/components/ui/ShowcaseWrapper.tsx +3 -3
- package/src/components/ui/Table.tsx +18 -6
- package/src/components/ui/Typography.tsx +4 -4
- package/src/components/ui/avatar-stat.test.tsx +14 -1
- package/src/components/ui/index.ts +13 -12
- package/src/components/ui/interaction-primitives.test.tsx +12 -6
- package/src/components/ui/marketing.test.tsx +39 -0
- package/src/components/ui/table.test.tsx +20 -0
- package/src/styles/suite-skin.css +405 -40
- package/src/styles/themes.css +45 -45
- package/src/styles/typography.css +25 -1
- package/src/suite/components/ai-panel.tsx +17 -17
- package/src/suite/components/app-switcher.tsx +9 -9
- package/src/suite/components/suite-app-layout.tsx +7 -4
- package/src/suite/components/suite-bottom-nav.tsx +1 -1
- package/src/suite/components/suite-clients-projects-directory.tsx +6 -6
- package/src/suite/components/suite-integration-picker.tsx +7 -7
- package/src/suite/components/suite-integration-rules.tsx +5 -5
- package/src/suite/components/suite-layout.test.tsx +32 -3
- package/src/suite/components/suite-layout.tsx +4 -4
- package/src/suite/components/suite-mobile-drawer.tsx +2 -2
- package/src/suite/components/suite-mobile-header.tsx +1 -1
- package/src/suite/components/suite-notification-bell.tsx +4 -4
- package/src/suite/components/suite-notification-settings.tsx +171 -0
- package/src/suite/components/suite-palette-library.test.tsx +46 -0
- package/src/suite/components/suite-palette-library.tsx +82 -15
- package/src/suite/components/suite-settings-layout.tsx +2 -2
- package/src/suite/components/suite-settings-mobile-nav.tsx +5 -9
- package/src/suite/components/suite-sidebar.test.tsx +39 -0
- package/src/suite/components/suite-sidebar.tsx +41 -19
- package/src/suite/components/suite-skeleton.tsx +6 -6
- package/src/suite/components/suite-user-menu.tsx +2 -2
- package/src/suite/components/today-calibrating.tsx +3 -3
- package/src/suite/components/today-ui.tsx +12 -12
- package/src/suite/components/workspace-switcher.tsx +3 -3
- package/src/suite/index.ts +6 -0
- package/tailwind-preset.ts +9 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useRef, type KeyboardEvent, type ReactNode } from "react";
|
|
3
4
|
import { cn } from "@/lib/cn";
|
|
4
5
|
|
|
5
6
|
export interface SegmentedControlOption {
|
|
6
7
|
value: string;
|
|
7
8
|
label: string;
|
|
8
|
-
icon?:
|
|
9
|
+
icon?: ReactNode;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export interface SegmentedControlProps {
|
|
@@ -13,30 +14,75 @@ export interface SegmentedControlProps {
|
|
|
13
14
|
value: string;
|
|
14
15
|
onChange: (value: string) => void;
|
|
15
16
|
className?: string;
|
|
17
|
+
"aria-label"?: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export function SegmentedControl({
|
|
20
|
+
export function SegmentedControl({
|
|
21
|
+
options,
|
|
22
|
+
value,
|
|
23
|
+
onChange,
|
|
24
|
+
className,
|
|
25
|
+
"aria-label": ariaLabel,
|
|
26
|
+
}: SegmentedControlProps) {
|
|
27
|
+
const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);
|
|
28
|
+
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
29
|
+
|
|
30
|
+
const handleKeyDown = (
|
|
31
|
+
event: KeyboardEvent<HTMLButtonElement>,
|
|
32
|
+
index: number,
|
|
33
|
+
) => {
|
|
34
|
+
let nextIndex: number;
|
|
35
|
+
|
|
36
|
+
switch (event.key) {
|
|
37
|
+
case "ArrowLeft":
|
|
38
|
+
case "ArrowUp":
|
|
39
|
+
nextIndex = (index - 1 + options.length) % options.length;
|
|
40
|
+
break;
|
|
41
|
+
case "ArrowRight":
|
|
42
|
+
case "ArrowDown":
|
|
43
|
+
nextIndex = (index + 1) % options.length;
|
|
44
|
+
break;
|
|
45
|
+
case "Home":
|
|
46
|
+
nextIndex = 0;
|
|
47
|
+
break;
|
|
48
|
+
case "End":
|
|
49
|
+
nextIndex = options.length - 1;
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
const nextOption = options[nextIndex];
|
|
57
|
+
if (!nextOption) return;
|
|
58
|
+
onChange(nextOption.value);
|
|
59
|
+
itemRefs.current[nextIndex]?.focus();
|
|
60
|
+
};
|
|
61
|
+
|
|
19
62
|
return (
|
|
20
63
|
<div
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)}
|
|
64
|
+
role="radiogroup"
|
|
65
|
+
aria-label={ariaLabel}
|
|
66
|
+
className={cn("ph-segmented-control", className)}
|
|
25
67
|
>
|
|
26
|
-
{options.map((option) => (
|
|
68
|
+
{options.map((option, index) => (
|
|
27
69
|
<button
|
|
28
70
|
key={option.value}
|
|
71
|
+
ref={(node) => {
|
|
72
|
+
itemRefs.current[index] = node;
|
|
73
|
+
}}
|
|
29
74
|
type="button"
|
|
30
|
-
|
|
75
|
+
role="radio"
|
|
76
|
+
aria-checked={value === option.value}
|
|
77
|
+
tabIndex={selectedIndex === index || (selectedIndex < 0 && index === 0) ? 0 : -1}
|
|
31
78
|
onClick={() => onChange(option.value)}
|
|
79
|
+
onKeyDown={(event) => handleKeyDown(event, index)}
|
|
32
80
|
className={cn(
|
|
33
|
-
"
|
|
34
|
-
value === option.value
|
|
35
|
-
? "bg-ph-surface text-ph-ink shadow-ph"
|
|
36
|
-
: "text-ph-subtle hover:text-ph-ink"
|
|
81
|
+
"ph-segmented-control__item",
|
|
82
|
+
value === option.value && "ph-segmented-control__item--selected",
|
|
37
83
|
)}
|
|
38
84
|
>
|
|
39
|
-
{option.icon && <span className="h-4 w-4">{option.icon}</span>}
|
|
85
|
+
{option.icon && <span className="h-4 w-4" aria-hidden>{option.icon}</span>}
|
|
40
86
|
{option.label}
|
|
41
87
|
</button>
|
|
42
88
|
))}
|
|
@@ -38,9 +38,9 @@ export function ShowcaseWrapper({
|
|
|
38
38
|
type="button"
|
|
39
39
|
onClick={() => setShowCode(!showCode)}
|
|
40
40
|
className={cn(
|
|
41
|
-
"flex h-10 w-10 items-center justify-center rounded-full border transition-
|
|
41
|
+
"ph-showcase-toggle flex h-10 w-10 items-center justify-center rounded-full border transition-colors",
|
|
42
42
|
showCode
|
|
43
|
-
? "border-ph-brand bg-ph-brand text-[var(--ph-on-accent)] shadow-
|
|
43
|
+
? "border-ph-brand bg-ph-brand text-[var(--ph-on-accent)] shadow-[var(--ph-shadow-control)]"
|
|
44
44
|
: "border-ph-border bg-ph-surface text-ph-subtle hover:border-ph-brand hover:text-ph-brand"
|
|
45
45
|
)}
|
|
46
46
|
title={showCode ? "Hide code" : "Show code"}
|
|
@@ -68,7 +68,7 @@ export function ShowcaseWrapper({
|
|
|
68
68
|
|
|
69
69
|
<div
|
|
70
70
|
className={cn(
|
|
71
|
-
"transition-
|
|
71
|
+
"transition-opacity duration-200",
|
|
72
72
|
showCode
|
|
73
73
|
? "block opacity-100"
|
|
74
74
|
: "hidden opacity-0 lg:hidden"
|
|
@@ -20,6 +20,7 @@ export interface TableProps<T> {
|
|
|
20
20
|
getRowKey?: (row: T, index: number) => Key;
|
|
21
21
|
className?: string;
|
|
22
22
|
caption?: string;
|
|
23
|
+
emptyState?: ReactNode;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function Table<T>({
|
|
@@ -32,10 +33,13 @@ export function Table<T>({
|
|
|
32
33
|
getRowKey,
|
|
33
34
|
className,
|
|
34
35
|
caption,
|
|
36
|
+
emptyState = "No data available.",
|
|
35
37
|
}: TableProps<T>) {
|
|
36
38
|
return (
|
|
37
39
|
<div
|
|
38
40
|
className={cn("ph-table-wrap", compact && "ph-table-compact", className)}
|
|
41
|
+
role="region"
|
|
42
|
+
aria-label={caption ?? "Data table"}
|
|
39
43
|
tabIndex={0}
|
|
40
44
|
>
|
|
41
45
|
<table className={cn("ph-table", zebra && "ph-table-zebra", ribbon && "ph-table--ribbon")}>
|
|
@@ -48,13 +52,21 @@ export function Table<T>({
|
|
|
48
52
|
</tr>
|
|
49
53
|
</thead>
|
|
50
54
|
<tbody>
|
|
51
|
-
{data.
|
|
52
|
-
<tr
|
|
53
|
-
{columns.
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
{data.length === 0 ? (
|
|
56
|
+
<tr>
|
|
57
|
+
<td colSpan={columns.length} className="ph-table-empty">
|
|
58
|
+
{emptyState}
|
|
59
|
+
</td>
|
|
56
60
|
</tr>
|
|
57
|
-
)
|
|
61
|
+
) : (
|
|
62
|
+
data.map((row, i) => (
|
|
63
|
+
<tr key={getRowKey?.(row, i) ?? i} style={getRowStyle?.(row)}>
|
|
64
|
+
{columns.map((col) => (
|
|
65
|
+
<td key={col.key} className={col.className}>{col.cell(row)}</td>
|
|
66
|
+
))}
|
|
67
|
+
</tr>
|
|
68
|
+
))
|
|
69
|
+
)}
|
|
58
70
|
</tbody>
|
|
59
71
|
</table>
|
|
60
72
|
</div>
|
|
@@ -99,7 +99,7 @@ export const H1 = forwardRef<HTMLHeadingElement, H1Props>(function H1(
|
|
|
99
99
|
{ children, tone = "default", weight, truncate, className, ...props },
|
|
100
100
|
ref,
|
|
101
101
|
) {
|
|
102
|
-
return <h1 ref={ref} {...props} className={textClass("
|
|
102
|
+
return <h1 ref={ref} {...props} className={textClass("ph-page-title font-bold", tone, weight, truncate, className)}>{children}</h1>;
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
/** H2 — section title */
|
|
@@ -107,7 +107,7 @@ export const H2 = forwardRef<HTMLHeadingElement, H2Props>(function H2(
|
|
|
107
107
|
{ children, tone = "default", weight, truncate, className, ...props },
|
|
108
108
|
ref,
|
|
109
109
|
) {
|
|
110
|
-
return <h2 ref={ref} {...props} className={textClass("text-2xl font-
|
|
110
|
+
return <h2 ref={ref} {...props} className={textClass("text-2xl font-semibold tracking-tight", tone, weight, truncate, className)}>{children}</h2>;
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
/** H3 — subsection title */
|
|
@@ -155,7 +155,7 @@ export const Caption = forwardRef<HTMLSpanElement, CaptionProps>(function Captio
|
|
|
155
155
|
{ children, tone = "muted", weight, truncate, className, ...props },
|
|
156
156
|
ref,
|
|
157
157
|
) {
|
|
158
|
-
return <span ref={ref} {...props} className={textClass("text-
|
|
158
|
+
return <span ref={ref} {...props} className={textClass("text-xs font-medium", tone, weight, truncate, className)}>{children}</span>;
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
/** Overline — tiny eyebrow text */
|
|
@@ -163,7 +163,7 @@ export const Overline = forwardRef<HTMLSpanElement, OverlineProps>(function Over
|
|
|
163
163
|
{ children, tone = "muted", weight, truncate, className, ...props },
|
|
164
164
|
ref,
|
|
165
165
|
) {
|
|
166
|
-
return <span ref={ref} {...props} className={textClass("text-[
|
|
166
|
+
return <span ref={ref} {...props} className={textClass("text-[length:var(--ph-type-overline)] font-semibold uppercase tracking-[0.08em]", tone, weight, truncate, className)}>{children}</span>;
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
/** Lead — intro paragraph under a Display/SectionTitle; measure-capped for readability */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { render, screen } from "@testing-library/react";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { Avatar } from "@/components/ui/Avatar";
|
|
3
|
+
import { Avatar, AvatarGroup } from "@/components/ui/Avatar";
|
|
4
4
|
import { Stat } from "@/components/ui/Stat";
|
|
5
5
|
|
|
6
6
|
describe("Avatar status", () => {
|
|
@@ -10,6 +10,19 @@ describe("Avatar status", () => {
|
|
|
10
10
|
const status = screen.getByRole("img", { name: "online status" });
|
|
11
11
|
expect(status).toHaveClass("bg-ph-success", "border-ph-surface");
|
|
12
12
|
});
|
|
13
|
+
|
|
14
|
+
it("uses semantic accent contrast and counts grouped children", () => {
|
|
15
|
+
const { container } = render(
|
|
16
|
+
<AvatarGroup max={1}>
|
|
17
|
+
<Avatar label="JD" />
|
|
18
|
+
<Avatar label="AR" />
|
|
19
|
+
</AvatarGroup>,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(container.querySelector(".ph-avatar")).toHaveClass("text-[var(--ph-on-accent)]");
|
|
23
|
+
expect(screen.getByText("+1")).toBeInTheDocument();
|
|
24
|
+
expect(container.querySelectorAll(".ph-avatar")).toHaveLength(2);
|
|
25
|
+
});
|
|
13
26
|
});
|
|
14
27
|
|
|
15
28
|
describe("Stat warning", () => {
|
|
@@ -17,6 +17,16 @@ export {
|
|
|
17
17
|
ButtonChrome as ButtonChrome,
|
|
18
18
|
type ButtonChromeProps as ButtonChromeProps,
|
|
19
19
|
} from "@/components/ui/ButtonChrome";
|
|
20
|
+
export {
|
|
21
|
+
MarketingHeader,
|
|
22
|
+
MarketingLink,
|
|
23
|
+
MarketingMotionControl,
|
|
24
|
+
MarketingReveal,
|
|
25
|
+
} from "@/components/ui/Marketing";
|
|
26
|
+
export {
|
|
27
|
+
AiWritingIllustration,
|
|
28
|
+
IntegrationBridgeIllustration,
|
|
29
|
+
} from "@/components/ui/MarketingIllustrations";
|
|
20
30
|
export {
|
|
21
31
|
Alert as Alert,
|
|
22
32
|
type AlertProps as AlertProps,
|
|
@@ -158,10 +168,7 @@ export {
|
|
|
158
168
|
type CommandItem,
|
|
159
169
|
type CommandPaletteProps,
|
|
160
170
|
} from "@/components/ui/CommandPalette";
|
|
161
|
-
export {
|
|
162
|
-
Calendar,
|
|
163
|
-
type CalendarProps,
|
|
164
|
-
} from "@/components/ui/Calendar";
|
|
171
|
+
export { Calendar, type CalendarProps } from "@/components/ui/Calendar";
|
|
165
172
|
export {
|
|
166
173
|
Tooltip,
|
|
167
174
|
TooltipProvider,
|
|
@@ -170,10 +177,7 @@ export {
|
|
|
170
177
|
type TooltipProviderProps,
|
|
171
178
|
type TooltipSide,
|
|
172
179
|
} from "@/components/ui/Tooltip";
|
|
173
|
-
export {
|
|
174
|
-
EmptyState,
|
|
175
|
-
type EmptyStateProps,
|
|
176
|
-
} from "@/components/ui/EmptyState";
|
|
180
|
+
export { EmptyState, type EmptyStateProps } from "@/components/ui/EmptyState";
|
|
177
181
|
export {
|
|
178
182
|
FilterChips,
|
|
179
183
|
type FilterChip,
|
|
@@ -256,10 +260,7 @@ export {
|
|
|
256
260
|
Spinner as Spinner,
|
|
257
261
|
type SpinnerProps as SpinnerProps,
|
|
258
262
|
} from "@/components/ui/Spinner";
|
|
259
|
-
export {
|
|
260
|
-
Dot as Dot,
|
|
261
|
-
type DotProps as DotProps,
|
|
262
|
-
} from "@/components/ui/Dot";
|
|
263
|
+
export { Dot as Dot, type DotProps as DotProps } from "@/components/ui/Dot";
|
|
263
264
|
export {
|
|
264
265
|
Chip as Chip,
|
|
265
266
|
type ChipProps as ChipProps,
|
|
@@ -97,13 +97,14 @@ describe("chip", () => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
describe("segmented control", () => {
|
|
100
|
-
it("exposes
|
|
100
|
+
it("exposes a radio group and moves selection with arrow keys", async () => {
|
|
101
101
|
const user = userEvent.setup();
|
|
102
102
|
|
|
103
103
|
function Fixture() {
|
|
104
104
|
const [value, setValue] = useState("board");
|
|
105
105
|
return (
|
|
106
106
|
<SegmentedControl
|
|
107
|
+
aria-label="View"
|
|
107
108
|
value={value}
|
|
108
109
|
onChange={setValue}
|
|
109
110
|
options={[
|
|
@@ -115,15 +116,20 @@ describe("segmented control", () => {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
render(<Fixture />);
|
|
118
|
-
expect(screen.getByRole("
|
|
119
|
-
|
|
119
|
+
expect(screen.getByRole("radiogroup", { name: "View" })).toBeInTheDocument();
|
|
120
|
+
const board = screen.getByRole("radio", { name: "Board" });
|
|
121
|
+
expect(board).toHaveAttribute(
|
|
122
|
+
"aria-checked",
|
|
120
123
|
"true",
|
|
121
124
|
);
|
|
122
125
|
|
|
123
|
-
await user.click(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
await user.click(board);
|
|
127
|
+
await user.keyboard("{ArrowRight}");
|
|
128
|
+
const list = screen.getByRole("radio", { name: "List" });
|
|
129
|
+
expect(list).toHaveAttribute(
|
|
130
|
+
"aria-checked",
|
|
126
131
|
"true",
|
|
127
132
|
);
|
|
133
|
+
expect(list).toHaveFocus();
|
|
128
134
|
});
|
|
129
135
|
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { MarketingHeader, MarketingMotionControl } from "./Marketing";
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
cleanup();
|
|
9
|
+
document.documentElement.removeAttribute("data-marketing-paused");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("marketing controls", () => {
|
|
13
|
+
it("names navigation and exposes shared focus targets", () => {
|
|
14
|
+
render(
|
|
15
|
+
<MarketingHeader
|
|
16
|
+
brand={<a href="https://example.com">ShellStack</a>}
|
|
17
|
+
navigation={<a href="https://example.com/features">Features</a>}
|
|
18
|
+
actions={<a href="https://example.com/login">Log in</a>}
|
|
19
|
+
/>,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(screen.getByRole("navigation", { name: "Product navigation" })).toBeInTheDocument();
|
|
23
|
+
expect(screen.getByRole("link", { name: "ShellStack" })).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("pauses and resumes landing animation state", async () => {
|
|
27
|
+
const user = userEvent.setup();
|
|
28
|
+
render(<MarketingMotionControl />);
|
|
29
|
+
|
|
30
|
+
const control = screen.getByRole("button", { name: "Pause landing animations" });
|
|
31
|
+
await user.click(control);
|
|
32
|
+
expect(control).toHaveAttribute("aria-pressed", "true");
|
|
33
|
+
expect(document.documentElement).toHaveAttribute("data-marketing-paused");
|
|
34
|
+
|
|
35
|
+
await user.click(control);
|
|
36
|
+
expect(control).toHaveAttribute("aria-pressed", "false");
|
|
37
|
+
expect(document.documentElement).not.toHaveAttribute("data-marketing-paused");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { Table } from "./Table";
|
|
5
|
+
|
|
6
|
+
describe("Table", () => {
|
|
7
|
+
it("names its scroll region and renders a custom empty state", () => {
|
|
8
|
+
render(
|
|
9
|
+
<Table
|
|
10
|
+
data={[] as Array<{ name: string }>}
|
|
11
|
+
columns={[{ key: "name", header: "Name", cell: (row) => row.name }]}
|
|
12
|
+
caption="People"
|
|
13
|
+
emptyState="No people yet."
|
|
14
|
+
/>,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
expect(screen.getByRole("region", { name: "People" })).toHaveAttribute("tabindex", "0");
|
|
18
|
+
expect(screen.getByRole("cell", { name: "No people yet." })).toHaveAttribute("colspan", "1");
|
|
19
|
+
});
|
|
20
|
+
});
|