Skip to content

Commit

Permalink
fix(badge): allow more data types for badge content
Browse files Browse the repository at this point in the history
Currently the badge's content is limited to `string` which excludes other legitimate use cases like numbers. These changes turn it into an `any` with the option configure it through a generic parameter since we aren't actually doing anything with the value, apart from forwarding it.

Fixes angular#20326.
  • Loading branch information
crisbeto committed Aug 16, 2020
1 parent 23d3c21 commit 0734e7f
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 16 deletions.
35 changes: 29 additions & 6 deletions src/material/badge/badge.spec.ts
Expand Up @@ -27,15 +27,38 @@ describe('MatBadge', () => {
}));

it('should update the badge based on attribute', () => {
let badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content')!;

expect(badgeContentDebugElement.textContent).toContain('1');
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
expect(badgeElement.textContent).toContain('1');

testComponent.badgeContent = '22';
fixture.detectChanges();
expect(badgeElement.textContent).toContain('22');
});

it('should be able to pass in falsy values to the badge content', () => {
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
expect(badgeElement.textContent).toContain('1');

testComponent.badgeContent = 0;
fixture.detectChanges();
expect(badgeElement.textContent).toContain('0');

badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content')!;
expect(badgeContentDebugElement.textContent).toContain('22');
testComponent.badgeContent = false;
fixture.detectChanges();
expect(badgeElement.textContent).toContain('false');
});

it('should treat null and undefined as empty strings in the badge content', () => {
const badgeElement = badgeNativeElement.querySelector('.mat-badge-content')!;
expect(badgeElement.textContent).toContain('1');

testComponent.badgeContent = null;
fixture.detectChanges();
expect(badgeElement.textContent?.trim()).toBe('');

testComponent.badgeContent = undefined;
fixture.detectChanges();
expect(badgeElement.textContent?.trim()).toBe('');
});

it('should apply class based on color attribute', () => {
Expand Down Expand Up @@ -234,7 +257,7 @@ describe('MatBadge', () => {
class BadgeTestApp {
@ViewChild(MatBadge) badgeInstance: MatBadge;
badgeColor: ThemePalette;
badgeContent: string | number = '1';
badgeContent: any = '1';
badgeDirection = 'above after';
badgeHidden = false;
badgeSize = 'medium';
Expand Down
22 changes: 16 additions & 6 deletions src/material/badge/badge.ts
Expand Up @@ -60,7 +60,8 @@ export type MatBadgeSize = 'small' | 'medium' | 'large';
'[class.mat-badge-disabled]': 'disabled',
},
})
export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges, CanDisable {
export class MatBadge<T = any> extends _MatBadgeMixinBase implements OnDestroy, OnChanges,
CanDisable {
/** Whether the badge has any content. */
_hasContent = false;

Expand Down Expand Up @@ -88,7 +89,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
@Input('matBadgePosition') position: MatBadgePosition = 'above after';

/** The content for the badge */
@Input('matBadge') content: string;
@Input('matBadge') content: T;

/** Message used to describe the decorated element via aria-describedby */
@Input('matBadgeDescription')
Expand Down Expand Up @@ -188,7 +189,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
if (!this._badgeElement) {
this._badgeElement = this._createBadgeElement();
} else {
this._badgeElement.textContent = this.content;
this._badgeElement.textContent = this._stringifyContent();
}
return this._badgeElement;
}
Expand All @@ -203,7 +204,7 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
this._clearExistingBadges(contentClass);
badgeElement.setAttribute('id', `mat-badge-content-${this._id}`);
badgeElement.classList.add(contentClass);
badgeElement.textContent = this.content;
badgeElement.textContent = this._stringifyContent();

if (this._animationMode === 'NoopAnimations') {
badgeElement.classList.add('_mat-animation-noopable');
Expand Down Expand Up @@ -246,11 +247,12 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
/** Adds css theme class given the color to the component host */
private _setColor(colorPalette: ThemePalette) {
if (colorPalette !== this._color) {
const classList = this._elementRef.nativeElement.classList;
if (this._color) {
this._elementRef.nativeElement.classList.remove(`mat-badge-${this._color}`);
classList.remove(`mat-badge-${this._color}`);
}
if (colorPalette) {
this._elementRef.nativeElement.classList.add(`mat-badge-${colorPalette}`);
classList.add(`mat-badge-${colorPalette}`);
}
}
}
Expand All @@ -270,6 +272,14 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges
}
}

/** Gets the string representation of the badge content. */
private _stringifyContent(): string {
// Convert null and undefined to an empty string which is consistent
// with how Angular handles them in inside template interpolations.
const content = this.content;
return content == null ? '' : `${content}`;
}

static ngAcceptInputType_disabled: BooleanInput;
static ngAcceptInputType_hidden: BooleanInput;
static ngAcceptInputType_overlap: BooleanInput;
Expand Down
8 changes: 4 additions & 4 deletions tools/public_api_guard/material/badge.d.ts
@@ -1,9 +1,9 @@
export declare class MatBadge extends _MatBadgeMixinBase implements OnDestroy, OnChanges, CanDisable {
export declare class MatBadge<T = any> extends _MatBadgeMixinBase implements OnDestroy, OnChanges, CanDisable {
_hasContent: boolean;
_id: number;
get color(): ThemePalette;
set color(value: ThemePalette);
content: string;
content: T;
get description(): string;
set description(newDescription: string);
get hidden(): boolean;
Expand All @@ -21,8 +21,8 @@ export declare class MatBadge extends _MatBadgeMixinBase implements OnDestroy, O
static ngAcceptInputType_disabled: BooleanInput;
static ngAcceptInputType_hidden: BooleanInput;
static ngAcceptInputType_overlap: BooleanInput;
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatBadge, "[matBadge]", never, { "disabled": "matBadgeDisabled"; "color": "matBadgeColor"; "overlap": "matBadgeOverlap"; "position": "matBadgePosition"; "content": "matBadge"; "description": "matBadgeDescription"; "size": "matBadgeSize"; "hidden": "matBadgeHidden"; }, {}, never>;
static ɵfac: i0.ɵɵFactoryDef<MatBadge, [null, null, null, null, { optional: true; }]>;
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatBadge<any>, "[matBadge]", never, { "disabled": "matBadgeDisabled"; "color": "matBadgeColor"; "overlap": "matBadgeOverlap"; "position": "matBadgePosition"; "content": "matBadge"; "description": "matBadgeDescription"; "size": "matBadgeSize"; "hidden": "matBadgeHidden"; }, {}, never>;
static ɵfac: i0.ɵɵFactoryDef<MatBadge<any>, [null, null, null, null, { optional: true; }]>;
}

export declare class MatBadgeModule {
Expand Down

0 comments on commit 0734e7f

Please sign in to comment.