@opensaas/stack-ui 0.1.7 → 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.
Files changed (47) hide show
  1. package/.turbo/turbo-build.log +3 -2
  2. package/CHANGELOG.md +4 -0
  3. package/dist/components/AdminUI.d.ts +2 -1
  4. package/dist/components/AdminUI.d.ts.map +1 -1
  5. package/dist/components/AdminUI.js +2 -2
  6. package/dist/components/ItemFormClient.d.ts.map +1 -1
  7. package/dist/components/ItemFormClient.js +78 -60
  8. package/dist/components/Navigation.d.ts +2 -1
  9. package/dist/components/Navigation.d.ts.map +1 -1
  10. package/dist/components/Navigation.js +3 -2
  11. package/dist/components/UserMenu.d.ts +11 -0
  12. package/dist/components/UserMenu.d.ts.map +1 -0
  13. package/dist/components/UserMenu.js +18 -0
  14. package/dist/components/fields/TextField.d.ts +2 -1
  15. package/dist/components/fields/TextField.d.ts.map +1 -1
  16. package/dist/components/fields/TextField.js +4 -2
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/primitives/button.d.ts +1 -1
  21. package/dist/styles/globals.css +24 -0
  22. package/package.json +14 -5
  23. package/src/components/AdminUI.tsx +3 -0
  24. package/src/components/ItemFormClient.tsx +84 -62
  25. package/src/components/Navigation.tsx +9 -20
  26. package/src/components/UserMenu.tsx +44 -0
  27. package/src/components/fields/TextField.tsx +7 -2
  28. package/src/index.ts +2 -0
  29. package/tests/browser/README.md +154 -0
  30. package/tests/browser/fields/CheckboxField.browser.test.tsx +245 -0
  31. package/tests/browser/fields/SelectField.browser.test.tsx +263 -0
  32. package/tests/browser/fields/TextField.browser.test.tsx +204 -0
  33. package/tests/browser/fields/__screenshots__/CheckboxField.browser.test.tsx/CheckboxField--Browser--edit-mode-should-not-be-clickable-when-disabled-1.png +0 -0
  34. package/tests/browser/fields/__screenshots__/CheckboxField.browser.test.tsx/CheckboxField--Browser--edit-mode-should-toggle-state-with-multiple-clicks-1.png +0 -0
  35. package/tests/browser/fields/__screenshots__/TextField.browser.test.tsx/TextField--Browser--edit-mode-should-handle-special-characters-1.png +0 -0
  36. package/tests/browser/fields/__screenshots__/TextField.browser.test.tsx/TextField--Browser--edit-mode-should-support-copy-and-paste-1.png +0 -0
  37. package/tests/browser/primitives/Button.browser.test.tsx +122 -0
  38. package/tests/browser/primitives/Dialog.browser.test.tsx +279 -0
  39. package/tests/browser/primitives/__screenshots__/Button.browser.test.tsx/Button--Browser--should-not-trigger-click-when-disabled-1.png +0 -0
  40. package/tests/components/CheckboxField.test.tsx +130 -0
  41. package/tests/components/DeleteButton.test.tsx +331 -0
  42. package/tests/components/IntegerField.test.tsx +147 -0
  43. package/tests/components/ListTable.test.tsx +457 -0
  44. package/tests/components/ListViewClient.test.tsx +415 -0
  45. package/tests/components/SearchBar.test.tsx +254 -0
  46. package/tests/components/SelectField.test.tsx +192 -0
  47. package/vitest.config.ts +20 -0
