Skip to content

Commit 01867ad

Browse files
crisbetojelbourn
authored andcommitted
feat(datepicker): allow for mat-datepicker-toggle icon to be customized (#9487)
Allows for the consumer to set a different icon for the `mat-datepicker-icon` via the `matDatepickerToggleIcon` directive. The reasoning for the change is that not all consumers might be using the Material icons or they may want to set something different (e.g. a chevron).
1 parent 83ca9a8 commit 01867ad

9 files changed

+73
-6
lines changed

src/lib/datepicker/datepicker-module.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from './datepicker';
2323
import {MatDatepickerInput} from './datepicker-input';
2424
import {MatDatepickerIntl} from './datepicker-intl';
25-
import {MatDatepickerToggle} from './datepicker-toggle';
25+
import {MatDatepickerToggle, MatDatepickerToggleIcon} from './datepicker-toggle';
2626
import {MatMonthView} from './month-view';
2727
import {MatMultiYearView} from './multi-year-view';
2828
import {MatYearView} from './year-view';
@@ -44,6 +44,7 @@ import {MatYearView} from './year-view';
4444
MatDatepickerContent,
4545
MatDatepickerInput,
4646
MatDatepickerToggle,
47+
MatDatepickerToggleIcon,
4748
MatMonthView,
4849
MatYearView,
4950
MatMultiYearView,
@@ -55,6 +56,7 @@ import {MatYearView} from './year-view';
5556
MatDatepickerContent,
5657
MatDatepickerInput,
5758
MatDatepickerToggle,
59+
MatDatepickerToggleIcon,
5860
MatMonthView,
5961
MatYearView,
6062
MatMultiYearView,
+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<button mat-icon-button type="button" [attr.aria-label]="_intl.openCalendarLabel"
22
[disabled]="disabled" (click)="_open($event)">
3-
<mat-icon>
3+
<mat-icon *ngIf="!_customIcon">
44
<svg viewBox="0 0 24 24" width="100%" height="100%" fill="currentColor"
55
style="vertical-align: top" focusable="false">
66
<path d="M0 0h24v24H0z" fill="none"/>
77
<path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"/>
88
</svg>
99
</mat-icon>
10+
11+
<ng-content select="[matDatepickerToggleIcon]"></ng-content>
1012
</button>

src/lib/datepicker/datepicker-toggle.ts

+12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
OnDestroy,
1818
SimpleChanges,
1919
ViewEncapsulation,
20+
Directive,
21+
ContentChild,
2022
} from '@angular/core';
2123
import {merge} from 'rxjs/observable/merge';
2224
import {of as observableOf} from 'rxjs/observable/of';
@@ -25,6 +27,13 @@ import {MatDatepicker} from './datepicker';
2527
import {MatDatepickerIntl} from './datepicker-intl';
2628

2729

