Skip to content

Commit f378caa

Browse files
kelsSgithub-actions[bot]kylebuch8castastrophe
authored
fix: pfe-autocomplete update aria-selected attribute (#1713)
* CPFED-4185: started adding updated aria haspop features to autocomplete * CPFED-4185: started adding updated aria haspop features to autocomplete * CPFED-4185: started working on value change observer for dropdown list items * CPFED-4185: updated aria selected functions in droplist component * CPFED-4185: updated aria selected functions in droplist component * CPFED-4185: worked through aria-selected issues with Wes and refactored some code to use less memory * CPFED-4185: worked through aria-selected issues with Wes and refactored some code to use less memory * CPFED-4185: code cleanup * Update elements/pfe-autocomplete/src/pfe-autocomplete.js Co-authored-by: [ Cassondra ] <[email protected]> * Update elements/pfe-autocomplete/src/pfe-autocomplete.js Co-authored-by: [ Cassondra ] <[email protected]> * Update elements/pfe-autocomplete/src/pfe-autocomplete.js Co-authored-by: [ Cassondra ] <[email protected]> * Update elements/pfe-autocomplete/src/pfe-search-droplist.scss Co-authored-by: [ Cassondra ] <[email protected]> * CPFED-4185: updated a11y attributes and the demo page to address Lighthouse a11y issues. Updated tests to check aria attributes. * Update CHANGELOG-1.x.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Kyle Buchanan <[email protected]> Co-authored-by: [ Cassondra ] <[email protected]>
1 parent 43a904e commit f378caa

File tree

8 files changed

+114
-18
lines changed

8 files changed

+114
-18
lines changed

CHANGELOG-1.x.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# 1.11.0 (2021)
22

3+
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: pfe-autocomplete update aria-selected attribute
34
- [d3ea7fa](https://github.com/patternfly/patternfly-elements/commit/d3ea7facb0c36b7f3e20e2568bdc4bf2e5a5a852) feat: Graceful failure for component registry
45
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: container sass placeholder using incorrect variable for spacing (#1522)
56
- [099dc3e](https://github.com/patternfly/patternfly-elements/commit/099dc3e2d3ce732a32ecde0644f5c03ec1e8dd9c) fix: Accordion alignment with latest design kit

elements/pfe-autocomplete/demo/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html>
2+
<html lang="en">
33

44
<head>
55
<title>PatternFly Elements: Auto-complete Demo</title>
@@ -58,8 +58,8 @@ <h2 class="pfe-headline-2" slot="pfe-band--header">Static Data</h2>
5858
<input placeholder="Enter Your Search Term" />
5959
</pfe-autocomplete>
6060
</div>
61-
<h4 class="pfe-headline-4">Static Selected Value: <span style="color:var(--pfe-theme--color--ui-accent)"
62-
id="staticSelectedValue"></span></h4>
61+
<h3 class="pfe-headline-4">Static Selected Value: <span style="color:var(--pfe-theme--color--ui-accent)"
62+
id="staticSelectedValue"></span></h3>
6363
</pfe-band>
6464

6565
<pfe-band color="darker">
@@ -70,7 +70,7 @@ <h2 class="pfe-headline-2" slot="pfe-band--header">Lazy Loading with init-value<
7070
<input placeholder="Enter Your Search Term" />
7171
</pfe-autocomplete>
7272
</div>
73-
<h4 class="pfe-headline-4">Ajax Selected Value: <span id="ajaxSelectedValue"></span></h4>
73+
<h3 class="pfe-headline-4">Ajax Selected Value: <span id="ajaxSelectedValue"></span></h3>
7474
</pfe-band>
7575

7676
<pfe-band color="lightest">

elements/pfe-autocomplete/src/pfe-autocomplete.js

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,11 @@ class PfeAutocomplete extends PFElement {
136136
this._input.setAttribute("aria-label", "Search");
137137
}
138138

139-
this._input.setAttribute("aria-autocomplete", "both");
139+
this._input.setAttribute("aria-autocomplete", "list");
140140
this._input.setAttribute("aria-haspopup", "true");
141+
this._input.setAttribute("aria-owns", "droplist-items");
142+
this._input.setAttribute("aria-controls", "droplist-items");
143+
this._input.setAttribute("aria-expanded", "false");
141144
this._input.setAttribute("type", "search");
142145
this._input.setAttribute("autocomplete", "off");
143146
this._input.setAttribute("autocorrect", "off");
@@ -260,6 +263,7 @@ class PfeAutocomplete extends PFElement {
260263
_closeDroplist() {
261264
this._dropdown.open = null;
262265
this._dropdown.removeAttribute("active-index");
266+
this._input.setAttribute("aria-expanded", "false");
263267
}
264268

265269
_openDroplist() {
@@ -269,6 +273,7 @@ class PfeAutocomplete extends PFElement {
269273
this.emitEvent(PfeAutocomplete.events.optionsShown, {
270274
composed: true,
271275
});
276+
this._input.setAttribute("aria-expanded", "true");
272277
}
273278

274279
_optionSelected(e) {
@@ -309,14 +314,24 @@ class PfeAutocomplete extends PFElement {
309314
this._closeDroplist();
310315
}
311316

317+
/**
318+
* Returns the HTML of the active element
319+
* @param {number} activeIndex Index of an element in the droplist
320+
* @return {string} The HTML inside of the given index as a string
321+
*/
312322
_activeOption(activeIndex) {
313323
if (activeIndex === null || activeIndex === "null") return;
314324
return this._dropdown.shadowRoot.querySelector("li:nth-child(" + (parseInt(activeIndex, 10) + 1) + ")").innerHTML;
315325
}
316326

327+
/**
328+
* Handle keyboard input, we care about arrow keys, enter, and escape
329+
* @param {object} e - keypress event
330+
*/
317331
_inputKeyUp(e) {
318332
let key = e.keyCode;
319333

334+
// Check to see if it's a key we care about
320335
if (
321336
this._dropdown.data.length === 0 &&
322337
key !== KEYCODE.DOWN &&
@@ -340,11 +355,14 @@ class PfeAutocomplete extends PFElement {
340355

341356
activeIndex -= 1;
342357

358+
// Go to the last item if we're at -1 index
343359
if (activeIndex < 0) {
344360
activeIndex = optionsLength - 1;
345361
}
346362

363+
// Get the HTML of the active element
347364
this._input.value = this._activeOption(activeIndex);
365+
348366
} else if (key === KEYCODE.DOWN) {
349367
if (!this._dropdown.open) {
350368
return;
@@ -357,7 +375,9 @@ class PfeAutocomplete extends PFElement {
357375
activeIndex = 0;
358376
}
359377

378+
// Go to the last item if we're at -1 index
360379
this._input.value = this._activeOption(activeIndex);
380+
361381
} else if (key === KEYCODE.ENTER) {
362382
if (this._activeOption(activeIndex)) {
363383
this.emitEvent(PfeAutocomplete.events.select, {
@@ -438,6 +458,7 @@ class PfeSearchDroplist extends PFElement {
438458
this.activeIndex = null;
439459
this._ul = this.shadowRoot.querySelector("ul");
440460
this._ul.addEventListener("mousedown", this._optionSelected.bind(this));
461+
441462
}
442463

443464
disconnectedCallback() {
@@ -472,25 +493,40 @@ class PfeSearchDroplist extends PFElement {
472493
.join("")}`;
473494
}
474495

496+
/**
497+
* Handle state changes when active droplist item has been changed
498+
*/
475499
_activeIndexChanged() {
500+
// Make a quick exit if necessary
476501
if (!this.data || this.data.length === 0 || this.activeIndex === null || this.activeIndex === "null") return;
477502

478-
// remove active class
479-
if (this._ul.querySelector(".active")) {
480-
this._ul.querySelector(".active").classList.remove("active");
481-
}
503+
// Previous element may not exist
504+
const previouslyActiveElement = this._ul.querySelector(".active");
505+
const activeOption = this._ul.querySelector("li:nth-child(" + (parseInt(this.activeIndex, 10) + 1) + ")");
482506

483-
// add active class to selected option
484-
let activeOption = this._ul.querySelector("li:nth-child(" + (parseInt(this.activeIndex, 10) + 1) + ")");
507+
// Handle any element that should no longer be selected
508+
if (previouslyActiveElement) {
509+
previouslyActiveElement.classList.remove("active");
510+
previouslyActiveElement.removeAttribute("aria-selected");
511+
}
485512

513+
// Update newly selected element to have proper attributes and settings
486514
activeOption.classList.add("active");
515+
// @note Set aria-selected on the active list item, should only occur on the list item that is being referenced
516+
// by the aria-activedescendant attribute. This attribute is required when creating a listbox autocomplete
517+
// component. It helps ensure that the screen reader user knows what element is active when moving through the
518+
// list of items with the arrow keys
519+
activeOption.setAttribute("aria-selected", "true");
487520

488521
// scroll to selected element when selected item with keyboard is out of view
489522
let ulWrapper = this.shadowRoot.querySelector(".droplist");
490523
let activeOptionHeight = activeOption.offsetHeight;
491524
activeOptionHeight += parseInt(window.getComputedStyle(activeOption).getPropertyValue("margin-bottom"), 10);
492525
ulWrapper.scrollTop = activeOption.offsetTop - ulWrapper.offsetHeight + activeOptionHeight;
526+
527+
return activeOption;
493528
}
529+
494530
}
495531

496532
PFElement.create(PfeSearchDroplist);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="suggestions-aria-help sr-only" aria-hidden="false" role="status"></div>
22
<div class="droplist">
3-
<ul role="listbox" tabindex="-1">
3+
<ul role="listbox" id="droplist-items" tabindex="-1">
44
</ul>
55
</div>

elements/pfe-autocomplete/src/pfe-search-droplist.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
display: block;
1313
}
1414

15+
:host([debug]) [aria-selected] {
16+
box-shadow: inset 0 0 0 pfe-var(ui--border-width--md) pfe-var(feedback--critical);
17+
}
18+
1519
.sr-only {
1620
position: absolute;
1721
width: 1px;

elements/pfe-autocomplete/test/old-test/pfe-autocomplete-aria_test.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,22 @@
2222
describe('<pfe-autocomplete>', () => {
2323
it('should contain appropriate aria attributes on input box', done => {
2424
const autocompleteElem = fixture('autocomplete-a11y');
25-
25+
2626
flush(() => {
2727
const input = autocompleteElem._input;
2828

2929
expect(input.getAttribute("role")).to.equal("combobox");
3030
expect(input.getAttribute("aria-label")).to.equal("Search");
31-
expect(input.getAttribute("aria-autocomplete")).to.equal("both");
31+
expect(input.getAttribute("aria-autocomplete")).to.equal("list");
3232
expect(input.getAttribute("aria-haspopup")).to.equal("true");
33+
expect(input.getAttribute("aria-expanded")).to.equal("false");
34+
expect(input.getAttribute("aria-owns")).to.equal("droplist-items");
35+
expect(input.getAttribute("aria-controls")).to.equal("droplist-items");
36+
expect(input.getAttribute("type")).to.equal("search");
37+
expect(input.getAttribute("autocomplete")).to.equal("off");
38+
expect(input.getAttribute("autocorrect")).to.equal("off");
39+
expect(input.getAttribute("autocapitalize")).to.equal("off");
40+
expect(input.getAttribute("spellcheck")).to.equal("false");
3341
done();
3442
});
3543
});

elements/pfe-autocomplete/test/pfe-autocomplete-aria_test.html

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,27 @@
2222
describe('<pfe-autocomplete>', () => {
2323
it('should contain appropriate aria attributes on input box', done => {
2424
const autocompleteElem = fixture('autocomplete-a11y');
25-
25+
2626
flush(() => {
2727
const input = autocompleteElem._input;
2828

2929
expect(input.getAttribute("role")).to.equal("combobox");
3030
expect(input.getAttribute("aria-label")).to.equal("Search");
31-
expect(input.getAttribute("aria-autocomplete")).to.equal("both");
31+
expect(input.getAttribute("aria-autocomplete")).to.equal("list");
3232
expect(input.getAttribute("aria-haspopup")).to.equal("true");
33+
expect(input.getAttribute("aria-expanded")).to.equal("false");
34+
expect(input.getAttribute("aria-owns")).to.equal("droplist-items");
35+
expect(input.getAttribute("aria-controls")).to.equal("droplist-items");
36+
expect(input.getAttribute("type")).to.equal("search");
37+
expect(input.getAttribute("autocomplete")).to.equal("off");
38+
expect(input.getAttribute("autocorrect")).to.equal("off");
39+
expect(input.getAttribute("autocapitalize")).to.equal("off");
40+
expect(input.getAttribute("spellcheck")).to.equal("false");
3341
done();
3442
});
3543
});
36-
});
44+
45+
}); // end describe()
3746

3847
a11ySuite('autocomplete-a11y');
3948
</script>

elements/pfe-autocomplete/test/pfe-autocomplete_test.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,44 @@ describe('<pfe-autocomplete>', () => {
220220
});
221221
});
222222

223+
it('should add aria-selected true on first element on keydown when dropdown is open', done => {
224+
flush(() => {
225+
const input = autocompleteElem._input;
226+
droplistElem.data = ['option 1', 'option 2'];
227+
droplistElem.reflow = true;
228+
droplistElem.open = true;
229+
input.focus();
230+
231+
input.addEventListener('keyup', (e) => {
232+
let option = droplistElem.shadowRoot.querySelector('li:nth-child(1)');
233+
234+
window.setTimeout(() => {
235+
expect(option.hasAttribute("aria-selected")).to.be.eql(true);
236+
done();
237+
}, 1000);
238+
});
239+
240+
MockInteractions.keyUpOn(input, 40);
241+
});
242+
});
243+
244+
it('should set aria-expanded to true when dropdown is open', done => {
245+
flush(() => {
246+
const input = autocompleteElem._input;
247+
input.focus();
248+
249+
input.addEventListener('keyup', (e) => {
250+
window.setTimeout(() => {
251+
// @todo: make sure this is checking if the value of aria-expanded is set to true
252+
expect(input.hasAttribute("aria-expanded")).to.be.eql(true);
253+
done();
254+
}, 1000);
255+
});
256+
257+
MockInteractions.keyUpOn(input, 40);
258+
});
259+
});
260+
223261
it('should update items list on mutation', () => {
224262
droplistElem.data = ['option 1', 'option 2'];
225263
droplistElem.reflow = true;
@@ -336,4 +374,4 @@ describe('<pfe-autocomplete>', () => {
336374
});
337375
});
338376

339-
});
377+
});

0 commit comments

Comments
 (0)