From ec833b4f5b311ddec00a33a3ff38d4018ed16da9 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 24 Sep 2017 11:39:13 -0500 Subject: [PATCH 1/5] chore(audit): audit boolean inputs #6370 --- src/lib/datepicker/datepicker.ts | 11 +++++++++-- src/lib/expansion/accordion-item.ts | 7 +++++-- src/lib/expansion/expansion-panel.ts | 11 ++++++++++- src/lib/menu/menu-directive.ts | 9 ++++++++- src/lib/slide-toggle/slide-toggle.ts | 1 + src/lib/tabs/tab-group.ts | 4 ++-- src/lib/tabs/tab-header.ts | 2 ++ 7 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index d0873f7f1980..0bfef34c4e42 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -143,7 +143,14 @@ export class MdDatepicker implements OnDestroy { * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather * than a popup and elements have more padding to allow for bigger touch targets. */ - @Input() touchUi = false; + @Input() + get touchUi() { + return this._touchUi; + } + set touchUi(value: any) { + this._touchUi = coerceBooleanProperty(value); + } + private _touchUi = false; /** Whether the datepicker pop-up should be disabled. */ @Input() @@ -224,7 +231,7 @@ export class MdDatepicker implements OnDestroy { } } - ngOnDestroy() { + ngOnDestroy(): void { this.close(); this._inputSubscription.unsubscribe(); this._disabledChange.complete(); diff --git a/src/lib/expansion/accordion-item.ts b/src/lib/expansion/accordion-item.ts index a1a83054aa29..b80e53567e39 100644 --- a/src/lib/expansion/accordion-item.ts +++ b/src/lib/expansion/accordion-item.ts @@ -17,6 +17,7 @@ import { } from '@angular/core'; import {UniqueSelectionDispatcher} from '@angular/material/core'; import {CdkAccordion} from './accordion'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; /** Used to generate unique ID for each expansion panel. */ let nextId = 0; @@ -38,8 +39,10 @@ export class AccordionItem implements OnDestroy { /** Whether the AccordionItem is expanded. */ @Input() - get expanded(): boolean { return this._expanded; } - set expanded(expanded: boolean) { + get expanded(): any { return this._expanded; } + set expanded(expanded: any) { + expanded = coerceBooleanProperty(expanded); + // Only emit events and update the internal value if the value changes. if (this._expanded !== expanded) { this._expanded = expanded; diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 5ed09e2fcf79..6b832695d9d1 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -25,6 +25,7 @@ import {CanDisable, mixinDisabled, UniqueSelectionDispatcher} from '@angular/mat import {Subject} from 'rxjs/Subject'; import {MdAccordion} from './accordion'; import {AccordionItem} from './accordion-item'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; // Boilerplate for applying mixins to MdExpansionPanel. @@ -79,8 +80,16 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2, }) export class MdExpansionPanel extends _MdExpansionPanelMixinBase implements CanDisable, OnChanges, OnDestroy { + /** Whether the toggle indicator should be hidden. */ - @Input() hideToggle: boolean = false; + @Input() + get hideToggle(): any { + return this._hideToggle; + } + set hideToggle(value: any) { + this._hideToggle = coerceBooleanProperty(value); + } + private _hideToggle = false; /** Stream that emits for changes in `@Input` properties. */ _inputChanges = new Subject(); diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index aa7d1c8dfef1..400d47b85c1c 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -36,6 +36,7 @@ import {throwMdMenuInvalidPositionX, throwMdMenuInvalidPositionY} from './menu-e import {MdMenuItem} from './menu-item'; import {MdMenuPanel} from './menu-panel'; import {MenuPositionX, MenuPositionY} from './menu-positions'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; /** Default `md-menu` options that can be overridden. */ @@ -119,7 +120,13 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { @ContentChildren(MdMenuItem) items: QueryList; /** Whether the menu should overlap its trigger. */ - @Input() overlapTrigger = this._defaultOptions.overlapTrigger; + @Input() + set overlapTrigger(value: any) { + this._defaultOptions.overlapTrigger = coerceBooleanProperty(value); + } + get overlapTrigger(): any { + return this._defaultOptions.overlapTrigger; + } /** * This method takes classes set on the host md-menu element and applies them on the diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index 114bf16e3184..3b8341d2bd02 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -125,6 +125,7 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy, @Input() get checked(): boolean { return this._checked; } set checked(value) { + value = coerceBooleanProperty(value); this._checked = !!value; this._changeDetectorRef.markForCheck(); } diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts index a80bc765b5b6..137011692732 100644 --- a/src/lib/tabs/tab-group.ts +++ b/src/lib/tabs/tab-group.ts @@ -24,7 +24,7 @@ import { OnDestroy, ViewEncapsulation, } from '@angular/core'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; import {Subscription} from 'rxjs/Subscription'; import {MdTab} from './tab'; import {merge} from 'rxjs/observable/merge'; @@ -110,7 +110,7 @@ export class MdTabGroup extends _MdTabGroupMixinBase implements AfterContentInit /** The index of the active tab. */ @Input() - set selectedIndex(value: number | null) { this._indexToSelect = value; } + set selectedIndex(value: number | null) { this._indexToSelect = coerceNumberProperty(value); } get selectedIndex(): number | null { return this._selectedIndex; } private _selectedIndex: number | null = null; diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index 782c20284083..89e2aaf76138 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -37,6 +37,7 @@ import {of as observableOf} from 'rxjs/observable/of'; import {Subscription} from 'rxjs/Subscription'; import {MdInkBar} from './ink-bar'; import {MdTabLabelWrapper} from './tab-label-wrapper'; +import {coerceNumberProperty} from '@angular/cdk/coercion'; /** @@ -124,6 +125,7 @@ export class MdTabHeader extends _MdTabHeaderMixinBase @Input() get selectedIndex(): number { return this._selectedIndex; } set selectedIndex(value: number) { + value = coerceNumberProperty(value); this._selectedIndexChanged = this._selectedIndex != value; this._selectedIndex = value; this._focusIndex = value; From be2fefd9794c04369487f1430a61522384b1bbfe Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 30 Sep 2017 14:04:41 -0500 Subject: [PATCH 2/5] chore(nit): nit feedback --- src/lib/slide-toggle/slide-toggle.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index fd9cfbe0c81f..d17cd114405f 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -123,8 +123,7 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro @Input() get checked(): boolean { return this._checked; } set checked(value) { - value = coerceBooleanProperty(value); - this._checked = !!value; + this._checked = !!coerceBooleanProperty(value); this._changeDetectorRef.markForCheck(); } /** An event will be dispatched each time the slide-toggle changes its value. */ From f89ae6fad90efc7182fe98c434af7c1c32b61205 Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 30 Sep 2017 18:10:00 -0500 Subject: [PATCH 3/5] chore(nit): remove !! --- src/lib/slide-toggle/slide-toggle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index d17cd114405f..394c2c545383 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -123,7 +123,7 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro @Input() get checked(): boolean { return this._checked; } set checked(value) { - this._checked = !!coerceBooleanProperty(value); + this._checked = coerceBooleanProperty(value); this._changeDetectorRef.markForCheck(); } /** An event will be dispatched each time the slide-toggle changes its value. */ From 682c2fda48fd4cc8d1223b81902434ede2f3a8e8 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 1 Oct 2017 10:45:58 -0500 Subject: [PATCH 4/5] chore(nit): update per feedback --- src/lib/datepicker/datepicker.ts | 8 ++++---- src/lib/menu/menu-directive.ts | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index 4050aca43e05..d2d9d052fb33 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -143,21 +143,21 @@ export class MatDatepicker implements OnDestroy { * than a popup and elements have more padding to allow for bigger touch targets. */ @Input() - get touchUi() { + get touchUi(): boolean { return this._touchUi; } - set touchUi(value: any) { + set touchUi(value: boolean) { this._touchUi = coerceBooleanProperty(value); } private _touchUi = false; /** Whether the datepicker pop-up should be disabled. */ @Input() - get disabled() { + get disabled(): boolean { return this._disabled === undefined && this._datepickerInput ? this._datepickerInput.disabled : this._disabled; } - set disabled(value: any) { + set disabled(value: boolean) { const newValue = coerceBooleanProperty(value); if (newValue !== this._disabled) { diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index 2465640170d3..c4046a16ba15 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -122,12 +122,13 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { /** Whether the menu should overlap its trigger. */ @Input() - set overlapTrigger(value: any) { - this._defaultOptions.overlapTrigger = coerceBooleanProperty(value); + set overlapTrigger(value: boolean) { + this._overlapTrigger = coerceBooleanProperty(value); } - get overlapTrigger(): any { - return this._defaultOptions.overlapTrigger; + get overlapTrigger(): boolean { + return this._overlapTrigger; } + private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger; /** * This method takes classes set on the host mat-menu element and applies them on the From 6095c65836e854282949e1d563a7173fb2718716 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 16 Oct 2017 11:01:21 -0500 Subject: [PATCH 5/5] chore(nit): address feedback --- src/cdk/coercion/number-property.ts | 2 ++ src/lib/datepicker/datepicker.ts | 2 +- src/lib/expansion/expansion-panel.ts | 4 ++-- src/lib/tabs/tab-group.ts | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cdk/coercion/number-property.ts b/src/cdk/coercion/number-property.ts index d10568e2700f..38d783974a6b 100644 --- a/src/cdk/coercion/number-property.ts +++ b/src/cdk/coercion/number-property.ts @@ -7,6 +7,8 @@ */ /** Coerces a data-bound value (typically a string) to a number. */ +export function coerceNumberProperty(value: any): number; +export function coerceNumberProperty(value: any, fallback: D): number | D; export function coerceNumberProperty(value: any, fallbackValue = 0) { // parseFloat(value) handles most of the cases we're interested in (it treats null, empty string, // and other non-number values as NaN, where Number just uses 0) but it considers the string diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index d6ffe3d773ac..11dc501b0932 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -232,7 +232,7 @@ export class MatDatepicker implements OnDestroy { } } - ngOnDestroy(): void { + ngOnDestroy() { this.close(); this._inputSubscription.unsubscribe(); this._disabledChange.complete(); diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 7a8484448dce..5aa4ac4a85be 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -94,10 +94,10 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase /** Whether the toggle indicator should be hidden. */ @Input() - get hideToggle(): any { + get hideToggle(): boolean { return this._hideToggle; } - set hideToggle(value: any) { + set hideToggle(value: boolean) { this._hideToggle = coerceBooleanProperty(value); } private _hideToggle = false; diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts index 8ba96483a717..da581c60b5bb 100644 --- a/src/lib/tabs/tab-group.ts +++ b/src/lib/tabs/tab-group.ts @@ -109,7 +109,9 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn /** The index of the active tab. */ @Input() - set selectedIndex(value: number | null) { this._indexToSelect = coerceNumberProperty(value); } + set selectedIndex(value: number | null) { + this._indexToSelect = coerceNumberProperty(value, null); + } get selectedIndex(): number | null { return this._selectedIndex; } private _selectedIndex: number | null = null;