mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(stepper): unable to internationalize labels (#7122)
This commit is contained in:
committed by
Kara
parent
ac70420c29
commit
6e3bbcb6ef
@@ -11,4 +11,4 @@ export * from './step-label';
|
||||
export * from './stepper';
|
||||
export * from './stepper-button';
|
||||
export * from './step-header';
|
||||
|
||||
export * from './stepper-intl';
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
<!-- It there is no label template, fall back to the text label. -->
|
||||
<div class="mat-step-text-label" *ngIf="_stringLabel()">{{label}}</div>
|
||||
|
||||
<div class="mat-step-optional" *ngIf="optional">Optional</div>
|
||||
<div class="mat-step-optional" *ngIf="optional">{{_intl.optionalLabel}}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,8 +8,18 @@
|
||||
|
||||
import {FocusMonitor} from '@angular/cdk/a11y';
|
||||
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
|
||||
import {Component, Input, ViewEncapsulation, ElementRef, OnDestroy, Renderer2} from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
ElementRef,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
import {MatStepLabel} from './step-label';
|
||||
import {MatStepperIntl} from './stepper-intl';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -25,6 +35,8 @@ import {MatStepLabel} from './step-label';
|
||||
preserveWhitespaces: false,
|
||||
})
|
||||
export class MatStepHeader implements OnDestroy {
|
||||
private _intlSubscription: Subscription;
|
||||
|
||||
/** Icon for the given step. */
|
||||
@Input() icon: string;
|
||||
|
||||
@@ -64,13 +76,17 @@ export class MatStepHeader implements OnDestroy {
|
||||
private _optional: boolean;
|
||||
|
||||
constructor(
|
||||
public _intl: MatStepperIntl,
|
||||
private _focusMonitor: FocusMonitor,
|
||||
private _element: ElementRef,
|
||||
renderer: Renderer2) {
|
||||
renderer: Renderer2,
|
||||
changeDetectorRef: ChangeDetectorRef) {
|
||||
_focusMonitor.monitor(_element.nativeElement, renderer, true);
|
||||
this._intlSubscription = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._intlSubscription.unsubscribe();
|
||||
this._focusMonitor.stopMonitoring(this._element.nativeElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
|
||||
/** Stepper data that is required for internationalization. */
|
||||
@Injectable()
|
||||
export class MatStepperIntl {
|
||||
/**
|
||||
* Stream that emits whenever the labels here are changed. Use this to notify
|
||||
* components if the labels have changed after initialization.
|
||||
*/
|
||||
changes: Subject<void> = new Subject<void>();
|
||||
|
||||
/** Label that is rendered below optional steps. */
|
||||
optionalLabel = 'Optional';
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {MatStepHeader} from './step-header';
|
||||
import {MatStepLabel} from './step-label';
|
||||
import {MatHorizontalStepper, MatStep, MatStepper, MatVerticalStepper} from './stepper';
|
||||
import {MatStepperNext, MatStepperPrevious} from './stepper-button';
|
||||
import {MatStepperIntl} from './stepper-intl';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -44,5 +45,6 @@ import {MatStepperNext, MatStepperPrevious} from './stepper-button';
|
||||
],
|
||||
declarations: [MatHorizontalStepper, MatVerticalStepper, MatStep, MatStepLabel, MatStepper,
|
||||
MatStepperNext, MatStepperPrevious, MatStepHeader],
|
||||
providers: [MatStepperIntl],
|
||||
})
|
||||
export class MatStepperModule {}
|
||||
|
||||
+24
-10
@@ -7,8 +7,8 @@ that drives a stepped workflow. Material stepper extends the CDK stepper and has
|
||||
styling.
|
||||
|
||||
### Stepper variants
|
||||
There are two stepper components: `mat-horizontal-stepper` and `mat-vertical-stepper`. They
|
||||
can be used the same way. The only difference is the orientation of stepper.
|
||||
There are two stepper components: `mat-horizontal-stepper` and `mat-vertical-stepper`. They
|
||||
can be used the same way. The only difference is the orientation of stepper.
|
||||
`mat-horizontal-stepper` selector can be used to create a horizontal stepper, and
|
||||
`mat-vertical-stepper` can be used to create a vertical stepper. `mat-step` components need to be
|
||||
placed inside either one of the two stepper components.
|
||||
@@ -26,7 +26,7 @@ If a step's label is only text, then the `label` attribute can be used.
|
||||
</mat-vertical-stepper>
|
||||
```
|
||||
|
||||
For more complex labels, add a template with the `matStepLabel` directive inside the
|
||||
For more complex labels, add a template with the `matStepLabel` directive inside the
|
||||
`mat-step`.
|
||||
```html
|
||||
<mat-vertical-stepper>
|
||||
@@ -49,14 +49,14 @@ There are two button directives to support navigation between different steps:
|
||||
<button mat-button matStepperNext>Next</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
</mat-horizontal-stepper>
|
||||
```
|
||||
|
||||
### Linear stepper
|
||||
The `linear` attribute can be set on `mat-horizontal-stepper` and `mat-vertical-stepper` to create
|
||||
a linear stepper that requires the user to complete previous steps before proceeding
|
||||
to following steps. For each `mat-step`, the `stepControl` attribute can be set to the top level
|
||||
`AbstractControl` that is used to check the validity of the step.
|
||||
`AbstractControl` that is used to check the validity of the step.
|
||||
|
||||
There are two possible approaches. One is using a single form for stepper, and the other is
|
||||
using a different form for each step.
|
||||
@@ -64,7 +64,7 @@ using a different form for each step.
|
||||
#### Using a single form
|
||||
When using a single form for the stepper, `matStepperPrevious` and `matStepperNext` have to be
|
||||
set to `type="button"` in order to prevent submission of the form before all steps
|
||||
are completed.
|
||||
are completed.
|
||||
|
||||
```html
|
||||
<form [formGroup]="formGroup">
|
||||
@@ -83,7 +83,7 @@ are completed.
|
||||
</div>
|
||||
</mat-step>
|
||||
...
|
||||
</mat-horizontal-stepper>
|
||||
</mat-horizontal-stepper>
|
||||
</form>
|
||||
```
|
||||
|
||||
@@ -106,11 +106,11 @@ are completed.
|
||||
|
||||
#### Optional step
|
||||
If completion of a step in linear stepper is not required, then the `optional` attribute can be set
|
||||
on `mat-step`.
|
||||
on `mat-step`.
|
||||
|
||||
#### Editable step
|
||||
By default, steps are editable, which means users can return to previously completed steps and
|
||||
edit their responses. `editable="true"` can be set on `mat-step` to change the default.
|
||||
edit their responses. `editable="true"` can be set on `mat-step` to change the default.
|
||||
|
||||
#### Completed step
|
||||
By default, the `completed` attribute of a step returns `true` if the step is valid (in case of
|
||||
@@ -124,6 +124,20 @@ this default `completed` behavior by setting the `completed` attribute as needed
|
||||
- <kbd>TAB</kbd>: Focuses the next tabbable element
|
||||
- <kbd>TAB</kbd>+<kbd>SHIFT</kbd>: Focuses the previous tabbable element
|
||||
|
||||
### Localizing labels
|
||||
Labels used by the stepper are provided through `MatStepperIntl`. Localization of these messages
|
||||
can be done by providing a subclass with translated values in your application root module.
|
||||
|
||||
```ts
|
||||
@NgModule({
|
||||
imports: [MatStepperModule],
|
||||
providers: [
|
||||
{provide: MatStepperIntl, useClass: MyIntl},
|
||||
],
|
||||
})
|
||||
export class MyApp {}
|
||||
```
|
||||
|
||||
### Accessibility
|
||||
The stepper is treated as a tabbed view for accessibility purposes, so it is given
|
||||
`role="tablist"` by default. The header of step that can be clicked to select the step
|
||||
@@ -131,4 +145,4 @@ is given `role="tab"`, and the content that can be expanded upon selection is gi
|
||||
`role="tabpanel"`. `aria-selected` attribute of step header and `aria-expanded` attribute of
|
||||
step content is automatically set based on step selection change.
|
||||
|
||||
The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`.
|
||||
The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`.
|
||||
|
||||
@@ -2,13 +2,14 @@ import {Directionality} from '@angular/cdk/bidi';
|
||||
import {ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes';
|
||||
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
|
||||
import {Component, DebugElement} from '@angular/core';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MatStepperModule} from './index';
|
||||
import {MatHorizontalStepper, MatStep, MatStepper, MatVerticalStepper} from './stepper';
|
||||
import {MatStepperNext, MatStepperPrevious} from './stepper-button';
|
||||
import {MatStepperIntl} from './stepper-intl';
|
||||
|
||||
const VALID_REGEX = /valid/;
|
||||
|
||||
@@ -95,6 +96,21 @@ describe('MatHorizontalStepper', () => {
|
||||
it('should set done icon if step is not editable and is completed', () => {
|
||||
assertCorrectStepIcon(fixture, false, 'done');
|
||||
});
|
||||
|
||||
it('should re-render when the i18n labels change',
|
||||
inject([MatStepperIntl], (intl: MatStepperIntl) => {
|
||||
const header = fixture.debugElement.queryAll(By.css('mat-step-header'))[2].nativeElement;
|
||||
const optionalLabel = header.querySelector('.mat-step-optional');
|
||||
|
||||
expect(optionalLabel).toBeTruthy();
|
||||
expect(optionalLabel.textContent).toBe('Optional');
|
||||
|
||||
intl.optionalLabel = 'Valgfri';
|
||||
intl.changes.next();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(optionalLabel.textContent).toBe('Valgfri');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('RTL', () => {
|
||||
@@ -686,7 +702,7 @@ function assertCorrectStepIcon(fixture: ComponentFixture<any>,
|
||||
<button mat-button matStepperNext>Next</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
<mat-step [label]="inputLabel">
|
||||
<mat-step [label]="inputLabel" optional>
|
||||
Content 3
|
||||
<div>
|
||||
<button mat-button matStepperPrevious>Back</button>
|
||||
|
||||
Reference in New Issue
Block a user