Skip to content

Commit

Permalink
fix(material/expansion-panel): Prevent focus issue during collapse an…
Browse files Browse the repository at this point in the history
…imation

This commit addresses an issue where mat-expansion-panels were breaking when a user attempted to quickly close and tab away from a panel after closing it. The issue occurred because the tab key was being pressed between the collapsing animation, causing the panel body to remain visible and receive focus.

To resolve this, we manually set the panel's visibility to hidden when the tab key is pressed during the collapsing animation. This ensures that the next element in the tab order receives focus as expected.

Fixes angular#27430.
  • Loading branch information
kharazian committed Aug 19, 2023
1 parent 5fca612 commit 8a302e3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 4 deletions.
20 changes: 16 additions & 4 deletions src/material/expansion/expansion-panel-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {FocusableOption, FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
import {ENTER, hasModifierKey, SPACE} from '@angular/cdk/keycodes';
import {ENTER, hasModifierKey, SPACE, TAB} from '@angular/cdk/keycodes';
import {
AfterViewInit,
Attribute,
Expand Down Expand Up @@ -179,6 +179,13 @@ export class MatExpansionPanelHeader
return null;
}

/** Handle default for keydown event. */
private _handleDefaultKeydown(event: KeyboardEvent) {
if (this.panel.accordion) {
this.panel.accordion._handleHeaderKeydown(event);
}
}

/** Handle keydown event calling to toggle() if appropriate. */
_keydown(event: KeyboardEvent) {
switch (event.keyCode) {
Expand All @@ -191,10 +198,15 @@ export class MatExpansionPanelHeader
}

break;
default:
if (this.panel.accordion) {
this.panel.accordion._handleHeaderKeydown(event);
case TAB:
if (this.panel.collapsingAnimation) {
this.panel._body.nativeElement.setAttribute('style', 'visibility: hidden');
}
this._handleDefaultKeydown(event);

break;
default:
this._handleDefaultKeydown(event);

return;
}
Expand Down
1 change: 1 addition & 0 deletions src/material/expansion/expansion-panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<div class="mat-expansion-panel-content"
role="region"
[@bodyExpansion]="_getExpandedState()"
(@bodyExpansion.start)="_bodyAnimationStart($event)"
(@bodyExpansion.done)="_bodyAnimationDone.next($event)"
[attr.aria-labelledby]="_headerId"
[id]="id"
Expand Down
14 changes: 14 additions & 0 deletions src/material/expansion/expansion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ export class MatExpansionPanel
private _document: Document;
private _hideToggle = false;
private _togglePosition: MatAccordionTogglePosition;
private _collapsingAnimation = false;

/** whether the element is currently undergoing the collapsing animation */
get collapsingAnimation(): boolean {
return this._collapsingAnimation;
}

/** Whether the toggle indicator should be hidden. */
@Input()
Expand Down Expand Up @@ -174,6 +180,7 @@ export class MatExpansionPanel
}),
)
.subscribe(event => {
this._collapsingAnimation = false;
if (event.fromState !== 'void') {
if (event.toState === 'expanded') {
this.afterExpand.emit();
Expand All @@ -188,6 +195,13 @@ export class MatExpansionPanel
}
}

/** Check and determine if the element is currently undergoing a collapsing animation. */
_bodyAnimationStart(event: AnimationEvent) {
if (event.toState === 'collapsed') {
this._collapsingAnimation = true;
}
}

/** Determines whether the expansion panel should have spacing between it and its siblings. */
_hasSpacing(): boolean {
if (this.accordion) {
Expand Down

0 comments on commit 8a302e3

Please sign in to comment.