@object-ui/components 0.5.0 → 3.0.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/.turbo/turbo-build.log +12 -25
- package/CHANGELOG.md +32 -0
- package/dist/index.css +1 -1
- package/dist/index.js +23987 -22576
- package/dist/index.umd.cjs +30 -30
- package/dist/src/custom/action-param-dialog.d.ts +21 -0
- package/dist/src/custom/index.d.ts +4 -0
- package/dist/src/custom/navigation-overlay.d.ts +50 -0
- package/dist/src/custom/view-skeleton.d.ts +37 -0
- package/dist/src/custom/view-states.d.ts +33 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/renderers/action/action-button.d.ts +11 -0
- package/dist/src/renderers/action/action-group.d.ts +25 -0
- package/dist/src/renderers/action/action-icon.d.ts +10 -0
- package/dist/src/renderers/action/action-menu.d.ts +19 -0
- package/dist/src/renderers/action/index.d.ts +0 -0
- package/dist/src/renderers/action/resolve-icon.d.ts +6 -0
- package/package.json +20 -19
- package/src/__tests__/PageRendererRegions.test.tsx +664 -55
- package/src/__tests__/__snapshots__/snapshot-critical.test.tsx.snap +811 -0
- package/src/__tests__/__snapshots__/snapshot.test.tsx.snap +327 -0
- package/src/__tests__/accessibility.test.tsx +137 -0
- package/src/__tests__/api-consistency.test.tsx +596 -0
- package/src/__tests__/color-contrast.test.tsx +212 -0
- package/src/__tests__/compliance.test.tsx +72 -0
- package/src/__tests__/edge-cases.test.tsx +285 -0
- package/src/__tests__/navigation-overlay.test.tsx +273 -0
- package/src/__tests__/snapshot-critical.test.tsx +317 -0
- package/src/__tests__/snapshot.test.tsx +205 -0
- package/src/__tests__/view-compliance.test.tsx +153 -0
- package/src/__tests__/wcag-audit.test.tsx +493 -0
- package/src/custom/action-param-dialog.tsx +264 -0
- package/src/custom/index.ts +4 -0
- package/src/custom/navigation-overlay.tsx +296 -0
- package/src/custom/view-skeleton.tsx +243 -0
- package/src/custom/view-states.tsx +153 -0
- package/src/index.ts +1 -0
- package/src/renderers/action/action-button.tsx +147 -0
- package/src/renderers/action/action-group.tsx +270 -0
- package/src/renderers/action/action-icon.tsx +150 -0
- package/src/renderers/action/action-menu.tsx +203 -0
- package/src/renderers/action/index.ts +18 -0
- package/src/renderers/action/resolve-icon.ts +35 -0
- package/src/renderers/complex/__tests__/data-table-batch-editing.test.tsx +275 -0
- package/src/renderers/complex/__tests__/data-table-cell-renderer.test.tsx +120 -0
- package/src/renderers/complex/__tests__/data-table-editing.test.tsx +221 -0
- package/src/renderers/complex/data-table.tsx +269 -33
- package/src/renderers/complex/resizable.tsx +20 -17
- package/src/renderers/data-display/list.tsx +1 -1
- package/src/renderers/data-display/table.tsx +1 -1
- package/src/renderers/data-display/tree-view.tsx +2 -1
- package/src/renderers/form/form.tsx +33 -10
- package/src/renderers/index.ts +1 -0
- package/src/renderers/layout/aspect-ratio.tsx +1 -1
- package/src/renderers/layout/page.tsx +416 -52
- package/src/renderers/navigation/sidebar.tsx +6 -0
- package/src/renderers/placeholders.tsx +2 -2
- package/src/stories/MockedData.stories.tsx +87 -37
- package/src/stories-json/Accessibility.mdx +297 -0
- package/src/stories-json/EdgeCases.stories.tsx +160 -0
- package/src/stories-json/GettingStarted.mdx +89 -0
- package/src/stories-json/Introduction.mdx +127 -0
- package/src/stories-json/accordion.stories.tsx +1 -1
- package/src/stories-json/aggrid.stories.tsx +1 -1
- package/src/stories-json/alert.stories.tsx +1 -1
- package/src/stories-json/aspect-ratio.stories.tsx +1 -1
- package/src/stories-json/avatar.stories.tsx +1 -1
- package/src/stories-json/badge.stories.tsx +1 -1
- package/src/stories-json/breadcrumb.stories.tsx +1 -1
- package/src/stories-json/button-group.stories.tsx +1 -1
- package/src/stories-json/button.stories.tsx +1 -1
- package/src/stories-json/calendar.stories.tsx +1 -1
- package/src/stories-json/card.stories.tsx +1 -1
- package/src/stories-json/carousel.stories.tsx +1 -1
- package/src/stories-json/charts.stories.tsx +1 -1
- package/src/stories-json/chatbot.stories.tsx +1 -1
- package/src/stories-json/code-editor.stories.tsx +1 -1
- package/src/stories-json/collapsible.stories.tsx +1 -1
- package/src/stories-json/controls.stories.tsx +1 -1
- package/src/stories-json/crm-live-data.stories.tsx +154 -0
- package/src/stories-json/data-table.stories.tsx +80 -4
- package/src/stories-json/data_display_extras.stories.tsx +1 -1
- package/src/stories-json/date-picker.stories.tsx +1 -1
- package/src/stories-json/detail-view.stories.tsx +1 -1
- package/src/stories-json/dialog.stories.tsx +1 -1
- package/src/stories-json/feedback_extras.stories.tsx +1 -1
- package/src/stories-json/feedback_others.stories.tsx +1 -1
- package/src/stories-json/form-variants.stories.tsx +210 -0
- package/src/stories-json/form_advanced.stories.tsx +1 -1
- package/src/stories-json/form_extras.stories.tsx +1 -1
- package/src/stories-json/grid.stories.tsx +1 -1
- package/src/stories-json/icon.stories.tsx +1 -1
- package/src/stories-json/input.stories.tsx +1 -1
- package/src/stories-json/kanban.stories.tsx +1 -1
- package/src/stories-json/layout_extended.stories.tsx +1 -1
- package/src/stories-json/layout_flex.stories.tsx +1 -1
- package/src/stories-json/list-view.stories.tsx +1 -1
- package/src/stories-json/markdown.stories.tsx +1 -1
- package/src/stories-json/menus.stories.tsx +1 -1
- package/src/stories-json/metric-card.stories.tsx +1 -1
- package/src/stories-json/navigation-menu.stories.tsx +1 -1
- package/src/stories-json/object-aggrid-advanced.stories.tsx +389 -0
- package/src/stories-json/object-aggrid.stories.tsx +1 -1
- package/src/stories-json/object-form.stories.tsx +1 -1
- package/src/stories-json/object-gantt.stories.tsx +1 -1
- package/src/stories-json/object-grid.stories.tsx +159 -1
- package/src/stories-json/object-map.stories.tsx +1 -1
- package/src/stories-json/object-view.stories.tsx +1 -1
- package/src/stories-json/overlay_extras.stories.tsx +1 -1
- package/src/stories-json/overlay_others.stories.tsx +1 -1
- package/src/stories-json/resizable.stories.tsx +1 -1
- package/src/stories-json/select.stories.tsx +1 -1
- package/src/stories-json/separator.stories.tsx +1 -1
- package/src/stories-json/statistic.stories.tsx +1 -1
- package/src/stories-json/tabs.stories.tsx +1 -1
- package/src/stories-json/timeline.stories.tsx +1 -1
- package/src/stories-json/typography.stories.tsx +1 -1
- package/src/ui/slider.tsx +6 -2
- package/src/stories/Introduction.mdx +0 -34
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive WCAG 2.1 AA audit for all ObjectUI Shadcn components.
|
|
11
|
+
*
|
|
12
|
+
* Runs axe-core against every UI primitive and custom component to verify
|
|
13
|
+
* WCAG 2.1 AA compliance. Part of Q1 2026 roadmap §1.2.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, expect } from 'vitest';
|
|
17
|
+
import { render } from '@testing-library/react';
|
|
18
|
+
import { axe } from 'vitest-axe';
|
|
19
|
+
import React from 'react';
|
|
20
|
+
|
|
21
|
+
// UI primitives
|
|
22
|
+
import {
|
|
23
|
+
Accordion,
|
|
24
|
+
AccordionContent,
|
|
25
|
+
AccordionItem,
|
|
26
|
+
AccordionTrigger,
|
|
27
|
+
Alert,
|
|
28
|
+
AlertDescription,
|
|
29
|
+
AlertTitle,
|
|
30
|
+
AspectRatio,
|
|
31
|
+
Avatar,
|
|
32
|
+
AvatarFallback,
|
|
33
|
+
AvatarImage,
|
|
34
|
+
Badge,
|
|
35
|
+
Breadcrumb,
|
|
36
|
+
BreadcrumbItem,
|
|
37
|
+
BreadcrumbLink,
|
|
38
|
+
BreadcrumbList,
|
|
39
|
+
BreadcrumbPage,
|
|
40
|
+
BreadcrumbSeparator,
|
|
41
|
+
Button,
|
|
42
|
+
Card,
|
|
43
|
+
CardContent,
|
|
44
|
+
CardDescription,
|
|
45
|
+
CardFooter,
|
|
46
|
+
CardHeader,
|
|
47
|
+
CardTitle,
|
|
48
|
+
Checkbox,
|
|
49
|
+
Collapsible,
|
|
50
|
+
CollapsibleContent,
|
|
51
|
+
CollapsibleTrigger,
|
|
52
|
+
Dialog,
|
|
53
|
+
DialogContent,
|
|
54
|
+
DialogDescription,
|
|
55
|
+
DialogHeader,
|
|
56
|
+
DialogTitle,
|
|
57
|
+
DialogTrigger,
|
|
58
|
+
Input,
|
|
59
|
+
Label,
|
|
60
|
+
Pagination,
|
|
61
|
+
PaginationContent,
|
|
62
|
+
PaginationItem,
|
|
63
|
+
PaginationLink,
|
|
64
|
+
PaginationNext,
|
|
65
|
+
PaginationPrevious,
|
|
66
|
+
Progress,
|
|
67
|
+
RadioGroup,
|
|
68
|
+
RadioGroupItem,
|
|
69
|
+
ScrollArea,
|
|
70
|
+
Select,
|
|
71
|
+
SelectContent,
|
|
72
|
+
SelectItem,
|
|
73
|
+
SelectTrigger,
|
|
74
|
+
SelectValue,
|
|
75
|
+
Separator,
|
|
76
|
+
Skeleton,
|
|
77
|
+
Slider,
|
|
78
|
+
Switch,
|
|
79
|
+
Table,
|
|
80
|
+
TableBody,
|
|
81
|
+
TableCell,
|
|
82
|
+
TableHead,
|
|
83
|
+
TableHeader,
|
|
84
|
+
TableRow,
|
|
85
|
+
Tabs,
|
|
86
|
+
TabsContent,
|
|
87
|
+
TabsList,
|
|
88
|
+
TabsTrigger,
|
|
89
|
+
Textarea,
|
|
90
|
+
Toggle,
|
|
91
|
+
ToggleGroup,
|
|
92
|
+
ToggleGroupItem,
|
|
93
|
+
Tooltip,
|
|
94
|
+
TooltipContent,
|
|
95
|
+
TooltipProvider,
|
|
96
|
+
TooltipTrigger,
|
|
97
|
+
Spinner,
|
|
98
|
+
Empty,
|
|
99
|
+
EmptyTitle,
|
|
100
|
+
EmptyDescription,
|
|
101
|
+
Kbd,
|
|
102
|
+
} from '@object-ui/components';
|
|
103
|
+
|
|
104
|
+
async function expectNoViolations(container: HTMLElement) {
|
|
105
|
+
const results = await axe(container);
|
|
106
|
+
const violations = (results as any).violations || [];
|
|
107
|
+
if (violations.length > 0) {
|
|
108
|
+
const messages = violations.map(
|
|
109
|
+
(v: any) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} instance(s))`
|
|
110
|
+
);
|
|
111
|
+
throw new Error(`Expected no accessibility violations, but found ${violations.length}:\n${messages.join('\n')}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
describe('WCAG 2.1 AA Audit — UI Primitives', () => {
|
|
116
|
+
// ---------- Layout & Structure ----------
|
|
117
|
+
|
|
118
|
+
it('Accordion', async () => {
|
|
119
|
+
const { container } = render(
|
|
120
|
+
<Accordion type="single" collapsible>
|
|
121
|
+
<AccordionItem value="item-1">
|
|
122
|
+
<AccordionTrigger>Section 1</AccordionTrigger>
|
|
123
|
+
<AccordionContent>Content for section 1</AccordionContent>
|
|
124
|
+
</AccordionItem>
|
|
125
|
+
<AccordionItem value="item-2">
|
|
126
|
+
<AccordionTrigger>Section 2</AccordionTrigger>
|
|
127
|
+
<AccordionContent>Content for section 2</AccordionContent>
|
|
128
|
+
</AccordionItem>
|
|
129
|
+
</Accordion>
|
|
130
|
+
);
|
|
131
|
+
await expectNoViolations(container);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('Alert', async () => {
|
|
135
|
+
const { container } = render(
|
|
136
|
+
<Alert>
|
|
137
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
138
|
+
<AlertDescription>This is an alert description.</AlertDescription>
|
|
139
|
+
</Alert>
|
|
140
|
+
);
|
|
141
|
+
await expectNoViolations(container);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('AspectRatio', async () => {
|
|
145
|
+
const { container } = render(
|
|
146
|
+
<AspectRatio ratio={16 / 9}>
|
|
147
|
+
<div>Content</div>
|
|
148
|
+
</AspectRatio>
|
|
149
|
+
);
|
|
150
|
+
await expectNoViolations(container);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('Avatar', async () => {
|
|
154
|
+
const { container } = render(
|
|
155
|
+
<Avatar>
|
|
156
|
+
<AvatarImage src="" alt="User avatar" />
|
|
157
|
+
<AvatarFallback>JD</AvatarFallback>
|
|
158
|
+
</Avatar>
|
|
159
|
+
);
|
|
160
|
+
await expectNoViolations(container);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('Badge variants', async () => {
|
|
164
|
+
const { container } = render(
|
|
165
|
+
<div>
|
|
166
|
+
<Badge>Default</Badge>
|
|
167
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
168
|
+
<Badge variant="outline">Outline</Badge>
|
|
169
|
+
<Badge variant="destructive">Destructive</Badge>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
await expectNoViolations(container);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('Breadcrumb', async () => {
|
|
176
|
+
const { container } = render(
|
|
177
|
+
<Breadcrumb>
|
|
178
|
+
<BreadcrumbList>
|
|
179
|
+
<BreadcrumbItem>
|
|
180
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
181
|
+
</BreadcrumbItem>
|
|
182
|
+
<BreadcrumbSeparator />
|
|
183
|
+
<BreadcrumbItem>
|
|
184
|
+
<BreadcrumbPage>Current</BreadcrumbPage>
|
|
185
|
+
</BreadcrumbItem>
|
|
186
|
+
</BreadcrumbList>
|
|
187
|
+
</Breadcrumb>
|
|
188
|
+
);
|
|
189
|
+
await expectNoViolations(container);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('Button variants', async () => {
|
|
193
|
+
const { container } = render(
|
|
194
|
+
<div>
|
|
195
|
+
<Button>Default</Button>
|
|
196
|
+
<Button variant="secondary">Secondary</Button>
|
|
197
|
+
<Button variant="outline">Outline</Button>
|
|
198
|
+
<Button variant="ghost">Ghost</Button>
|
|
199
|
+
<Button variant="link">Link</Button>
|
|
200
|
+
<Button variant="destructive">Destructive</Button>
|
|
201
|
+
<Button disabled>Disabled</Button>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
await expectNoViolations(container);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('Card', async () => {
|
|
208
|
+
const { container } = render(
|
|
209
|
+
<Card>
|
|
210
|
+
<CardHeader>
|
|
211
|
+
<CardTitle>Title</CardTitle>
|
|
212
|
+
<CardDescription>Description</CardDescription>
|
|
213
|
+
</CardHeader>
|
|
214
|
+
<CardContent><p>Content</p></CardContent>
|
|
215
|
+
<CardFooter><Button>Action</Button></CardFooter>
|
|
216
|
+
</Card>
|
|
217
|
+
);
|
|
218
|
+
await expectNoViolations(container);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ---------- Form Controls ----------
|
|
222
|
+
|
|
223
|
+
it('Checkbox with label', async () => {
|
|
224
|
+
const { container } = render(
|
|
225
|
+
<div className="flex items-center space-x-2">
|
|
226
|
+
<Checkbox id="terms" />
|
|
227
|
+
<Label htmlFor="terms">Accept terms</Label>
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
await expectNoViolations(container);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('Input with label', async () => {
|
|
234
|
+
const { container } = render(
|
|
235
|
+
<div>
|
|
236
|
+
<Label htmlFor="name">Name</Label>
|
|
237
|
+
<Input id="name" type="text" />
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
await expectNoViolations(container);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('RadioGroup', async () => {
|
|
244
|
+
const { container } = render(
|
|
245
|
+
<RadioGroup defaultValue="option-1" aria-label="Choose option">
|
|
246
|
+
<div className="flex items-center space-x-2">
|
|
247
|
+
<RadioGroupItem value="option-1" id="r1" />
|
|
248
|
+
<Label htmlFor="r1">Option 1</Label>
|
|
249
|
+
</div>
|
|
250
|
+
<div className="flex items-center space-x-2">
|
|
251
|
+
<RadioGroupItem value="option-2" id="r2" />
|
|
252
|
+
<Label htmlFor="r2">Option 2</Label>
|
|
253
|
+
</div>
|
|
254
|
+
</RadioGroup>
|
|
255
|
+
);
|
|
256
|
+
await expectNoViolations(container);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('Select', async () => {
|
|
260
|
+
const { container } = render(
|
|
261
|
+
<Select>
|
|
262
|
+
<SelectTrigger aria-label="Select a fruit">
|
|
263
|
+
<SelectValue placeholder="Pick one" />
|
|
264
|
+
</SelectTrigger>
|
|
265
|
+
<SelectContent>
|
|
266
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
267
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
268
|
+
</SelectContent>
|
|
269
|
+
</Select>
|
|
270
|
+
);
|
|
271
|
+
await expectNoViolations(container);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('Slider', async () => {
|
|
275
|
+
// Radix Slider renders an internal thumb with role="slider".
|
|
276
|
+
// The aria-label on Root propagates to the thumb element.
|
|
277
|
+
const { container } = render(
|
|
278
|
+
<Slider defaultValue={[50]} max={100} step={1} aria-label="Volume" />
|
|
279
|
+
);
|
|
280
|
+
await expectNoViolations(container);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('Switch with label', async () => {
|
|
284
|
+
const { container } = render(
|
|
285
|
+
<div className="flex items-center space-x-2">
|
|
286
|
+
<Switch id="airplane-mode" />
|
|
287
|
+
<Label htmlFor="airplane-mode">Airplane Mode</Label>
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
await expectNoViolations(container);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('Textarea with label', async () => {
|
|
294
|
+
const { container } = render(
|
|
295
|
+
<div>
|
|
296
|
+
<Label htmlFor="message">Message</Label>
|
|
297
|
+
<Textarea id="message" placeholder="Type your message" />
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
await expectNoViolations(container);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// ---------- Navigation ----------
|
|
304
|
+
|
|
305
|
+
it('Pagination', async () => {
|
|
306
|
+
const { container } = render(
|
|
307
|
+
<Pagination>
|
|
308
|
+
<PaginationContent>
|
|
309
|
+
<PaginationItem>
|
|
310
|
+
<PaginationPrevious href="#" />
|
|
311
|
+
</PaginationItem>
|
|
312
|
+
<PaginationItem>
|
|
313
|
+
<PaginationLink href="#" isActive>1</PaginationLink>
|
|
314
|
+
</PaginationItem>
|
|
315
|
+
<PaginationItem>
|
|
316
|
+
<PaginationLink href="#">2</PaginationLink>
|
|
317
|
+
</PaginationItem>
|
|
318
|
+
<PaginationItem>
|
|
319
|
+
<PaginationNext href="#" />
|
|
320
|
+
</PaginationItem>
|
|
321
|
+
</PaginationContent>
|
|
322
|
+
</Pagination>
|
|
323
|
+
);
|
|
324
|
+
await expectNoViolations(container);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('Tabs', async () => {
|
|
328
|
+
const { container } = render(
|
|
329
|
+
<Tabs defaultValue="tab1">
|
|
330
|
+
<TabsList>
|
|
331
|
+
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
332
|
+
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
333
|
+
</TabsList>
|
|
334
|
+
<TabsContent value="tab1">Content 1</TabsContent>
|
|
335
|
+
<TabsContent value="tab2">Content 2</TabsContent>
|
|
336
|
+
</Tabs>
|
|
337
|
+
);
|
|
338
|
+
await expectNoViolations(container);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// ---------- Data Display ----------
|
|
342
|
+
|
|
343
|
+
it('Table', async () => {
|
|
344
|
+
const { container } = render(
|
|
345
|
+
<Table>
|
|
346
|
+
<TableHeader>
|
|
347
|
+
<TableRow>
|
|
348
|
+
<TableHead>Name</TableHead>
|
|
349
|
+
<TableHead>Email</TableHead>
|
|
350
|
+
</TableRow>
|
|
351
|
+
</TableHeader>
|
|
352
|
+
<TableBody>
|
|
353
|
+
<TableRow>
|
|
354
|
+
<TableCell>Alice</TableCell>
|
|
355
|
+
<TableCell>alice@example.com</TableCell>
|
|
356
|
+
</TableRow>
|
|
357
|
+
</TableBody>
|
|
358
|
+
</Table>
|
|
359
|
+
);
|
|
360
|
+
await expectNoViolations(container);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('Progress', async () => {
|
|
364
|
+
const { container } = render(
|
|
365
|
+
<Progress value={66} aria-label="Upload progress" />
|
|
366
|
+
);
|
|
367
|
+
await expectNoViolations(container);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('Separator', async () => {
|
|
371
|
+
const { container } = render(
|
|
372
|
+
<div>
|
|
373
|
+
<p>Above</p>
|
|
374
|
+
<Separator />
|
|
375
|
+
<p>Below</p>
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
await expectNoViolations(container);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('ScrollArea', async () => {
|
|
382
|
+
const { container } = render(
|
|
383
|
+
<ScrollArea className="h-[200px] w-[200px]">
|
|
384
|
+
<div style={{ height: 400 }}>Scrollable content</div>
|
|
385
|
+
</ScrollArea>
|
|
386
|
+
);
|
|
387
|
+
await expectNoViolations(container);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('Skeleton loading state', async () => {
|
|
391
|
+
const { container } = render(
|
|
392
|
+
<div role="status" aria-label="Loading">
|
|
393
|
+
<Skeleton className="h-12 w-12 rounded-full" />
|
|
394
|
+
<Skeleton className="h-4 w-[250px]" />
|
|
395
|
+
<Skeleton className="h-4 w-[200px]" />
|
|
396
|
+
</div>
|
|
397
|
+
);
|
|
398
|
+
await expectNoViolations(container);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// ---------- Toggles & Interactions ----------
|
|
402
|
+
|
|
403
|
+
it('Toggle', async () => {
|
|
404
|
+
const { container } = render(
|
|
405
|
+
<Toggle aria-label="Toggle bold">
|
|
406
|
+
<span>B</span>
|
|
407
|
+
</Toggle>
|
|
408
|
+
);
|
|
409
|
+
await expectNoViolations(container);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('ToggleGroup', async () => {
|
|
413
|
+
const { container } = render(
|
|
414
|
+
<ToggleGroup type="single" aria-label="Text alignment">
|
|
415
|
+
<ToggleGroupItem value="left" aria-label="Align left">L</ToggleGroupItem>
|
|
416
|
+
<ToggleGroupItem value="center" aria-label="Align center">C</ToggleGroupItem>
|
|
417
|
+
<ToggleGroupItem value="right" aria-label="Align right">R</ToggleGroupItem>
|
|
418
|
+
</ToggleGroup>
|
|
419
|
+
);
|
|
420
|
+
await expectNoViolations(container);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('Collapsible', async () => {
|
|
424
|
+
const { container } = render(
|
|
425
|
+
<Collapsible>
|
|
426
|
+
<CollapsibleTrigger asChild>
|
|
427
|
+
<Button>Toggle</Button>
|
|
428
|
+
</CollapsibleTrigger>
|
|
429
|
+
<CollapsibleContent>Hidden content</CollapsibleContent>
|
|
430
|
+
</Collapsible>
|
|
431
|
+
);
|
|
432
|
+
await expectNoViolations(container);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// ---------- Overlays ----------
|
|
436
|
+
|
|
437
|
+
it('Tooltip', async () => {
|
|
438
|
+
const { container } = render(
|
|
439
|
+
<TooltipProvider>
|
|
440
|
+
<Tooltip>
|
|
441
|
+
<TooltipTrigger asChild>
|
|
442
|
+
<Button>Hover me</Button>
|
|
443
|
+
</TooltipTrigger>
|
|
444
|
+
<TooltipContent>
|
|
445
|
+
<p>Tooltip text</p>
|
|
446
|
+
</TooltipContent>
|
|
447
|
+
</Tooltip>
|
|
448
|
+
</TooltipProvider>
|
|
449
|
+
);
|
|
450
|
+
await expectNoViolations(container);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('Dialog', async () => {
|
|
454
|
+
const { container } = render(
|
|
455
|
+
<Dialog>
|
|
456
|
+
<DialogTrigger asChild>
|
|
457
|
+
<Button>Open</Button>
|
|
458
|
+
</DialogTrigger>
|
|
459
|
+
<DialogContent>
|
|
460
|
+
<DialogHeader>
|
|
461
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
462
|
+
<DialogDescription>Dialog description text.</DialogDescription>
|
|
463
|
+
</DialogHeader>
|
|
464
|
+
</DialogContent>
|
|
465
|
+
</Dialog>
|
|
466
|
+
);
|
|
467
|
+
await expectNoViolations(container);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe('WCAG 2.1 AA Audit — Custom Components', () => {
|
|
472
|
+
it('Spinner', async () => {
|
|
473
|
+
const { container } = render(<Spinner aria-label="Loading" />);
|
|
474
|
+
await expectNoViolations(container);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('Empty state', async () => {
|
|
478
|
+
const { container } = render(
|
|
479
|
+
<Empty>
|
|
480
|
+
<EmptyTitle>No results</EmptyTitle>
|
|
481
|
+
<EmptyDescription>Try a different search.</EmptyDescription>
|
|
482
|
+
</Empty>
|
|
483
|
+
);
|
|
484
|
+
await expectNoViolations(container);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('Kbd', async () => {
|
|
488
|
+
const { container } = render(
|
|
489
|
+
<p>Press <Kbd>Ctrl</Kbd> + <Kbd>S</Kbd> to save</p>
|
|
490
|
+
);
|
|
491
|
+
await expectNoViolations(container);
|
|
492
|
+
});
|
|
493
|
+
});
|