Skip to content

Commit

Permalink
feat(stepper): allow number icon to be customized and expose template…
Browse files Browse the repository at this point in the history
… context variables

* Allows for the step number icon to be overwritten using `matStepperIcon="number"`.
* Exposes the `index`, `active` and `optional` template variables to `matStepperIcon`.

Fixes angular#10513.
  • Loading branch information
crisbeto committed Mar 22, 2018
1 parent f882243 commit 0b44deb
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 18 deletions.
18 changes: 15 additions & 3 deletions src/lib/stepper/step-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,27 @@
[class.mat-step-icon-not-touched]="state == 'number' && !selected"
[ngSwitch]="state">

<span *ngSwitchCase="'number'">{{index + 1}}</span>
<ng-container *ngSwitchCase="'number'" [ngSwitch]="!!(iconOverrides && iconOverrides.number)">
<ng-container
*ngSwitchCase="true"
[ngTemplateOutlet]="iconOverrides.number"
[ngTemplateOutletContext]="_getIconContext()"></ng-container>
<span *ngSwitchDefault>{{index + 1}}</span>
</ng-container>

<ng-container *ngSwitchCase="'edit'" [ngSwitch]="!!(iconOverrides && iconOverrides.edit)">
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="iconOverrides.edit"></ng-container>
<ng-container
*ngSwitchCase="true"
[ngTemplateOutlet]="iconOverrides.edit"
[ngTemplateOutletContext]="_getIconContext()"></ng-container>
<mat-icon *ngSwitchDefault>create</mat-icon>
</ng-container>

<ng-container *ngSwitchCase="'done'" [ngSwitch]="!!(iconOverrides && iconOverrides.done)">
<ng-container *ngSwitchCase="true" [ngTemplateOutlet]="iconOverrides.done"></ng-container>
<ng-container
*ngSwitchCase="true"
[ngTemplateOutlet]="iconOverrides.done"
[ngTemplateOutletContext]="_getIconContext()"></ng-container>
<mat-icon *ngSwitchDefault>done</mat-icon>
</ng-container>
</div>
Expand Down
12 changes: 11 additions & 1 deletion src/lib/stepper/step-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import {Subscription} from 'rxjs/Subscription';
import {MatStepLabel} from './step-label';
import {MatStepperIntl} from './stepper-intl';
import {MatStepperIconContext} from './stepper-icon';


@Component({
Expand All @@ -44,7 +45,7 @@ export class MatStepHeader implements OnDestroy {
@Input() label: MatStepLabel | string;

/** Overrides for the header icons, passed in via the stepper. */
@Input() iconOverrides: {[key: string]: TemplateRef<any>};
@Input() iconOverrides: {[key: string]: TemplateRef<MatStepperIconContext>};

/** Index of the given step. */
@Input() index: number;
Expand Down Expand Up @@ -87,6 +88,15 @@ export class MatStepHeader implements OnDestroy {
return this._element.nativeElement;
}

/** Template context variables that are exposed to the `matStepperIcon` instances. */
_getIconContext(): MatStepperIconContext {
return {
index: this.index,
active: this.active,
optional: this.optional
};
}

focus() {
this._getHostElement().focus();
}
Expand Down
14 changes: 12 additions & 2 deletions src/lib/stepper/stepper-icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@

import {Directive, Input, TemplateRef} from '@angular/core';

/** Template context available to an attached `matStepperIcon`. */
export interface MatStepperIconContext {
/** Index of the step. */
index: number;
/** Whether the step is currently active. */
active: boolean;
/** Whether the step is optional. */
optional: boolean;
}

/**
* Template to be used to override the icons inside the step header.
*/
Expand All @@ -16,7 +26,7 @@ import {Directive, Input, TemplateRef} from '@angular/core';
})
export class MatStepperIcon {
/** Name of the icon to be overridden. */
@Input('matStepperIcon') name: 'edit' | 'done';
@Input('matStepperIcon') name: 'edit' | 'done' | 'number';

constructor(public templateRef: TemplateRef<any>) { }
constructor(public templateRef: TemplateRef<MatStepperIconContext>) {}
}
8 changes: 7 additions & 1 deletion src/lib/stepper/stepper.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ this default `completed` behavior by setting the `completed` attribute as needed
#### Overriding icons
By default, the step headers will use the `create` and `done` icons from the Material design icon
set via `<mat-icon>` elements. If you want to provide a different set of icons, you can do so
by placing a `matStepperIcon` for each of the icons that you want to override:
by placing a `matStepperIcon` for each of the icons that you want to override. The `index`,
`active`, and `optional` values of the individual steps are available through template variables:

```html
<mat-vertical-stepper>
Expand All @@ -136,6 +137,11 @@ by placing a `matStepperIcon` for each of the icons that you want to override:
<mat-icon>done_all</mat-icon>
</ng-template>

<!-- Custom icon with a context variable. -->
<ng-template matStepperIcon="number" let-index="index">
{{index + 10}}
</ng-template>

<!-- Stepper steps go here -->
</mat-vertical-stepper>
```
Expand Down
26 changes: 25 additions & 1 deletion src/lib/stepper/stepper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,13 @@ describe('MatStepper', () => {

expect(header.textContent).toContain('Custom done');
});

it('should allow for the `number` icon to be overridden with context', () => {
const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper));
const headers = stepperDebugElement.nativeElement.querySelectorAll('mat-step-header');

expect(headers[2].textContent).toContain('III');
});
});

