Skip to content

Commit 263f36f

Browse files
Mnickiigavinbarron
andauthored
fix: dismiss login flyout when moving out of the popup (#2637)
use a focus trap to fix menu dismiss issues with shift+tab return focus to login button when closing flyout rewrite implementation to handle shift+tab natively add shift+tab case while at fluent-card --------- Co-authored-by: Gavin Barron <[email protected]>
1 parent 48ea18b commit 263f36f

File tree

1 file changed

+43
-5
lines changed
  • packages/mgt-components/src/components/mgt-login

1 file changed

+43
-5
lines changed

packages/mgt-components/src/components/mgt-login/mgt-login.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ export class MgtLogin extends MgtTemplatedComponent {
320320
const expandedState: boolean | undefined = showSignedInState ? this._isFlyoutOpen : undefined;
321321
return html`
322322
<fluent-button
323+
id="login-button"
323324
aria-expanded="${ifDefined(expandedState)}"
324325
appearance=${appearance}
325326
aria-label="${ifDefined(isSignedIn ? undefined : this.strings.signInLinkSubtitle)}"
@@ -351,14 +352,49 @@ export class MgtLogin extends MgtTemplatedComponent {
351352
light-dismiss
352353
@opened=${this.flyoutOpened}
353354
@closed=${this.flyoutClosed}>
354-
<div slot="flyout">
355-
<fluent-card class="flyout-card">
356-
${this.renderFlyoutContent()}
357-
</fluent-card>
358-
</div>
355+
<fluent-card
356+
slot="flyout"
357+
tabindex="0"
358+
class="flyout-card"
359+
@keydown=${this.onUserKeyDown}
360+
>
361+
${this.renderFlyoutContent()}
362+
</fluent-card>
359363
</mgt-flyout>`;
360364
}
361365

366+
/**
367+
* Tracks tabbing through the flyout (keydown)
368+
*/
369+
private readonly onUserKeyDown = (e: KeyboardEvent): void => {
370+
if (!this.flyout.isOpen) {
371+
return;
372+
}
373+
374+
const el = this.renderRoot.querySelector('.popup-content');
375+
const focusableEls = el.querySelectorAll('ul, fluent-button');
376+
const firstFocusableEl = el.querySelector('#signout-button') || focusableEls[0];
377+
const lastFocusableEl =
378+
el.querySelector('#signin-different-account-button') || focusableEls[focusableEls.length - 1];
379+
380+
if (e.key === 'Tab' && e.shiftKey && firstFocusableEl === e.target) {
381+
e.preventDefault();
382+
(lastFocusableEl as HTMLElement)?.focus();
383+
}
384+
if (e.key === 'Tab' && !e.shiftKey && lastFocusableEl === e.target) {
385+
e.preventDefault();
386+
(firstFocusableEl as HTMLElement)?.focus();
387+
}
388+
if (e.key === 'Escape') {
389+
const loginButton = this.renderRoot.querySelector('#login-button');
390+
(loginButton as HTMLElement)?.focus();
391+
}
392+
const fluentCardEl = this.renderRoot.querySelector('fluent-card');
393+
if (e.shiftKey && e.key === 'Tab' && e.target === fluentCardEl) {
394+
this.hideFlyout();
395+
}
396+
};
397+
362398
/**
363399
* Render the flyout menu content.
364400
*
@@ -435,6 +471,7 @@ export class MgtLogin extends MgtTemplatedComponent {
435471
template ||
436472
html`
437473
<fluent-button
474+
id="signout-button"
438475
appearance="stealth"
439476
size="medium"
440477
class="flyout-command"
@@ -472,6 +509,7 @@ export class MgtLogin extends MgtTemplatedComponent {
472509
return html`
473510
<div class="add-account">
474511
<fluent-button
512+
id="signin-different-account-button"
475513
appearance="stealth"
476514
aria-label="${this.strings.signInWithADifferentAccount}"
477515
@click=${() => void this.login()}>

0 commit comments

Comments
 (0)