feat(stepper): allow number icon to be customized and expose template context variables (#10516)

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

Fixes #10513.
This commit is contained in:
Kristiyan Kostadinov
2018-03-27 18:58:46 +02:00
committed by Joey Perrott
parent b8bb62f30b
commit 946fd84a15
6 changed files with 78 additions and 18 deletions
+15 -3
View File
@@ -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>
+11 -1
View File
@@ -20,6 +20,7 @@ import {
import {Subscription} from 'rxjs';
import {MatStepLabel} from './step-label';
import {MatStepperIntl} from './stepper-intl';
import {MatStepperIconContext} from './stepper-icon';
@Component({
@@ -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;
@@ -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();
}
+12 -2
View File
@@ -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.
*/
@@ -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>) {}
}
+7 -1
View File
@@ -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>
@@ -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>
```
+25 -1
View File
@@ -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', () => {
@@ -1025,6 +1032,9 @@ 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>
@@ -1032,7 +1042,21 @@ class SimpleStepperWithStepControlAndCompletedBinding {
</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: `
+8 -10
View File
@@ -31,7 +31,7 @@ import {MatStepHeader} from './step-header';
import {MatStepLabel} from './step-label';
import {takeUntil} from 'rxjs/operators';
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;
@@ -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());