Skip to content

Commit fa8de67

Browse files
authored
Merge pull request #2955 from SUI-Components/feat/molecule-autosuggest-dropdownlist-a11y
Feat/molecule autosuggest dropdownlist a11y
2 parents 020b760 + 06951a5 commit fa8de67

File tree

4 files changed

+49
-21
lines changed

4 files changed

+49
-21
lines changed

components/molecule/autosuggest/src/components/SingleSelection/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import InputWithClearUI from '../InputWithClearUI/index.js'
88

99
const MoleculeAutosuggestSingleSelection = ({
1010
autoFocus,
11+
'aria-expanded': ariaExpanded,
1112
ariaLabel,
1213
autoComplete = 'nope',
1314
children,
1415
design,
1516
disabled,
1617
iconClear,
1718
id,
19+
dropdownListId,
1820
innerRefInput: refInput = {},
1921
inputMode,
2022
isOpen,
@@ -64,6 +66,11 @@ const MoleculeAutosuggestSingleSelection = ({
6466
return (
6567
<>
6668
<InputWithClearUI
69+
role="combobox"
70+
aria-haspopup="true"
71+
aria-autocomplete="list"
72+
aria-controls={dropdownListId}
73+
aria-expanded={ariaExpanded}
6774
ariaLabel={ariaLabel}
6875
autoComplete={autoComplete}
6976
autoFocus={autoFocus}
@@ -94,6 +101,8 @@ const MoleculeAutosuggestSingleSelection = ({
94101
</InputWithClearUI>
95102
{(value || isOpen) && (
96103
<MoleculeDropdownList
104+
id={dropdownListId}
105+
aria-labelledby={id}
97106
size={size}
98107
onSelect={handleSelection}
99108
visible={isOpen && Children.count(children) > 0}

components/molecule/autosuggest/src/index.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ const MoleculeAutosuggest = ({
8989
}
9090
)
9191

92+
const accessibilityProps = {
93+
tabIndex: '0',
94+
role: 'combobox',
95+
'aria-expanded': isOpenState,
96+
...(multiselection && {'aria-controls': id})
97+
}
98+
9299
const closeList = ev => {
93100
const {current: domMoleculeAutosuggest} = innerRefMoleculeAutosuggest
94101
handleToggle(ev, {isOpen: false})
@@ -209,6 +216,7 @@ const MoleculeAutosuggest = ({
209216
onSelect,
210217
onToggle: handleToggle,
211218
state,
219+
...(!multiselection && {...accessibilityProps}),
212220
...restProps
213221
}
214222

@@ -219,15 +227,12 @@ const MoleculeAutosuggest = ({
219227
return (
220228
<div
221229
ref={refMoleculeAutosuggest}
222-
tabIndex="0"
223230
className={className}
224231
onKeyDown={handleKeyDown}
225232
onFocus={handleFocusIn}
226233
onBlur={handleFocusOut}
227234
onClick={handleClick}
228-
role="combobox"
229-
aria-controls={id}
230-
aria-expanded={isOpenState}
235+
{...(multiselection && {...accessibilityProps})}
231236
>
232237
<AutosuggestSelection {...autosuggestSelectionProps} />
233238
</div>

components/molecule/autosuggest/test/index.test.js

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,11 @@ describe(json.name, () => {
162162

163163
// When
164164
const {getByRole} = setup(props)
165-
const autoSuggestElement = getByRole('combobox')
166-
const autoSuggestInputElement = getByRole('textbox')
165+
const autoSuggestInputElement = getByRole('combobox')
167166

168167
// Then
169168
expect(() => getByRole('listbox')).to.throw()
170169
expect(() => getByRole('option')).to.throw()
171-
expect(autoSuggestElement.innerHTML).to.be.a('string')
172-
expect(autoSuggestElement.innerHTML).to.not.have.lengthOf(0)
173170
expect(autoSuggestInputElement.value).to.equal('')
174171
})
175172

@@ -187,11 +184,11 @@ describe(json.name, () => {
187184

188185
// When
189186
const {getByRole, getAllByRole} = setup(props)
187+
const combo = getByRole('combobox')
190188

191189
// Then
192-
expect(getByRole('combobox').innerHTML).to.be.a('string')
193-
expect(getByRole('combobox').innerHTML).to.not.have.lengthOf(0)
194-
expect(getByRole('textbox').value).to.equal(props.value)
190+
expect(combo.tagName).to.eq('INPUT')
191+
expect(combo.value).to.equal(props.value)
195192
expect(() => getByRole('listbox', {hidden: true})).to.not.throw()
196193
expect(() =>
197194
getAllByRole('option', {
@@ -218,11 +215,11 @@ describe(json.name, () => {
218215

219216
// When
220217
const {getByRole, getAllByRole} = setup(props)
218+
const combo = getByRole('combobox')
221219

222220
// Then
223-
expect(getByRole('combobox').innerHTML).to.be.a('string')
224-
expect(getByRole('combobox').innerHTML).to.not.have.lengthOf(0)
225-
expect(getByRole('textbox').value).to.equal(props.value)
221+
expect(combo.tagName).to.eq('INPUT')
222+
expect(combo.value).to.equal(props.value)
226223
expect(() => getByRole('listbox', {hidden: true})).to.not.throw()
227224
expect(() =>
228225
getAllByRole('option', {
@@ -250,11 +247,11 @@ describe(json.name, () => {
250247

251248
// When
252249
const {getByRole, getAllByRole} = setup(props)
250+
const combo = getByRole('combobox')
253251

254252
// Then
255-
expect(getByRole('combobox').innerHTML).to.be.a('string')
256-
expect(getByRole('combobox').innerHTML).to.not.have.lengthOf(0)
257-
expect(getByRole('textbox').value).to.equal(props.value)
253+
expect(combo.tagName).to.eq('INPUT')
254+
expect(combo.value).to.equal(props.value)
258255
expect(() => getByRole('listbox', {hidden: true})).to.not.throw()
259256
expect(() =>
260257
getAllByRole('option', {
@@ -294,17 +291,17 @@ describe(json.name, () => {
294291
// When
295292
const {getByRole, rerender} = setup(props)
296293
keyDownEvents.forEach(keyDownEvent => fireEvent.keyDown(getByRole('combobox'), keyDownEvent))
297-
fireEvent.change(getByRole('textbox'), changeEvent)
294+
fireEvent.change(getByRole('combobox'), changeEvent)
298295

299296
// Then
300-
expect(getByRole('textbox').value).to.equal('')
297+
expect(getByRole('combobox').value).to.equal('')
301298

302299
// And
303300
// When
304301

305302
rerender(<Component {...props} />)
306303

307-
expect(getByRole('textbox').value).to.equal(changeEvent.target.value)
304+
expect(getByRole('combobox').value).to.equal(changeEvent.target.value)
308305
sinon.assert.called(spy)
309306
})
310307
})

components/molecule/dropdownList/src/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const MoleculeDropdownList = forwardRef(
2121
(
2222
{
2323
children,
24+
id,
2425
onSelect,
2526
position = POSITIONS.BOTTOM,
2627
alwaysRender = true,
@@ -30,6 +31,7 @@ const MoleculeDropdownList = forwardRef(
3031
visible,
3132
onKeyDown,
3233
'aria-label': ariaLabel,
34+
'aria-labelledby': ariaLabelledby,
3335
...props
3436
},
3537
forwardedRef
@@ -94,7 +96,16 @@ const MoleculeDropdownList = forwardRef(
9496
if (!visible && !alwaysRender) return null
9597

9698
return (
97-
<ul ref={ref} tabIndex={0} onKeyDown={handleKeyDown} className={classNames} role="listbox" aria-label={ariaLabel}>
99+
<ul
100+
id={id}
101+
ref={ref}
102+
tabIndex={0}
103+
onKeyDown={handleKeyDown}
104+
className={classNames}
105+
role="listbox"
106+
aria-label={ariaLabel}
107+
aria-labelledby={ariaLabelledby}
108+
>
98109
{Children.toArray(children)
99110
.filter(Boolean)
100111
.map((child, index) => (
@@ -110,12 +121,18 @@ const MoleculeDropdownList = forwardRef(
110121
MoleculeDropdownList.displayName = 'MoleculeDropdownList'
111122

112123
MoleculeDropdownList.propTypes = {
124+
/** HTML id attribute */
125+
id: PropTypes.string,
126+
113127
/** No matter if is visible or invisible, render always the content */
114128
alwaysRender: PropTypes.bool,
115129

116130
/** aria-label for accessibility */
117131
'aria-label': PropTypes.string,
118132

133+
/** aria-labelledby for accessibility */
134+
'aria-labelledby': PropTypes.string,
135+
119136
/** Content to be included in the list (MoleculeDropdownOption) */
120137
children: PropTypes.node,
121138

0 commit comments

Comments
 (0)