Skip to content

feat(datepicker): allow for mat-datepicker-toggle icon to be customized #9487

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
Jan 24, 2018
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
4 changes: 3 additions & 1 deletion src/lib/datepicker/datepicker-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from './datepicker';
import {MatDatepickerInput} from './datepicker-input';
import {MatDatepickerIntl} from './datepicker-intl';
import {MatDatepickerToggle} from './datepicker-toggle';
import {MatDatepickerToggle, MatDatepickerToggleIcon} from './datepicker-toggle';
import {MatMonthView} from './month-view';
import {MatMultiYearView} from './multi-year-view';
import {MatYearView} from './year-view';
Expand All @@ -44,6 +44,7 @@ import {MatYearView} from './year-view';
MatDatepickerContent,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepickerToggleIcon,
MatMonthView,
MatYearView,
MatMultiYearView,
Expand All @@ -55,6 +56,7 @@ import {MatYearView} from './year-view';
MatDatepickerContent,
MatDatepickerInput,
MatDatepickerToggle,
MatDatepickerToggleIcon,
MatMonthView,
MatYearView,
MatMultiYearView,
Expand Down
4 changes: 3 additions & 1 deletion src/lib/datepicker/datepicker-toggle.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<button mat-icon-button type="button" [attr.aria-label]="_intl.openCalendarLabel"
[disabled]="disabled" (click)="_open($event)">
<mat-icon>
<mat-icon *ngIf="!_customIcon">
<svg viewBox="0 0 24 24" width="100%" height="100%" fill="currentColor"
style="vertical-align: top" focusable="false">
<path d="M0 0h24v24H0z" fill="none"/>
<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"/>
</svg>
</mat-icon>

<ng-content select="[matDatepickerToggleIcon]"></ng-content>
</button>
12 changes: 12 additions & 0 deletions src/lib/datepicker/datepicker-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
OnDestroy,
SimpleChanges,
ViewEncapsulation,
Directive,
ContentChild,
} from '@angular/core';
import {merge} from 'rxjs/observable/merge';
import {of as observableOf} from 'rxjs/observable/of';
Expand All @@ -25,6 +27,13 @@ import {MatDatepicker} from './datepicker';
import {MatDatepickerIntl} from './datepicker-intl';


