From 04dfc525cfe69352ebe36f8edc3dc623725d7e2d Mon Sep 17 00:00:00 2001 From: naaajii Date: Sun, 29 Sep 2024 01:45:28 +0500 Subject: [PATCH 1/2] fix(material/badge): remove the console warning removes the console.warn when badge is used with aria hidden `mat-icon` fixes #27796 --- src/material/badge/badge.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/material/badge/badge.ts b/src/material/badge/badge.ts index 806f2146d192..d0cad1037674 100644 --- a/src/material/badge/badge.ts +++ b/src/material/badge/badge.ts @@ -164,20 +164,6 @@ export class MatBadge implements OnInit, OnDestroy { if (nativeElement.nodeType !== nativeElement.ELEMENT_NODE) { throw Error('matBadge must be attached to an element node.'); } - - // Heads-up for developers to avoid putting matBadge on - // as it is aria-hidden by default docs mention this at: - // https://material.angular.io/components/badge/overview#accessibility - if ( - nativeElement.tagName.toLowerCase() === 'mat-icon' && - nativeElement.getAttribute('aria-hidden') === 'true' - ) { - console.warn( - `Detected a matBadge on an "aria-hidden" "". ` + - `Consider setting aria-hidden="false" in order to surface the information assistive technology.` + - `\n${nativeElement.outerHTML}`, - ); - } } } From 8a979ba7152c29243a84754a65f7b7ddfb79dffa Mon Sep 17 00:00:00 2001 From: naaajii Date: Sun, 29 Sep 2024 03:19:51 +0500 Subject: [PATCH 2/2] fix(material/badge): move inline description element to be next sibling move inline description element to be next sibling where `matBadge` was applied instead of being within the non interactive element and invisible to screen readers --- src/material/badge/badge.spec.ts | 28 ++++++++++++++++++++++++---- src/material/badge/badge.ts | 11 ++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/material/badge/badge.spec.ts b/src/material/badge/badge.spec.ts index 7c5a6ca9da22..3429ebcf2055 100644 --- a/src/material/badge/badge.spec.ts +++ b/src/material/badge/badge.spec.ts @@ -243,16 +243,16 @@ describe('MatBadge', () => { badgeHostNativeElement = badgeHostDebugElement.nativeElement; }); - it('should insert the description inline after the host', () => { + it('should insert description as next sibling for host', () => { testComponent.description.set('Extra info'); fixture.detectChanges(); - const inlineDescription = badgeHostNativeElement.querySelector('.cdk-visually-hidden')!; + const inlineDescription = badgeHostNativeElement.nextSibling!; expect(inlineDescription) .withContext('A visually hidden description element should exist') .toBeDefined(); expect(inlineDescription.textContent) - .withContext('The badge host next sibling should contain its description') + .withContext('The badge next sibling should contain its description') .toBe('Extra info'); testComponent.description.set('Different info'); @@ -263,7 +263,7 @@ describe('MatBadge', () => { .toBe('Different info'); }); - it('should not apply aria-describedby for non-interactive hosts', () => { + it('should not apply aria-describedby for non-interactive host', () => { testComponent.description.set('Extra info'); fixture.detectChanges(); @@ -271,6 +271,26 @@ describe('MatBadge', () => { .withContext('Non-interactive hosts should not have aria-describedby') .toBeFalse(); }); + + it('should not insert description as next sibling if description is not provided', () => { + fixture.detectChanges(); + + expect(badgeHostNativeElement.nextSibling).toBeFalsy(); + }); + + it('should not create multiple description elements if description changes', () => { + testComponent.description.set('one'); + fixture.detectChanges(); + + let siblings = fixture.nativeElement.querySelectorAll('.cdk-visually-hidden'); + expect(siblings.length).toBe(1); + + testComponent.description.set('two'); + fixture.detectChanges(); + + siblings = fixture.nativeElement.querySelectorAll('.cdk-visually-hidden'); + expect(siblings.length).toBe(1); + }); }); }); diff --git a/src/material/badge/badge.ts b/src/material/badge/badge.ts index d0cad1037674..89c55c0575cd 100644 --- a/src/material/badge/badge.ts +++ b/src/material/badge/badge.ts @@ -296,6 +296,9 @@ export class MatBadge implements OnInit, OnDestroy { } private _updateInlineDescription() { + // We do not need to need empty description element for non interactive elements. + if (!this.description) return; + // Create the inline description element if it doesn't exist if (!this._inlineBadgeDescription) { this._inlineBadgeDescription = this._document.createElement('span'); @@ -303,7 +306,13 @@ export class MatBadge implements OnInit, OnDestroy { } this._inlineBadgeDescription.textContent = this.description; - this._badgeElement?.appendChild(this._inlineBadgeDescription); + // We want to add the inline description element right after the not interactive element that + // badge is on therefore we can't use appendChild here as they will keep getting stacked in + // last. + this._elementRef.nativeElement.parentNode?.insertBefore( + this._inlineBadgeDescription, + this._elementRef.nativeElement.nextSibling, + ); } private _removeInlineDescription() {