@titan-design/react-ui 0.1.0 → 0.1.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 +10 -5
- package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
- package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
- package/src/components/custom/DateTime/DateTime.tsx +235 -0
- package/src/components/custom/DateTime/index.ts +2 -0
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
- package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
- package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
- package/src/components/custom/EmptyState/index.ts +2 -0
- package/src/components/custom/Metric/Metric.stories.tsx +133 -0
- package/src/components/custom/Metric/Metric.test.tsx +120 -0
- package/src/components/custom/Metric/Metric.tsx +92 -0
- package/src/components/custom/Metric/index.ts +2 -0
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
- package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
- package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
- package/src/components/custom/Sidebar/index.ts +18 -0
- package/src/components/custom/Table/Table.stories.tsx +481 -0
- package/src/components/custom/Table/Table.test.tsx +531 -0
- package/src/components/custom/Table/Table.tsx +696 -0
- package/src/components/custom/Table/index.ts +22 -0
- package/src/components/custom/Typography/Typography.stories.tsx +153 -0
- package/src/components/custom/Typography/Typography.test.tsx +107 -0
- package/src/components/custom/Typography/Typography.tsx +218 -0
- package/src/components/custom/Typography/index.ts +19 -0
- package/src/components/custom/index.ts +8 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
- package/src/components/custom/stepper/Stepper.test.tsx +230 -0
- package/src/components/custom/stepper/Stepper.tsx +250 -0
- package/src/components/custom/stepper/index.ts +10 -0
- package/src/components/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +154 -0
- package/src/components/ui/alert/Alert.test.tsx +202 -0
- package/src/components/ui/alert/Alert.tsx +170 -0
- package/src/components/ui/alert/index.ts +8 -0
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
- package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
- package/src/components/ui/autocomplete/index.ts +2 -0
- package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
- package/src/components/ui/avatar/Avatar.test.tsx +135 -0
- package/src/components/ui/avatar/Avatar.tsx +168 -0
- package/src/components/ui/avatar/index.ts +2 -0
- package/src/components/ui/badge/Badge.stories.tsx +100 -0
- package/src/components/ui/badge/Badge.test.tsx +86 -0
- package/src/components/ui/badge/Badge.tsx +105 -0
- package/src/components/ui/badge/index.ts +2 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
- package/src/components/ui/breadcrumbs/index.ts +2 -0
- package/src/components/ui/button/Button.stories.tsx +216 -0
- package/src/components/ui/button/Button.test.tsx +132 -0
- package/src/components/ui/button/Button.tsx +242 -0
- package/src/components/ui/button/index.ts +10 -0
- package/src/components/ui/card/Card.stories.tsx +443 -0
- package/src/components/ui/card/Card.test.tsx +265 -0
- package/src/components/ui/card/Card.tsx +380 -0
- package/src/components/ui/card/index.ts +20 -0
- package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
- package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
- package/src/components/ui/checkbox/Checkbox.tsx +163 -0
- package/src/components/ui/checkbox/index.ts +2 -0
- package/src/components/ui/chip/Chip.stories.tsx +112 -0
- package/src/components/ui/chip/Chip.test.tsx +119 -0
- package/src/components/ui/chip/Chip.tsx +151 -0
- package/src/components/ui/chip/index.ts +2 -0
- package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
- package/src/components/ui/collapse/Collapse.test.tsx +298 -0
- package/src/components/ui/collapse/Collapse.tsx +275 -0
- package/src/components/ui/collapse/index.ts +18 -0
- package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
- package/src/components/ui/data-row/DataRow.test.tsx +78 -0
- package/src/components/ui/data-row/DataRow.tsx +29 -0
- package/src/components/ui/data-row/index.ts +2 -0
- package/src/components/ui/divider/Divider.stories.tsx +71 -0
- package/src/components/ui/divider/Divider.test.tsx +55 -0
- package/src/components/ui/divider/Divider.tsx +37 -0
- package/src/components/ui/divider/index.ts +2 -0
- package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
- package/src/components/ui/drawer/Drawer.test.tsx +142 -0
- package/src/components/ui/drawer/Drawer.tsx +228 -0
- package/src/components/ui/drawer/index.ts +9 -0
- package/src/components/ui/form-field/FormField.stories.tsx +260 -0
- package/src/components/ui/form-field/FormField.test.tsx +280 -0
- package/src/components/ui/form-field/FormField.tsx +308 -0
- package/src/components/ui/form-field/index.ts +2 -0
- package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
- package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
- package/src/components/ui/help-tip/HelpTip.tsx +229 -0
- package/src/components/ui/help-tip/index.ts +2 -0
- package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
- package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
- package/src/components/ui/icon-box/IconBox.tsx +57 -0
- package/src/components/ui/icon-box/index.ts +1 -0
- package/src/components/ui/index.ts +36 -0
- package/src/components/ui/input/Input.stories.tsx +192 -0
- package/src/components/ui/input/Input.test.tsx +86 -0
- package/src/components/ui/input/Input.tsx +273 -0
- package/src/components/ui/input/index.ts +2 -0
- package/src/components/ui/link/Link.stories.tsx +78 -0
- package/src/components/ui/link/Link.test.tsx +94 -0
- package/src/components/ui/link/Link.tsx +86 -0
- package/src/components/ui/link/index.ts +2 -0
- package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
- package/src/components/ui/list-item/ListItem.test.tsx +128 -0
- package/src/components/ui/list-item/ListItem.tsx +89 -0
- package/src/components/ui/list-item/index.ts +14 -0
- package/src/components/ui/menu/Menu.stories.tsx +180 -0
- package/src/components/ui/menu/Menu.test.tsx +353 -0
- package/src/components/ui/menu/Menu.tsx +222 -0
- package/src/components/ui/menu/index.ts +2 -0
- package/src/components/ui/modal/Modal.stories.tsx +350 -0
- package/src/components/ui/modal/Modal.test.tsx +159 -0
- package/src/components/ui/modal/Modal.tsx +254 -0
- package/src/components/ui/modal/index.ts +19 -0
- package/src/components/ui/popover/Popover.stories.tsx +190 -0
- package/src/components/ui/popover/Popover.test.tsx +277 -0
- package/src/components/ui/popover/Popover.tsx +162 -0
- package/src/components/ui/popover/index.ts +2 -0
- package/src/components/ui/progress/Progress.stories.tsx +190 -0
- package/src/components/ui/progress/Progress.test.tsx +181 -0
- package/src/components/ui/progress/Progress.tsx +320 -0
- package/src/components/ui/progress/index.ts +9 -0
- package/src/components/ui/radio/Radio.stories.tsx +199 -0
- package/src/components/ui/radio/Radio.test.tsx +166 -0
- package/src/components/ui/radio/Radio.tsx +200 -0
- package/src/components/ui/radio/index.ts +2 -0
- package/src/components/ui/section/Section.stories.tsx +111 -0
- package/src/components/ui/section/Section.test.tsx +121 -0
- package/src/components/ui/section/Section.tsx +52 -0
- package/src/components/ui/section/index.ts +6 -0
- package/src/components/ui/select/Select.stories.tsx +286 -0
- package/src/components/ui/select/Select.test.tsx +180 -0
- package/src/components/ui/select/Select.tsx +236 -0
- package/src/components/ui/select/index.ts +2 -0
- package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
- package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
- package/src/components/ui/skeleton/Skeleton.tsx +229 -0
- package/src/components/ui/skeleton/index.ts +16 -0
- package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
- package/src/components/ui/spinner/Spinner.test.tsx +67 -0
- package/src/components/ui/spinner/Spinner.tsx +72 -0
- package/src/components/ui/spinner/index.ts +2 -0
- package/src/components/ui/stack/Stack.stories.tsx +127 -0
- package/src/components/ui/stack/Stack.test.tsx +184 -0
- package/src/components/ui/stack/Stack.tsx +76 -0
- package/src/components/ui/stack/index.ts +2 -0
- package/src/components/ui/surface/Surface.stories.tsx +91 -0
- package/src/components/ui/surface/Surface.test.tsx +63 -0
- package/src/components/ui/surface/Surface.tsx +51 -0
- package/src/components/ui/surface/index.ts +1 -0
- package/src/components/ui/switch/Switch.stories.tsx +111 -0
- package/src/components/ui/switch/Switch.test.tsx +112 -0
- package/src/components/ui/switch/Switch.tsx +103 -0
- package/src/components/ui/switch/index.ts +2 -0
- package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
- package/src/components/ui/tabs/Tabs.test.tsx +192 -0
- package/src/components/ui/tabs/Tabs.tsx +253 -0
- package/src/components/ui/tabs/index.ts +16 -0
- package/src/components/ui/toast/Toast.stories.tsx +255 -0
- package/src/components/ui/toast/Toast.test.tsx +207 -0
- package/src/components/ui/toast/Toast.tsx +327 -0
- package/src/components/ui/toast/index.ts +8 -0
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
- package/src/components/ui/toolbar-button/index.ts +7 -0
- package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
- package/src/components/ui/tooltip/Tooltip.test.tsx +179 -0
- package/src/components/ui/tooltip/Tooltip.tsx +143 -0
- package/src/components/ui/tooltip/index.ts +2 -0
- package/src/index.ts +10 -0
- package/src/test/setup.ts +19 -0
- package/src/theme/Colors.stories.tsx +258 -0
- package/src/theme/config.ts +193 -0
- package/src/theme/elevation.stories.tsx +346 -0
- package/src/theme/elevation.test.ts +27 -0
- package/src/theme/elevation.ts +567 -0
- package/src/theme/index.ts +39 -0
- package/src/theme/shadows.stories.tsx +523 -0
- package/src/theme/shadows.ts +439 -0
- package/src/theme/tokens/index.ts +2 -0
- package/src/theme/tokens/primitives.ts +354 -0
- package/src/theme/tokens/semantic.ts +381 -0
- package/src/types/jest-axe.d.ts +16 -0
- package/src/types/nativewind.d.ts +54 -0
- package/src/utils/cn.ts +14 -0
- package/src/utils/colors.ts +140 -0
- package/src/utils/form.ts +188 -0
- package/src/utils/index.ts +29 -0
- package/src/utils/useTheme.ts +72 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import {
|
|
5
|
+
Table,
|
|
6
|
+
TableHeader,
|
|
7
|
+
TableBody,
|
|
8
|
+
TableRow,
|
|
9
|
+
TableHeaderCell,
|
|
10
|
+
TableCell,
|
|
11
|
+
TablePagination,
|
|
12
|
+
TableEmptyState,
|
|
13
|
+
TableSelectAllCell,
|
|
14
|
+
TableSelectCell,
|
|
15
|
+
} from './Table'
|
|
16
|
+
|
|
17
|
+
function renderBasicTable() {
|
|
18
|
+
return render(
|
|
19
|
+
<Table>
|
|
20
|
+
<TableHeader>
|
|
21
|
+
<TableRow>
|
|
22
|
+
<TableHeaderCell>Name</TableHeaderCell>
|
|
23
|
+
<TableHeaderCell>Email</TableHeaderCell>
|
|
24
|
+
<TableHeaderCell>Status</TableHeaderCell>
|
|
25
|
+
</TableRow>
|
|
26
|
+
</TableHeader>
|
|
27
|
+
<TableBody>
|
|
28
|
+
<TableRow>
|
|
29
|
+
<TableCell>John Doe</TableCell>
|
|
30
|
+
<TableCell>john@example.com</TableCell>
|
|
31
|
+
<TableCell>Active</TableCell>
|
|
32
|
+
</TableRow>
|
|
33
|
+
<TableRow>
|
|
34
|
+
<TableCell>Jane Smith</TableCell>
|
|
35
|
+
<TableCell>jane@example.com</TableCell>
|
|
36
|
+
<TableCell>Inactive</TableCell>
|
|
37
|
+
</TableRow>
|
|
38
|
+
</TableBody>
|
|
39
|
+
</Table>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe('Table', () => {
|
|
44
|
+
it('renders header and body content', () => {
|
|
45
|
+
renderBasicTable()
|
|
46
|
+
expect(screen.getByText('Name')).toBeInTheDocument()
|
|
47
|
+
expect(screen.getByText('Email')).toBeInTheDocument()
|
|
48
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
|
49
|
+
expect(screen.getByText('jane@example.com')).toBeInTheDocument()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('renders string children in TableCell as text', () => {
|
|
53
|
+
renderBasicTable()
|
|
54
|
+
expect(screen.getByText('Active')).toBeInTheDocument()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('renders custom children in TableCell', () => {
|
|
58
|
+
render(
|
|
59
|
+
<Table>
|
|
60
|
+
<TableBody>
|
|
61
|
+
<TableRow>
|
|
62
|
+
<TableCell>
|
|
63
|
+
<span data-testid="custom-cell">Custom Content</span>
|
|
64
|
+
</TableCell>
|
|
65
|
+
</TableRow>
|
|
66
|
+
</TableBody>
|
|
67
|
+
</Table>
|
|
68
|
+
)
|
|
69
|
+
expect(screen.getByTestId('custom-cell')).toBeInTheDocument()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('loading state', () => {
|
|
73
|
+
it('shows loading skeleton when isLoading', () => {
|
|
74
|
+
render(
|
|
75
|
+
<Table isLoading>
|
|
76
|
+
<TableBody>
|
|
77
|
+
<TableRow>
|
|
78
|
+
<TableCell>Hidden content</TableCell>
|
|
79
|
+
</TableRow>
|
|
80
|
+
</TableBody>
|
|
81
|
+
</Table>
|
|
82
|
+
)
|
|
83
|
+
// Loading skeleton replaces children
|
|
84
|
+
expect(screen.queryByText('Hidden content')).not.toBeInTheDocument()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('respects loadingRowCount', () => {
|
|
88
|
+
render(<Table isLoading loadingRowCount={3} />)
|
|
89
|
+
// Should render without errors
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('sorting', () => {
|
|
94
|
+
it('renders sortable header cells', () => {
|
|
95
|
+
const onSort = vi.fn()
|
|
96
|
+
render(
|
|
97
|
+
<Table sortColumn="name" sortDirection="asc" onSort={onSort}>
|
|
98
|
+
<TableHeader>
|
|
99
|
+
<TableRow>
|
|
100
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
101
|
+
<TableHeaderCell sortKey="email">Email</TableHeaderCell>
|
|
102
|
+
</TableRow>
|
|
103
|
+
</TableHeader>
|
|
104
|
+
</Table>
|
|
105
|
+
)
|
|
106
|
+
expect(screen.getByText('Name')).toBeInTheDocument()
|
|
107
|
+
expect(screen.getByText('Email')).toBeInTheDocument()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('calls onSort when sortable header cell is clicked', () => {
|
|
111
|
+
const onSort = vi.fn()
|
|
112
|
+
render(
|
|
113
|
+
<Table onSort={onSort}>
|
|
114
|
+
<TableHeader>
|
|
115
|
+
<TableRow>
|
|
116
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
117
|
+
</TableRow>
|
|
118
|
+
</TableHeader>
|
|
119
|
+
</Table>
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
fireEvent.click(screen.getByText('Name'))
|
|
123
|
+
expect(onSort).toHaveBeenCalledWith('name')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('shows sort direction indicator for sorted column', () => {
|
|
127
|
+
render(
|
|
128
|
+
<Table sortColumn="name" sortDirection="asc" onSort={vi.fn()}>
|
|
129
|
+
<TableHeader>
|
|
130
|
+
<TableRow>
|
|
131
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
132
|
+
</TableRow>
|
|
133
|
+
</TableHeader>
|
|
134
|
+
</Table>
|
|
135
|
+
)
|
|
136
|
+
// Ascending arrow
|
|
137
|
+
expect(screen.getByText('\u2191')).toBeInTheDocument()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('shows descending indicator', () => {
|
|
141
|
+
render(
|
|
142
|
+
<Table sortColumn="name" sortDirection="desc" onSort={vi.fn()}>
|
|
143
|
+
<TableHeader>
|
|
144
|
+
<TableRow>
|
|
145
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
146
|
+
</TableRow>
|
|
147
|
+
</TableHeader>
|
|
148
|
+
</Table>
|
|
149
|
+
)
|
|
150
|
+
expect(screen.getByText('\u2193')).toBeInTheDocument()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('shows neutral sort indicator for unsorted sortable column', () => {
|
|
154
|
+
render(
|
|
155
|
+
<Table sortColumn="email" sortDirection="asc" onSort={vi.fn()}>
|
|
156
|
+
<TableHeader>
|
|
157
|
+
<TableRow>
|
|
158
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
159
|
+
</TableRow>
|
|
160
|
+
</TableHeader>
|
|
161
|
+
</Table>
|
|
162
|
+
)
|
|
163
|
+
expect(screen.getByText('\u2195')).toBeInTheDocument()
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('non-sortable header cell does not trigger onSort', () => {
|
|
167
|
+
const onSort = vi.fn()
|
|
168
|
+
render(
|
|
169
|
+
<Table onSort={onSort}>
|
|
170
|
+
<TableHeader>
|
|
171
|
+
<TableRow>
|
|
172
|
+
<TableHeaderCell>Actions</TableHeaderCell>
|
|
173
|
+
</TableRow>
|
|
174
|
+
</TableHeader>
|
|
175
|
+
</Table>
|
|
176
|
+
)
|
|
177
|
+
fireEvent.click(screen.getByText('Actions'))
|
|
178
|
+
expect(onSort).not.toHaveBeenCalled()
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('row selection', () => {
|
|
183
|
+
it('renders select all checkbox when selectable', () => {
|
|
184
|
+
render(
|
|
185
|
+
<Table selectable selectedRows={new Set()} rowIds={['1', '2']}>
|
|
186
|
+
<TableHeader>
|
|
187
|
+
<TableRow>
|
|
188
|
+
<TableSelectAllCell />
|
|
189
|
+
<TableHeaderCell>Name</TableHeaderCell>
|
|
190
|
+
</TableRow>
|
|
191
|
+
</TableHeader>
|
|
192
|
+
</Table>
|
|
193
|
+
)
|
|
194
|
+
expect(screen.getByRole('checkbox')).toBeInTheDocument()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('does not render select cells when not selectable', () => {
|
|
198
|
+
render(
|
|
199
|
+
<Table selectable={false}>
|
|
200
|
+
<TableHeader>
|
|
201
|
+
<TableRow>
|
|
202
|
+
<TableSelectAllCell />
|
|
203
|
+
<TableHeaderCell>Name</TableHeaderCell>
|
|
204
|
+
</TableRow>
|
|
205
|
+
</TableHeader>
|
|
206
|
+
</Table>
|
|
207
|
+
)
|
|
208
|
+
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('calls onSelectAll when select all is clicked', () => {
|
|
212
|
+
const onSelectAll = vi.fn()
|
|
213
|
+
render(
|
|
214
|
+
<Table selectable selectedRows={new Set()} rowIds={['1', '2']} onSelectAll={onSelectAll}>
|
|
215
|
+
<TableHeader>
|
|
216
|
+
<TableRow>
|
|
217
|
+
<TableSelectAllCell />
|
|
218
|
+
</TableRow>
|
|
219
|
+
</TableHeader>
|
|
220
|
+
</Table>
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
fireEvent.click(screen.getByRole('checkbox'))
|
|
224
|
+
expect(onSelectAll).toHaveBeenCalledWith(true)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('calls onSelectRow for individual row selection', () => {
|
|
228
|
+
const onSelectRow = vi.fn()
|
|
229
|
+
render(
|
|
230
|
+
<Table selectable selectedRows={new Set()} onSelectRow={onSelectRow}>
|
|
231
|
+
<TableBody>
|
|
232
|
+
<TableRow>
|
|
233
|
+
<TableSelectCell rowId="1" />
|
|
234
|
+
<TableCell>John</TableCell>
|
|
235
|
+
</TableRow>
|
|
236
|
+
</TableBody>
|
|
237
|
+
</Table>
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
fireEvent.click(screen.getByRole('checkbox'))
|
|
241
|
+
expect(onSelectRow).toHaveBeenCalledWith('1', true)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('shows checked state for selected rows', () => {
|
|
245
|
+
render(
|
|
246
|
+
<Table selectable selectedRows={new Set(['1'])}>
|
|
247
|
+
<TableBody>
|
|
248
|
+
<TableRow>
|
|
249
|
+
<TableSelectCell rowId="1" />
|
|
250
|
+
</TableRow>
|
|
251
|
+
</TableBody>
|
|
252
|
+
</Table>
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
const checkbox = screen.getByRole('checkbox')
|
|
256
|
+
expect(checkbox).toBeInTheDocument()
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
describe('TableRow', () => {
|
|
261
|
+
it('renders with selected style', () => {
|
|
262
|
+
render(
|
|
263
|
+
<Table>
|
|
264
|
+
<TableBody>
|
|
265
|
+
<TableRow isSelected>
|
|
266
|
+
<TableCell>Selected row</TableCell>
|
|
267
|
+
</TableRow>
|
|
268
|
+
</TableBody>
|
|
269
|
+
</Table>
|
|
270
|
+
)
|
|
271
|
+
expect(screen.getByText('Selected row')).toBeInTheDocument()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('supports non-hoverable rows', () => {
|
|
275
|
+
render(
|
|
276
|
+
<Table>
|
|
277
|
+
<TableBody>
|
|
278
|
+
<TableRow isHoverable={false}>
|
|
279
|
+
<TableCell>Static row</TableCell>
|
|
280
|
+
</TableRow>
|
|
281
|
+
</TableBody>
|
|
282
|
+
</Table>
|
|
283
|
+
)
|
|
284
|
+
expect(screen.getByText('Static row')).toBeInTheDocument()
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
describe('TableHeaderCell alignment', () => {
|
|
289
|
+
const alignments = ['left', 'center', 'right'] as const
|
|
290
|
+
alignments.forEach((align) => {
|
|
291
|
+
it(`renders with ${align} alignment`, () => {
|
|
292
|
+
render(
|
|
293
|
+
<Table>
|
|
294
|
+
<TableHeader>
|
|
295
|
+
<TableRow>
|
|
296
|
+
<TableHeaderCell align={align}>Header</TableHeaderCell>
|
|
297
|
+
</TableRow>
|
|
298
|
+
</TableHeader>
|
|
299
|
+
</Table>
|
|
300
|
+
)
|
|
301
|
+
expect(screen.getByText('Header')).toBeInTheDocument()
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
describe('TableCell alignment', () => {
|
|
307
|
+
const alignments = ['left', 'center', 'right'] as const
|
|
308
|
+
alignments.forEach((align) => {
|
|
309
|
+
it(`renders with ${align} alignment`, () => {
|
|
310
|
+
render(
|
|
311
|
+
<Table>
|
|
312
|
+
<TableBody>
|
|
313
|
+
<TableRow>
|
|
314
|
+
<TableCell align={align}>Content</TableCell>
|
|
315
|
+
</TableRow>
|
|
316
|
+
</TableBody>
|
|
317
|
+
</Table>
|
|
318
|
+
)
|
|
319
|
+
expect(screen.getByText('Content')).toBeInTheDocument()
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
describe('TablePagination', () => {
|
|
325
|
+
it('renders pagination info', () => {
|
|
326
|
+
render(
|
|
327
|
+
<TablePagination
|
|
328
|
+
page={0}
|
|
329
|
+
pageSize={10}
|
|
330
|
+
totalItems={50}
|
|
331
|
+
onPageChange={vi.fn()}
|
|
332
|
+
/>
|
|
333
|
+
)
|
|
334
|
+
expect(screen.getByText('1-10 of 50')).toBeInTheDocument()
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('calls onPageChange when next is clicked', () => {
|
|
338
|
+
const onPageChange = vi.fn()
|
|
339
|
+
render(
|
|
340
|
+
<TablePagination
|
|
341
|
+
page={0}
|
|
342
|
+
pageSize={10}
|
|
343
|
+
totalItems={50}
|
|
344
|
+
onPageChange={onPageChange}
|
|
345
|
+
/>
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
fireEvent.click(screen.getByLabelText('Next page'))
|
|
349
|
+
expect(onPageChange).toHaveBeenCalledWith(1)
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('calls onPageChange when previous is clicked', () => {
|
|
353
|
+
const onPageChange = vi.fn()
|
|
354
|
+
render(
|
|
355
|
+
<TablePagination
|
|
356
|
+
page={2}
|
|
357
|
+
pageSize={10}
|
|
358
|
+
totalItems={50}
|
|
359
|
+
onPageChange={onPageChange}
|
|
360
|
+
/>
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
fireEvent.click(screen.getByLabelText('Previous page'))
|
|
364
|
+
expect(onPageChange).toHaveBeenCalledWith(1)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it('disables previous button on first page', () => {
|
|
368
|
+
render(
|
|
369
|
+
<TablePagination
|
|
370
|
+
page={0}
|
|
371
|
+
pageSize={10}
|
|
372
|
+
totalItems={50}
|
|
373
|
+
onPageChange={vi.fn()}
|
|
374
|
+
/>
|
|
375
|
+
)
|
|
376
|
+
const prevButton = screen.getByLabelText('Previous page')
|
|
377
|
+
expect(prevButton).toHaveAttribute('aria-disabled', 'true')
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
it('disables next button on last page', () => {
|
|
381
|
+
render(
|
|
382
|
+
<TablePagination
|
|
383
|
+
page={4}
|
|
384
|
+
pageSize={10}
|
|
385
|
+
totalItems={50}
|
|
386
|
+
onPageChange={vi.fn()}
|
|
387
|
+
/>
|
|
388
|
+
)
|
|
389
|
+
const nextButton = screen.getByLabelText('Next page')
|
|
390
|
+
expect(nextButton).toHaveAttribute('aria-disabled', 'true')
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
it('calls onPageSizeChange when page size option is clicked', () => {
|
|
394
|
+
const onPageSizeChange = vi.fn()
|
|
395
|
+
render(
|
|
396
|
+
<TablePagination
|
|
397
|
+
page={0}
|
|
398
|
+
pageSize={10}
|
|
399
|
+
totalItems={50}
|
|
400
|
+
onPageChange={vi.fn()}
|
|
401
|
+
onPageSizeChange={onPageSizeChange}
|
|
402
|
+
/>
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
fireEvent.click(screen.getByText('25'))
|
|
406
|
+
expect(onPageSizeChange).toHaveBeenCalledWith(25)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('renders custom pageSizeOptions', () => {
|
|
410
|
+
render(
|
|
411
|
+
<TablePagination
|
|
412
|
+
page={0}
|
|
413
|
+
pageSize={5}
|
|
414
|
+
totalItems={50}
|
|
415
|
+
pageSizeOptions={[5, 15, 30]}
|
|
416
|
+
onPageChange={vi.fn()}
|
|
417
|
+
/>
|
|
418
|
+
)
|
|
419
|
+
expect(screen.getByText('5')).toBeInTheDocument()
|
|
420
|
+
expect(screen.getByText('15')).toBeInTheDocument()
|
|
421
|
+
expect(screen.getByText('30')).toBeInTheDocument()
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('shows correct range for middle pages', () => {
|
|
425
|
+
render(
|
|
426
|
+
<TablePagination
|
|
427
|
+
page={2}
|
|
428
|
+
pageSize={10}
|
|
429
|
+
totalItems={50}
|
|
430
|
+
onPageChange={vi.fn()}
|
|
431
|
+
/>
|
|
432
|
+
)
|
|
433
|
+
expect(screen.getByText('21-30 of 50')).toBeInTheDocument()
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it('shows correct range for last partial page', () => {
|
|
437
|
+
render(
|
|
438
|
+
<TablePagination
|
|
439
|
+
page={2}
|
|
440
|
+
pageSize={10}
|
|
441
|
+
totalItems={25}
|
|
442
|
+
onPageChange={vi.fn()}
|
|
443
|
+
/>
|
|
444
|
+
)
|
|
445
|
+
expect(screen.getByText('21-25 of 25')).toBeInTheDocument()
|
|
446
|
+
})
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
describe('TableEmptyState', () => {
|
|
450
|
+
it('renders with default text', () => {
|
|
451
|
+
render(<TableEmptyState />)
|
|
452
|
+
expect(screen.getByText('No data')).toBeInTheDocument()
|
|
453
|
+
expect(screen.getByText('There are no items to display.')).toBeInTheDocument()
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
it('renders with custom title and description', () => {
|
|
457
|
+
render(
|
|
458
|
+
<TableEmptyState title="No users" description="Add users to see them here." />
|
|
459
|
+
)
|
|
460
|
+
expect(screen.getByText('No users')).toBeInTheDocument()
|
|
461
|
+
expect(screen.getByText('Add users to see them here.')).toBeInTheDocument()
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
it('renders with action element', () => {
|
|
465
|
+
render(
|
|
466
|
+
<TableEmptyState
|
|
467
|
+
title="No data"
|
|
468
|
+
action={<button>Add Item</button>}
|
|
469
|
+
/>
|
|
470
|
+
)
|
|
471
|
+
expect(screen.getByText('Add Item')).toBeInTheDocument()
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('renders with custom icon', () => {
|
|
475
|
+
render(
|
|
476
|
+
<TableEmptyState
|
|
477
|
+
title="No data"
|
|
478
|
+
icon={<span data-testid="empty-icon">E</span>}
|
|
479
|
+
/>
|
|
480
|
+
)
|
|
481
|
+
expect(screen.getByTestId('empty-icon')).toBeInTheDocument()
|
|
482
|
+
})
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
describe('accessibility', () => {
|
|
486
|
+
it('has no accessibility violations for basic table', async () => {
|
|
487
|
+
const { container } = renderBasicTable()
|
|
488
|
+
const results = await axe(container)
|
|
489
|
+
expect(results).toHaveNoViolations()
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
it('has no accessibility violations for pagination', async () => {
|
|
493
|
+
const { container } = render(
|
|
494
|
+
<TablePagination
|
|
495
|
+
page={0}
|
|
496
|
+
pageSize={10}
|
|
497
|
+
totalItems={50}
|
|
498
|
+
onPageChange={vi.fn()}
|
|
499
|
+
/>
|
|
500
|
+
)
|
|
501
|
+
const results = await axe(container)
|
|
502
|
+
expect(results).toHaveNoViolations()
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
it('sortable header has button role and label', () => {
|
|
506
|
+
render(
|
|
507
|
+
<Table onSort={vi.fn()}>
|
|
508
|
+
<TableHeader>
|
|
509
|
+
<TableRow>
|
|
510
|
+
<TableHeaderCell sortKey="name">Name</TableHeaderCell>
|
|
511
|
+
</TableRow>
|
|
512
|
+
</TableHeader>
|
|
513
|
+
</Table>
|
|
514
|
+
)
|
|
515
|
+
expect(screen.getByRole('button', { name: /sort by name/i })).toBeInTheDocument()
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
it('pagination buttons have accessible labels', () => {
|
|
519
|
+
render(
|
|
520
|
+
<TablePagination
|
|
521
|
+
page={1}
|
|
522
|
+
pageSize={10}
|
|
523
|
+
totalItems={50}
|
|
524
|
+
onPageChange={vi.fn()}
|
|
525
|
+
/>
|
|
526
|
+
)
|
|
527
|
+
expect(screen.getByLabelText('Previous page')).toBeInTheDocument()
|
|
528
|
+
expect(screen.getByLabelText('Next page')).toBeInTheDocument()
|
|
529
|
+
})
|
|
530
|
+
})
|
|
531
|
+
})
|