Skip to content

Commit 75f73ae

Browse files
authored
fix(badge): allow more data types for badge content (#20331)
Currently the badge's content is limited to `string` which excludes other legitimate use cases like numbers. These changes turn it into an `string | number | undefined | null` since we aren't actually doing anything with the value, apart from forwarding it. Fixes #20326.
1 parent 22eca83 commit 75f73ae

File tree

3 files changed

+40
-12
lines changed

3 files changed

+40
-12
lines changed

src/material/badge/badge.spec.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,34 @@ describe('MatBadge', () => {
2727
}));
2828

2929
it('should update the badge based on attribute', () => {
30-
let badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content')!;
31-
32-
expect(badgeContentDebugElement.textContent).toContain('1');
30+
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
31+
expect(badgeElement.textContent).toContain('1');
3332

3433
testComponent.badgeContent = '22';
3534
fixture.detectChanges();
35+
expect(badgeElement.textContent).toContain('22');
36+
});
37+
38+
it('should be able to pass in falsy values to the badge content', () => {
39+
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
40+
expect(badgeElement.textContent).toContain('1');
41+
42+
testComponent.badgeContent = 0;
43+
fixture.detectChanges();
44+
expect(badgeElement.textContent).toContain('0');
45+
});
46+
47+
it('should treat null and undefined as empty strings in the badge content', () => {
48+
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
49+
expect(badgeElement.textContent).toContain('1');
3650

37-
badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content')!;
38-
expect(badgeContentDebugElement.textContent).toContain('22');
51+
testComponent.badgeContent = null;
52+
fixture.detectChanges();
53+
expect(badgeElement.textContent?.trim()).toBe('');
54+
55+
testComponent.badgeContent = undefined;
56+
fixture.detectChanges();
57+
expect(badgeElement.textContent?.trim()).toBe('');
3958
});
4059

4160
it('should apply class based on color attribute', () => {
@@ -234,7 +253,7 @@ describe('MatBadge', () => {
234253
class BadgeTestApp {
235254
@ViewChild(MatBadge) badgeInstance: MatBadge;
236255
badgeColor: ThemePalette;
237-
badgeContent: string | number = '1';
256+
badgeContent: string | number | undefined | null = '1';
238257
badgeDirection = 'above after';
239258
badgeHidden = false;
240259
badgeSize = 'medium';

src/material/badge/badge.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
8888
@Input('matBadgePosition') position: MatBadgePosition = 'above after';
8989

9090
/** The content for the badge */
91-
@Input('matBadge') content: string;
91+
@Input('matBadge') content: string | number | undefined | null;
9292

9393
/** Message used to describe the decorated element via aria-describedby */
9494
@Input('matBadgeDescription')
@@ -188,7 +188,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
188188
if (!this._badgeElement) {
189189
this._badgeElement = this._createBadgeElement();
190190
} else {
191-
this._badgeElement.textContent = this.content;
191+
this._badgeElement.textContent = this._stringifyContent();
192192
}
193193
return this._badgeElement;
194194
}
@@ -203,7 +203,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
203203
this._clearExistingBadges(contentClass);
204204
badgeElement.setAttribute('id', `mat-badge-content-${this._id}`);
205205
badgeElement.classList.add(contentClass);
206-
badgeElement.textContent = this.content;
206+
badgeElement.textContent = this._stringifyContent();
207207

208208
if (this._animationMode === 'NoopAnimations') {
209209
badgeElement.classList.add('_mat-animation-noopable');
@@ -246,11 +246,12 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
246246
/** Adds css theme class given the color to the component host */
247247
private _setColor(colorPalette: ThemePalette) {
248248
if (colorPalette !== this._color) {
249+
const classList = this._elementRef.nativeElement.classList;
249250
if (this._color) {
250-
this._elementRef.nativeElement.classList.remove(`mat-badge-${this._color}`);
251+
classList.remove(`mat-badge-${this._color}`);
251252
}
252253
if (colorPalette) {
253-
this._elementRef.nativeElement.classList.add(`mat-badge-${colorPalette}`);
254+
classList.add(`mat-badge-${colorPalette}`);
254255
}
255256
}
256257
}
@@ -270,6 +271,14 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
270271
}
271272
}
272273

274+
/** Gets the string representation of the badge content. */
275+
private _stringifyContent(): string {
276+
// Convert null and undefined to an empty string which is consistent
277+
// with how Angular handles them in inside template interpolations.
278+
const content = this.content;
279+
return content == null ? '' : `${content}`;
280+
}
281+
273282
static ngAcceptInputType_disabled: BooleanInput;
274283
static ngAcceptInputType_hidden: BooleanInput;
275284
static ngAcceptInputType_overlap: BooleanInput;

tools/public_api_guard/material/badge.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export declare class MatBadge extends _MatBadgeMixinBase implements OnDestroy, O
33
_id: number;
44
get color(): ThemePalette;
55
set color(value: ThemePalette);
6-
content: string;
6+
content: string | number | undefined | null;
77
get description(): string;
88
set description(newDescription: string);
99
get hidden(): boolean;

0 commit comments

Comments
 (0)