From 95ef66c8c10f2c9c8ad96c44fa3af167fb3feb21 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 823a0cf7f2e4..8d4881954085 100644 --- a/src/material/button/button-base.ts +++ b/src/material/button/button-base.ts @@ -215,6 +215,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', @@ -224,7 +226,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". @@ -267,6 +269,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 4577eb16526e..eb4599299386 100644 --- a/src/material/button/button.spec.ts +++ b/src/material/button/button.spec.ts @@ -316,13 +316,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', () => { @@ -354,6 +356,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'); + }); }); });