/** Can be used to override the icon of a `matDatepickerToggle`. */
@Directive({
selector: '[matDatepickerToggleIcon]'
})
export class MatDatepickerToggleIcon {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a special directive? what if we just treat any projected content as a custom icon?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but it gets a little dirtier when you try to figure out whether to hide the default icon. We'd have to loop through all the children of the content element and count the amount of element nodes (skipping comments, text nodes etc.) on every change detection.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I thought there was an easier way to check if any content was projected, but it looks like you're right



@Component({
moduleId: module.id,
selector: 'mat-datepicker-toggle',
Expand Down Expand Up @@ -53,6 +62,9 @@ export class MatDatepickerToggle<D> implements AfterContentInit, OnChanges, OnDe
}
private _disabled: boolean;

/** Custom icon set by the consumer. */
@ContentChild(MatDatepickerToggleIcon) _customIcon: MatDatepickerToggleIcon;

constructor(public _intl: MatDatepickerIntl, private _changeDetectorRef: ChangeDetectorRef) {}

ngOnChanges(changes: SimpleChanges) {
Expand Down
11 changes: 8 additions & 3 deletions src/lib/datepicker/datepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ can easily be used as a prefix or suffix on the material input:
</mat-form-field>
```

If you want to customize the icon that is rendered inside the `mat-datepicker-toggle`, you can do so
by using the `matDatepickerToggleIcon` directive:

<!-- example(datepicker-custom-icon) -->

### Setting the calendar starting view

The `startView` property of `<mat-datepicker>` can be used to set the view that will show up when
Expand Down Expand Up @@ -99,7 +104,7 @@ Each validation property has a different error that can be checked:
* A value that violates the `min` property will have a `matDatepickerMin` error.
* A value that violates the `max` property will have a `matDatepickerMax` error.
* A value that violates the `matDatepickerFilter` property will have a `matDatepickerFilter` error.

### Input and change events

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

|Module |Date type|Supported locales |Dependencies |Import from |
|---------------------|---------|-----------------------------------------------------------------------|----------------------------------|----------------------------------|
Expand Down Expand Up @@ -338,7 +343,7 @@ In multi-year view:

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

Expand Down
25 changes: 24 additions & 1 deletion src/lib/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('MatDatepicker', () => {
}

afterEach(inject([OverlayContainer], (container: OverlayContainer) => {
container.getContainerElement().parentNode!.removeChild(container.getContainerElement());
container.ngOnDestroy();
}));

describe('with MatNativeDateModule', () => {
Expand Down Expand Up @@ -742,6 +742,19 @@ describe('MatDatepicker', () => {
}));
});

describe('datepicker with custom mat-datepicker-toggle icon', () => {
it('should be able to override the mat-datepicker-toggle icon', fakeAsync(() => {
const fixture = createComponent(DatepickerWithCustomIcon, [MatNativeDateModule]);
fixture.detectChanges();

expect(fixture.nativeElement.querySelector('.mat-datepicker-toggle .custom-icon'))
.toBeTruthy('Expected custom icon to be rendered.');

expect(fixture.nativeElement.querySelector('.mat-datepicker-toggle mat-icon'))
.toBeFalsy('Expected default icon to be removed.');
}));
});

describe('datepicker inside mat-form-field', () => {
let fixture: ComponentFixture<FormFieldDatepicker>;
let testComponent: FormFieldDatepicker;
Expand Down Expand Up @@ -1289,6 +1302,16 @@ class DatepickerWithToggle {
touchUI = true;
}

@Component({
template: `
<input [matDatepicker]="d">
<mat-datepicker-toggle [for]="d">
<div class="custom-icon" matDatepickerToggleIcon></div>
</mat-datepicker-toggle>
<mat-datepicker #d></mat-datepicker>
`,
})
class DatepickerWithCustomIcon {}

@Component({
template: `
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/** No CSS for this example */
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<mat-form-field class="example-full-width">
<input matInput [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker">
<mat-icon matDatepickerToggleIcon>keyboard_arrow_down</mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

/** @title Datepicker with custom icon */
@Component({
selector: 'datepicker-custom-icon-example',
templateUrl: 'datepicker-custom-icon-example.html',
styleUrls: ['datepicker-custom-icon-example.css'],
})
export class DatepickerCustomIconExample {}
6 changes: 6 additions & 0 deletions src/material-examples/example-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {ChipsInputExample} from './chips-input/chips-input-example';
import {ChipsOverviewExample} from './chips-overview/chips-overview-example';
import {ChipsStackedExample} from './chips-stacked/chips-stacked-example';
import {DatepickerApiExample} from './datepicker-api/datepicker-api-example';
import {DatepickerCustomIconExample} from './datepicker-custom-icon/datepicker-custom-icon-example';
import {DatepickerDisabledExample} from './datepicker-disabled/datepicker-disabled-example';
import {DatepickerEventsExample} from './datepicker-events/datepicker-events-example';
import {DatepickerFilterExample} from './datepicker-filter/datepicker-filter-example';
Expand Down Expand Up @@ -197,6 +198,10 @@ export const EXAMPLE_COMPONENTS: {[key: string]: LiveExample} = {
title: 'Datepicker open method',
component: DatepickerApiExample
},
'datepicker-custom-icon': {
title: 'Datepicker with custom icon',
component: DatepickerCustomIconExample
},
'datepicker-disabled': {
title: 'Disabled datepicker',
component: DatepickerDisabledExample
Expand Down Expand Up @@ -621,6 +626,7 @@ export const EXAMPLE_LIST = [
ChipsOverviewExample,
ChipsStackedExample,
DatepickerApiExample,
DatepickerCustomIconExample,
DatepickerDisabledExample,
DatepickerEventsExample,
DatepickerFilterExample,
Expand Down