Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions goldens/material/chips/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const MAT_CHIP_AVATAR: InjectionToken<unknown>;
// @public
export const MAT_CHIP_EDIT: InjectionToken<unknown>;

// @public
export const MAT_CHIP_LEADING_ICON: InjectionToken<unknown>;

// @public
export const MAT_CHIP_LISTBOX_CONTROL_VALUE_ACCESSOR: any;

Expand All @@ -56,13 +59,15 @@ export const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken<MatChipsDefaultOptions>;
// @public
export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck, OnDestroy {
constructor();
protected _allAvatars: QueryList<MatChipAvatar>;
protected _allEditIcons: QueryList<MatChipEdit>;
protected _allLeadingIcons: QueryList<MatChipAvatar>;
protected _allLeadingIcons: QueryList<MatChipLeadingIcon>;
protected _allRemoveIcons: QueryList<MatChipRemove>;
protected _allTrailingIcons: QueryList<MatChipTrailingIcon>;
_animationsDisabled: boolean;
ariaDescription: string | null;
ariaLabel: string | null;
avatar: MatChipAvatar;
protected basicChipAttrName: string;
// (undocumented)
_changeDetectorRef: ChangeDetectorRef;
Expand All @@ -87,14 +92,15 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
// (undocumented)
_hasFocus(): boolean;
_hasInteractiveActions(): boolean;
_hasLeadingGraphic(): boolean;
_hasTrailingIcon(): boolean;
highlighted: boolean;
id: string;
// (undocumented)
protected _injector: Injector;
_isBasicChip: boolean;
_isRippleDisabled(): boolean;
leadingIcon: MatChipAvatar;
leadingIcon: MatChipLeadingIcon;
// (undocumented)
static ngAcceptInputType_disabled: unknown;
// (undocumented)
Expand Down Expand Up @@ -129,7 +135,7 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
// (undocumented)
protected _value: any;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatChip, "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", ["matChip"], { "role": { "alias": "role"; "required": false; }; "id": { "alias": "id"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaDescription": { "alias": "aria-description"; "required": false; }; "value": { "alias": "value"; "required": false; }; "color": { "alias": "color"; "required": false; }; "removable": { "alias": "removable"; "required": false; }; "highlighted": { "alias": "highlighted"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "removed": "removed"; "destroyed": "destroyed"; }, ["leadingIcon", "editIcon", "trailingIcon", "removeIcon", "_allLeadingIcons", "_allTrailingIcons", "_allEditIcons", "_allRemoveIcons"], ["mat-chip-avatar, [matChipAvatar]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MatChip, "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", ["matChip"], { "role": { "alias": "role"; "required": false; }; "id": { "alias": "id"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaDescription": { "alias": "aria-description"; "required": false; }; "value": { "alias": "value"; "required": false; }; "color": { "alias": "color"; "required": false; }; "removable": { "alias": "removable"; "required": false; }; "highlighted": { "alias": "highlighted"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "removed": "removed"; "destroyed": "destroyed"; }, ["avatar", "leadingIcon", "editIcon", "trailingIcon", "removeIcon", "_allAvatars", "_allLeadingIcons", "_allTrailingIcons", "_allEditIcons", "_allRemoveIcons"], ["mat-chip-avatar, [matChipAvatar], mat-chip-leading-icon, [matChipLeadingIcon]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatChip, never>;
}
Expand Down Expand Up @@ -325,6 +331,14 @@ export interface MatChipInputEvent {
value: string;
}

// @public
export class MatChipLeadingIcon {
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<MatChipLeadingIcon, "mat-chip-leading-icon, [matChipLeadingIcon]", never, {}, {}, never, never, true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatChipLeadingIcon, never>;
}

// @public
export class MatChipListbox extends MatChipSet implements AfterContentInit, OnDestroy, ControlValueAccessor {
ariaOrientation: 'horizontal' | 'vertical';
Expand Down Expand Up @@ -418,7 +432,7 @@ export class MatChipOption extends MatChip implements OnInit {
_setSelectedState(isSelected: boolean, isUserInput: boolean, emitEvent: boolean): void;
toggleSelected(isUserInput?: boolean): boolean;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatChipOption, "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", never, { "selectable": { "alias": "selectable"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; }, { "selectionChange": "selectionChange"; }, never, ["mat-chip-avatar, [matChipAvatar]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MatChipOption, "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", never, { "selectable": { "alias": "selectable"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; }, { "selectionChange": "selectionChange"; }, never, ["mat-chip-avatar, [matChipAvatar], mat-chip-leading-icon, [matChipLeadingIcon]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatChipOption, never>;
}
Expand Down Expand Up @@ -469,7 +483,7 @@ export class MatChipRow extends MatChip implements AfterViewInit, OnDestroy {
// (undocumented)
ngOnDestroy(): void;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatChipRow, "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", never, { "editable": { "alias": "editable"; "required": false; }; }, { "edited": "edited"; }, ["contentEditInput"], ["[matChipEdit]", "mat-chip-avatar, [matChipAvatar]", "[matChipEditInput]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MatChipRow, "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", never, { "editable": { "alias": "editable"; "required": false; }; }, { "edited": "edited"; }, ["contentEditInput"], ["[matChipEdit]", "mat-chip-avatar, [matChipAvatar], mat-chip-leading-icon, [matChipLeadingIcon]", "[matChipEditInput]", "*", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatChipRow, never>;
}
Expand Down Expand Up @@ -547,7 +561,7 @@ export class MatChipsModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<MatChipsModule>;
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<MatChipsModule, never, [typeof MatRippleModule, typeof MatChipAction, typeof MatChip, typeof MatChipAvatar, typeof MatChipEdit, typeof MatChipEditInput, typeof MatChipGrid, typeof MatChipInput, typeof MatChipListbox, typeof MatChipOption, typeof MatChipRemove, typeof MatChipRow, typeof MatChipSet, typeof MatChipTrailingIcon], [typeof i2.BidiModule, typeof MatChip, typeof MatChipAvatar, typeof MatChipEdit, typeof MatChipEditInput, typeof MatChipGrid, typeof MatChipInput, typeof MatChipListbox, typeof MatChipOption, typeof MatChipRemove, typeof MatChipRow, typeof MatChipSet, typeof MatChipTrailingIcon]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<MatChipsModule, never, [typeof MatRippleModule, typeof MatChipAction, typeof MatChip, typeof MatChipAvatar, typeof MatChipEdit, typeof MatChipEditInput, typeof MatChipGrid, typeof MatChipInput, typeof MatChipLeadingIcon, typeof MatChipListbox, typeof MatChipOption, typeof MatChipRemove, typeof MatChipRow, typeof MatChipSet, typeof MatChipTrailingIcon], [typeof i2.BidiModule, typeof MatChip, typeof MatChipAvatar, typeof MatChipEdit, typeof MatChipEditInput, typeof MatChipGrid, typeof MatChipInput, typeof MatChipLeadingIcon, typeof MatChipListbox, typeof MatChipOption, typeof MatChipRemove, typeof MatChipRow, typeof MatChipSet, typeof MatChipTrailingIcon]>;
}

// @public
Expand Down
12 changes: 12 additions & 0 deletions goldens/material/chips/testing/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export interface ChipInputHarnessFilters extends BaseHarnessFilters {
value?: string | RegExp;
}

// @public (undocumented)
export interface ChipLeadingIconHarnessFilters extends BaseHarnessFilters {
}

// @public (undocumented)
export interface ChipListboxHarnessFilters extends BaseHarnessFilters {
disabled?: boolean;
Expand Down Expand Up @@ -103,6 +107,7 @@ export class MatChipGridHarness extends ComponentHarness {
export class MatChipHarness extends ContentContainerComponentHarness {
geEditButton(filter?: ChipEditHarnessFilters): Promise<MatChipEditHarness>;
getAvatar(filter?: ChipAvatarHarnessFilters): Promise<MatChipAvatarHarness | null>;
getLeadingIcon(filter?: ChipLeadingIconHarnessFilters): Promise<MatChipLeadingIconHarness | null>;
getRemoveButton(filter?: ChipRemoveHarnessFilters): Promise<MatChipRemoveHarness>;
getText(): Promise<string>;
// (undocumented)
Expand Down Expand Up @@ -130,6 +135,13 @@ export class MatChipInputHarness extends ComponentHarness {
static with<T extends MatChipInputHarness>(this: ComponentHarnessConstructor<T>, options?: ChipInputHarnessFilters): HarnessPredicate<T>;
}

// @public
export class MatChipLeadingIconHarness extends ComponentHarness {
// (undocumented)
static hostSelector: string;
static with<T extends MatChipLeadingIconHarness>(this: ComponentHarnessConstructor<T>, options?: ChipLeadingIconHarnessFilters): HarnessPredicate<T>;
}

// @public
export class MatChipListboxHarness extends ComponentHarness {
getChips(filter?: ChipOptionHarnessFilters): Promise<MatChipOptionHarness[]>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<mat-chip-set aria-label="Chip examples">
<mat-chip>
<mat-icon matChipLeadingIcon>flight</mat-icon>
Flight
</mat-chip>
<mat-chip>
<mat-icon matChipLeadingIcon>hotel</mat-icon>
Hotel
<mat-icon matChipTrailingIcon>star</mat-icon>
</mat-chip>
<mat-chip>
<mat-icon matChipLeadingIcon>music_note</mat-icon>
Music
<button matChipRemove aria-label="Remove music">
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
</mat-chip-set>

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Component} from '@angular/core';
import {MatChipsModule} from '@angular/material/chips';
import {MatIconModule} from '@angular/material/icon';

/**
* @title Chips with icons
*/
@Component({
selector: 'chips-icons-example',
templateUrl: 'chips-icons-example.html',
styleUrl: 'chips-icons-example.css',
imports: [MatChipsModule, MatIconModule],
})
export class ChipsIconsExample {}
1 change: 1 addition & 0 deletions src/components-examples/material/chips/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export {ChipsReactiveFormExample} from './chips-reactive-form/chips-reactive-for
export {ChipsSignalFormExample} from './chips-signal-form/chips-signal-form-example';
export {ChipsTemplateFormExample} from './chips-template-form/chips-template-form-example';
export {ChipsAvatarExample} from './chips-avatar/chips-avatar-example';
export {ChipsIconsExample} from './chips-icons/chips-icons-example';
19 changes: 19 additions & 0 deletions src/dev-app/chips/chips-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ <h4>With avatar, icons, and color</h4>
<mat-icon matChipTrailingIcon>star_border</mat-icon>
</mat-chip>

<mat-chip>
<mat-icon matChipLeadingIcon>flight</mat-icon>
Flight
</mat-chip>

<mat-chip>
<mat-icon matChipLeadingIcon>hotel</mat-icon>
Hotel
<mat-icon matChipTrailingIcon>star</mat-icon>
</mat-chip>

<mat-chip>
<mat-icon matChipLeadingIcon>music_note</mat-icon>
Music
<button matChipRemove aria-label="Remove music">
<mat-icon>close</mat-icon>
</button>
</mat-chip>

</mat-chip-set>

<h4>With Events</h4>
Expand Down
19 changes: 18 additions & 1 deletion src/material/chips/chip-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
import {ENTER, SPACE} from '@angular/cdk/keycodes';
import {Directive} from '@angular/core';
import {MatChipAction, MatChipContent} from './chip-action';
import {MAT_CHIP_AVATAR, MAT_CHIP_EDIT, MAT_CHIP_REMOVE, MAT_CHIP_TRAILING_ICON} from './tokens';
import {
MAT_CHIP_AVATAR,
MAT_CHIP_EDIT,
MAT_CHIP_LEADING_ICON,
MAT_CHIP_REMOVE,
MAT_CHIP_TRAILING_ICON,
} from './tokens';

/** Avatar image within a chip. */
@Directive({
Expand All @@ -22,6 +28,17 @@ import {MAT_CHIP_AVATAR, MAT_CHIP_EDIT, MAT_CHIP_REMOVE, MAT_CHIP_TRAILING_ICON}
})
export class MatChipAvatar {}

/** Non-interactive leading icon in a chip. */
@Directive({
selector: 'mat-chip-leading-icon, [matChipLeadingIcon]',
host: {
'class': 'mat-mdc-chip-leading-icon mdc-evolution-chip__icon mdc-evolution-chip__icon--primary',
'aria-hidden': 'true',
},
providers: [{provide: MAT_CHIP_LEADING_ICON, useExisting: MatChipLeadingIcon}],
})
export class MatChipLeadingIcon {}

/** Non-interactive trailing icon in a chip. */
@Directive({
selector: 'mat-chip-trailing-icon, [matChipTrailingIcon]',
Expand Down
2 changes: 1 addition & 1 deletion src/material/chips/chip-option.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
role="option">
@if (_hasLeadingGraphic()) {
<span class="mdc-evolution-chip__graphic mat-mdc-chip-graphic">
<ng-content select="mat-chip-avatar, [matChipAvatar]"></ng-content>
<ng-content select="mat-chip-avatar, [matChipAvatar], mat-chip-leading-icon, [matChipLeadingIcon]"></ng-content>
<span class="mdc-evolution-chip__checkmark">
<svg
class="mdc-evolution-chip__checkmark-svg"
Expand Down
19 changes: 18 additions & 1 deletion src/material/chips/chip-option.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
MAT_CHIPS_DEFAULT_OPTIONS,
MatChipAvatar,
MatChipEvent,
MatChipLeadingIcon,
MatChipListbox,
MatChipOption,
MatChipSelectionChange,
Expand Down Expand Up @@ -373,6 +374,18 @@ describe('Option Chips', () => {
expect(chipNativeElement.querySelector('.mat-mdc-chip-graphic')).toBeTruthy();
expect(chipNativeElement.classList).toContain('mdc-evolution-chip--with-primary-graphic');
});

it('displays checkmark graphic when leading icon is provided', () => {
testComponent.selected = true;
testComponent.leadingIconLabel = 'star';
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();

expect(chipNativeElement.querySelector('.mat-mdc-chip-graphic')).toBeTruthy();
expect(chipNativeElement.classList).toContain('mdc-evolution-chip--with-primary-graphic');
expect(chipNativeElement.classList).toContain('mat-mdc-chip-with-leading-icon');
expect(chipNativeElement.classList).not.toContain('mat-mdc-chip-with-avatar');
});
});

it('should contain a focus indicator inside the text label', () => {
Expand All @@ -395,12 +408,15 @@ describe('Option Chips', () => {
@if (avatarLabel) {
<span class="avatar" matChipAvatar>{{avatarLabel}}</span>
}
@if (leadingIconLabel) {
<span class="leading-icon" matChipLeadingIcon>{{leadingIconLabel}}</span>
}
{{name}}
</mat-chip-option>
</div>
}
</mat-chip-listbox>`,
imports: [MatChipListbox, MatChipOption, MatChipAvatar],
imports: [MatChipListbox, MatChipOption, MatChipAvatar, MatChipLeadingIcon],
changeDetection: ChangeDetectionStrategy.Eager,
})
class SingleChip {
Expand All @@ -414,6 +430,7 @@ class SingleChip {
ariaLabel: string | null = null;
ariaDescription: string | null = null;
avatarLabel: string | null = null;
leadingIconLabel: string | null = null;

chipDestroy: (event?: MatChipEvent) => void = () => {};
chipSelectionChange: (event?: MatChipSelectionChange) => void = () => {};
Expand Down
11 changes: 6 additions & 5 deletions src/material/chips/chip-option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export class MatChipSelectionChange {
'[class.mat-mdc-chip-selected]': 'selected',
'[class.mat-mdc-chip-multiple]': '_chipListMultiple',
'[class.mat-mdc-chip-disabled]': 'disabled',
'[class.mat-mdc-chip-with-avatar]': 'leadingIcon',
'[class.mat-mdc-chip-with-avatar]': 'avatar',
'[class.mat-mdc-chip-with-leading-icon]': 'leadingIcon',
'[class.mdc-evolution-chip--disabled]': 'disabled',
'[class.mdc-evolution-chip--selected]': 'selected',
// This class enables the transition on the checkmark. Usually MDC adds it when selection
Expand All @@ -59,9 +60,9 @@ export class MatChipSelectionChange {
// because they also have an exit animation that we don't care about.
'[class.mdc-evolution-chip--selecting]': '!_animationsDisabled',
'[class.mdc-evolution-chip--with-trailing-action]': '_hasTrailingIcon()',
'[class.mdc-evolution-chip--with-primary-icon]': 'leadingIcon',
'[class.mdc-evolution-chip--with-primary-icon]': 'leadingIcon || avatar',
'[class.mdc-evolution-chip--with-primary-graphic]': '_hasLeadingGraphic()',
'[class.mdc-evolution-chip--with-avatar]': 'leadingIcon',
'[class.mdc-evolution-chip--with-avatar]': 'avatar',
'[class.mat-mdc-chip-highlighted]': 'highlighted',
'[class.mat-mdc-chip-with-trailing-icon]': '_hasTrailingIcon()',
'[attr.tabindex]': 'null',
Expand Down Expand Up @@ -181,8 +182,8 @@ export class MatChipOption extends MatChip implements OnInit {
}
}

_hasLeadingGraphic() {
if (this.leadingIcon) {
override _hasLeadingGraphic() {
if (this.leadingIcon || this.avatar) {
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/material/chips/chip-row.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
[disabled]="disabled"
[attr.aria-description]="ariaDescription"
[attr.aria-label]="ariaLabel">
@if (leadingIcon) {
@if (_hasLeadingGraphic()) {
<span class="mdc-evolution-chip__graphic mat-mdc-chip-graphic">
<ng-content select="mat-chip-avatar, [matChipAvatar]"></ng-content>
<ng-content select="mat-chip-avatar, [matChipAvatar], mat-chip-leading-icon, [matChipLeadingIcon]"></ng-content>
</span>
}

Expand Down
19 changes: 19 additions & 0 deletions src/material/chips/chip-row.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ describe('Row Chips', () => {
expect(chipNativeElement.classList).toContain('mat-warn');
});

it('supports a leading icon', () => {
testComponent.showLeadingIcon = true;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();

expect(chipInstance.leadingIcon).toBeTruthy();
expect(chipNativeElement.classList).toContain('mat-mdc-chip-with-leading-icon');
expect(chipNativeElement.classList).toContain('mdc-evolution-chip--with-primary-icon');
expect(chipNativeElement.classList).toContain('mdc-evolution-chip--with-primary-graphic');
expect(chipNativeElement.classList).not.toContain('mat-mdc-chip-with-avatar');
expect(
chipNativeElement.querySelector('.mat-mdc-chip-graphic [matChipLeadingIcon]'),
).toBeTruthy();
});

it('allows removal', () => {
spyOn(testComponent, 'chipRemove');

Expand Down Expand Up @@ -514,6 +529,9 @@ describe('Row Chips', () => {
@if (showEditIcon) {
<button matChipEdit>edit</button>
}
@if (showLeadingIcon) {
<span matChipLeadingIcon>leading</span>
}
{{name}}
@if (removable) {
<button matChipRemove>x</button>
Expand Down Expand Up @@ -542,6 +560,7 @@ class SingleChip {
shouldShow: boolean = true;
editable: boolean = false;
showEditIcon: boolean = false;
showLeadingIcon = false;
useCustomEditInput: boolean = true;
showTrailingIcon = false;
ariaLabel: string | null = null;
Expand Down
Loading
Loading