Skip to content

Commit 106562f

Browse files
authored
Merge branch 'angular:main' into chips-tb
2 parents f34e284 + 8da079d commit 106562f

File tree

12 files changed

+107
-51
lines changed

12 files changed

+107
-51
lines changed

goldens/material/chips/index.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
5959
protected _allTrailingIcons: QueryList<MatChipTrailingIcon>;
6060
_animationsDisabled: boolean;
6161
ariaDescription: string | null;
62-
_ariaDescriptionId: string;
6362
ariaLabel: string | null;
6463
protected basicChipAttrName: string;
6564
// (undocumented)
@@ -84,6 +83,7 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
8483
_handlePrimaryActionInteraction(): void;
8584
// (undocumented)
8685
_hasFocus(): boolean;
86+
_hasInteractiveActions(): boolean;
8787
_hasTrailingIcon(): boolean;
8888
highlighted: boolean;
8989
id: string;

goldens/material/stepper/index.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ export class MatStepHeader extends CdkStepHeader implements AfterViewInit, OnDes
8282
// (undocumented)
8383
_getDefaultTextForState(state: StepState): string;
8484
_getHostElement(): HTMLElement;
85+
// (undocumented)
86+
protected _hasEmptyLabel(): boolean;
87+
// (undocumented)
88+
protected _hasErrorLabel(): boolean;
89+
// (undocumented)
90+
protected _hasOptionalLabel(): boolean;
8591
iconOverrides: {
8692
[key: string]: TemplateRef<MatStepperIconContext>;
8793
};

src/material/chips/chip-option.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
<button
55
matChipAction
66
[_allowFocusWhenDisabled]="true"
7-
[attr.aria-selected]="ariaSelected"
7+
[attr.aria-description]="ariaDescription"
88
[attr.aria-label]="ariaLabel"
9-
[attr.aria-describedby]="_ariaDescriptionId"
9+
[attr.aria-selected]="ariaSelected"
1010
role="option">
1111
@if (_hasLeadingGraphic()) {
1212
<span class="mdc-evolution-chip__graphic mat-mdc-chip-graphic">
@@ -35,5 +35,3 @@
3535
<ng-content select="mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"></ng-content>
3636
</span>
3737
}
38-
39-
<span class="cdk-visually-hidden" [id]="_ariaDescriptionId">{{ariaDescription}}</span>

src/material/chips/chip-option.spec.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,10 @@ describe('Option Chips', () => {
337337

338338
expect(optionElement.getAttribute('aria-label')).toMatch(/option name/i);
339339

340-
const optionElementDescribedBy = optionElement!.getAttribute('aria-describedby');
341-
expect(optionElementDescribedBy)
342-
.withContext('expected primary grid cell to have a non-empty aria-describedby attribute')
340+
const optionElementDescription = optionElement!.getAttribute('aria-description');
341+
expect(optionElementDescription)
342+
.withContext('expected primary grid cell to have a non-empty aria-description attribute')
343343
.toBeTruthy();
344-
345-
const optionElementDescriptions = Array.from(
346-
(fixture.nativeElement as HTMLElement).querySelectorAll(
347-
optionElementDescribedBy!
348-
.split(/\s+/g)
349-
.map(x => `#${x}`)
350-
.join(','),
351-
),
352-
);
353-
354-
const optionElementDescription = optionElementDescriptions
355-
.map(x => x.textContent?.trim())
356-
.join(' ')
357-
.trim();
358-
359344
expect(optionElementDescription).toMatch(/option description/i);
360345
});
361346

src/material/chips/chip-row.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary" role="gridcell"
1111
matChipAction
1212
[disabled]="disabled"
13-
[attr.aria-label]="ariaLabel"
14-
[attr.aria-describedby]="_ariaDescriptionId">
13+
[attr.aria-description]="ariaDescription"
14+
[attr.aria-label]="ariaLabel">
1515
@if (leadingIcon) {
1616
<span class="mdc-evolution-chip__graphic mat-mdc-chip-graphic">
1717
<ng-content select="mat-chip-avatar, [matChipAvatar]"></ng-content>
@@ -40,5 +40,3 @@
4040
<ng-content select="mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"></ng-content>
4141
</span>
4242
}
43-
44-
<span class="cdk-visually-hidden" [id]="_ariaDescriptionId">{{ariaDescription}}</span>

src/material/chips/chip-row.spec.ts

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,43 @@ describe('Row Chips', () => {
436436
}));
437437
});
438438

