From a98c886163312f591c3bc8539aa8686b94459566 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 30 Oct 2024 13:14:13 +0100 Subject: [PATCH] fix(material/button): anchor not handling disabledInteractive correctly (#29938) Fixes that the anchor-based `MatButton` wasn't setting `aria-disabled` when `disabledInteractive` is enabled. (cherry picked from commit 6b3a371686ff90f6f7f5c301ec23797840007620) --- src/material/button/button-base.ts | 9 +++++++-- src/material/button/button.spec.ts | 27 ++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/material/button/button-base.ts b/src/material/button/button-base.ts index c198de65c612..cd800d1ee237 100644 --- a/src/material/button/button-base.ts +++ b/src/material/button/button-base.ts @@ -222,6 +222,8 @@ export class MatButtonBase implements AfterViewInit, OnDestroy { /** Shared host configuration for buttons using the `` tag. */ export const MAT_ANCHOR_HOST = { + // Note that this is basically a noop on anchors, + // but it appears that some internal apps depend on it. '[attr.disabled]': '_getDisabledAttribute()', '[class.mat-mdc-button-disabled]': 'disabled', '[class.mat-mdc-button-disabled-interactive]': 'disabledInteractive', @@ -231,7 +233,7 @@ export const MAT_ANCHOR_HOST = { // consistency with the `mat-button` applied on native buttons where even // though they have an index, they're not tabbable. '[attr.tabindex]': 'disabled && !disabledInteractive ? -1 : tabIndex', - '[attr.aria-disabled]': '_getDisabledAttribute()', + '[attr.aria-disabled]': '_getAriaDisabled()', // MDC automatically applies the primary theme color to the button, but we want to support // an unthemed version. If color is undefined, apply a CSS class that makes it easy to // select and style this "theme". @@ -278,6 +280,9 @@ export class MatAnchorBase extends MatButtonBase implements OnInit, OnDestroy { }; protected override _getAriaDisabled() { - return this.ariaDisabled == null ? this.disabled : this.ariaDisabled; + if (this.ariaDisabled != null) { + return this.ariaDisabled; + } + return this.disabled || null; } } diff --git a/src/material/button/button.spec.ts b/src/material/button/button.spec.ts index a08c854be7d1..184a6e6f64a3 100644 --- a/src/material/button/button.spec.ts +++ b/src/material/button/button.spec.ts @@ -405,13 +405,15 @@ describe('MatButton', () => { describe('interactive disabled buttons', () => { let fixture: ComponentFixture; let button: HTMLButtonElement; + let anchor: HTMLAnchorElement; beforeEach(() => { fixture = TestBed.createComponent(TestApp); fixture.componentInstance.isDisabled = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); - button = fixture.debugElement.query(By.css('button'))!.nativeElement; + button = fixture.nativeElement.querySelector('button'); + anchor = fixture.nativeElement.querySelector('a'); }); it('should set a class when allowing disabled interactivity', () => { @@ -443,6 +445,29 @@ describe('MatButton', () => { expect(button.hasAttribute('disabled')).toBe(false); }); + + it('should set aria-disabled on anchor when disabledInteractive is enabled', () => { + fixture.componentInstance.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + expect(anchor.hasAttribute('aria-disabled')).toBe(false); + expect(anchor.hasAttribute('disabled')).toBe(false); + expect(anchor.classList).not.toContain('mat-mdc-button-disabled-interactive'); + + fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + expect(anchor.getAttribute('aria-disabled')).toBe('true'); + expect(anchor.hasAttribute('disabled')).toBe(true); + expect(anchor.classList).not.toContain('mat-mdc-button-disabled-interactive'); + + fixture.componentInstance.disabledInteractive = true; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + expect(anchor.getAttribute('aria-disabled')).toBe('true'); + expect(anchor.hasAttribute('disabled')).toBe(false); + expect(anchor.classList).toContain('mat-mdc-button-disabled-interactive'); + }); }); });