@opensaas/stack-ui 0.1.6 → 0.3.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 +3 -2
- package/CHANGELOG.md +11 -0
- package/dist/components/AdminUI.d.ts +2 -1
- package/dist/components/AdminUI.d.ts.map +1 -1
- package/dist/components/AdminUI.js +2 -2
- package/dist/components/ItemFormClient.d.ts.map +1 -1
- package/dist/components/ItemFormClient.js +78 -60
- package/dist/components/Navigation.d.ts +2 -1
- package/dist/components/Navigation.d.ts.map +1 -1
- package/dist/components/Navigation.js +3 -2
- package/dist/components/UserMenu.d.ts +11 -0
- package/dist/components/UserMenu.d.ts.map +1 -0
- package/dist/components/UserMenu.js +18 -0
- package/dist/components/fields/TextField.d.ts +2 -1
- package/dist/components/fields/TextField.d.ts.map +1 -1
- package/dist/components/fields/TextField.js +4 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/primitives/button.d.ts +1 -1
- package/dist/styles/globals.css +25 -1
- package/package.json +14 -5
- package/src/components/AdminUI.tsx +3 -0
- package/src/components/ItemFormClient.tsx +84 -62
- package/src/components/Navigation.tsx +9 -20
- package/src/components/UserMenu.tsx +44 -0
- package/src/components/fields/TextField.tsx +7 -2
- package/src/index.ts +2 -0
- package/tests/browser/README.md +154 -0
- package/tests/browser/fields/CheckboxField.browser.test.tsx +245 -0
- package/tests/browser/fields/SelectField.browser.test.tsx +263 -0
- package/tests/browser/fields/TextField.browser.test.tsx +204 -0
- package/tests/browser/fields/__screenshots__/CheckboxField.browser.test.tsx/CheckboxField--Browser--edit-mode-should-not-be-clickable-when-disabled-1.png +0 -0
- package/tests/browser/fields/__screenshots__/CheckboxField.browser.test.tsx/CheckboxField--Browser--edit-mode-should-toggle-state-with-multiple-clicks-1.png +0 -0
- package/tests/browser/fields/__screenshots__/TextField.browser.test.tsx/TextField--Browser--edit-mode-should-handle-special-characters-1.png +0 -0
- package/tests/browser/fields/__screenshots__/TextField.browser.test.tsx/TextField--Browser--edit-mode-should-support-copy-and-paste-1.png +0 -0
- package/tests/browser/primitives/Button.browser.test.tsx +122 -0
- package/tests/browser/primitives/Dialog.browser.test.tsx +279 -0
- package/tests/browser/primitives/__screenshots__/Button.browser.test.tsx/Button--Browser--should-not-trigger-click-when-disabled-1.png +0 -0
- package/tests/components/CheckboxField.test.tsx +130 -0
- package/tests/components/DeleteButton.test.tsx +331 -0
- package/tests/components/IntegerField.test.tsx +147 -0
- package/tests/components/ListTable.test.tsx +457 -0
- package/tests/components/ListViewClient.test.tsx +415 -0
- package/tests/components/SearchBar.test.tsx +254 -0
- package/tests/components/SelectField.test.tsx +192 -0
- package/vitest.config.ts +20 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import userEvent from '@testing-library/user-event'
|
|
4
|
+
import { ListTable } from '../../src/components/standalone/ListTable.js'
|
|
5
|
+
|
|
6
|
+
describe('ListTable', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
items: [
|
|
9
|
+
{ id: '1', title: 'First Post', status: 'published', views: 100 },
|
|
10
|
+
{ id: '2', title: 'Second Post', status: 'draft', views: 50 },
|
|
11
|
+
{ id: '3', title: 'Third Post', status: 'published', views: 200 },
|
|
12
|
+
],
|
|
13
|
+
fieldTypes: {
|
|
14
|
+
title: 'text',
|
|
15
|
+
status: 'select',
|
|
16
|
+
views: 'integer',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('rendering', () => {
|
|
21
|
+
it('should render all items in table', () => {
|
|
22
|
+
render(<ListTable {...defaultProps} />)
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText('First Post')).toBeInTheDocument()
|
|
25
|
+
expect(screen.getByText('Second Post')).toBeInTheDocument()
|
|
26
|
+
expect(screen.getByText('Third Post')).toBeInTheDocument()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should render column headers', () => {
|
|
30
|
+
render(<ListTable {...defaultProps} />)
|
|
31
|
+
|
|
32
|
+
expect(screen.getByText('Title')).toBeInTheDocument()
|
|
33
|
+
expect(screen.getByText('Status')).toBeInTheDocument()
|
|
34
|
+
expect(screen.getByText('Views')).toBeInTheDocument()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should show empty state when no items', () => {
|
|
38
|
+
render(<ListTable {...defaultProps} items={[]} />)
|
|
39
|
+
|
|
40
|
+
expect(screen.getByText('No items found')).toBeInTheDocument()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should show custom empty message', () => {
|
|
44
|
+
render(<ListTable {...defaultProps} items={[]} emptyMessage="No posts available" />)
|
|
45
|
+
|
|
46
|
+
expect(screen.getByText('No posts available')).toBeInTheDocument()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should only render specified columns when provided', () => {
|
|
50
|
+
render(<ListTable {...defaultProps} columns={['title', 'status']} />)
|
|
51
|
+
|
|
52
|
+
expect(screen.getByText('Title')).toBeInTheDocument()
|
|
53
|
+
expect(screen.getByText('Status')).toBeInTheDocument()
|
|
54
|
+
expect(screen.queryByText('Views')).not.toBeInTheDocument()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should apply custom className', () => {
|
|
58
|
+
const { container } = render(<ListTable {...defaultProps} className="custom-class" />)
|
|
59
|
+
|
|
60
|
+
expect(container.querySelector('.custom-class')).toBeInTheDocument()
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('sorting', () => {
|
|
65
|
+
it('should sort items ascending when header clicked', async () => {
|
|
66
|
+
const user = userEvent.setup()
|
|
67
|
+
render(<ListTable {...defaultProps} />)
|
|
68
|
+
|
|
69
|
+
await user.click(screen.getByText('Title'))
|
|
70
|
+
|
|
71
|
+
const rows = screen.getAllByRole('row')
|
|
72
|
+
expect(rows[1]).toHaveTextContent('First Post')
|
|
73
|
+
expect(rows[2]).toHaveTextContent('Second Post')
|
|
74
|
+
expect(rows[3]).toHaveTextContent('Third Post')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should toggle sort order when same header clicked twice', async () => {
|
|
78
|
+
const user = userEvent.setup()
|
|
79
|
+
render(<ListTable {...defaultProps} />)
|
|
80
|
+
|
|
81
|
+
const titleHeader = screen.getByText('Title')
|
|
82
|
+
await user.click(titleHeader)
|
|
83
|
+
await user.click(titleHeader)
|
|
84
|
+
|
|
85
|
+
const rows = screen.getAllByRole('row')
|
|
86
|
+
expect(rows[1]).toHaveTextContent('Third Post')
|
|
87
|
+
expect(rows[2]).toHaveTextContent('Second Post')
|
|
88
|
+
expect(rows[3]).toHaveTextContent('First Post')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should show sort indicator on active column', async () => {
|
|
92
|
+
const user = userEvent.setup()
|
|
93
|
+
render(<ListTable {...defaultProps} />)
|
|
94
|
+
|
|
95
|
+
await user.click(screen.getByText('Title'))
|
|
96
|
+
|
|
97
|
+
expect(screen.getByText('↑')).toBeInTheDocument()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should not sort when sortable is false', async () => {
|
|
101
|
+
const user = userEvent.setup()
|
|
102
|
+
render(<ListTable {...defaultProps} sortable={false} />)
|
|
103
|
+
|
|
104
|
+
await user.click(screen.getByText('Title'))
|
|
105
|
+
|
|
106
|
+
// Should not show sort indicator
|
|
107
|
+
expect(screen.queryByText('↑')).not.toBeInTheDocument()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should not show hover effect on headers when sortable is false', () => {
|
|
111
|
+
render(<ListTable {...defaultProps} sortable={false} />)
|
|
112
|
+
|
|
113
|
+
const titleHeader = screen.getByText('Title')
|
|
114
|
+
const headerCell = titleHeader.closest('th')
|
|
115
|
+
expect(headerCell).not.toHaveClass('cursor-pointer')
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('row interactions', () => {
|
|
120
|
+
it('should call onRowClick when row clicked', async () => {
|
|
121
|
+
const onRowClick = vi.fn()
|
|
122
|
+
const user = userEvent.setup()
|
|
123
|
+
|
|
124
|
+
render(<ListTable {...defaultProps} onRowClick={onRowClick} />)
|
|
125
|
+
|
|
126
|
+
const rows = screen.getAllByRole('row')
|
|
127
|
+
await user.click(rows[1]) // Click first data row (skip header)
|
|
128
|
+
|
|
129
|
+
expect(onRowClick).toHaveBeenCalledWith(defaultProps.items[0])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should add cursor-pointer class when onRowClick provided', () => {
|
|
133
|
+
const onRowClick = vi.fn()
|
|
134
|
+
render(<ListTable {...defaultProps} onRowClick={onRowClick} />)
|
|
135
|
+
|
|
136
|
+
const rows = screen.getAllByRole('row')
|
|
137
|
+
expect(rows[1]).toHaveClass('cursor-pointer')
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should not add cursor-pointer class when onRowClick not provided', () => {
|
|
141
|
+
render(<ListTable {...defaultProps} />)
|
|
142
|
+
|
|
143
|
+
const rows = screen.getAllByRole('row')
|
|
144
|
+
expect(rows[1]).not.toHaveClass('cursor-pointer')
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('custom actions', () => {
|
|
149
|
+
it('should render actions column when renderActions provided', () => {
|
|
150
|
+
const renderActions = () => <button>Delete</button>
|
|
151
|
+
|
|
152
|
+
render(<ListTable {...defaultProps} renderActions={renderActions} />)
|
|
153
|
+
|
|
154
|
+
expect(screen.getByText('Actions')).toBeInTheDocument()
|
|
155
|
+
expect(screen.getAllByText('Delete')).toHaveLength(3)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should not render actions column when renderActions not provided', () => {
|
|
159
|
+
render(<ListTable {...defaultProps} />)
|
|
160
|
+
|
|
161
|
+
expect(screen.queryByText('Actions')).not.toBeInTheDocument()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should pass item to renderActions function', () => {
|
|
165
|
+
const renderActions = vi.fn(() => <button>Delete</button>)
|
|
166
|
+
|
|
167
|
+
render(<ListTable {...defaultProps} renderActions={renderActions} />)
|
|
168
|
+
|
|
169
|
+
expect(renderActions).toHaveBeenCalledWith(defaultProps.items[0])
|
|
170
|
+
expect(renderActions).toHaveBeenCalledWith(defaultProps.items[1])
|
|
171
|
+
expect(renderActions).toHaveBeenCalledWith(defaultProps.items[2])
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should not propagate click to row when action clicked', async () => {
|
|
175
|
+
const onRowClick = vi.fn()
|
|
176
|
+
const onActionClick = vi.fn()
|
|
177
|
+
const user = userEvent.setup()
|
|
178
|
+
|
|
179
|
+
const renderActions = () => <button onClick={onActionClick}>Delete</button>
|
|
180
|
+
|
|
181
|
+
render(<ListTable {...defaultProps} onRowClick={onRowClick} renderActions={renderActions} />)
|
|
182
|
+
|
|
183
|
+
const deleteButton = screen.getAllByText('Delete')[0]
|
|
184
|
+
await user.click(deleteButton)
|
|
185
|
+
|
|
186
|
+
expect(onActionClick).toHaveBeenCalled()
|
|
187
|
+
expect(onRowClick).not.toHaveBeenCalled()
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('relationships', () => {
|
|
192
|
+
it('should render relationship as link when relationshipRefs provided', () => {
|
|
193
|
+
const items = [
|
|
194
|
+
{
|
|
195
|
+
id: '1',
|
|
196
|
+
title: 'Post 1',
|
|
197
|
+
author: { id: 'user-1', name: 'John Doe' },
|
|
198
|
+
},
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
render(
|
|
202
|
+
<ListTable
|
|
203
|
+
items={items}
|
|
204
|
+
fieldTypes={{ title: 'text', author: 'relationship' }}
|
|
205
|
+
relationshipRefs={{ author: 'User.posts' }}
|
|
206
|
+
columns={['title', 'author']}
|
|
207
|
+
/>,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
const authorLink = screen.getByRole('link', { name: 'John Doe' })
|
|
211
|
+
expect(authorLink).toBeInTheDocument()
|
|
212
|
+
expect(authorLink).toHaveAttribute('href', '/admin/user/user-1')
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should use custom basePath in relationship links', () => {
|
|
216
|
+
const items = [
|
|
217
|
+
{
|
|
218
|
+
id: '1',
|
|
219
|
+
title: 'Post 1',
|
|
220
|
+
author: { id: 'user-1', name: 'John Doe' },
|
|
221
|
+
},
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
render(
|
|
225
|
+
<ListTable
|
|
226
|
+
items={items}
|
|
227
|
+
fieldTypes={{ title: 'text', author: 'relationship' }}
|
|
228
|
+
relationshipRefs={{ author: 'User.posts' }}
|
|
229
|
+
basePath="/custom"
|
|
230
|
+
columns={['title', 'author']}
|
|
231
|
+
/>,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
const authorLink = screen.getByRole('link', { name: 'John Doe' })
|
|
235
|
+
expect(authorLink).toHaveAttribute('href', '/custom/user/user-1')
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('should render multiple relationships as comma-separated links', () => {
|
|
239
|
+
const items = [
|
|
240
|
+
{
|
|
241
|
+
id: '1',
|
|
242
|
+
title: 'Post 1',
|
|
243
|
+
tags: [
|
|
244
|
+
{ id: 'tag-1', name: 'JavaScript' },
|
|
245
|
+
{ id: 'tag-2', name: 'TypeScript' },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
const { container } = render(
|
|
251
|
+
<ListTable
|
|
252
|
+
items={items}
|
|
253
|
+
fieldTypes={{ title: 'text', tags: 'relationship' }}
|
|
254
|
+
relationshipRefs={{ tags: 'Tag.posts' }}
|
|
255
|
+
columns={['title', 'tags']}
|
|
256
|
+
/>,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
expect(screen.getByRole('link', { name: 'JavaScript' })).toBeInTheDocument()
|
|
260
|
+
expect(screen.getByRole('link', { name: 'TypeScript' })).toBeInTheDocument()
|
|
261
|
+
// Check for comma separator in the container
|
|
262
|
+
expect(container.textContent).toContain(', ')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should show dash for null relationship', () => {
|
|
266
|
+
const items = [{ id: '1', title: 'Post 1', author: null }]
|
|
267
|
+
|
|
268
|
+
render(
|
|
269
|
+
<ListTable
|
|
270
|
+
items={items}
|
|
271
|
+
fieldTypes={{ title: 'text', author: 'relationship' }}
|
|
272
|
+
relationshipRefs={{ author: 'User.posts' }}
|
|
273
|
+
columns={['title', 'author']}
|
|
274
|
+
/>,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
const cells = screen.getAllByRole('cell')
|
|
278
|
+
const authorCell = cells.find((cell) => cell.textContent === '-')
|
|
279
|
+
expect(authorCell).toBeInTheDocument()
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('should show dash for empty relationship array', () => {
|
|
283
|
+
const items = [{ id: '1', title: 'Post 1', tags: [] }]
|
|
284
|
+
|
|
285
|
+
render(
|
|
286
|
+
<ListTable
|
|
287
|
+
items={items}
|
|
288
|
+
fieldTypes={{ title: 'text', tags: 'relationship' }}
|
|
289
|
+
relationshipRefs={{ tags: 'Tag.posts' }}
|
|
290
|
+
columns={['title', 'tags']}
|
|
291
|
+
/>,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
const cells = screen.getAllByRole('cell')
|
|
295
|
+
const tagsCell = cells.find((cell) => cell.textContent === '-')
|
|
296
|
+
expect(tagsCell).toBeInTheDocument()
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('should display relationship as text when relationshipRefs not provided', () => {
|
|
300
|
+
const items = [
|
|
301
|
+
{
|
|
302
|
+
id: '1',
|
|
303
|
+
title: 'Post 1',
|
|
304
|
+
author: { id: 'user-1', name: 'John Doe' },
|
|
305
|
+
},
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
render(
|
|
309
|
+
<ListTable
|
|
310
|
+
items={items}
|
|
311
|
+
fieldTypes={{ title: 'text', author: 'relationship' }}
|
|
312
|
+
columns={['title', 'author']}
|
|
313
|
+
/>,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
|
317
|
+
expect(screen.queryByRole('link', { name: 'John Doe' })).not.toBeInTheDocument()
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('should not propagate relationship link click to row', async () => {
|
|
321
|
+
const onRowClick = vi.fn()
|
|
322
|
+
const user = userEvent.setup()
|
|
323
|
+
|
|
324
|
+
const items = [
|
|
325
|
+
{
|
|
326
|
+
id: '1',
|
|
327
|
+
title: 'Post 1',
|
|
328
|
+
author: { id: 'user-1', name: 'John Doe' },
|
|
329
|
+
},
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
render(
|
|
333
|
+
<ListTable
|
|
334
|
+
items={items}
|
|
335
|
+
fieldTypes={{ title: 'text', author: 'relationship' }}
|
|
336
|
+
relationshipRefs={{ author: 'User.posts' }}
|
|
337
|
+
columns={['title', 'author']}
|
|
338
|
+
onRowClick={onRowClick}
|
|
339
|
+
/>,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
const authorLink = screen.getByRole('link', { name: 'John Doe' })
|
|
343
|
+
await user.click(authorLink)
|
|
344
|
+
|
|
345
|
+
expect(onRowClick).not.toHaveBeenCalled()
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
describe('field display values', () => {
|
|
350
|
+
it('should display checkbox values as Yes/No', () => {
|
|
351
|
+
const items = [
|
|
352
|
+
{ id: '1', title: 'Post 1', published: true },
|
|
353
|
+
{ id: '2', title: 'Post 2', published: false },
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
render(
|
|
357
|
+
<ListTable
|
|
358
|
+
items={items}
|
|
359
|
+
fieldTypes={{ title: 'text', published: 'checkbox' }}
|
|
360
|
+
columns={['title', 'published']}
|
|
361
|
+
/>,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
expect(screen.getByText('Yes')).toBeInTheDocument()
|
|
365
|
+
expect(screen.getByText('No')).toBeInTheDocument()
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('should display timestamp values as formatted dates', () => {
|
|
369
|
+
const items = [{ id: '1', title: 'Post 1', createdAt: '2024-01-01T12:00:00Z' }]
|
|
370
|
+
|
|
371
|
+
render(
|
|
372
|
+
<ListTable
|
|
373
|
+
items={items}
|
|
374
|
+
fieldTypes={{ title: 'text', createdAt: 'timestamp' }}
|
|
375
|
+
columns={['title', 'createdAt']}
|
|
376
|
+
/>,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
const cells = screen.getAllByRole('cell')
|
|
380
|
+
const dateCell = cells.find((cell) => cell.textContent?.includes('2024'))
|
|
381
|
+
expect(dateCell).toBeInTheDocument()
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should display null/undefined values as dash', () => {
|
|
385
|
+
const items = [{ id: '1', title: 'Post 1', description: null }]
|
|
386
|
+
|
|
387
|
+
render(
|
|
388
|
+
<ListTable
|
|
389
|
+
items={items}
|
|
390
|
+
fieldTypes={{ title: 'text', description: 'text' }}
|
|
391
|
+
columns={['title', 'description']}
|
|
392
|
+
/>,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
const cells = screen.getAllByRole('cell')
|
|
396
|
+
const descCell = cells.find((cell) => cell.textContent === '-')
|
|
397
|
+
expect(descCell).toBeInTheDocument()
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
describe('column filtering', () => {
|
|
402
|
+
it('should exclude password fields by default', () => {
|
|
403
|
+
const items = [{ id: '1', username: 'john', password: 'secret123' }]
|
|
404
|
+
|
|
405
|
+
render(<ListTable items={items} fieldTypes={{ username: 'text', password: 'password' }} />)
|
|
406
|
+
|
|
407
|
+
expect(screen.getByText('Username')).toBeInTheDocument()
|
|
408
|
+
expect(screen.queryByText('Password')).not.toBeInTheDocument()
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
it('should exclude createdAt and updatedAt by default', () => {
|
|
412
|
+
const items = [
|
|
413
|
+
{
|
|
414
|
+
id: '1',
|
|
415
|
+
title: 'Post 1',
|
|
416
|
+
createdAt: '2024-01-01T12:00:00Z',
|
|
417
|
+
updatedAt: '2024-01-02T12:00:00Z',
|
|
418
|
+
},
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
render(
|
|
422
|
+
<ListTable
|
|
423
|
+
items={items}
|
|
424
|
+
fieldTypes={{
|
|
425
|
+
title: 'text',
|
|
426
|
+
createdAt: 'timestamp',
|
|
427
|
+
updatedAt: 'timestamp',
|
|
428
|
+
}}
|
|
429
|
+
/>,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
expect(screen.getByText('Title')).toBeInTheDocument()
|
|
433
|
+
expect(screen.queryByText('Created At')).not.toBeInTheDocument()
|
|
434
|
+
expect(screen.queryByText('Updated At')).not.toBeInTheDocument()
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
it('should show createdAt and updatedAt when explicitly in columns', () => {
|
|
438
|
+
const items = [
|
|
439
|
+
{
|
|
440
|
+
id: '1',
|
|
441
|
+
title: 'Post 1',
|
|
442
|
+
createdAt: '2024-01-01T12:00:00Z',
|
|
443
|
+
},
|
|
444
|
+
]
|
|
445
|
+
|
|
446
|
+
render(
|
|
447
|
+
<ListTable
|
|
448
|
+
items={items}
|
|
449
|
+
fieldTypes={{ title: 'text', createdAt: 'timestamp' }}
|
|
450
|
+
columns={['title', 'createdAt']}
|
|
451
|
+
/>,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
expect(screen.getByText('Created At')).toBeInTheDocument()
|
|
455
|
+
})
|
|
456
|
+
})
|
|
457
|
+
})
|