439+
describe('_hasInteractiveActions', () => {
440+
it('should return true if the chip has a remove icon', () => {
441+
testComponent.removable = true;
442+
fixture.changeDetectorRef.markForCheck();
443+
fixture.detectChanges();
444+
expect(chipInstance._hasInteractiveActions()).toBe(true);
445+
});
446+
447+
it('should return true if the chip has an edit icon', () => {
448+
testComponent.editable = true;
449+
testComponent.showEditIcon = true;
450+
fixture.changeDetectorRef.markForCheck();
451+
fixture.detectChanges();
452+
expect(chipInstance._hasInteractiveActions()).toBe(true);
453+
});
454+
455+
it('should return true even with a non-interactive trailing icon', () => {
456+
testComponent.showTrailingIcon = true;
457+
fixture.changeDetectorRef.markForCheck();
458+
fixture.detectChanges();
459+
expect(chipInstance._hasInteractiveActions()).toBe(true);
460+
});
461+
462+
it('should return false if all actions are non-interactive', () => {
463+
// Make primary action non-interactive for testing purposes.
464+
chipInstance.primaryAction.isInteractive = false;
465+
testComponent.showTrailingIcon = true;
466+
testComponent.removable = false; // remove icon is interactive
467+
fixture.changeDetectorRef.markForCheck();
468+
fixture.detectChanges();
469+
470+
// The trailing icon is not interactive.
471+
expect(chipInstance.trailingIcon.isInteractive).toBe(false);
472+
expect(chipInstance._hasInteractiveActions()).toBe(false);
473+
});
474+
});
475+
439476
describe('with edit icon', () => {
440477
beforeEach(async () => {
441478
testComponent.showEditIcon = true;
@@ -468,25 +505,10 @@ describe('Row Chips', () => {
468505

469506
expect(primaryGridCell!.getAttribute('aria-label')).toMatch(/chip name/i);
470507

471-
const primaryGridCellDescribedBy = primaryGridCell!.getAttribute('aria-describedby');
472-
expect(primaryGridCellDescribedBy)
473-
.withContext('expected primary grid cell to have a non-empty aria-describedby attribute')
508+
const primaryGridCellDescription = primaryGridCell!.getAttribute('aria-description');
509+
expect(primaryGridCellDescription)
510+
.withContext('expected primary grid cell to have a non-empty aria-description attribute')
474511
.toBeTruthy();
475-
476-
const primaryGridCellDescriptions = Array.from(
477-
(fixture.nativeElement as HTMLElement).querySelectorAll(
478-
primaryGridCellDescribedBy!
479-
.split(/\s+/g)
480-
.map(x => `#${x}`)
481-
.join(','),
482-
),
483-
);
484-
485-
const primaryGridCellDescription = primaryGridCellDescriptions
486-
.map(x => x.textContent?.trim())
487-
.join(' ')
488-
.trim();
489-
490512
expect(primaryGridCellDescription).toMatch(/chip description/i);
491513
});
492514
});
@@ -507,10 +529,15 @@ describe('Row Chips', () => {
507529
<button matChipEdit>edit</button>
508530
}
509531
{{name}}
510-
<button matChipRemove>x</button>
532+
@if (removable) {
533+
<button matChipRemove>x</button>
534+
}
511535
@if (useCustomEditInput) {
512536
<span class="projected-edit-input" matChipEditInput></span>
513537
}
538+
@if (showTrailingIcon) {
539+
<span matChipTrailingIcon>trailing</span>
540+
}
514541
</mat-chip-row>
515542
<input matInput [matChipInputFor]="chipGrid" #chipInput>
516543
</div>
@@ -529,6 +556,7 @@ class SingleChip {
529556
editable: boolean = false;
530557
showEditIcon: boolean = false;
531558
useCustomEditInput: boolean = true;
559+
showTrailingIcon = false;
532560
ariaLabel: string | null = null;
533561
ariaDescription: string | null = null;
534562

src/material/chips/chip.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ describe('MatChip', () => {
117117
expect(primaryAction.hasAttribute('tabindex')).toBe(false);
118118
});
119119

120+
it('should disable the ripple if there are no interactive actions', () => {
121+
// expect(chipInstance._isRippleDisabled()).toBe(false); TODO(andreyd)
122+
123+
// Make primary action non-interactive for testing purposes.
124+
chipInstance.primaryAction.isInteractive = false;
125+
fixture.changeDetectorRef.markForCheck();
126+
fixture.detectChanges();
127+
128+
expect(chipInstance._hasInteractiveActions()).toBe(false);
129+
expect(chipInstance._isRippleDisabled()).toBe(true);
130+
});
131+
120132
it('should return the chip text if value is undefined', () => {
121133
expect(chipInstance.value.trim()).toBe(fixture.componentInstance.name);
122134
});

src/material/chips/chip.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,6 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
166166
/** ARIA description for the content of the chip. */
167167
@Input('aria-description') ariaDescription: string | null = null;
168168

169-
/** Id of a span that contains this chip's aria description. */
170-
_ariaDescriptionId = `${this.id}-aria-description`;
171-
172169
/** Whether the chip list is disabled. */
173170
_chipListDisabled: boolean = false;
174171

@@ -331,6 +328,7 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
331328
this.disableRipple ||
332329
this._animationsDisabled ||
333330
this._isBasicChip ||
331+
!this._hasInteractiveActions() ||
334332
!!this._globalRippleOptions?.disabled
335333
);
336334
}
@@ -400,6 +398,11 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
400398
// Empty here, but is overwritten in child classes.
401399
}
402400

401+
/** Returns whether the chip has any interactive actions. */
402+
_hasInteractiveActions(): boolean {
403+
return this._getActions().some(a => a.isInteractive);
404+
}
405+
403406
/** Handles interactions with the edit action of the chip. */
404407
_edit(event: Event) {
405408
// Empty here, but is overwritten in child classes.

src/material/stepper/step-header.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@
4141
<div class="mat-step-text-label">{{label}}</div>
4242
}
4343

44-
@if (optional && state != 'error') {
44+
@if (_hasOptionalLabel()) {
4545
<div class="mat-step-optional">{{_intl.optionalLabel}}</div>
4646
}
4747

48-
@if (state === 'error') {
48+
@if (_hasErrorLabel()) {
4949
<div class="mat-step-sub-label-error">{{errorMessage}}</div>
5050
}
5151
</div>

src/material/stepper/step-header.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ $fallbacks: m3-stepper.get-tokens();
143143
font-size: token-utils.slot(stepper-header-selected-state-label-text-size, $fallbacks);
144144
font-weight: token-utils.slot(stepper-header-selected-state-label-text-weight, $fallbacks);
145145
}
146+
147+
.mat-step-header-empty-label & {
148+
min-width: 0;
149+
}
146150
}
147151

148152
.mat-step-text-label {

0 commit comments

Comments
 (0)