Skip to content

Commit 5260ce0

Browse files
gavinbarronmusale
andauthored
fix: announce teams channel results when you type (#2561)
Removes lostFocus guards Adds aria-atomic and aria-relevant attributes to cater for changing values in the dropdown Adding aria-live and other roles to no results template to ensure annoucing Shortened people picker placeholder to ensure it doesn't overflow with increased text spacing --------- Signed-off-by: Martin Musale <[email protected]> Co-authored-by: Martin Musale <[email protected]>
1 parent bd17195 commit 5260ce0

File tree

3 files changed

+41
-24
lines changed

3 files changed

+41
-24
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
export const strings = {
9-
inputPlaceholderText: 'Start typing a name',
9+
inputPlaceholderText: 'Search for a name',
1010
maxSelectionsPlaceHolder: 'Max contacts added',
1111
maxSelectionsAriaLabel: 'Maximum contact selections reached',
1212
noResultsFound: "We didn't find any matches.",

packages/mgt-components/src/components/mgt-teams-channel-picker/mgt-teams-channel-picker.ts

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,12 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
234234
return this._items;
235235
}
236236

237+
private get _inputWrapper(): HTMLElement {
238+
return this.renderRoot.querySelector<HTMLElement>('fluent-text-field');
239+
}
237240
// User input in search
238241
private get _input(): HTMLInputElement {
239-
const wrapper = this.renderRoot.querySelector<HTMLElement>('fluent-text-field');
242+
const wrapper: HTMLElement = this._inputWrapper;
240243
const input = wrapper.shadowRoot.querySelector<HTMLInputElement>('input');
241244
return input;
242245
}
@@ -252,7 +255,6 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
252255

253256
// determines loading state
254257
@state() private _isDropdownVisible: boolean;
255-
@state() private _isFocused: boolean;
256258

257259
constructor() {
258260
super();
@@ -356,19 +358,22 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
356358
autocomplete="off"
357359
appearance="outline"
358360
id="teams-channel-picker-input"
359-
aria-label="Select a channel"
360-
placeholder="${this._selectedItemState ? '' : this.strings.inputPlaceholderText} "
361-
label="teams-channel-picker-input"
362361
role="combobox"
362+
placeholder="${this._selectedItemState ? '' : this.strings.inputPlaceholderText} "
363+
aria-label=${this.strings.inputPlaceholderText}
363364
aria-expanded="${this._isDropdownVisible}"
365+
label="teams-channel-picker-input"
366+
@focus=${this.handleFocus}
367+
@keyup=${this.handleInputChanged}
364368
@click=${this.handleInputClick}
365369
@keydown=${this.handleInputKeydown}
366-
@keyup=${this.handleInputChanged}
367370
>
368371
<div tabindex="0" slot="start" style="width: max-content;">${this.renderSelected()}</div>
369372
<div tabindex="0" slot="end">${this.renderChevrons()}${this.renderCloseButton()}</div>
370373
</fluent-text-field>
371-
<fluent-card class=${classMap(dropdownClasses)}>
374+
<fluent-card
375+
class=${classMap(dropdownClasses)}
376+
>
372377
${this.renderDropdown()}
373378
</fluent-card>
374379
</div>`
@@ -418,6 +423,7 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
418423
icon = html`<img
419424
class="team-photo"
420425
alt="${this._selectedItemState.parent.item.displayName}"
426+
role="img"
421427
src=${src} />`;
422428
}
423429

@@ -600,6 +606,11 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
600606
<fluent-tree-view
601607
class="tree-view"
602608
dir=${this.direction}
609+
aria-live="polite"
610+
aria-relevant="all"
611+
aria-atomic="true"
612+
aria-label=${this.strings.teamsChannels}
613+
aria-orientation="horizontal"
603614
@keydown=${this.onKeydownTreeView}>
604615
${repeat(
605616
items,
@@ -608,7 +619,7 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
608619
if (obj.channels) {
609620
icon = html`<img
610621
class="team-photo"
611-
alt="${obj.item.displayName}"
622+
alt="${this.strings.photoFor} ${obj.item.displayName}"
612623
src=${
613624
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
614625
this.teamsPhotos[obj.item.id]?.photo
@@ -617,8 +628,8 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
617628
return html`
618629
<fluent-tree-item
619630
?expanded=${obj?.isExpanded}
620-
@click=${(e: Event) => this.handleTeamTreeItemClick(e)}>
621-
${icon}${obj.item.displayName}
631+
@click=${this.handleTeamTreeItemClick}>
632+
<span slot="start">${icon}</span>${obj.item.displayName}
622633
${repeat(
623634
obj?.channels,
624635
(channels: ChannelPickerItemState) => channels.item,
@@ -667,8 +678,11 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
667678
<div class="message-parent">
668679
<div
669680
label="search-error-text"
670-
aria-label="We didn't find any matches."
671-
class="search-error-text">
681+
aria-live="polite"
682+
aria-relevant="all"
683+
aria-atomic="true"
684+
class="search-error-text"
685+
>
672686
${this.strings.noResultsFound}
673687
</div>
674688
</div>
@@ -774,7 +788,7 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
774788
}
775789
}
776790

777-
private handleTeamTreeItemClick(event: Event) {
791+
handleTeamTreeItemClick = (event: Event) => {
778792
event.preventDefault();
779793
event.stopImmediatePropagation();
780794
const element = event.target as HTMLElement;
@@ -792,7 +806,7 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
792806
element.setAttribute('selected', 'true');
793807
}
794808
}
795-
}
809+
};
796810

797811
handleInputChanged = (e: KeyboardEvent) => {
798812
const target = e.target as HTMLInputElement;
@@ -913,7 +927,6 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
913927
};
914928

915929
private readonly gainedFocus = () => {
916-
this._isFocused = true;
917930
const input = this._input;
918931
if (input) {
919932
input.focus();
@@ -922,18 +935,15 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
922935
this._isDropdownVisible = true;
923936
this.toggleChevron();
924937
this.resetFocusState();
938+
this.requestUpdate();
925939
};
926940

927941
private readonly lostFocus = () => {
928-
const input = this._input;
929-
if (input) {
930-
input.value = this._inputValue = '';
931-
input.textContent = '';
932-
const wrapper = this.renderRoot.querySelector<HTMLInputElement>('fluent-text-field');
933-
wrapper.value = '';
934-
}
942+
this._input.value = this._inputValue = '';
943+
this._input.textContent = '';
944+
const wrapper = this._inputWrapper as HTMLInputElement;
945+
wrapper.value = '';
935946

936-
this._isFocused = false;
937947
this._isDropdownVisible = false;
938948
this.filterList();
939949
this.toggleChevron();
@@ -944,6 +954,11 @@ export class MgtTeamsChannelPicker extends MgtTemplatedComponent {
944954
}
945955
};
946956

957+
handleFocus = () => {
958+
this.lostFocus();
959+
this.gainedFocus();
960+
};
961+
947962
private selectChannel(item: ChannelPickerItemState) {
948963
if (item && this._selectedItemState !== item) {
949964
this._input.setAttribute('disabled', 'true');

packages/mgt-components/src/components/mgt-teams-channel-picker/strings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ export const strings = {
1616
inputPlaceholderText: 'Select a channel',
1717
noResultsFound: "We didn't find any matches.",
1818
loadingMessage: 'Loading...',
19+
photoFor: 'Teams photo for',
20+
teamsChannels: 'Teams and channels results',
1921
closeButtonAriaLabel: 'remove the selected channel'
2022
};

0 commit comments

Comments
 (0)