30+
/** Can be used to override the icon of a `matDatepickerToggle`. */
31+
@Directive({
32+
selector: '[matDatepickerToggleIcon]'
33+
})
34+
export class MatDatepickerToggleIcon {}
35+
36+
2837
@Component({
2938
moduleId: module.id,
3039
selector: 'mat-datepicker-toggle',
@@ -54,6 +63,9 @@ export class MatDatepickerToggle<D> implements AfterContentInit, OnChanges, OnDe
5463
}
5564
private _disabled: boolean;
5665

66+
/** Custom icon set by the consumer. */
67+
@ContentChild(MatDatepickerToggleIcon) _customIcon: MatDatepickerToggleIcon;
68+
5769
constructor(public _intl: MatDatepickerIntl, private _changeDetectorRef: ChangeDetectorRef) {}
5870

5971
ngOnChanges(changes: SimpleChanges) {

src/lib/datepicker/datepicker.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ can easily be used as a prefix or suffix on the material input:
3232
</mat-form-field>
3333
```
3434

35+
If you want to customize the icon that is rendered inside the `mat-datepicker-toggle`, you can do so
36+
by using the `matDatepickerToggleIcon` directive:
37+
38+
<!-- example(datepicker-custom-icon) -->
39+
3540
### Setting the calendar starting view
3641

3742
The `startView` property of `<mat-datepicker>` can be used to set the view that will show up when
@@ -99,7 +104,7 @@ Each validation property has a different error that can be checked:
99104
* A value that violates the `min` property will have a `matDatepickerMin` error.
100105
* A value that violates the `max` property will have a `matDatepickerMax` error.
101106
* A value that violates the `matDatepickerFilter` property will have a `matDatepickerFilter` error.
102-
107+
103108
### Input and change events
104109

105110
The input's native `(input)` and `(change)` events will only trigger due to user interaction with
@@ -171,7 +176,7 @@ It's also possible to set the locale at runtime using the `setLocale` method of
171176
The datepicker was built to be date implementation agnostic. This means that it can be made to work
172177
with a variety of different date implementations. However it also means that developers need to make
173178
sure to provide the appropriate pieces for the datepicker to work with their chosen implementation.
174-
The easiest way to ensure this is just to import one of the pre-made modules:
179+
The easiest way to ensure this is just to import one of the pre-made modules:
175180

176181
|Module |Date type|Supported locales |Dependencies |Import from |
177182
|---------------------|---------|-----------------------------------------------------------------------|----------------------------------|----------------------------------|
@@ -338,7 +343,7 @@ In multi-year view:
338343

339344
This error is thrown if you have not provided all of the injectables the datepicker needs to work.
340345
The easiest way to resolve this is to import the `MatNativeDateModule` or `MatMomentDateModule` in
341-
your application's root module. See
346+
your application's root module. See
342347
[_Choosing a date implementation_](#choosing-a-date-implementation-and-date-format-settings)) for
343348
more information.
344349

src/lib/datepicker/datepicker.spec.ts

+24-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('MatDatepicker', () => {
4949
}
5050

5151
afterEach(inject([OverlayContainer], (container: OverlayContainer) => {
52-
container.getContainerElement().parentNode!.removeChild(container.getContainerElement());
52+
container.ngOnDestroy();
5353
}));
5454

5555
describe('with MatNativeDateModule', () => {
@@ -761,6 +761,19 @@ describe('MatDatepicker', () => {
761761
}));
762762
});
763763

764+
describe('datepicker with custom mat-datepicker-toggle icon', () => {
765+
it('should be able to override the mat-datepicker-toggle icon', fakeAsync(() => {
766+
const fixture = createComponent(DatepickerWithCustomIcon, [MatNativeDateModule]);
767+
fixture.detectChanges();
768+
769+
expect(fixture.nativeElement.querySelector('.mat-datepicker-toggle .custom-icon'))
770+
.toBeTruthy('Expected custom icon to be rendered.');
771+
772+
expect(fixture.nativeElement.querySelector('.mat-datepicker-toggle mat-icon'))
773+
.toBeFalsy('Expected default icon to be removed.');
774+
}));
775+
});
776+
764777
describe('datepicker inside mat-form-field', () => {
765778
let fixture: ComponentFixture<FormFieldDatepicker>;
766779
let testComponent: FormFieldDatepicker;
@@ -1308,6 +1321,16 @@ class DatepickerWithToggle {
13081321
touchUI = true;
13091322
}
13101323

1324+
@Component({
1325+
template: `
1326+
<input [matDatepicker]="d">
1327+
<mat-datepicker-toggle [for]="d">
1328+
<div class="custom-icon" matDatepickerToggleIcon></div>
1329+
</mat-datepicker-toggle>
1330+
<mat-datepicker #d></mat-datepicker>
1331+
`,
1332+
})
1333+
class DatepickerWithCustomIcon {}
13111334

13121335
@Component({
13131336
template: `
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/** No CSS for this example */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<mat-form-field class="example-full-width">
2+
<input matInput [matDatepicker]="picker" placeholder="Choose a date">
3+
<mat-datepicker-toggle matSuffix [for]="picker">
4+
<mat-icon matDatepickerToggleIcon>keyboard_arrow_down</mat-icon>
5+
</mat-datepicker-toggle>
6+
<mat-datepicker #picker></mat-datepicker>
7+
</mat-form-field>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {Component} from '@angular/core';
2+
3+
/** @title Datepicker with custom icon */
4+
@Component({
5+
selector: 'datepicker-custom-icon-example',
6+
templateUrl: 'datepicker-custom-icon-example.html',
7+
styleUrls: ['datepicker-custom-icon-example.css'],
8+
})
9+
export class DatepickerCustomIconExample {}

src/material-examples/example-module.ts

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {ChipsInputExample} from './chips-input/chips-input-example';
2222
import {ChipsOverviewExample} from './chips-overview/chips-overview-example';
2323
import {ChipsStackedExample} from './chips-stacked/chips-stacked-example';
2424
import {DatepickerApiExample} from './datepicker-api/datepicker-api-example';
25+
import {DatepickerCustomIconExample} from './datepicker-custom-icon/datepicker-custom-icon-example';
2526
import {DatepickerDisabledExample} from './datepicker-disabled/datepicker-disabled-example';
2627
import {DatepickerEventsExample} from './datepicker-events/datepicker-events-example';
2728
import {DatepickerFilterExample} from './datepicker-filter/datepicker-filter-example';
@@ -200,6 +201,10 @@ export const EXAMPLE_COMPONENTS: {[key: string]: LiveExample} = {
200201
title: 'Datepicker open method',
201202
component: DatepickerApiExample
202203
},
204+
'datepicker-custom-icon': {
205+
title: 'Datepicker with custom icon',
206+
component: DatepickerCustomIconExample
207+
},
203208
'datepicker-disabled': {
204209
title: 'Disabled datepicker',
205210
component: DatepickerDisabledExample
@@ -636,6 +641,7 @@ export const EXAMPLE_LIST = [
636641
ChipsOverviewExample,
637642
ChipsStackedExample,
638643
DatepickerApiExample,
644+
DatepickerCustomIconExample,
639645
DatepickerDisabledExample,
640646
DatepickerEventsExample,
641647
DatepickerFilterExample,

0 commit comments

Comments
 (0)