Skip to content

feat: add change emitters to the Intl providers #5867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/lib/datepicker/calendar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {MdCalendar} from './calendar';
import {By} from '@angular/platform-browser';
Expand Down Expand Up @@ -148,6 +148,18 @@ describe('MdCalendar', () => {
expect(testComponent.selected).toEqual(new Date(2017, JAN, 31));
});

it('should re-render when the i18n labels have changed',
inject([MdDatepickerIntl], (intl: MdDatepickerIntl) => {
const button = fixture.debugElement.nativeElement
.querySelector('.mat-calendar-period-button');

intl.switchToYearViewLabel = 'Go to year view?';
intl.changes.emit();
fixture.detectChanges();

expect(button.getAttribute('aria-label')).toBe('Go to year view?');
}));

describe('a11y', () => {
describe('calendar body', () => {
let calendarBodyEl: HTMLElement;
Expand Down
20 changes: 17 additions & 3 deletions src/lib/datepicker/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
NgZone,
Optional,
Output,
ViewEncapsulation
ViewEncapsulation,
ChangeDetectorRef,
OnDestroy,
} from '@angular/core';
import {
DOWN_ARROW,
Expand All @@ -36,6 +38,7 @@ import {createMissingDateImplError} from './datepicker-errors';
import {MD_DATE_FORMATS, MdDateFormats} from '../core/datetime/date-formats';
import {MATERIAL_COMPATIBILITY_MODE} from '../core';
import {first} from '../core/rxjs/index';
import {Subscription} from 'rxjs/Subscription';


/**
Expand All @@ -53,7 +56,9 @@ import {first} from '../core/rxjs/index';
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdCalendar<D> implements AfterContentInit {
export class MdCalendar<D> implements AfterContentInit, OnDestroy {
private _intlChanges: Subscription;

/** A date representing the period (month or year) to start the calendar in. */
@Input() startAt: D;

Expand Down Expand Up @@ -123,13 +128,18 @@ export class MdCalendar<D> implements AfterContentInit {
private _ngZone: NgZone,
@Optional() @Inject(MATERIAL_COMPATIBILITY_MODE) public _isCompatibilityMode: boolean,
@Optional() private _dateAdapter: DateAdapter<D>,
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats) {
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats,
changeDetectorRef: ChangeDetectorRef) {

if (!this._dateAdapter) {
throw createMissingDateImplError('DateAdapter');
}

if (!this._dateFormats) {
throw createMissingDateImplError('MD_DATE_FORMATS');
}

this._intlChanges = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
}

ngAfterContentInit() {
Expand All @@ -138,6 +148,10 @@ export class MdCalendar<D> implements AfterContentInit {
this._monthView = this.startView != 'year';
}

ngOnDestroy() {
this._intlChanges.unsubscribe();
}

/** Handles date selection in the month view. */
_dateSelected(date: D): void {
if (!this._dateAdapter.sameDate(date, this.selected)) {
Expand Down
8 changes: 7 additions & 1 deletion src/lib/datepicker/datepicker-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable} from '@angular/core';
import {Injectable, EventEmitter} from '@angular/core';


/** Datepicker data that requires internationalization. */
@Injectable()
export class MdDatepickerIntl {
/**
* Stream that emits whenever the labels here are changed. Use this to notify
* components if the labels have changed after initialization.
*/
changes: EventEmitter<void> = new EventEmitter<void>();

/** A label for the calendar popup (used by screen readers). */
calendarLabel = 'Calendar';

Expand Down
22 changes: 19 additions & 3 deletions src/lib/datepicker/datepicker-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ChangeDetectionStrategy, Component, Input, ViewEncapsulation} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
Input,
ViewEncapsulation,
OnDestroy,
ChangeDetectorRef,
} from '@angular/core';
import {MdDatepicker} from './datepicker';
import {MdDatepickerIntl} from './datepicker-intl';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Subscription} from 'rxjs/Subscription';


@Component({
Expand All @@ -27,7 +35,9 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion';
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdDatepickerToggle<D> {
export class MdDatepickerToggle<D> implements OnDestroy {
private _intlChanges: Subscription;

/** Datepicker instance that the button will toggle. */
@Input('mdDatepickerToggle') datepicker: MdDatepicker<D>;

Expand All @@ -45,7 +55,13 @@ export class MdDatepickerToggle<D> {
}
private _disabled: boolean;

constructor(public _intl: MdDatepickerIntl) {}
constructor(public _intl: MdDatepickerIntl, changeDetectorRef: ChangeDetectorRef) {
this._intlChanges = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
}

ngOnDestroy() {
this._intlChanges.unsubscribe();
}

_open(event: Event): void {
if (this.datepicker && !this.disabled) {
Expand Down
13 changes: 12 additions & 1 deletion src/lib/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {MdDatepickerModule} from './index';
import {MdDatepickerModule, MdDatepickerIntl} from './index';
import {MdDatepicker} from './datepicker';
import {MdDatepickerInput} from './datepicker-input';
import {MdInputModule} from '../input/index';
Expand Down Expand Up @@ -532,6 +532,17 @@ describe('MdDatepicker', () => {

expect(document.activeElement).toBe(toggle, 'Expected focus to be restored to toggle.');
});

it('should re-render when the i18n labels change',
inject([MdDatepickerIntl], (intl: MdDatepickerIntl) => {
const toggle = fixture.debugElement.query(By.css('button')).nativeElement;

intl.openCalendarLabel = 'Open the calendar, perhaps?';
intl.changes.emit();
fixture.detectChanges();

expect(toggle.getAttribute('aria-label')).toBe('Open the calendar, perhaps?');
}));
});

describe('datepicker inside input-container', () => {
Expand Down
8 changes: 7 additions & 1 deletion src/lib/paginator/paginator-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable} from '@angular/core';
import {Injectable, EventEmitter} from '@angular/core';

/**
* To modify the labels and text displayed, create a new instance of MdPaginatorIntl and
* include it in a custom provider
*/
@Injectable()
export class MdPaginatorIntl {
/**
* Stream that emits whenever the labels here are changed. Use this to notify
* components if the labels have changed after initialization.
*/
changes: EventEmitter<void> = new EventEmitter<void>();

/** A label for the page size selector. */
itemsPerPageLabel = 'Items per page:';

Expand Down
13 changes: 12 additions & 1 deletion src/lib/paginator/paginator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {MdPaginatorModule} from './index';
import {MdPaginator, PageEvent} from './paginator';
import {Component, ElementRef, ViewChild} from '@angular/core';
Expand Down Expand Up @@ -90,6 +90,17 @@ describe('MdPaginator', () => {
expect(getPreviousButton(fixture).getAttribute('aria-label')).toBe('Previous page');
expect(getNextButton(fixture).getAttribute('aria-label')).toBe('Next page');
});

it('should re-render when the i18n labels change',
inject([MdPaginatorIntl], (intl: MdPaginatorIntl) => {
const label = fixture.nativeElement.querySelector('.mat-paginator-page-size-label');

intl.itemsPerPageLabel = '1337 items per page';
intl.changes.emit();
fixture.detectChanges();

expect(label.textContent).toBe('1337 items per page');
}));
});

describe('when navigating with the navigation buttons', () => {
Expand Down
18 changes: 14 additions & 4 deletions src/lib/paginator/paginator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
*/

import {
ChangeDetectionStrategy, ChangeDetectorRef,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
ViewEncapsulation,
OnDestroy,
} from '@angular/core';
import {MdPaginatorIntl} from './paginator-intl';
import {MATERIAL_COMPATIBILITY_MODE} from '../core';
import {Subscription} from 'rxjs/Subscription';

/** The default page size if there is no page size and there are no provided page size options. */
const DEFAULT_PAGE_SIZE = 50;
Expand Down Expand Up @@ -55,8 +58,9 @@ export class PageEvent {
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class MdPaginator implements OnInit {
export class MdPaginator implements OnInit, OnDestroy {
private _initialized: boolean;
private _intlChanges: Subscription;

/** The zero-based page index of the displayed list of items. Defaulted to 0. */
@Input()
Expand Down Expand Up @@ -101,13 +105,19 @@ export class MdPaginator implements OnInit {
_displayedPageSizeOptions: number[];

constructor(public _intl: MdPaginatorIntl,
private _changeDetectorRef: ChangeDetectorRef) { }
private _changeDetectorRef: ChangeDetectorRef) {
this._intlChanges = _intl.changes.subscribe(() => this._changeDetectorRef.markForCheck());
}

ngOnInit() {
this._initialized = true;
this._updateDisplayedPageSizeOptions();
}

ngOnDestroy() {
this._intlChanges.unsubscribe();
}

/** Advances to the next page if it exists. */
nextPage() {
if (!this.hasNextPage()) { return; }
Expand Down
9 changes: 8 additions & 1 deletion src/lib/sort/sort-header-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable} from '@angular/core';
import {Injectable, EventEmitter} from '@angular/core';
import {SortDirection} from './sort-direction';

/**
Expand All @@ -15,6 +15,13 @@ import {SortDirection} from './sort-direction';
*/
@Injectable()
export class MdSortHeaderIntl {
/**
* Stream that emits whenever the labels here are changed. Use this to notify
* components if the labels have changed after initialization.
*/
changes: EventEmitter<void> = new EventEmitter<void>();

/** ARIA label for the sorting button. */
sortButtonLabel = (id: string) => {
return `Change sorting for ${id}`;
}
Expand Down
12 changes: 7 additions & 5 deletions src/lib/sort/sort-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {CdkColumnDef} from '@angular/cdk/table';
import {coerceBooleanProperty} from '../core';
import {getMdSortHeaderNotContainedWithinMdSortError} from './sort-errors';
import {Subscription} from 'rxjs/Subscription';
import {merge} from 'rxjs/observable/merge';

/**
* Applies sorting behavior (click to change sort) and styles to an element, including an
Expand All @@ -43,8 +44,7 @@ import {Subscription} from 'rxjs/Subscription';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdSortHeader implements MdSortable {
/** @docs-private */
sortSubscription: Subscription;
private _rerenderSubscription: Subscription;

/**
* ID of this sort header. If used within the context of a CdkColumnDef, this will default to
Expand All @@ -69,14 +69,16 @@ export class MdSortHeader implements MdSortable {
set _id(v: string) { this.id = v; }

constructor(public _intl: MdSortHeaderIntl,
private _changeDetectorRef: ChangeDetectorRef,
changeDetectorRef: ChangeDetectorRef,
@Optional() public _sort: MdSort,
@Optional() public _cdkColumnDef: CdkColumnDef) {
if (!_sort) {
throw getMdSortHeaderNotContainedWithinMdSortError();
}

this.sortSubscription = _sort.mdSortChange.subscribe(() => _changeDetectorRef.markForCheck());
this._rerenderSubscription = merge(_sort.mdSortChange, _intl.changes).subscribe(() => {
changeDetectorRef.markForCheck();
});
}

ngOnInit() {
Expand All @@ -89,7 +91,7 @@ export class MdSortHeader implements MdSortable {

ngOnDestroy() {
this._sort.deregister(this);
this.sortSubscription.unsubscribe();
this._rerenderSubscription.unsubscribe();
}

/** Whether this MdSortHeader is currently sorted in either ascending or descending order. */
Expand Down
20 changes: 16 additions & 4 deletions src/lib/sort/sort.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {Component, ElementRef, ViewChild} from '@angular/core';
import {MdSort, MdSortHeader, Sort, SortDirection, MdSortModule} from './index';
import {By} from '@angular/platform-browser';
import {MdSort, MdSortHeader, Sort, SortDirection, MdSortModule, MdSortHeaderIntl} from './index';
import {CdkTableModule, DataSource, CollectionViewer} from '@angular/cdk/table';
import {Observable} from 'rxjs/Observable';
import {dispatchMouseEvent} from '@angular/cdk/testing';
import {
getMdSortDuplicateMdSortableIdError,
getMdSortHeaderMissingIdError,
getMdSortHeaderNotContainedWithinMdSortError
} from './sort-errors';
import {wrappedErrorMessage} from '@angular/cdk/testing';
import {wrappedErrorMessage, dispatchMouseEvent} from '@angular/cdk/testing';
import {map} from '../core/rxjs/index';
import {MdTableModule} from '../table/index';

Expand Down Expand Up @@ -141,6 +141,18 @@ describe('MdSort', () => {
const button = fixture.nativeElement.querySelector('#defaultSortHeaderA button');
expect(button.getAttribute('aria-label')).toBe('Change sorting for defaultSortHeaderA');
});

it('should re-render when the i18n labels have changed',
inject([MdSortHeaderIntl], (intl: MdSortHeaderIntl) => {
const header = fixture.debugElement.query(By.directive(MdSortHeader)).nativeElement;
const button = header.querySelector('.mat-sort-header-button');

intl.sortButtonLabel = () => 'Sort all of the things';
intl.changes.emit();
fixture.detectChanges();

expect(button.getAttribute('aria-label')).toBe('Sort all of the things');
}));
});

/**
Expand Down