Skip to content

Commit 6488705

Browse files
authored
fix: people-picker set focus on list navigation (#2219)
sets focus on the currently selected item when using keyboard to chose a person uses focus styling fixes an issue which would cause the page to scroll when navigating in the suggestions list if the page had scroll space below the end of the suggestions list
1 parent 274af2c commit 6488705

File tree

3 files changed

+37
-25
lines changed

3 files changed

+37
-25
lines changed

packages/mgt-components/src/components/mgt-people-picker/mgt-people-picker.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ mgt-people-picker .root {
126126
color: $dropdown-item__text__color--hover;
127127
}
128128
}
129-
&.focused {
129+
130+
&:focus,
131+
&:focus-within {
130132
background-color: $dropdown-item__background-color--hover;
131133
.people-person-text-area {
132134
color: $dropdown-item__text__color--hover;

packages/mgt-components/src/components/mgt-people-picker/mgt-people-picker.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,8 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
572572
private defaultPeople: IDynamicPerson[];
573573

574574
// tracking of user arrow key input for selection
575-
private _arrowSelectionCount: number = -1;
575+
@state() private _arrowSelectionCount = -1;
576+
576577
// List of people requested if group property is provided
577578
private _groupPeople: IDynamicPerson[];
578579
private _debouncedSearch: { (): void; (): void };
@@ -852,13 +853,18 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
852853
*/
853854
protected renderFlyout(anchor: TemplateResult): TemplateResult {
854855
return html`
855-
<mgt-flyout light-dismiss class="flyout">
856-
${anchor}
857-
<div slot="flyout" class="flyout-root" @wheel=${(e: WheelEvent) => this.handleSectionScroll(e)}>
858-
${this.renderFlyoutContent()}
859-
</div>
860-
</mgt-flyout>
861-
`;
856+
<mgt-flyout light-dismiss class="flyout">
857+
${anchor}
858+
<div
859+
slot="flyout"
860+
class="flyout-root"
861+
@wheel=${(e: WheelEvent) => this.handleSectionScroll(e)}
862+
@keydown=${(e: KeyboardEvent) => this.onUserKeyDown(e)}
863+
>
864+
${this.renderFlyoutContent()}
865+
</div>
866+
</mgt-flyout>
867+
`;
862868
}
863869

864870
/**
@@ -1310,6 +1316,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
13101316
if (this.input) {
13111317
this.input.setAttribute('aria-expanded', 'true');
13121318
}
1319+
this._arrowSelectionCount = -1;
13131320
}
13141321

13151322
/**
@@ -1388,13 +1395,13 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
13881395
private gainedFocus() {
13891396
this.clearHighlighted();
13901397
this._isFocused = true;
1391-
this.loadState();
1398+
void this.loadState();
1399+
this.showFlyout();
13921400
}
13931401

13941402
// handle input blur
13951403
private lostFocus() {
13961404
this._isFocused = false;
1397-
this._arrowSelectionCount = -1;
13981405
if (this.input) {
13991406
this.input.setAttribute('aria-expanded', 'false');
14001407
this.input.setAttribute('aria-activedescendant', '');
@@ -1403,9 +1410,9 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
14031410
const peopleList = this.renderRoot.querySelector('.people-list');
14041411

14051412
if (peopleList) {
1406-
for (let i = 0; i < peopleList.children.length; i++) {
1407-
peopleList.children[i].classList.remove('focused');
1408-
peopleList.children[i].setAttribute('aria-selected', 'false');
1413+
for (const el of peopleList.children) {
1414+
el.classList.remove('focused');
1415+
el.setAttribute('aria-selected', 'false');
14091416
}
14101417
}
14111418

@@ -1627,9 +1634,8 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
16271634

16281635
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
16291636
this.handleArrowSelection(event);
1630-
if (input.value.length > 0) {
1631-
event.preventDefault();
1632-
}
1637+
// prevent page from scrolling
1638+
event.preventDefault();
16331639
}
16341640

16351641
if (event.code === 'Enter') {
@@ -1863,28 +1869,31 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
18631869
if (this._arrowSelectionCount === -1) {
18641870
this._arrowSelectionCount = 0;
18651871
} else {
1866-
this._arrowSelectionCount = (this._arrowSelectionCount + 1) % peopleList.children.length;
1872+
this._arrowSelectionCount =
1873+
(this._arrowSelectionCount + 1 + peopleList.children.length) % peopleList.children.length;
18671874
}
18681875
}
18691876
}
18701877

18711878
// reset background color
18721879
// reset aria-selected to false
1873-
// tslint:disable-next-line: prefer-for-of
1874-
for (let i = 0; i < peopleList.children.length; i++) {
1875-
peopleList.children[i].classList.remove('focused');
1876-
peopleList.children[i].setAttribute('aria-selected', 'false');
1880+
for (const person of peopleList?.children) {
1881+
const p = person as HTMLElement;
1882+
p.setAttribute('aria-selected', 'false');
1883+
p.removeAttribute('tabindex');
1884+
p.blur();
18771885
}
18781886

18791887
// set selected background
18801888
// set aria-selected to true
18811889
const focusedItem = peopleList.children[this._arrowSelectionCount] as HTMLElement;
18821890

18831891
if (focusedItem) {
1884-
focusedItem.classList.add('focused');
1892+
focusedItem.setAttribute('tabindex', '0');
1893+
focusedItem.focus();
18851894
focusedItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
18861895
focusedItem.setAttribute('aria-selected', 'true');
1887-
this.input.setAttribute('aria-activedescendant', peopleList.children[this._arrowSelectionCount].id);
1896+
this.input.setAttribute('aria-activedescendant', focusedItem.id);
18881897
}
18891898
}
18901899
}

packages/mgt-components/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"outDir": "dist/es6",
55
"sourceRoot": "src",
66
"rootDir": "src",
7-
"composite": true
7+
"composite": true,
8+
"lib": ["dom", "es5", "DOM.Iterable"]
89
},
910
"references": [{ "path": "../mgt-element" }],
1011
"include": ["src"],

0 commit comments

Comments
 (0)