describe('RTL', () => {
Expand Down Expand Up @@ -1025,14 +1032,31 @@ class SimpleStepperWithStepControlAndCompletedBinding {
<mat-horizontal-stepper>
<ng-template matStepperIcon="edit">Custom edit</ng-template>
<ng-template matStepperIcon="done">Custom done</ng-template>
<ng-template matStepperIcon="number" let-index="index">
{{getRomanNumeral(index + 1)}}
</ng-template>
<mat-step>Content 1</mat-step>
<mat-step>Content 2</mat-step>
<mat-step>Content 3</mat-step>
</mat-horizontal-stepper>
`
})
class IconOverridesStepper {}
class IconOverridesStepper {
getRomanNumeral(value: number) {
return {
1: 'I',
2: 'II',
3: 'III',
4: 'IV',
5: 'V',
6: 'VI',
7: 'VII',
8: 'VIII',
9: 'IX'
}[value];
}
}

@Component({
template: `
Expand Down
18 changes: 8 additions & 10 deletions src/lib/stepper/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {takeUntil} from 'rxjs/operators/takeUntil';
import {MatStepHeader} from './step-header';
import {MatStepLabel} from './step-label';
import {matStepperAnimations} from './stepper-animations';
import {MatStepperIcon} from './stepper-icon';
import {MatStepperIcon, MatStepperIconContext} from './stepper-icon';

/** Workaround for https://github.com/angular/angular/issues/17849 */
export const _MatStep = CdkStep;
Expand Down Expand Up @@ -83,20 +83,18 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
@ContentChildren(MatStepperIcon) _icons: QueryList<MatStepperIcon>;

/** Consumer-specified template-refs to be used to override the header icons. */
_iconOverrides: {[key: string]: TemplateRef<any>} = {};
_iconOverrides: {[key: string]: TemplateRef<MatStepperIconContext>} = {};

ngAfterContentInit() {
const icons = this._icons.toArray();
const editOverride = icons.find(icon => icon.name === 'edit');
const doneOverride = icons.find(icon => icon.name === 'done');

if (editOverride) {
this._iconOverrides.edit = editOverride.templateRef;
}
['edit', 'done', 'number'].forEach(name => {
const override = icons.find(icon => icon.name === name);

if (doneOverride) {
this._iconOverrides.done = doneOverride.templateRef;
}
if (override) {
this._iconOverrides[name] = override.templateRef;
}
});

// Mark the component for change detection whenever the content children query changes
this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged());
Expand Down

0 comments on commit 0b44deb

Please sign in to comment.