Skip to content

Commit c7c6262

Browse files
crisbetojelbourn
authored andcommitted
feat(paginator): add provider to configure default options (#17595)
Adds the `MAT_PAGINATOR_CONFIG` provider that allows consumers to configure the default options for all paginators in a module. Fixes #17123.
1 parent ee96e05 commit c7c6262

File tree

3 files changed

+150
-46
lines changed

3 files changed

+150
-46
lines changed

src/material/paginator/paginator.spec.ts

Lines changed: 94 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,31 @@
1-
import {async, ComponentFixture, TestBed, inject, tick, fakeAsync} from '@angular/core/testing';
2-
import {Component, ViewChild} from '@angular/core';
1+
import {ComponentFixture, TestBed, tick, fakeAsync} from '@angular/core/testing';
2+
import {Component, ViewChild, Type, Provider} from '@angular/core';
33
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
44
import {dispatchMouseEvent} from '@angular/cdk/testing/private';
55
import {ThemePalette} from '@angular/material/core';
66
import {MatSelect} from '@angular/material/select';
77
import {By} from '@angular/platform-browser';
88
import {MatPaginatorModule, MatPaginator, MatPaginatorIntl} from './index';
9+
import {MAT_PAGINATOR_DEFAULT_OPTIONS, MatPaginatorDefaultOptions} from './paginator';
910

1011

1112
describe('MatPaginator', () => {
12-
let fixture: ComponentFixture<MatPaginatorApp>;
13-
let component: MatPaginatorApp;
14-
let paginator: MatPaginator;
15-
16-
beforeEach(async(() => {
13+
function createComponent<T>(type: Type<T>, providers: Provider[] = []): ComponentFixture<T> {
1714
TestBed.configureTestingModule({
18-
imports: [
19-
MatPaginatorModule,
20-
NoopAnimationsModule,
21-
],
22-
declarations: [
23-
MatPaginatorApp,
24-
MatPaginatorWithoutPageSizeApp,
25-
MatPaginatorWithoutOptionsApp,
26-
MatPaginatorWithoutInputsApp,
27-
MatPaginatorWithStringValues
28-
],
29-
providers: [MatPaginatorIntl]
15+
imports: [MatPaginatorModule, NoopAnimationsModule],
16+
declarations: [type],
17+
providers: [MatPaginatorIntl, ...providers]
3018
}).compileComponents();
31-
}));
3219

33-
beforeEach(() => {
34-
fixture = TestBed.createComponent(MatPaginatorApp);
20+
const fixture = TestBed.createComponent(type);
3521
fixture.detectChanges();
36-
37-
component = fixture.componentInstance;
38-
paginator = component.paginator;
39-
});
22+
return fixture;
23+
}
4024

4125
describe('with the default internationalization provider', () => {
4226
it('should show the right range text', () => {
27+
const fixture = createComponent(MatPaginatorApp);
28+
const component = fixture.componentInstance;
4329
const rangeElement = fixture.nativeElement.querySelector('.mat-paginator-range-label');
4430

4531
// View second page of list of 100, each page contains 10 items.
@@ -86,27 +72,32 @@ describe('MatPaginator', () => {
8672
});
8773

8874
it('should show right aria-labels for select and buttons', () => {
75+
const fixture = createComponent(MatPaginatorApp);
8976
const select = fixture.nativeElement.querySelector('.mat-select');
9077
expect(select.getAttribute('aria-label')).toBe('Items per page:');
9178

9279
expect(getPreviousButton(fixture).getAttribute('aria-label')).toBe('Previous page');
9380
expect(getNextButton(fixture).getAttribute('aria-label')).toBe('Next page');
9481
});
9582

96-
it('should re-render when the i18n labels change',
97-
inject([MatPaginatorIntl], (intl: MatPaginatorIntl) => {
98-
const label = fixture.nativeElement.querySelector('.mat-paginator-page-size-label');
83+
it('should re-render when the i18n labels change', () => {
84+
const fixture = createComponent(MatPaginatorApp);
85+
const label = fixture.nativeElement.querySelector('.mat-paginator-page-size-label');
86+
const intl = TestBed.get<MatPaginatorIntl>(MatPaginatorIntl);
9987

100-
intl.itemsPerPageLabel = '1337 items per page';
101-
intl.changes.next();
102-
fixture.detectChanges();
88+
intl.itemsPerPageLabel = '1337 items per page';
89+
intl.changes.next();
90+
fixture.detectChanges();
10391

104-
expect(label.textContent!.trim()).toBe('1337 items per page');
105-
}));
92+
expect(label.textContent!.trim()).toBe('1337 items per page');
93+
});
10694
});
10795

10896
describe('when navigating with the next and previous buttons', () => {
10997
it('should be able to go to the next page', () => {
98+
const fixture = createComponent(MatPaginatorApp);
99+
const component = fixture.componentInstance;
100+
const paginator = component.paginator;
110101
expect(paginator.pageIndex).toBe(0);
111102

112103
dispatchMouseEvent(getNextButton(fixture), 'click');
@@ -119,6 +110,9 @@ describe('MatPaginator', () => {
119110
});
120111

121112
it('should be able to go to the previous page', () => {
113+
const fixture = createComponent(MatPaginatorApp);
114+
const component = fixture.componentInstance;
115+
const paginator = component.paginator;
122116
paginator.pageIndex = 1;
123117
fixture.detectChanges();
124118
expect(paginator.pageIndex).toBe(1);
@@ -134,6 +128,7 @@ describe('MatPaginator', () => {
134128
});
135129

136130
it('should be able to show the first/last buttons', () => {
131+
const fixture = createComponent(MatPaginatorApp);
137132
expect(getFirstButton(fixture))
138133
.toBeNull('Expected first button to not exist.');
139134

@@ -151,6 +146,9 @@ describe('MatPaginator', () => {
151146
});
152147

153148
it('should mark itself as initialized', fakeAsync(() => {
149+
const fixture = createComponent(MatPaginatorApp);
150+
const component = fixture.componentInstance;
151+
const paginator = component.paginator;
154152
let isMarkedInitialized = false;
155153
paginator.initialized.subscribe(() => isMarkedInitialized = true);
156154

@@ -159,16 +157,24 @@ describe('MatPaginator', () => {
159157
}));
160158

161159
it('should not allow a negative pageSize', () => {
160+
const fixture = createComponent(MatPaginatorApp);
161+
const component = fixture.componentInstance;
162+
const paginator = component.paginator;
162163
paginator.pageSize = -1337;
163164
expect(paginator.pageSize).toBeGreaterThanOrEqual(0);
164165
});
165166

166167
it('should not allow a negative pageIndex', () => {
168+
const fixture = createComponent(MatPaginatorApp);
169+
const component = fixture.componentInstance;
170+
const paginator = component.paginator;
167171
paginator.pageIndex = -42;
168172
expect(paginator.pageIndex).toBeGreaterThanOrEqual(0);
169173
});
170174

171175
it('should be able to set the color of the form field', () => {
176+
const fixture = createComponent(MatPaginatorApp);
177+
const component = fixture.componentInstance;
172178
const formField: HTMLElement = fixture.nativeElement.querySelector('.mat-form-field');
173179

174180
expect(formField.classList).toContain('mat-primary');
@@ -181,8 +187,14 @@ describe('MatPaginator', () => {
181187
});
182188

183189
describe('when showing the first and last button', () => {
190+
let fixture: ComponentFixture<MatPaginatorApp>;
191+
let component: MatPaginatorApp;
192+
let paginator: MatPaginator;
184193

185194
beforeEach(() => {
195+
fixture = createComponent(MatPaginatorApp);
196+
component = fixture.componentInstance;
197+
paginator = component.paginator;
186198
component.showFirstLastButtons = true;
187199
fixture.detectChanges();
188200
});
@@ -245,6 +257,9 @@ describe('MatPaginator', () => {
245257
});
246258

247259
it('should mark for check when inputs are changed directly', () => {
260+
const fixture = createComponent(MatPaginatorApp);
261+
const component = fixture.componentInstance;
262+
const paginator = component.paginator;
248263
const rangeElement = fixture.nativeElement.querySelector('.mat-paginator-range-label');
249264

250265
expect(rangeElement.innerText.trim()).toBe('1 – 10 of 100');
@@ -270,21 +285,23 @@ describe('MatPaginator', () => {
270285
});
271286

272287
it('should default the page size options to the page size if no options provided', () => {
273-
const withoutOptionsAppFixture = TestBed.createComponent(MatPaginatorWithoutOptionsApp);
274-
withoutOptionsAppFixture.detectChanges();
288+
const fixture = createComponent(MatPaginatorWithoutOptionsApp);
289+
fixture.detectChanges();
275290

276-
expect(withoutOptionsAppFixture.componentInstance.paginator._displayedPageSizeOptions)
277-
.toEqual([10]);
291+
expect(fixture.componentInstance.paginator._displayedPageSizeOptions).toEqual([10]);
278292
});
279293

280294
it('should default the page size to the first page size option if not provided', () => {
281-
const withoutPageSizeAppFixture = TestBed.createComponent(MatPaginatorWithoutPageSizeApp);
282-
withoutPageSizeAppFixture.detectChanges();
295+
const fixture = createComponent(MatPaginatorWithoutPageSizeApp);
296+
fixture.detectChanges();
283297

284-
expect(withoutPageSizeAppFixture.componentInstance.paginator.pageSize).toEqual(10);
298+
expect(fixture.componentInstance.paginator.pageSize).toEqual(10);
285299
});
286300

287301
it('should show a sorted list of page size options including the current page size', () => {
302+
const fixture = createComponent(MatPaginatorApp);
303+
const component = fixture.componentInstance;
304+
const paginator = component.paginator;
288305
expect(paginator._displayedPageSizeOptions).toEqual([5, 10, 25, 100]);
289306

290307
component.pageSize = 30;
@@ -298,6 +315,10 @@ describe('MatPaginator', () => {
298315
});
299316

300317
it('should be able to change the page size while keeping the first item present', () => {
318+
const fixture = createComponent(MatPaginatorApp);
319+
const component = fixture.componentInstance;
320+
const paginator = component.paginator;
321+
301322
// Start on the third page of a list of 100 with a page size of 10.
302323
component.pageIndex = 4;
303324
component.pageSize = 10;
@@ -339,6 +360,10 @@ describe('MatPaginator', () => {
339360
});
340361

341362
it('should keep track of the right number of pages', () => {
363+
const fixture = createComponent(MatPaginatorApp);
364+
const component = fixture.componentInstance;
365+
const paginator = component.paginator;
366+
342367
component.pageSize = 10;
343368
component.length = 100;
344369
fixture.detectChanges();
@@ -356,6 +381,10 @@ describe('MatPaginator', () => {
356381
});
357382

358383
it('should show a select only if there are multiple options', () => {
384+
const fixture = createComponent(MatPaginatorApp);
385+
const component = fixture.componentInstance;
386+
const paginator = component.paginator;
387+
359388
expect(paginator._displayedPageSizeOptions).toEqual([5, 10, 25, 100]);
360389
expect(fixture.nativeElement.querySelector('.mat-select')).not.toBeNull();
361390

@@ -367,17 +396,18 @@ describe('MatPaginator', () => {
367396
});
368397

369398
it('should handle the number inputs being passed in as strings', () => {
370-
const withStringFixture = TestBed.createComponent(MatPaginatorWithStringValues);
371-
withStringFixture.detectChanges();
399+
const fixture = createComponent(MatPaginatorWithStringValues);
400+
fixture.detectChanges();
372401

373-
const withStringPaginator = withStringFixture.componentInstance.paginator;
402+
const withStringPaginator = fixture.componentInstance.paginator;
374403
expect(withStringPaginator.pageIndex).toEqual(0);
375404
expect(withStringPaginator.length).toEqual(100);
376405
expect(withStringPaginator.pageSize).toEqual(10);
377406
expect(withStringPaginator.pageSizeOptions).toEqual([5, 10, 25, 100]);
378407
});
379408

380409
it('should be able to hide the page size select', () => {
410+
const fixture = createComponent(MatPaginatorApp);
381411
const element = fixture.nativeElement;
382412

383413
expect(element.querySelector('.mat-paginator-page-size'))
@@ -391,6 +421,7 @@ describe('MatPaginator', () => {
391421
});
392422

393423
it('should be able to disable all the controls in the paginator via the binding', () => {
424+
const fixture = createComponent(MatPaginatorApp);
394425
const select: MatSelect =
395426
fixture.debugElement.query(By.directive(MatSelect))!.componentInstance;
396427

@@ -414,6 +445,25 @@ describe('MatPaginator', () => {
414445
expect(getLastButton(fixture).hasAttribute('disabled')).toBe(true);
415446
});
416447

448+
449+
it('should be able to configure the default options via a provider', () => {
450+
const fixture = createComponent(MatPaginatorWithoutInputsApp, [{
451+
provide: MAT_PAGINATOR_DEFAULT_OPTIONS,
452+
useValue: {
453+
pageSize: 7,
454+
pageSizeOptions: [7, 14, 21],
455+
hidePageSize: true,
456+
showFirstLastButtons: true
457+
} as MatPaginatorDefaultOptions
458+
}]);
459+
const paginator = fixture.componentInstance.paginator;
460+
461+
expect(paginator.pageSize).toBe(7);
462+
expect(paginator.pageSizeOptions).toEqual([7, 14, 21]);
463+
expect(paginator.hidePageSize).toBe(true);
464+
expect(paginator.showFirstLastButtons).toBe(true);
465+
});
466+
417467
});
418468

419469
function getPreviousButton(fixture: ComponentFixture<any>) {

src/material/paginator/paginator.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import {
2222
OnInit,
2323
Output,
2424
ViewEncapsulation,
25+
InjectionToken,
26+
Inject,
27+
Optional,
2528
} from '@angular/core';
2629
import {Subscription} from 'rxjs';
2730
import {MatPaginatorIntl} from './paginator-intl';
@@ -59,6 +62,26 @@ export class PageEvent {
5962
length: number;
6063
}
6164

65+
66+
/** Object that can be used to configure the default options for the paginator module. */
67+
export interface MatPaginatorDefaultOptions {
68+
/** Number of items to display on a page. By default set to 50. */
69+
pageSize?: number;
70+
71+
/** The set of provided page size options to display to the user. */
72+
pageSizeOptions?: number[];
73+
74+
/** Whether to hide the page size selection UI from the user. */
75+
hidePageSize?: boolean;
76+
77+
/** Whether to show the first/last buttons UI to the user. */
78+
showFirstLastButtons?: boolean;
79+
}
80+
81+
/** Injection token that can be used to provide the default options for the paginator module. */
82+
export const MAT_PAGINATOR_DEFAULT_OPTIONS =
83+
new InjectionToken<MatPaginatorDefaultOptions>('MAT_PAGINATOR_DEFAULT_OPTIONS');
84+
6285
// Boilerplate for applying mixins to MatPaginator.
6386
/** @docs-private */
6487
class MatPaginatorBase {}
@@ -150,9 +173,31 @@ export class MatPaginator extends _MatPaginatorBase implements OnInit, OnDestroy
150173
_displayedPageSizeOptions: number[];
151174

152175
constructor(public _intl: MatPaginatorIntl,
153-
private _changeDetectorRef: ChangeDetectorRef) {
176+
private _changeDetectorRef: ChangeDetectorRef,
177+
@Optional() @Inject(MAT_PAGINATOR_DEFAULT_OPTIONS)
178+
defaults?: MatPaginatorDefaultOptions) {
154179
super();
155180
this._intlChanges = _intl.changes.subscribe(() => this._changeDetectorRef.markForCheck());
181+
182+
if (defaults) {
183+
const {pageSize, pageSizeOptions, hidePageSize, showFirstLastButtons} = defaults;
184+
185+
if (pageSize != null) {
186+
this._pageSize = pageSize;
187+
}
188+
189+
if (pageSizeOptions != null) {
190+
this._pageSizeOptions = pageSizeOptions;
191+
}
192+
193+
if (hidePageSize != null) {
194+
this._hidePageSize = hidePageSize;
195+
}
196+
197+
if (showFirstLastButtons != null) {
198+
this._showFirstLastButtons = showFirstLastButtons;
199+
}
200+
}
156201
}
157202

158203
ngOnInit() {

tools/public_api_guard/material/paginator.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export declare const MAT_PAGINATOR_DEFAULT_OPTIONS: InjectionToken<MatPaginatorDefaultOptions>;
2+
13
export declare const MAT_PAGINATOR_INTL_PROVIDER: {
24
provide: typeof MatPaginatorIntl;
35
deps: Optional[][];
@@ -23,7 +25,7 @@ export declare class MatPaginator extends _MatPaginatorBase implements OnInit, O
2325
set pageSizeOptions(value: number[]);
2426
get showFirstLastButtons(): boolean;
2527
set showFirstLastButtons(value: boolean);
26-
constructor(_intl: MatPaginatorIntl, _changeDetectorRef: ChangeDetectorRef);
28+
constructor(_intl: MatPaginatorIntl, _changeDetectorRef: ChangeDetectorRef, defaults?: MatPaginatorDefaultOptions);
2729
_changePageSize(pageSize: number): void;
2830
_nextButtonsDisabled(): boolean;
2931
_previousButtonsDisabled(): boolean;
@@ -46,6 +48,13 @@ export declare class MatPaginator extends _MatPaginatorBase implements OnInit, O
4648
static ɵfac: i0.ɵɵFactoryDef<MatPaginator>;
4749
}
4850

51+
export interface MatPaginatorDefaultOptions {
52+
hidePageSize?: boolean;
53+
pageSize?: number;
54+
pageSizeOptions?: number[];
55+
showFirstLastButtons?: boolean;
56+
}
57+
4958
export declare class MatPaginatorIntl {
5059
readonly changes: Subject<void>;
5160
firstPageLabel: string;

0 commit comments

Comments
 (0)