@oslokommune/punkt-react 15.4.6 → 16.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/CHANGELOG.md +74 -0
- package/dist/index.d.ts +38 -15
- package/dist/punkt-react.es.js +12025 -10664
- package/dist/punkt-react.umd.js +562 -549
- package/package.json +5 -5
- package/src/components/accordion/Accordion.test.tsx +3 -2
- package/src/components/alert/Alert.test.tsx +2 -1
- package/src/components/backlink/BackLink.test.tsx +2 -1
- package/src/components/button/Button.test.tsx +4 -3
- package/src/components/calendar/Calendar.interaction.test.tsx +2 -1
- package/src/components/checkbox/Checkbox.test.tsx +2 -1
- package/src/components/combobox/Combobox.accessibility.test.tsx +277 -0
- package/src/components/combobox/Combobox.core.test.tsx +469 -0
- package/src/components/combobox/Combobox.interaction.test.tsx +607 -0
- package/src/components/combobox/Combobox.selection.test.tsx +548 -0
- package/src/components/combobox/Combobox.tsx +59 -54
- package/src/components/combobox/ComboboxInput.tsx +140 -0
- package/src/components/combobox/ComboboxTags.tsx +110 -0
- package/src/components/combobox/Listbox.tsx +172 -0
- package/src/components/combobox/types.ts +145 -0
- package/src/components/combobox/useComboboxState.ts +1141 -0
- package/src/components/datepicker/Datepicker.accessibility.test.tsx +5 -4
- package/src/components/datepicker/Datepicker.input.test.tsx +3 -2
- package/src/components/datepicker/Datepicker.selection.test.tsx +8 -8
- package/src/components/datepicker/Datepicker.validation.test.tsx +2 -1
- package/src/components/radio/RadioButton.test.tsx +3 -2
- package/src/components/searchinput/SearchInput.test.tsx +6 -5
- package/src/components/tabs/Tabs.test.tsx +13 -12
- package/src/components/tag/Tag.test.tsx +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
|
|
5
5
|
"homepage": "https://punkt.oslo.kommune.no",
|
|
6
6
|
"author": "Team Designsystem, Oslo Origo",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@lit-labs/ssr-dom-shim": "^1.2.1",
|
|
41
41
|
"@lit/react": "^1.0.7",
|
|
42
|
-
"@oslokommune/punkt-elements": "^
|
|
42
|
+
"@oslokommune/punkt-elements": "^16.0.0",
|
|
43
43
|
"classnames": "^2.5.1",
|
|
44
44
|
"prettier": "^3.3.3",
|
|
45
45
|
"react-hook-form": "^7.53.0"
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"@eslint/compat": "^2.0.2",
|
|
50
50
|
"@eslint/eslintrc": "^3.3.3",
|
|
51
51
|
"@eslint/js": "^9.37.0",
|
|
52
|
-
"@oslokommune/punkt-assets": "^
|
|
53
|
-
"@oslokommune/punkt-css": "^
|
|
52
|
+
"@oslokommune/punkt-assets": "^16.0.0",
|
|
53
|
+
"@oslokommune/punkt-css": "^16.0.0",
|
|
54
54
|
"@testing-library/jest-dom": "^6.5.0",
|
|
55
55
|
"@testing-library/react": "^16.0.1",
|
|
56
56
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
110
110
|
},
|
|
111
111
|
"license": "MIT",
|
|
112
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "b3e88b7c06f9a63c2e911693af6b9860cd73c27b"
|
|
113
113
|
}
|
|
@@ -2,6 +2,7 @@ import '@testing-library/jest-dom'
|
|
|
2
2
|
|
|
3
3
|
import { fireEvent,render, screen } from '@testing-library/react'
|
|
4
4
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
import { vi } from 'vitest'
|
|
5
6
|
import { createRef } from 'react'
|
|
6
7
|
|
|
7
8
|
import { PktAccordion } from './Accordion'
|
|
@@ -22,7 +23,7 @@ describe('PktAccordion', () => {
|
|
|
22
23
|
})
|
|
23
24
|
|
|
24
25
|
test('renders children', () => {
|
|
25
|
-
const mockToggleOpen =
|
|
26
|
+
const mockToggleOpen = vi.fn()
|
|
26
27
|
render(
|
|
27
28
|
<PktAccordion>
|
|
28
29
|
<PktAccordionItem title="Title 1" id="item1" onClick={mockToggleOpen}>
|
|
@@ -144,7 +145,7 @@ describe('PktAccordionItem', () => {
|
|
|
144
145
|
})
|
|
145
146
|
|
|
146
147
|
test('calls onClick handler', () => {
|
|
147
|
-
const mockOnClick =
|
|
148
|
+
const mockOnClick = vi.fn()
|
|
148
149
|
const { container } = render(
|
|
149
150
|
<PktAccordionItem title="Title" id="item1" onClick={mockOnClick}>
|
|
150
151
|
Content
|
|
@@ -2,6 +2,7 @@ import '@testing-library/jest-dom'
|
|
|
2
2
|
|
|
3
3
|
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
|
|
4
4
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
import { vi } from 'vitest'
|
|
5
6
|
|
|
6
7
|
import { PktAlert } from './Alert'
|
|
7
8
|
|
|
@@ -11,7 +12,7 @@ afterEach(cleanup)
|
|
|
11
12
|
|
|
12
13
|
describe('PktAlert', () => {
|
|
13
14
|
test('calls onClose when close button is clicked', async () => {
|
|
14
|
-
const onClose =
|
|
15
|
+
const onClose = vi.fn()
|
|
15
16
|
const { getByLabelText } = render(
|
|
16
17
|
<PktAlert closeAlert={true} onClose={onClose}>
|
|
17
18
|
Hello World
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/react'
|
|
2
2
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
3
|
+
import { vi } from 'vitest'
|
|
3
4
|
|
|
4
5
|
import { PktBackLink } from './BackLink'
|
|
5
6
|
|
|
@@ -23,7 +24,7 @@ describe('PktBackLink', () => {
|
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
it('calls onClick when clicked', async () => {
|
|
26
|
-
const onClickMock =
|
|
27
|
+
const onClickMock = vi.fn()
|
|
27
28
|
const { getByText } = render(<PktBackLink text="Back" onClick={onClickMock} />)
|
|
28
29
|
await window.customElements.whenDefined('pkt-backlink')
|
|
29
30
|
const link = getByText('Back')
|
|
@@ -3,6 +3,7 @@ import '@testing-library/jest-dom'
|
|
|
3
3
|
import { render, screen } from '@testing-library/react'
|
|
4
4
|
import userEvent from '@testing-library/user-event'
|
|
5
5
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
6
|
+
import { vi } from 'vitest'
|
|
6
7
|
import { createRef } from 'react'
|
|
7
8
|
import { useForm } from 'react-hook-form'
|
|
8
9
|
|
|
@@ -13,7 +14,7 @@ expect.extend(toHaveNoViolations)
|
|
|
13
14
|
// Mock Form Component
|
|
14
15
|
const MockForm = ({ buttonPosition }: { buttonPosition: 'inside' | 'outside' }) => {
|
|
15
16
|
const { register, handleSubmit, reset } = useForm()
|
|
16
|
-
const onSubmit =
|
|
17
|
+
const onSubmit = vi.fn()
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<>
|
|
@@ -95,7 +96,7 @@ test('forwardRef works correctly', async () => {
|
|
|
95
96
|
})
|
|
96
97
|
|
|
97
98
|
test('PktButton triggers click when focused and enter is pressed', async () => {
|
|
98
|
-
const handleClick =
|
|
99
|
+
const handleClick = vi.fn()
|
|
99
100
|
render(<PktButton onClick={handleClick}>trøkk her</PktButton>)
|
|
100
101
|
|
|
101
102
|
const button = screen.getByRole('button', { name: /trøkk her/i })
|
|
@@ -108,7 +109,7 @@ test('PktButton triggers click when focused and enter is pressed', async () => {
|
|
|
108
109
|
})
|
|
109
110
|
|
|
110
111
|
test('PktButton triggers click when focused and space is pressed', async () => {
|
|
111
|
-
const handleClick =
|
|
112
|
+
const handleClick = vi.fn()
|
|
112
113
|
render(<PktButton onClick={handleClick}>Trøkk igjen</PktButton>)
|
|
113
114
|
|
|
114
115
|
const button = screen.getByRole('button')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@testing-library/jest-dom'
|
|
2
2
|
import { render, fireEvent, act } from '@testing-library/react'
|
|
3
|
+
import { vi } from 'vitest'
|
|
3
4
|
|
|
4
5
|
import { PktCalendar, IPktCalendar } from './Calendar'
|
|
5
6
|
|
|
@@ -41,7 +42,7 @@ describe('PktCalendar', () => {
|
|
|
41
42
|
})
|
|
42
43
|
|
|
43
44
|
test('handles escape key', () => {
|
|
44
|
-
const onClose =
|
|
45
|
+
const onClose = vi.fn()
|
|
45
46
|
const { container } = createCalendar({ onClose })
|
|
46
47
|
|
|
47
48
|
const calendarEl = container.querySelector('.pkt-calendar') as HTMLElement
|
|
@@ -2,6 +2,7 @@ import '@testing-library/jest-dom'
|
|
|
2
2
|
|
|
3
3
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
4
4
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
import { vi } from 'vitest'
|
|
5
6
|
|
|
6
7
|
import { PktCheckbox } from './Checkbox'
|
|
7
8
|
|
|
@@ -45,7 +46,7 @@ describe('PktCheckbox', () => {
|
|
|
45
46
|
})
|
|
46
47
|
|
|
47
48
|
test('handles onClick callback', async () => {
|
|
48
|
-
const onClickMock =
|
|
49
|
+
const onClickMock = vi.fn()
|
|
49
50
|
const { getByLabelText } = render(<PktCheckbox id="myCheckbox" label="My Checkbox" onClick={onClickMock} />)
|
|
50
51
|
|
|
51
52
|
// Get the checkbox label element
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render } from '@testing-library/react'
|
|
4
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
|
|
6
|
+
import type { IPktComboboxOption } from 'shared-types/combobox'
|
|
7
|
+
import { PktCombobox } from './Combobox'
|
|
8
|
+
import type { IPktCombobox } from './types'
|
|
9
|
+
|
|
10
|
+
expect.extend(toHaveNoViolations)
|
|
11
|
+
|
|
12
|
+
const comboboxId = 'test-combobox'
|
|
13
|
+
const label = 'Test Combobox'
|
|
14
|
+
|
|
15
|
+
const getDefaultOptions = (): IPktComboboxOption[] => [
|
|
16
|
+
{ value: 'apple', label: 'Apple' },
|
|
17
|
+
{ value: 'banana', label: 'Banana' },
|
|
18
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const createComboboxTest = (props: Partial<IPktCombobox> = {}) => {
|
|
22
|
+
const defaultProps: IPktCombobox = {
|
|
23
|
+
label,
|
|
24
|
+
id: comboboxId,
|
|
25
|
+
...props,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return render(<PktCombobox {...defaultProps} />)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('PktCombobox', () => {
|
|
32
|
+
describe('Accessibility (axe)', () => {
|
|
33
|
+
test('basic combobox has no accessibility violations', async () => {
|
|
34
|
+
const { container } = createComboboxTest()
|
|
35
|
+
|
|
36
|
+
const results = await axe(container)
|
|
37
|
+
expect(results).toHaveNoViolations()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('combobox with options has no accessibility violations', async () => {
|
|
41
|
+
const { container } = createComboboxTest({
|
|
42
|
+
options: getDefaultOptions(),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const results = await axe(container)
|
|
46
|
+
expect(results).toHaveNoViolations()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('combobox with text input has no accessibility violations', async () => {
|
|
50
|
+
const { container } = createComboboxTest({
|
|
51
|
+
allowUserInput: true,
|
|
52
|
+
options: getDefaultOptions(),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const results = await axe(container)
|
|
56
|
+
expect(results).toHaveNoViolations()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('combobox with typeahead has no accessibility violations', async () => {
|
|
60
|
+
const { container } = createComboboxTest({
|
|
61
|
+
typeahead: true,
|
|
62
|
+
options: getDefaultOptions(),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const results = await axe(container)
|
|
66
|
+
expect(results).toHaveNoViolations()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('multiple combobox has no accessibility violations', async () => {
|
|
70
|
+
const { container } = createComboboxTest({
|
|
71
|
+
multiple: true,
|
|
72
|
+
options: getDefaultOptions(),
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Exclude nested-interactive: the decorative checkbox inside li[role="option"]
|
|
76
|
+
// is aria-hidden and non-focusable, but axe flags it anyway.
|
|
77
|
+
// Selection state is conveyed by aria-selected on the option element.
|
|
78
|
+
const results = await axe(container, {
|
|
79
|
+
rules: { 'nested-interactive': { enabled: false } },
|
|
80
|
+
})
|
|
81
|
+
expect(results).toHaveNoViolations()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('disabled combobox has no accessibility violations', async () => {
|
|
85
|
+
const { container } = createComboboxTest({ disabled: true })
|
|
86
|
+
|
|
87
|
+
const results = await axe(container)
|
|
88
|
+
expect(results).toHaveNoViolations()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('combobox with error state has no accessibility violations', async () => {
|
|
92
|
+
const { container } = createComboboxTest({
|
|
93
|
+
hasError: true,
|
|
94
|
+
errorMessage: 'Required field',
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const results = await axe(container)
|
|
98
|
+
expect(results).toHaveNoViolations()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('combobox with selected value has no accessibility violations', async () => {
|
|
102
|
+
const { container } = createComboboxTest({
|
|
103
|
+
defaultValue: 'apple',
|
|
104
|
+
options: getDefaultOptions(),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const results = await axe(container)
|
|
108
|
+
expect(results).toHaveNoViolations()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('combobox with multiple selected values has no accessibility violations', async () => {
|
|
112
|
+
const { container } = createComboboxTest({
|
|
113
|
+
multiple: true,
|
|
114
|
+
defaultValue: ['apple', 'banana'],
|
|
115
|
+
options: getDefaultOptions(),
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Exclude nested-interactive: decorative checkboxes inside options (see above)
|
|
119
|
+
const results = await axe(container, {
|
|
120
|
+
rules: { 'nested-interactive': { enabled: false } },
|
|
121
|
+
})
|
|
122
|
+
expect(results).toHaveNoViolations()
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('ARIA attributes', () => {
|
|
127
|
+
test('select-only combobox has correct ARIA attributes', () => {
|
|
128
|
+
const { container } = createComboboxTest()
|
|
129
|
+
|
|
130
|
+
const comboboxInput = container.querySelector('.pkt-combobox__input')
|
|
131
|
+
|
|
132
|
+
expect(comboboxInput?.getAttribute('role')).toBe('combobox')
|
|
133
|
+
expect(comboboxInput?.getAttribute('aria-controls')).toBe(`${comboboxId}-listbox`)
|
|
134
|
+
expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
|
|
135
|
+
expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
|
|
136
|
+
expect(comboboxInput?.getAttribute('aria-labelledby')).toBe(`${comboboxId}-combobox-label`)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('combobox aria-expanded updates when dropdown opens', () => {
|
|
140
|
+
const { container } = createComboboxTest()
|
|
141
|
+
|
|
142
|
+
const comboboxInput = container.querySelector('.pkt-combobox__input')
|
|
143
|
+
expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
|
|
144
|
+
|
|
145
|
+
fireEvent.click(comboboxInput!)
|
|
146
|
+
|
|
147
|
+
expect(comboboxInput?.getAttribute('aria-expanded')).toBe('true')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('text input has correct ARIA attributes for allowUserInput', () => {
|
|
151
|
+
const { container } = createComboboxTest({ allowUserInput: true })
|
|
152
|
+
|
|
153
|
+
const textInput = container.querySelector('input[type="text"][role="combobox"]')
|
|
154
|
+
|
|
155
|
+
expect(textInput?.getAttribute('role')).toBe('combobox')
|
|
156
|
+
expect(textInput?.getAttribute('aria-controls')).toBe(`${comboboxId}-listbox`)
|
|
157
|
+
expect(textInput?.getAttribute('aria-label')).toBe(label)
|
|
158
|
+
expect(textInput?.getAttribute('aria-autocomplete')).toBe('list')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('text input has correct ARIA attributes for typeahead', () => {
|
|
162
|
+
const { container } = createComboboxTest({ typeahead: true })
|
|
163
|
+
|
|
164
|
+
const textInput = container.querySelector('input[type="text"]')
|
|
165
|
+
expect(textInput?.getAttribute('aria-autocomplete')).toBe('both')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('text input sets aria-activedescendant when value is selected', () => {
|
|
169
|
+
const { container } = createComboboxTest({
|
|
170
|
+
allowUserInput: true,
|
|
171
|
+
defaultValue: 'apple',
|
|
172
|
+
options: getDefaultOptions(),
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const textInput = container.querySelector('input[type="text"]')
|
|
176
|
+
expect(textInput?.getAttribute('aria-activedescendant')).toBeTruthy()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('text input aria-expanded reflects dropdown state', () => {
|
|
180
|
+
const { container } = createComboboxTest({ allowUserInput: true })
|
|
181
|
+
|
|
182
|
+
const textInput = container.querySelector('input[type="text"]')
|
|
183
|
+
expect(textInput?.getAttribute('aria-expanded')).toBe('false')
|
|
184
|
+
|
|
185
|
+
fireEvent.focus(textInput!)
|
|
186
|
+
|
|
187
|
+
expect(textInput?.getAttribute('aria-expanded')).toBe('true')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('listbox has correct id for aria-controls reference', () => {
|
|
191
|
+
const { container } = createComboboxTest()
|
|
192
|
+
|
|
193
|
+
const listbox = container.querySelector('.pkt-listbox')
|
|
194
|
+
expect(listbox?.getAttribute('id')).toBe(`${comboboxId}-listbox`)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('listbox has role=listbox', () => {
|
|
198
|
+
const { container } = createComboboxTest({ options: getDefaultOptions() })
|
|
199
|
+
|
|
200
|
+
const listbox = container.querySelector('.pkt-listbox')
|
|
201
|
+
expect(listbox?.getAttribute('role')).toBe('listbox')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('listbox has aria-label with component label', () => {
|
|
205
|
+
const { container } = createComboboxTest({ options: getDefaultOptions() })
|
|
206
|
+
|
|
207
|
+
const listbox = container.querySelector('.pkt-listbox')
|
|
208
|
+
expect(listbox?.getAttribute('aria-label')).toBe(`Liste: ${label}`)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('options have role=option with aria-selected', () => {
|
|
212
|
+
const { container } = createComboboxTest({
|
|
213
|
+
defaultValue: 'apple',
|
|
214
|
+
options: getDefaultOptions(),
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const options = container.querySelectorAll('.pkt-listbox__option')
|
|
218
|
+
expect(options[0].getAttribute('role')).toBe('option')
|
|
219
|
+
expect(options[0].getAttribute('aria-selected')).toBe('true')
|
|
220
|
+
expect(options[1].getAttribute('aria-selected')).toBe('false')
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
describe('Keyboard accessibility', () => {
|
|
225
|
+
test('select-only combobox is focusable when not disabled', () => {
|
|
226
|
+
const { container } = createComboboxTest()
|
|
227
|
+
|
|
228
|
+
const comboboxInput = container.querySelector('.pkt-combobox__input') as HTMLElement
|
|
229
|
+
expect(comboboxInput.getAttribute('tabindex')).toBe('0')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
test('select-only combobox is not focusable when disabled', () => {
|
|
233
|
+
const { container } = createComboboxTest({ disabled: true })
|
|
234
|
+
|
|
235
|
+
const comboboxInput = container.querySelector('.pkt-combobox__input') as HTMLElement
|
|
236
|
+
expect(comboboxInput.getAttribute('tabindex')).toBe('-1')
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
test('text input is part of tab order', () => {
|
|
240
|
+
const { container } = createComboboxTest({ allowUserInput: true })
|
|
241
|
+
|
|
242
|
+
const textInput = container.querySelector('input[type="text"]') as HTMLElement
|
|
243
|
+
expect(textInput).toBeInTheDocument()
|
|
244
|
+
// Text inputs are naturally tabbable (no tabindex needed)
|
|
245
|
+
expect(textInput.hasAttribute('tabindex')).toBe(false)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('text input is disabled when component is disabled', () => {
|
|
249
|
+
const { container } = createComboboxTest({
|
|
250
|
+
allowUserInput: true,
|
|
251
|
+
disabled: true,
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const textInput = container.querySelector('input[type="text"]') as HTMLInputElement
|
|
255
|
+
expect(textInput).toBeDisabled()
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('Label association', () => {
|
|
260
|
+
test('input wrapper label targets text input when allowUserInput', () => {
|
|
261
|
+
const { container } = createComboboxTest({ allowUserInput: true })
|
|
262
|
+
|
|
263
|
+
const labelEl = container.querySelector('label')
|
|
264
|
+
expect(labelEl).toBeInTheDocument()
|
|
265
|
+
expect(labelEl?.getAttribute('for')).toBe(`${comboboxId}-input`)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
test('input wrapper uses fieldset/legend when no text input (select-only)', () => {
|
|
269
|
+
const { container } = createComboboxTest()
|
|
270
|
+
|
|
271
|
+
const fieldset = container.querySelector('fieldset')
|
|
272
|
+
expect(fieldset).toBeInTheDocument()
|
|
273
|
+
const legend = container.querySelector('legend')
|
|
274
|
+
expect(legend).toBeInTheDocument()
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
})
|