@@ -0,0 +1,245 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen, act } from '@testing-library/react'
3
+ import { userEvent } from 'vitest/browser'
4
+ import React from 'react'
5
+ import { CheckboxField } from '../../../src/components/fields/CheckboxField.js'
6
+
7
+ describe('CheckboxField (Browser)', () => {
8
+ describe('edit mode', () => {
9
+ it('should render checkbox with label', async () => {
10
+ render(<CheckboxField name="active" value={false} onChange={() => {}} label="Is Active" />)
11
+
12
+ expect(screen.getByLabelText('Is Active')).toBeInTheDocument()
13
+ expect(screen.getByRole('checkbox')).toBeInTheDocument()
14
+ })
15
+
16
+ it('should display checked state when value is true', async () => {
17
+ render(<CheckboxField name="active" value={true} onChange={() => {}} label="Is Active" />)
18
+
19
+ const checkbox = screen.getByRole('checkbox')
20
+ expect(checkbox).toBeChecked()
21
+ })
22
+
23
+ it('should display unchecked state when value is false', async () => {
24
+ render(<CheckboxField name="active" value={false} onChange={() => {}} label="Is Active" />)
25
+
26
+ const checkbox = screen.getByRole('checkbox')
27
+ expect(checkbox).not.toBeChecked()
28
+ })
29
+
30
+ it('should call onChange with true when clicked', async () => {
31
+ let currentValue = false
32
+ const handleChange = (value: boolean) => {
33
+ currentValue = value
34
+ }
35
+
36
+ render(
37
+ <CheckboxField
38
+ name="active"
39
+ value={currentValue}
40
+ onChange={handleChange}
41
+ label="Is Active"
42
+ />,
43
+ )
44
+
45
+ const checkbox = screen.getByRole('checkbox')
46
+ await userEvent.click(checkbox)
47
+
48
+ expect(currentValue).toBe(true)
49
+ })
50
+
51
+ it('should call onChange with false when unchecked', async () => {
52
+ let currentValue = true
53
+ const handleChange = (value: boolean) => {
54
+ currentValue = value
55
+ }
56
+
57
+ render(
58
+ <CheckboxField
59
+ name="active"
60
+ value={currentValue}
61
+ onChange={handleChange}
62
+ label="Is Active"
63
+ />,
64
+ )
65
+
66
+ const checkbox = screen.getByRole('checkbox')
67
+ await userEvent.click(checkbox)
68
+
69
+ expect(currentValue).toBe(false)
70
+ })
71
+
72
+ it('should toggle state with multiple clicks', async () => {
73
+ function TestComponent() {
74
+ const [value, setValue] = React.useState(false)
75
+ return (
76
+ <CheckboxField
77
+ name="active"
78
+ value={value}
79
+ onChange={setValue}
80
+ label="Is Active"
81
+ data-testid="checkbox-field"
82
+ />
83
+ )
84
+ }
85
+
86
+ await act(async () => {
87
+ render(<TestComponent />)
88
+ })
89
+
90
+ const checkbox = screen.getByRole('checkbox')
91
+
92
+ // First click - check
93
+ expect(checkbox).not.toBeChecked()
94
+ await act(async () => {
95
+ await userEvent.click(checkbox)
96
+ })
97
+ expect(checkbox).toBeChecked()
98
+
99
+ // Second click - uncheck
100
+ await act(async () => {
101
+ await userEvent.click(checkbox)
102
+ })
103
+ expect(checkbox).not.toBeChecked()
104
+
105
+ // Third click - check again
106
+ await act(async () => {
107
+ await userEvent.click(checkbox)
108
+ })
109
+ expect(checkbox).toBeChecked()
110
+ })
111
+
112
+ it('should display error message', async () => {
113
+ render(
114
+ <CheckboxField
115
+ name="terms"
116
+ value={false}
117
+ onChange={() => {}}
118
+ label="Accept Terms"
119
+ error="You must accept the terms"
120
+ />,
121
+ )
122
+
123
+ expect(screen.getByText('You must accept the terms')).toBeInTheDocument()
124
+ })
125
+
126
+ it('should be disabled when disabled prop is true', async () => {
127
+ render(
128
+ <CheckboxField
129
+ name="active"
130
+ value={false}
131
+ onChange={() => {}}
132
+ label="Is Active"
133
+ disabled
134
+ />,
135
+ )
136
+
137
+ const checkbox = screen.getByRole('checkbox')
138
+ expect(checkbox).toBeDisabled()
139
+ })
140
+
141
+ it('should not be clickable when disabled', async () => {
142
+ let currentValue = false
143
+ const handleChange = (value: boolean) => {
144
+ currentValue = value
145
+ }
146
+
147
+ render(
148
+ <CheckboxField
149
+ name="active"
150
+ value={currentValue}
151
+ onChange={handleChange}
152
+ label="Is Active"
153
+ disabled
154
+ />,
155
+ )
156
+
157
+ const checkbox = screen.getByRole('checkbox')
158
+
159
+ // Verify checkbox is disabled - browsers prevent clicking disabled checkboxes
160
+ expect(checkbox).toBeDisabled()
161
+
162
+ // In real browsers, disabled checkboxes cannot be clicked
163
+ expect(currentValue).toBe(false)
164
+ })
165
+
166
+ it('should support keyboard interaction with Space', async () => {
167
+ let currentValue = false
168
+ const handleChange = (value: boolean) => {
169
+ currentValue = value
170
+ }
171
+
172
+ render(
173
+ <CheckboxField
174
+ name="active"
175
+ value={currentValue}
176
+ onChange={handleChange}
177
+ label="Is Active"
178
+ />,
179
+ )
180
+
181
+ const checkbox = screen.getByRole('checkbox')
182
+ checkbox.focus()
183
+ expect(document.activeElement).toBe(checkbox)
184
+
185
+ await userEvent.keyboard(' ')
186
+
187
+ expect(currentValue).toBe(true)
188
+ })
189
+
190
+ it('should support clicking on label to toggle', async () => {
191
+ let currentValue = false
192
+ const handleChange = (value: boolean) => {
193
+ currentValue = value
194
+ }
195
+
196
+ render(
197
+ <CheckboxField
198
+ name="active"
199
+ value={currentValue}
200
+ onChange={handleChange}
201
+ label="Is Active"
202
+ />,
203
+ )
204
+
205
+ const label = screen.getByText('Is Active')
206
+ await userEvent.click(label)
207
+
208
+ expect(currentValue).toBe(true)
209
+ })
210
+ })
211
+
212
+ describe('read mode', () => {
213
+ it('should render "Yes" when value is true', async () => {
214
+ render(
215
+ <CheckboxField
216
+ name="active"
217
+ value={true}
218
+ onChange={() => {}}
219
+ label="Is Active"
220
+ mode="read"
221
+ />,
222
+ )
223
+
224
+ expect(screen.getByText('Is Active')).toBeInTheDocument()
225
+ expect(screen.getByText('Yes')).toBeInTheDocument()
226
+ expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
227
+ })
228
+
229
+ it('should render "No" when value is false', async () => {
230
+ render(
231
+ <CheckboxField
232
+ name="active"
233
+ value={false}
234
+ onChange={() => {}}
235
+ label="Is Active"
236
+ mode="read"
237
+ />,
238
+ )
239
+
240
+ expect(screen.getByText('Is Active')).toBeInTheDocument()
241
+ expect(screen.getByText('No')).toBeInTheDocument()
242
+ expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
243
+ })
244
+ })
245
+ })
@@ -0,0 +1,263 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen, waitFor, act } from '@testing-library/react'
3
+ import { userEvent } from 'vitest/browser'
4
+ import React from 'react'
5
+ import { SelectField } from '../../../src/components/fields/SelectField.js'
6
+
7
+ const mockOptions = [
8
+ { label: 'Option 1', value: 'option1' },
9
+ { label: 'Option 2', value: 'option2' },
10
+ { label: 'Option 3', value: 'option3' },
11
+ ]
12
+
13
+ describe('SelectField (Browser)', () => {
14
+ describe('edit mode', () => {
15
+ it('should render select field with label', async () => {
16
+ await act(async () => {
17
+ render(
18
+ <SelectField
19
+ name="status"
20
+ value={null}
21
+ onChange={() => {}}
22
+ label="Status"
23
+ options={mockOptions}
24
+ />,
25
+ )
26
+ })
27
+
28
+ expect(screen.getByText('Status')).toBeInTheDocument()
29
+ expect(screen.getByRole('combobox')).toBeInTheDocument()
30
+ })
31
+
32
+ it('should display selected value', async () => {
33
+ await act(async () => {
34
+ render(
35
+ <SelectField
36
+ name="status"
37
+ value="option2"
38
+ onChange={() => {}}
39
+ label="Status"
40
+ options={mockOptions}
41
+ />,
42
+ )
43
+ })
44
+
45
+ expect(screen.getByRole('combobox')).toHaveTextContent('Option 2')
46
+ })
47
+
48
+ it('should open dropdown when clicked', async () => {
49
+ await act(async () => {
50
+ render(
51
+ <SelectField
52
+ name="status"
53
+ value={null}
54
+ onChange={() => {}}
55
+ label="Status"
56
+ options={mockOptions}
57
+ />,
58
+ )
59
+ })
60
+
61
+ const trigger = screen.getByRole('combobox')
62
+
63
+ await act(async () => {
64
+ await userEvent.click(trigger)
65
+ })
66
+
67
+ await waitFor(() => {
68
+ expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument()
69
+ expect(screen.getByRole('option', { name: 'Option 2' })).toBeInTheDocument()
70
+ expect(screen.getByRole('option', { name: 'Option 3' })).toBeInTheDocument()
71
+ })
72
+ })
73
+
74
+ it('should call onChange when option is selected', async () => {
75
+ let selectedValue: string | null = null
76
+ const handleChange = (value: string | null) => {
77
+ selectedValue = value
78
+ }
79
+
80
+ await act(async () => {
81
+ render(
82
+ <SelectField
83
+ name="status"
84
+ value={null}
85
+ onChange={handleChange}
86
+ label="Status"
87
+ options={mockOptions}
88
+ />,
89
+ )
90
+ })
91
+
92
+ const trigger = screen.getByRole('combobox')
93
+
94
+ await act(async () => {
95
+ await userEvent.click(trigger)
96
+ })
97
+
98
+ await waitFor(() => {
99
+ expect(screen.getByRole('option', { name: 'Option 2' })).toBeInTheDocument()
100
+ })
101
+
102
+ const option = screen.getByRole('option', { name: 'Option 2' })
103
+
104
+ await act(async () => {
105
+ await userEvent.click(option)
106
+ })
107
+
108
+ expect(selectedValue).toBe('option2')
109
+ })
110
+
111
+ it('should show required indicator when required', async () => {
112
+ await act(async () => {
113
+ render(
114
+ <SelectField
115
+ name="status"
116
+ value={null}
117
+ onChange={() => {}}
118
+ label="Status"
119
+ options={mockOptions}
120
+ required
121
+ />,
122
+ )
123
+ })
124
+
125
+ expect(screen.getByText('*')).toBeInTheDocument()
126
+ })
127
+
128
+ it('should display error message', async () => {
129
+ await act(async () => {
130
+ render(
131
+ <SelectField
132
+ name="status"
133
+ value={null}
134
+ onChange={() => {}}
135
+ label="Status"
136
+ options={mockOptions}
137
+ error="Status is required"
138
+ />,
139
+ )
140
+ })
141
+
142
+ expect(screen.getByText('Status is required')).toBeInTheDocument()
143
+ })
144
+
145
+ it('should be disabled when disabled prop is true', async () => {
146
+ await act(async () => {
147
+ render(
148
+ <SelectField
149
+ name="status"
150
+ value={null}
151
+ onChange={() => {}}
152
+ label="Status"
153
+ options={mockOptions}
154
+ disabled
155
+ />,
156
+ )
157
+ })
158
+
159
+ const trigger = screen.getByRole('combobox')
160
+ expect(trigger).toBeDisabled()
161
+ })
162
+
163
+ it('should support keyboard navigation', async () => {
164
+ await act(async () => {
165
+ render(
166
+ <SelectField
167
+ name="status"
168
+ value={null}
169
+ onChange={() => {}}
170
+ label="Status"
171
+ options={mockOptions}
172
+ />,
173
+ )
174
+ })
175
+
176
+ const trigger = screen.getByRole('combobox')
177
+ trigger.focus()
178
+ expect(document.activeElement).toBe(trigger)
179
+
180
+ // Open with Space key (more reliable than Enter for Radix Select)
181
+ await act(async () => {
182
+ await userEvent.keyboard(' ')
183
+ })
184
+
185
+ await waitFor(
186
+ () => {
187
+ expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument()
188
+ },
189
+ { timeout: 3000 },
190
+ )
191
+ })
192
+
193
+ it('should close dropdown when Escape is pressed', async () => {
194
+ await act(async () => {
195
+ render(
196
+ <SelectField
197
+ name="status"
198
+ value={null}
199
+ onChange={() => {}}
200
+ label="Status"
201
+ options={mockOptions}
202
+ />,
203
+ )
204
+ })
205
+
206
+ const trigger = screen.getByRole('combobox')
207
+
208
+ await act(async () => {
209
+ await userEvent.click(trigger)
210
+ })
211
+
212
+ await waitFor(() => {
213
+ expect(screen.getByRole('option', { name: 'Option 1' })).toBeInTheDocument()
214
+ })
215
+
216
+ await act(async () => {
217
+ await userEvent.keyboard('{Escape}')
218
+ })
219
+
220
+ await waitFor(() => {
221
+ expect(screen.queryByRole('option', { name: 'Option 1' })).not.toBeInTheDocument()
222
+ })
223
+ })
224
+ })
225
+
226
+ describe('read mode', () => {
227
+ it('should render selected option label', async () => {
228
+ await act(async () => {
229
+ render(
230
+ <SelectField
231
+ name="status"
232
+ value="option2"
233
+ onChange={() => {}}
234
+ label="Status"
235
+ options={mockOptions}
236
+ mode="read"
237
+ />,
238
+ )
239
+ })
240
+
241
+ expect(screen.getByText('Status')).toBeInTheDocument()
242
+ expect(screen.getByText('Option 2')).toBeInTheDocument()
243
+ expect(screen.queryByRole('combobox')).not.toBeInTheDocument()
244
+ })
245
+
246
+ it('should show dash when value is null', async () => {
247
+ await act(async () => {
248
+ render(
249
+ <SelectField
250
+ name="status"
251
+ value={null}
252
+ onChange={() => {}}
253
+ label="Status"
254
+ options={mockOptions}
255
+ mode="read"
256
+ />,
257
+ )
258
+ })
259
+
260
+ expect(screen.getByText('-')).toBeInTheDocument()
261
+ })
262
+ })
263
+ })
@@ -0,0 +1,204 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen, waitFor, act } from '@testing-library/react'
3
+ import { userEvent } from 'vitest/browser'
4
+ import React from 'react'
5
+ import { TextField } from '../../../src/components/fields/TextField.js'
6
+
7
+ describe('TextField (Browser)', () => {
8
+ describe('edit mode', () => {
9
+ it('should render text input with label', async () => {
10
+ render(<TextField name="username" value="" onChange={() => {}} label="Username" />)
11
+
12
+ expect(screen.getByLabelText('Username')).toBeInTheDocument()
13
+ expect(screen.getByRole('textbox')).toBeInTheDocument()
14
+ })
15
+
16
+ it('should display current value', async () => {
17
+ render(<TextField name="username" value="john" onChange={() => {}} label="Username" />)
18
+
19
+ const input = screen.getByRole('textbox')
20
+ expect(input).toHaveValue('john')
21
+ })
22
+
23
+ it('should call onChange when user types', async () => {
24
+ let currentValue = ''
25
+ const handleChange = (value: string) => {
26
+ currentValue = value
27
+ }
28
+
29
+ render(<TextField name="username" value="" onChange={handleChange} label="Username" />)
30
+
31
+ const input = screen.getByRole('textbox')
32
+ await userEvent.type(input, 'test')
33
+
34
+ // Check that onChange was called and value updated
35
+ expect(currentValue).toBeTruthy()
36
+ })
37
+
38
+ it('should handle clearing input', async () => {
39
+ let currentValue = 'initial'
40
+ const handleChange = (value: string) => {
41
+ currentValue = value
42
+ }
43
+
44
+ render(
45
+ <TextField name="username" value={currentValue} onChange={handleChange} label="Username" />,
46
+ )
47
+
48
+ const input = screen.getByRole('textbox')
49
+ await userEvent.clear(input)
50
+
51
+ expect(currentValue).toBe('')
52
+ })
53
+
54
+ it('should show required indicator when required', async () => {
55
+ render(<TextField name="username" value="" onChange={() => {}} label="Username" required />)
56
+
57
+ expect(screen.getByText('*')).toBeInTheDocument()
58
+ })
59
+
60
+ it('should display error message', async () => {
61
+ render(
62
+ <TextField
63
+ name="username"
64
+ value=""
65
+ onChange={() => {}}
66
+ label="Username"
67
+ error="Username is required"
68
+ />,
69
+ )
70
+
71
+ expect(screen.getByText('Username is required')).toBeInTheDocument()
72
+ })
73
+
74
+ it('should be disabled when disabled prop is true', async () => {
75
+ render(<TextField name="username" value="" onChange={() => {}} label="Username" disabled />)
76
+
77
+ const input = screen.getByRole('textbox')
78
+ expect(input).toBeDisabled()
79
+ })
80
+
81
+ it('should not accept input when disabled', async () => {
82
+ let currentValue = ''
83
+ const handleChange = (value: string) => {
84
+ currentValue = value
85
+ }
86
+
87
+ render(
88
+ <TextField
89
+ name="username"
90
+ value={currentValue}
91
+ onChange={handleChange}
92
+ label="Username"
93
+ disabled
94
+ />,
95
+ )
96
+
97
+ const input = screen.getByRole('textbox')
98
+ await userEvent.type(input, 'test')
99
+
100
+ expect(currentValue).toBe('')
101
+ })
102
+
103
+ it('should show placeholder text', async () => {
104
+ render(
105
+ <TextField
106
+ name="username"
107
+ value=""
108
+ onChange={() => {}}
109
+ label="Username"
110
+ placeholder="Enter your username"
111
+ />,
112
+ )
113
+
114
+ expect(screen.getByPlaceholderText('Enter your username')).toBeInTheDocument()
115
+ })
116
+
117
+ it('should handle programmatic value changes', async () => {
118
+ function TestComponent() {
119
+ const [value, setValue] = React.useState('')
120
+ return (
121
+ <div>
122
+ <button onClick={() => setValue('programmatic value')}>Set Value</button>
123
+ <TextField name="username" value={value} onChange={setValue} label="Username" />
124
+ </div>
125
+ )
126
+ }
127
+
128
+ await act(async () => {
129
+ render(<TestComponent />)
130
+ })
131
+
132
+ const input = screen.getByRole('textbox')
133
+ const button = screen.getByRole('button')
134
+
135
+ // Click button to set value programmatically
136
+ await act(async () => {
137
+ await userEvent.click(button)
138
+ })
139
+
140
+ await waitFor(() => {
141
+ expect(input).toHaveValue('programmatic value')
142
+ })
143
+ })
144
+
145
+ it('should handle focus and blur events', async () => {
146
+ render(<TextField name="username" value="" onChange={() => {}} label="Username" />)
147
+
148
+ const input = screen.getByRole('textbox')
149
+
150
+ // Focus
151
+ input.focus()
152
+ expect(document.activeElement).toBe(input)
153
+
154
+ // Blur
155
+ input.blur()
156
+ expect(document.activeElement).not.toBe(input)
157
+ })
158
+
159
+ it('should handle text input with keyboard', async () => {
160
+ function TestComponent() {
161
+ const [value, setValue] = React.useState('')
162
+ return <TextField name="username" value={value} onChange={setValue} label="Username" />
163
+ }
164
+
165
+ await act(async () => {
166
+ render(<TestComponent />)
167
+ })
168
+
169
+ const input = screen.getByRole('textbox')
170
+
171
+ await act(async () => {
172
+ await userEvent.click(input)
173
+ })
174
+
175
+ await act(async () => {
176
+ await userEvent.keyboard('test123')
177
+ })
178
+
179
+ await waitFor(() => {
180
+ expect(input).toHaveValue('test123')
181
+ })
182
+ })
183
+ })
184
+
185
+ describe('read mode', () => {
186
+ it('should render value as text', async () => {
187
+ render(
188
+ <TextField name="username" value="john" onChange={() => {}} label="Username" mode="read" />,
189
+ )
190
+
191
+ expect(screen.getByText('Username')).toBeInTheDocument()
192
+ expect(screen.getByText('john')).toBeInTheDocument()
193
+ expect(screen.queryByRole('textbox')).not.toBeInTheDocument()
194
+ })
195
+
196
+ it('should show dash when value is empty', async () => {
197
+ render(
198
+ <TextField name="username" value="" onChange={() => {}} label="Username" mode="read" />,
199
+ )
200
+
201
+ expect(screen.getByText('-')).toBeInTheDocument()
202
+ })
203
+ })
204
+ })