diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts index 5c5b860a5e3b..8fb9d1463001 100644 --- a/src/cdk/stepper/stepper.ts +++ b/src/cdk/stepper/stepper.ts @@ -255,7 +255,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy { * @deprecated use `steps` instead * @breaking-change 9.0.0 remove this property */ - @ContentChildren(CdkStep) _steps: QueryList; + @ContentChildren(CdkStep, {descendants: true}) _steps: QueryList; /** * We need to store the steps in an Iterable due to strict template type checking with *ngFor and @@ -273,7 +273,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy { * @deprecated Type to be changed to `QueryList`. * @breaking-change 8.0.0 */ - @ContentChildren(CdkStepHeader) _stepHeader: QueryList; + @ContentChildren(CdkStepHeader, {descendants: true}) _stepHeader: QueryList; /** Whether the validity of previous steps should be checked or not. */ @Input() diff --git a/src/material/stepper/stepper.spec.ts b/src/material/stepper/stepper.spec.ts index f6dacfb351c3..2c83ef869f40 100644 --- a/src/material/stepper/stepper.spec.ts +++ b/src/material/stepper/stepper.spec.ts @@ -1102,6 +1102,79 @@ describe('MatStepper', () => { expect(stepper._getIndicatorType(1)).toBe(STEP_STATE.EDIT); }); }); + + describe('indirect descendants', () => { + it('should be able to change steps when steps are indirect descendants', () => { + const fixture = createComponent(StepperWithIndirectDescendantSteps); + fixture.detectChanges(); + + const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header')); + const stepperComponent = + fixture.debugElement.query(By.directive(MatStepper))!.componentInstance; + + expect(stepperComponent.selectedIndex).toBe(0); + expect(stepperComponent.selected instanceof MatStep).toBe(true); + + // select the second step + let stepHeaderEl = stepHeaders[1].nativeElement; + stepHeaderEl.click(); + fixture.detectChanges(); + + expect(stepperComponent.selectedIndex).toBe(1); + expect(stepperComponent.selected instanceof MatStep).toBe(true); + + // select the third step + stepHeaderEl = stepHeaders[2].nativeElement; + stepHeaderEl.click(); + fixture.detectChanges(); + + expect(stepperComponent.selectedIndex).toBe(2); + expect(stepperComponent.selected instanceof MatStep).toBe(true); + }); + + it('should allow for the `edit` icon to be overridden', () => { + const fixture = createComponent(IndirectDescendantIconOverridesStepper); + fixture.detectChanges(); + + const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper))!; + const stepperComponent: MatStepper = stepperDebugElement.componentInstance; + + stepperComponent.steps.toArray()[0].editable = true; + stepperComponent.next(); + fixture.detectChanges(); + + const header = stepperDebugElement.nativeElement.querySelector('mat-step-header'); + + expect(header.textContent).toContain('Custom edit'); + }); + + it('should allow for the `done` icon to be overridden', () => { + const fixture = createComponent(IndirectDescendantIconOverridesStepper); + fixture.detectChanges(); + + const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper))!; + const stepperComponent: MatStepper = stepperDebugElement.componentInstance; + + stepperComponent.steps.toArray()[0].editable = false; + stepperComponent.next(); + fixture.detectChanges(); + + const header = stepperDebugElement.nativeElement.querySelector('mat-step-header'); + + expect(header.textContent).toContain('Custom done'); + }); + + it('should allow for the `number` icon to be overridden with context', () => { + const fixture = createComponent(IndirectDescendantIconOverridesStepper); + fixture.detectChanges(); + + const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper))!; + const headers = stepperDebugElement.nativeElement.querySelectorAll('mat-step-header'); + + expect(headers[2].textContent).toContain('III'); + }); + + }); }); /** Asserts that keyboard interaction works correctly. */ @@ -1510,6 +1583,26 @@ class IconOverridesStepper { } } +@Component({ + template: ` + + + Custom edit + Custom done + + {{getRomanNumeral(index + 1)}} + + + + Content 1 + Content 2 + Content 3 + +` +}) +class IndirectDescendantIconOverridesStepper extends IconOverridesStepper { +} + @Component({ template: ` @@ -1536,3 +1629,18 @@ class StepperWithAriaInputs { ariaLabel: string; ariaLabelledby: string; } + + +@Component({ + template: ` + + + Content 1 + Content 2 + Content 3 + + + ` +}) +class StepperWithIndirectDescendantSteps { +} diff --git a/src/material/stepper/stepper.ts b/src/material/stepper/stepper.ts index 3611fb0a4329..b567ae663584 100644 --- a/src/material/stepper/stepper.ts +++ b/src/material/stepper/stepper.ts @@ -87,10 +87,10 @@ export class MatStepper extends CdkStepper implements AfterContentInit { @ViewChildren(MatStepHeader) _stepHeader: QueryList; /** Steps that the stepper holds. */ - @ContentChildren(MatStep) _steps: QueryList; + @ContentChildren(MatStep, {descendants: true}) _steps: QueryList; /** Custom icon overrides passed in by the consumer. */ - @ContentChildren(MatStepperIcon) _icons: QueryList; + @ContentChildren(MatStepperIcon, {descendants: true}) _icons: QueryList; /** Event emitted when the current step is done transitioning in. */ @Output() readonly animationDone: EventEmitter = new EventEmitter();