-
+ |
{{ calendar.weekNumbers[i] }}
();
@Output() onViewMode = new EventEmitter();
@@ -89,9 +89,9 @@ export class BsDaysCalendarViewComponent {
@Output() onHover = new EventEmitter();
@Output() onHoverWeek = new EventEmitter();
- isWeekHovered: boolean;
+ isWeekHovered?: boolean;
isiOS: boolean;
- isShowTooltip: boolean;
+ isShowTooltip?: boolean;
constructor(private _config: BsDatepickerConfig) {
this.isiOS = (/iPad|iPhone|iPod/.test(navigator.platform) ||
diff --git a/src/datepicker/themes/bs/bs-months-calendar-view.component.ts b/src/datepicker/themes/bs/bs-months-calendar-view.component.ts
index a21f1515f8..d82348d88b 100644
--- a/src/datepicker/themes/bs/bs-months-calendar-view.component.ts
+++ b/src/datepicker/themes/bs/bs-months-calendar-view.component.ts
@@ -20,7 +20,7 @@ import {
-
+
();
@Output() onViewMode = new EventEmitter();
diff --git a/src/datepicker/themes/bs/bs-years-calendar-view.component.ts b/src/datepicker/themes/bs/bs-years-calendar-view.component.ts
index 8bdd6d9273..5b0a658c29 100644
--- a/src/datepicker/themes/bs/bs-years-calendar-view.component.ts
+++ b/src/datepicker/themes/bs/bs-years-calendar-view.component.ts
@@ -21,7 +21,7 @@ import {
-
+
();
@Output() onViewMode = new EventEmitter();
diff --git a/src/datepicker/tsconfig.json b/src/datepicker/tsconfig.json
index 08cb9fc56b..8bcea89917 100644
--- a/src/datepicker/tsconfig.json
+++ b/src/datepicker/tsconfig.json
@@ -15,12 +15,12 @@
],
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
- "strict": false,
- "noImplicitReturns": false,
+ "strict": true,
+ "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"angularCompilerOptions": {
- "strictInjectionParameters": false,
- "strictTemplates": false
+ "strictInjectionParameters": true,
+ "strictTemplates": true
}
}
diff --git a/src/datepicker/tsconfig.spec.json b/src/datepicker/tsconfig.spec.json
index 2c5eb23fd1..bab26dee25 100644
--- a/src/datepicker/tsconfig.spec.json
+++ b/src/datepicker/tsconfig.spec.json
@@ -1,6 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
+ "strict": false,
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
diff --git a/src/datepicker/utils/bs-calendar-utils.ts b/src/datepicker/utils/bs-calendar-utils.ts
index 25e3edad82..e009679c98 100644
--- a/src/datepicker/utils/bs-calendar-utils.ts
+++ b/src/datepicker/utils/bs-calendar-utils.ts
@@ -23,31 +23,36 @@ export function getStartingDayOfCalendar(date: Date,
return shiftDate(date, {day: -offset});
}
-export function calculateDateOffset(weekday: number, startingDayOffset: number): number {
- if (startingDayOffset === 0) {
+export function calculateDateOffset(weekday: number, startingDayOffset?: number): number {
+ const _startingDayOffset = Number(startingDayOffset);
+ if (isNaN(_startingDayOffset)) {
+ return 0;
+ }
+
+ if (_startingDayOffset === 0) {
return weekday;
}
- const offset = weekday - startingDayOffset % 7;
+ const offset = weekday - _startingDayOffset % 7;
return offset < 0 ? offset + 7 : offset;
}
-export function isMonthDisabled(date: Date, min: Date, max: Date): boolean {
+export function isMonthDisabled(date: Date, min?: Date, max?: Date): boolean {
const minBound = min && isBefore(endOf(date, 'month'), min, 'day');
const maxBound = max && isAfter(startOf(date, 'month'), max, 'day');
- return minBound || maxBound;
+ return minBound || maxBound || false;
}
-export function isYearDisabled(date: Date, min: Date, max: Date): boolean {
+export function isYearDisabled(date: Date, min?: Date, max?: Date): boolean {
const minBound = min && isBefore(endOf(date, 'year'), min, 'day');
const maxBound = max && isAfter(startOf(date, 'year'), max, 'day');
- return minBound || maxBound;
+ return minBound || maxBound || false;
}
-export function isDisabledDate(date: Date, datesDisabled: Date[]): boolean {
+export function isDisabledDate(date?: Date, datesDisabled?: Date[]): boolean {
if (!datesDisabled || !isArray(datesDisabled) || !datesDisabled.length) {
return false;
}
@@ -55,7 +60,7 @@ export function isDisabledDate(date: Date, datesDisabled: Date[]): boolean {
return datesDisabled.some((dateDisabled: Date) => isSame(date, dateDisabled, 'date'));
}
-export function isEnabledDate(date: Date, datesEnabled: Date[]): boolean {
+export function isEnabledDate(date?: Date, datesEnabled?: Date[]): boolean {
if (!datesEnabled || !isArray(datesEnabled) || !datesEnabled.length) {
return false;
}
@@ -63,8 +68,8 @@ export function isEnabledDate(date: Date, datesEnabled: Date[]): boolean {
return !datesEnabled.some((enabledDate: Date) => isSame(date, enabledDate, 'date'));
}
-export function getYearsCalendarInitialDate(state: BsDatepickerState, calendarIndex = 0): Date {
+export function getYearsCalendarInitialDate(state: BsDatepickerState, calendarIndex = 0): Date | undefined {
const model = state && state.yearsCalendarModel && state.yearsCalendarModel[calendarIndex];
- return model && model.years && model.years[0] && model.years[0][0] && model.years[0][0].date;
+ return model?.years[0] && model.years[0][0] && model.years[0][0].date;
}
diff --git a/src/datepicker/yearpicker.component.ts b/src/datepicker/yearpicker.component.ts
index e2ebfcb108..fecfb7c4df 100644
--- a/src/datepicker/yearpicker.component.ts
+++ b/src/datepicker/yearpicker.component.ts
@@ -7,53 +7,57 @@ import { DatePickerInnerComponent } from './datepicker-inner.component';
@Component({
selector: 'yearpicker',
template: `
-
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
- |
-
-
-
+
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+ |
+
+
+
`,
styles: [
`
- :host .btn-info .text-success {
- color: #fff !important;
- }
- `
+ :host .btn-info .text-success {
+ color: #fff !important;
+ }
+ `
]
})
export class YearPickerComponent implements OnInit {
datePicker: DatePickerInnerComponent;
- title: string;
+ title?: string;
rows = [];
constructor(datePicker: DatePickerInnerComponent) {
@@ -71,21 +75,25 @@ export class YearPickerComponent implements OnInit {
this.datePicker.stepYear = { years: this.datePicker.yearRange };
this.datePicker.setRefreshViewHandler(function(): void {
- const years = new Array(this.yearRange);
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
+ const that = this;
+ const years = new Array(that.yearRange);
let date: Date;
- const start = self.getStartingYear(this.activeDate.getFullYear());
+ const start = self.getStartingYear(that.activeDate.getFullYear()) || 0;
- for (let i = 0; i < this.yearRange; i++) {
+ for (let i = 0; i < that.yearRange; i++) {
date = new Date(start + i, 0, 1);
- date = this.fixTimeZone(date);
- years[i] = this.createDateObject(date, this.formatYear);
- years[i].uid = this.uniqueId + '-' + i;
+ date = that.fixTimeZone(date);
+ years[i] = that.createDateObject(date, that.formatYear);
+ years[i].uid = that.uniqueId + '-' + i;
}
- self.title = [years[0].label, years[this.yearRange - 1].label].join(
+ self.title = [years[0].label, years[that.yearRange - 1].label].join(
' - '
);
- self.rows = this.split(years, self.datePicker.yearColLimit);
+ self.rows = that.split(years, self.datePicker.yearColLimit);
}, 'year');
this.datePicker.setCompareHandler(function(
@@ -98,10 +106,13 @@ export class YearPickerComponent implements OnInit {
this.datePicker.refreshView();
}
- protected getStartingYear(year: number): number {
+ protected getStartingYear(year: number): number | undefined {
// todo: parseInt
- return (
- (year - 1) / this.datePicker.yearRange * this.datePicker.yearRange + 1
- );
+ if (this.datePicker && this.datePicker.yearRange) {
+ return (
+ (year - 1) / this.datePicker.yearRange * this.datePicker.yearRange + 1
+ );
+ }
+ return undefined;
}
}
diff --git a/src/dropdown/bs-dropdown-menu.directive.ts b/src/dropdown/bs-dropdown-menu.directive.ts
index 86d5c81258..2fdc8898cd 100644
--- a/src/dropdown/bs-dropdown-menu.directive.ts
+++ b/src/dropdown/bs-dropdown-menu.directive.ts
@@ -9,7 +9,7 @@ export class BsDropdownMenuDirective {
constructor(
_state: BsDropdownState,
_viewContainer: ViewContainerRef,
- _templateRef: TemplateRef
+ _templateRef: TemplateRef
) {
_state.resolveDropdownMenu({
templateRef: _templateRef,
diff --git a/src/dropdown/bs-dropdown-toggle.directive.ts b/src/dropdown/bs-dropdown-toggle.directive.ts
index c327905a59..d6196971f8 100644
--- a/src/dropdown/bs-dropdown-toggle.directive.ts
+++ b/src/dropdown/bs-dropdown-toggle.directive.ts
@@ -21,12 +21,12 @@ import { BsDropdownDirective } from './bs-dropdown.directive';
}
})
export class BsDropdownToggleDirective implements OnDestroy {
- @HostBinding('attr.disabled') isDisabled: boolean = null;
- @HostBinding('attr.aria-expanded') isOpen: boolean;
+ @HostBinding('attr.disabled') isDisabled: undefined | true;
+ @HostBinding('attr.aria-expanded') isOpen = false;
private _subscriptions: Subscription[] = [];
- private _documentClickListener: () => void;
- private _escKeyUpListener: () => void;
+ private _documentClickListener?: () => void;
+ private _escKeyUpListener?: () => void;
constructor(
private _changeDetectorRef: ChangeDetectorRef,
@@ -59,8 +59,8 @@ export class BsDropdownToggleDirective implements OnDestroy {
}
});
} else {
- this._documentClickListener();
- this._escKeyUpListener();
+ this._documentClickListener && this._documentClickListener();
+ this._escKeyUpListener && this._escKeyUpListener();
}
}
)
@@ -68,9 +68,8 @@ export class BsDropdownToggleDirective implements OnDestroy {
// populate disabled state
this._subscriptions.push(
- this._state.isDisabledChange.subscribe(
- (value: boolean) => (this.isDisabled = value || null)
- )
+ this._state.isDisabledChange
+ .subscribe((value: boolean) => this.isDisabled = value || void 0)
);
}
diff --git a/src/dropdown/bs-dropdown.directive.ts b/src/dropdown/bs-dropdown.directive.ts
index 3b46b13c92..d1659e5904 100644
--- a/src/dropdown/bs-dropdown.directive.ts
+++ b/src/dropdown/bs-dropdown.directive.ts
@@ -37,21 +37,21 @@ export class BsDropdownDirective implements OnInit, OnDestroy {
/**
* Placement of a popover. Accepts: "top", "bottom", "left", "right"
*/
- @Input() placement: string;
+ @Input() placement?: string;
/**
* Specifies events that should trigger. Supports a space separated list of
* event names.
*/
- @Input() triggers: string;
+ @Input() triggers?: string;
/**
* A selector specifying the element the popover should be appended to.
*/
- @Input() container: string;
+ @Input() container?: string;
/**
* This attribute indicates that the dropdown should be opened upwards
*/
- @Input() dropup: boolean;
+ @Input() dropup = true;
/**
* Indicates that dropdown will be closed on item or document click,
@@ -154,8 +154,8 @@ export class BsDropdownDirective implements OnInit, OnDestroy {
// todo: move to component loader
private _isInlineOpen = false;
- private _inlinedMenu: EmbeddedViewRef;
- private _isDisabled: boolean;
+ private _inlinedMenu?: EmbeddedViewRef;
+ private _isDisabled = false;
private _subscriptions: Subscription[] = [];
private _isInited = false;
private _factoryDropDownAnimation: AnimationFactory;
@@ -243,7 +243,9 @@ export class BsDropdownDirective implements OnInit, OnDestroy {
this._inlinedMenu = this._dropdown._inlineViewRef;
this.addBs4Polyfills();
- this._renderer.addClass(this._inlinedMenu.rootNodes[0].parentNode, 'open');
+ if (this._inlinedMenu) {
+ this._renderer.addClass(this._inlinedMenu.rootNodes[0].parentNode, 'open');
+ }
this.playAnimation();
}
@@ -345,8 +347,9 @@ export class BsDropdownDirective implements OnInit, OnDestroy {
private playAnimation(): void {
if (this._state.isAnimated && this._inlinedMenu) {
setTimeout(() => {
- this._factoryDropDownAnimation.create(this._inlinedMenu.rootNodes[0])
- .play();
+ if (this._inlinedMenu) {
+ this._factoryDropDownAnimation.create(this._inlinedMenu.rootNodes[0]).play();
+ }
});
}
}
diff --git a/src/dropdown/bs-dropdown.state.ts b/src/dropdown/bs-dropdown.state.ts
index 43652990d2..0f6cc4ab28 100644
--- a/src/dropdown/bs-dropdown.state.ts
+++ b/src/dropdown/bs-dropdown.state.ts
@@ -1,12 +1,13 @@
import { EventEmitter, Injectable } from '@angular/core';
import { BsComponentRef } from 'ngx-bootstrap/component-loader';
+import { BsDropdownMenuDirective } from './bs-dropdown-menu.directive';
@Injectable()
export class BsDropdownState {
direction: 'down' | 'up' = 'down';
- autoClose: boolean;
- insideClick: boolean;
- isAnimated: boolean;
+ autoClose = true;
+ insideClick = false;
+ isAnimated = false;
isOpenChange = new EventEmitter();
isDisabledChange = new EventEmitter();
toggleClick = new EventEmitter();
@@ -14,8 +15,8 @@ export class BsDropdownState {
/**
* Content to be displayed as popover.
*/
- dropdownMenu: Promise>;
- resolveDropdownMenu: (componentRef: BsComponentRef) => void;
+ dropdownMenu: Promise>;
+ resolveDropdownMenu!: (componentRef: BsComponentRef) => void;
constructor() {
this.dropdownMenu = new Promise(resolve => {
diff --git a/src/dropdown/testing/bs-dropdown.directive.spec.ts b/src/dropdown/testing/bs-dropdown.directive.spec.ts
index 214be197a3..e7bca4274c 100644
--- a/src/dropdown/testing/bs-dropdown.directive.spec.ts
+++ b/src/dropdown/testing/bs-dropdown.directive.spec.ts
@@ -72,7 +72,8 @@ describe('Directive: Dropdown', () => {
}));
it('should be closed by default', () => {
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ const dropdown = element.querySelector('[dropdown]');
+ expect(dropdown?.classList).not.toContain('open');
});
it('autoClose value should be true by default', () => {
@@ -86,84 +87,84 @@ describe('Directive: Dropdown', () => {
it('should be opened if isOpen === true and toggle on isOpen changes', () => {
context.isOpen = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
context.isOpen = false;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
context.isOpen = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
});
it('should toggle by click', () => {
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
});
it('should be closed if was opened by click and then isOpen === false was set', () => {
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
context.isOpen = false;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
});
it('should close by click on any element inside the dropdown', fakeAsync(() => {
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
tick();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
element.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should close by click on any element outside the dropdown', () => {
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- element.querySelector('h1').click();
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ element.querySelector('h1')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
});
it('should be opened if isOpen === true and toggle on isOpen changes', () => {
context.isOpen = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
context.isOpen = false;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
});
it('should change and update isOpen when it is opened or closed', fakeAsync(() => {
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
expect(context.isOpen).toBe(true);
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain(
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain(
'open'
);
expect(context.isOpen).toBe(false);
@@ -172,180 +173,182 @@ describe('Directive: Dropdown', () => {
it('should has class dropup if property dropup equal true', () => {
context.dropup = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('dropup');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('dropup');
});
it('should not open if isDisabled equal true', () => {
context.isDisabled = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
context.isDisabled = false;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
});
it('should close if only dropdown button was clicked if autoClose equal false', fakeAsync(() => {
context.autoClose = false;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
element.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('h1').click();
+ element.querySelector('h1')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should not close by click on menu item if autoClose equal true', fakeAsync(() => {
context.autoClose = true;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('value isOpenChange emits event', () => {
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
expect(context.isOpenChangeValue).toBeTruthy();
});
it('should close if only dropdown button was clicked if autoClose equal false', fakeAsync(() => {
context.autoClose = false;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
element.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('h1').click();
+ element.querySelector('h1')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should not close by click on menu item if insideClick equal true', fakeAsync(() => {
context.insideClick = true;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('ul').click();
+ element.querySelector('ul')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('h1').click();
+ element.querySelector('h1')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should close by click on menu item if insideClick equal false', fakeAsync(() => {
context.insideClick = false;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should close by click on menu item if insideClick equal false', fakeAsync(() => {
context.insideClick = false;
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
}));
it('should change aria-expanded property, when dropdown was opened', fakeAsync(() => {
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- expect(element.querySelector('[dropdownToggle]').getAttribute('aria-expanded')).toEqual('true');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ expect(element.querySelector('[dropdownToggle]')?.getAttribute('aria-expanded')).toEqual('true');
tick();
- element.querySelector('li').click();
+ element.querySelector('li')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdownToggle]').getAttribute('aria-expanded')).toEqual('false');
+ expect(element.querySelector('[dropdownToggle]')?.getAttribute('aria-expanded')).toEqual('false');
}));
it('should change disabled property, when dropdown was opened', fakeAsync(() => {
context.isDisabled = true;
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- expect(element.querySelector('[dropdownToggle]').getAttribute('disabled')).toEqual('true');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ expect(element.querySelector('[dropdownToggle]')?.getAttribute('disabled')).toEqual('true');
}));
it('should open if container is body', () => {
context.container = 'body';
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- element.querySelector('button').click();
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).not.toContain('open');
+ expect(element.querySelector('[dropdown]')?.classList).not.toContain('open');
});
it('should open if isBs3 method return true', fakeAsync(() => {
context.placement = 'bottom';
- const tempVal = window['__theme'];
- window['__theme'] = 'bs4';
+ const tempVal = (window as never)['__theme'];
+ // eslint-disable-next-line
+ (window as any)['__theme'] = 'bs4';
fixture.detectChanges();
- element.querySelector('button').click();
+ element.querySelector('button')?.click();
fixture.detectChanges();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- expect(element.querySelector('[dropdownToggle]').getAttribute('aria-expanded')).toEqual('true');
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ expect(element.querySelector('[dropdownToggle]')?.getAttribute('aria-expanded')).toEqual('true');
tick();
- expect(element.querySelector('[dropdown]').classList).toContain('open');
- window['__theme'] = tempVal;
+ expect(element.querySelector('[dropdown]')?.classList).toContain('open');
+ // eslint-disable-next-line
+ (window as any)['__theme'] = tempVal;
}));
});
diff --git a/src/dropdown/tsconfig.json b/src/dropdown/tsconfig.json
index f0ebfd6589..8bcea89917 100644
--- a/src/dropdown/tsconfig.json
+++ b/src/dropdown/tsconfig.json
@@ -15,7 +15,7 @@
],
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
- "strict": false,
+ "strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
diff --git a/src/modal/bs-modal-ref.service.ts b/src/modal/bs-modal-ref.service.ts
index cce5161af5..5d746dd651 100644
--- a/src/modal/bs-modal-ref.service.ts
+++ b/src/modal/bs-modal-ref.service.ts
@@ -6,20 +6,20 @@ export class BsModalRef {
/**
* Event that is fired when the modal behind the ref starts hiding
*/
- onHide: EventEmitter;
+ onHide?: EventEmitter;
/**
* Event that is fired when the modal behind the ref finishes hiding
*/
- onHidden: EventEmitter;
+ onHidden?: EventEmitter;
/**
* Allow user to ID for the modal. Otherwise, a unique number will be given
*/
- id: number;
+ id?: number;
/**
* Reference to a component inside the modal. Null if modal's been created with TemplateRef
*/
- content?: T | null;
+ content?: T;
/**
* Hides the modal
diff --git a/src/modal/bs-modal.service.ts b/src/modal/bs-modal.service.ts
index afd0564f87..65952c2916 100644
--- a/src/modal/bs-modal.service.ts
+++ b/src/modal/bs-modal.service.ts
@@ -36,10 +36,10 @@ export class BsModalService {
protected scrollbarWidth = 0;
- protected backdropRef: ComponentRef;
+ protected backdropRef?: ComponentRef;
private _backdropLoader: ComponentLoader;
private modalsCount = 0;
- private lastDismissReason = null;
+ private lastDismissReason?: string;
private loaders: ComponentLoader[] = [];
@@ -49,11 +49,7 @@ export class BsModalService {
rendererFactory: RendererFactory2,
private clf: ComponentLoaderFactory,
@Optional() @Inject(MODAL_CONFIG_DEFAULT_OVERRIDE) private modalDefaultOption: ModalOptions) {
- this._backdropLoader = this.clf.createLoader(
- null,
- null,
- null
- );
+ this._backdropLoader = this.clf.createLoader();
this._renderer = rendererFactory.createRenderer(null, null);
this.config = modalDefaultOption ?
(Object.assign({}, modalConfigDefaults, modalDefaultOption)) :
@@ -77,7 +73,7 @@ export class BsModalService {
this.config.id = id;
this._showBackdrop();
- this.lastDismissReason = null;
+ this.lastDismissReason = void 0;
return this._showModal(content);
}
@@ -96,7 +92,7 @@ export class BsModalService {
_showBackdrop(): void {
const isBackdropEnabled =
- this.config.backdrop || this.config.backdrop === 'static';
+ this.config.backdrop === true || this.config.backdrop === 'static';
const isBackdropInDOM =
!this.backdropRef || !this.backdropRef.instance.isShown;
@@ -137,9 +133,11 @@ export class BsModalService {
.provide({ provide: BsModalRef, useValue: bsModalRef })
.attach(ModalContainerComponent)
.to('body');
- bsModalRef.hide = () => modalContainerRef.instance.hide();
+ bsModalRef.hide = () => modalContainerRef.instance?.hide();
bsModalRef.setClass = (newClass: string) => {
- modalContainerRef.instance.config.class = newClass;
+ if (modalContainerRef.instance) {
+ modalContainerRef.instance.config.class = newClass;
+ }
};
bsModalRef.onHidden = new EventEmitter();
@@ -156,17 +154,19 @@ export class BsModalService {
bsModalService: this,
id: this.config.id
});
- modalContainerRef.instance.level = this.getModalsCount();
- bsModalRef.content = modalLoader.getInnerComponent() || null;
- bsModalRef.id = modalContainerRef.instance.config?.id;
+ if (modalContainerRef.instance) {
+ modalContainerRef.instance.level = this.getModalsCount();
+ bsModalRef.content = modalLoader.getInnerComponent();
+ bsModalRef.id = modalContainerRef.instance.config?.id;
+ }
return bsModalRef;
}
_hideModal(id?: number): void {
if (id != null) {
- const indexToRemove = this.loaders.findIndex(loader => loader.instance.config.id === id);
+ const indexToRemove = this.loaders.findIndex(loader => loader.instance?.config.id === id);
const modalLoader = this.loaders[indexToRemove];
if (modalLoader) {
modalLoader.hide(id);
@@ -174,7 +174,9 @@ export class BsModalService {
} else {
this.loaders.forEach(
(loader: ComponentLoader) => {
- loader.hide(loader.instance.config.id);
+ if (loader.instance) {
+ loader.hide(loader.instance.config.id);
+ }
}
);
}
@@ -191,7 +193,7 @@ export class BsModalService {
removeBackdrop(): void {
this._renderer.removeClass(document.body, CLASS_NAME.OPEN);
this._backdropLoader.hide();
- this.backdropRef = null;
+ this.backdropRef = void 0;
}
/** Checks if the body is overflowing and sets scrollbar width */
@@ -235,11 +237,7 @@ export class BsModalService {
}
private _createLoaders(): void {
- const loader = this.clf.createLoader(
- null,
- null,
- null
- );
+ const loader = this.clf.createLoader();
this.copyEvent(loader.onBeforeShow, this.onShow);
this.copyEvent(loader.onShown, this.onShown);
this.copyEvent(loader.onBeforeHide, this.onHide);
@@ -249,12 +247,14 @@ export class BsModalService {
private removeLoaders(id?: number): void {
if (id != null) {
- const indexToRemove = this.loaders.findIndex(loader => loader.instance.config.id === id);
+ const indexToRemove = this.loaders.findIndex(loader => loader.instance?.config.id === id);
if (indexToRemove >= 0) {
this.loaders.splice(indexToRemove, 1);
this.loaders.forEach(
(loader: ComponentLoader, i: number) => {
- loader.instance.level = i + 1;
+ if (loader.instance) {
+ loader.instance.level = i + 1;
+ }
}
);
}
diff --git a/src/modal/modal-backdrop.component.ts b/src/modal/modal-backdrop.component.ts
index a26f1a91e3..3fe7ff992c 100644
--- a/src/modal/modal-backdrop.component.ts
+++ b/src/modal/modal-backdrop.component.ts
@@ -18,7 +18,6 @@ export class ModalBackdropComponent implements OnInit {
set isAnimated(value: boolean) {
this._isAnimated = value;
- // this.renderer.setElementClass(this.element.nativeElement, `${ClassName.FADE}`, value);
}
get isShown(): boolean {
@@ -56,7 +55,7 @@ export class ModalBackdropComponent implements OnInit {
element: ElementRef;
renderer: Renderer2;
- protected _isAnimated: boolean;
+ protected _isAnimated = false;
protected _isShown = false;
constructor(element: ElementRef, renderer: Renderer2) {
diff --git a/src/modal/modal-container.component.ts b/src/modal/modal-container.component.ts
index 268722f974..7ad0c29b85 100644
--- a/src/modal/modal-container.component.ts
+++ b/src/modal/modal-container.component.ts
@@ -37,9 +37,9 @@ import { isBs3 } from 'ngx-bootstrap/utils';
export class ModalContainerComponent implements OnInit, OnDestroy {
config: ModalOptions;
isShown = false;
- level: number;
- isAnimated: boolean;
- bsModalService: BsModalService;
+ level?: number;
+ isAnimated = false;
+ bsModalService?: BsModalService;
private isModalHiding = false;
private clickStartedInContent = false;
@@ -69,7 +69,7 @@ export class ModalContainerComponent implements OnInit, OnDestroy {
);
}, this.isAnimated ? TRANSITION_DURATIONS.BACKDROP : 0);
if (document && document.body) {
- if (this.bsModalService.getModalsCount() === 1) {
+ if (this.bsModalService && this.bsModalService.getModalsCount() === 1) {
this.bsModalService.checkScrollbar();
this.bsModalService.setScrollbar();
}
@@ -97,13 +97,13 @@ export class ModalContainerComponent implements OnInit, OnDestroy {
return;
}
- this.bsModalService.setDismissReason(DISMISS_REASONS.BACKRDOP);
+ this.bsModalService?.setDismissReason(DISMISS_REASONS.BACKRDOP);
this.hide();
}
@HostListener('window:popstate')
onPopState(): void {
- this.bsModalService.setDismissReason(DISMISS_REASONS.BACK);
+ this.bsModalService?.setDismissReason(DISMISS_REASONS.BACK);
this.hide();
}
@@ -119,9 +119,9 @@ export class ModalContainerComponent implements OnInit, OnDestroy {
if (
this.config.keyboard &&
- this.level === this.bsModalService.getModalsCount()
+ this.level === this.bsModalService?.getModalsCount()
) {
- this.bsModalService.setDismissReason(DISMISS_REASONS.ESC);
+ this.bsModalService?.setDismissReason(DISMISS_REASONS.ESC);
this.hide();
}
}
@@ -159,11 +159,11 @@ export class ModalContainerComponent implements OnInit, OnDestroy {
if (
document &&
document.body &&
- this.bsModalService.getModalsCount() === 1
+ this.bsModalService?.getModalsCount() === 1
) {
this._renderer.removeClass(document.body, CLASS_NAME.OPEN);
}
- this.bsModalService.hide(this.config.id);
+ this.bsModalService?.hide(this.config.id);
this.isModalHiding = false;
}, this.isAnimated ? TRANSITION_DURATIONS.MODAL : 0);
}
diff --git a/src/modal/modal-options.class.ts b/src/modal/modal-options.class.ts
index c148f0ef6a..ddb677ca2e 100644
--- a/src/modal/modal-options.class.ts
+++ b/src/modal/modal-options.class.ts
@@ -65,7 +65,7 @@ export const modalConfigDefaults: ModalOptions = {
class: '',
animated: true,
initialState: {},
- closeInterceptor: null
+ closeInterceptor: void 0
};
export const MODAL_CONFIG_DEFAULT_OVERRIDE: InjectionToken =
diff --git a/src/modal/modal.directive.ts b/src/modal/modal.directive.ts
index cbe496a7f3..ba77d9c811 100644
--- a/src/modal/modal.directive.ts
+++ b/src/modal/modal.directive.ts
@@ -35,7 +35,7 @@ export class ModalDirective implements OnDestroy, OnInit {
}
/** allows to provide a callback to intercept the closure of the modal */
- @Input() closeInterceptor: CloseInterceptorFn;
+ @Input() closeInterceptor?: CloseInterceptorFn;
/** This event fires immediately when the `show` instance method is called. */
@Output()
@@ -60,7 +60,7 @@ export class ModalDirective implements OnDestroy, OnInit {
* Possible values: `backdrop-click`, `esc` and `id: number`
* (if modal was closed by direct call of `.hide()`).
*/
- dismissReason: string;
+ dismissReason?: string;
get isShown(): boolean {
return this._isShown;
@@ -77,7 +77,7 @@ export class ModalDirective implements OnDestroy, OnInit {
protected timerRmBackDrop = 0;
// reference to backdrop component
- protected backdrop: ComponentRef;
+ protected backdrop?: ComponentRef;
private _backdrop: ComponentLoader;
private isNested = false;
@@ -135,7 +135,6 @@ export class ModalDirective implements OnDestroy, OnInit {
}
ngOnDestroy() {
- this.config = void 0;
if (this._isShown) {
this._isShown = false;
this.hideModal();
@@ -161,7 +160,7 @@ export class ModalDirective implements OnDestroy, OnInit {
/** Allows to manually open modal */
show(): void {
- this.dismissReason = null;
+ this.dismissReason = void 0;
this.onShow.emit(this);
if (this._isShown) {
return;
diff --git a/src/modal/testing/bs-modal.service.spec.ts b/src/modal/testing/bs-modal.service.spec.ts
index ee80cadae1..ab3f44278a 100644
--- a/src/modal/testing/bs-modal.service.spec.ts
+++ b/src/modal/testing/bs-modal.service.spec.ts
@@ -9,11 +9,11 @@ import { BsModalService, ModalOptions } from '../index';
class TestService { }
describe('Service: BsModal', () => {
- let mockModalComponentRef;
- let mockComponentLoader;
+ let mockModalComponentRef: { instance: {[k: string]: jest.Mock} } | undefined;
+ let mockComponentLoader: { [k: string]: jest.Mock };
let bsModalService: BsModalService;
- function createMockComponentLoader(baseName: string) {
+ function createMockComponentLoader(baseName: string): { [k: string]: jest.Mock } {
const componentLoader = createSpyObj(baseName, [
'attach',
'getInnerComponent',
@@ -40,7 +40,7 @@ describe('Service: BsModal', () => {
}
beforeEach(() => {
- mockModalComponentRef = null;
+ mockModalComponentRef = void 0;
let createdBackdrop = false;
const mockBackdropComponentLoader = createMockComponentLoader('backdropComponentLoader');
mockComponentLoader = createMockComponentLoader('modalComponentLoader');
diff --git a/src/modal/tsconfig.json b/src/modal/tsconfig.json
index f0ebfd6589..8bcea89917 100644
--- a/src/modal/tsconfig.json
+++ b/src/modal/tsconfig.json
@@ -15,7 +15,7 @@
],
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
- "strict": false,
+ "strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
diff --git a/src/pagination/pager.component.ts b/src/pagination/pager.component.ts
index d73a6d770b..5979664f2a 100644
--- a/src/pagination/pager.component.ts
+++ b/src/pagination/pager.component.ts
@@ -11,14 +11,14 @@ import {
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { ConfigModel, PagesModel } from './models';
+
import { PageChangedEvent } from './pagination.component';
import { PaginationConfig } from './pagination.config';
-import { ConfigModel, PagesModel } from './models';
-
export const PAGER_CONTROL_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => PagerComponent),
+ useExisting: forwardRef(() => PagerComponent),
multi: true
};
@@ -28,40 +28,58 @@ export const PAGER_CONTROL_VALUE_ACCESSOR: Provider = {
providers: [PAGER_CONTROL_VALUE_ACCESSOR]
})
export class PagerComponent implements ControlValueAccessor, OnInit {
- config: ConfigModel;
+ config?: Partial;
/** if `true` aligns each link to the sides of pager */
- @Input() align: boolean;
+ @Input() align = true;
/** limit number for page links in pager */
- @Input() maxSize: number;
+ @Input() maxSize?: number;
/** if false first and last buttons will be hidden */
- @Input() boundaryLinks: boolean;
+ @Input() boundaryLinks = false;
/** if false previous and next buttons will be hidden */
- @Input() directionLinks: boolean;
+ @Input() directionLinks = true;
// labels
/** first button text */
- @Input() firstText: string;
+ @Input() firstText = 'First';
/** previous button text */
- @Input() previousText: string;
+ @Input() previousText = '« Previous';
/** next button text */
- @Input() nextText: string;
+ @Input() nextText = 'Next »';
/** last button text */
- @Input() lastText: string;
+ @Input() lastText = 'Last';
/** if true current page will in the middle of pages list */
- @Input() rotate: boolean;
+ @Input() rotate = true;
// css
/** add class to */
- @Input() pageBtnClass: string;
+ @Input() pageBtnClass = '';
/** if true pagination component will be disabled */
- @Input() disabled: boolean;
+ @Input() disabled = false;
/** fired when total pages count changes, $event:number equals to total pages count */
- @Output() numPages: EventEmitter = new EventEmitter();
+ @Output() numPages = new EventEmitter();
/** fired when page was changed, $event:{page, itemsPerPage} equals to
* object with current page index and number of items per page
*/
@Output()
- pageChanged: EventEmitter = new EventEmitter();
+ pageChanged = new EventEmitter();
+ onChange = Function.prototype;
+ onTouched = Function.prototype;
+ classMap = '';
+ pages?: PagesModel[];
+ protected inited = false;
+
+ constructor(private elementRef: ElementRef,
+ paginationConfig: PaginationConfig,
+ private changeDetection: ChangeDetectorRef) {
+ this.elementRef = elementRef;
+ if (!this.config) {
+ this.configureOptions(
+ Object.assign({}, paginationConfig.main, paginationConfig.pager)
+ );
+ }
+ }
+
+ protected _itemsPerPage = 15;
/** maximum number of items per page. If value less than 1 will display all items on one page */
@Input()
@@ -74,6 +92,8 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
this.totalPages = this.calculateTotalPages();
}
+ protected _totalItems = 0;
+
/** total number of items in all pages */
@Input()
get totalItems(): number {
@@ -85,6 +105,8 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
this.totalPages = this.calculateTotalPages();
}
+ protected _totalPages = 0;
+
get totalPages(): number {
return this._totalPages;
}
@@ -97,6 +119,12 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
}
}
+ protected _page = 1;
+
+ get page(): number {
+ return this._page;
+ }
+
set page(value: number) {
const _previous = this._page;
this._page = value > this.totalPages ? this.totalPages : value || 1;
@@ -112,34 +140,7 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
});
}
- get page(): number {
- return this._page;
- }
-
- onChange = Function.prototype;
- onTouched = Function.prototype;
-
- classMap: string;
- pages: PagesModel[];
-
- protected _itemsPerPage: number;
- protected _totalItems: number;
- protected _totalPages: number;
- protected inited = false;
- protected _page = 1;
-
- constructor(private elementRef: ElementRef,
- paginationConfig: PaginationConfig,
- private changeDetection: ChangeDetectorRef) {
- this.elementRef = elementRef;
- if (!this.config) {
- this.configureOptions(
- Object.assign({}, paginationConfig.main, paginationConfig.pager)
- );
- }
- }
-
- configureOptions(config: ConfigModel): void {
+ configureOptions(config: Partial): void {
this.config = Object.assign({}, config);
}
@@ -147,29 +148,29 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
if (typeof window !== 'undefined') {
this.classMap = this.elementRef.nativeElement.getAttribute('class') || '';
}
- // watch for maxSize
+ /* // watch for maxSize
this.maxSize =
- typeof this.maxSize !== 'undefined' ? this.maxSize : this.config.maxSize;
+ typeof this.maxSize !== 'undefined' ? this.maxSize : this.config?.maxSize || 0;
this.rotate =
- typeof this.rotate !== 'undefined' ? this.rotate : this.config.rotate;
+ !!(typeof this.rotate !== 'undefined' ? this.rotate : this.config?.rotate);
this.boundaryLinks =
- typeof this.boundaryLinks !== 'undefined'
+ !!(typeof this.boundaryLinks !== 'undefined'
? this.boundaryLinks
- : this.config.boundaryLinks;
+ : this.config?.boundaryLinks);
this.directionLinks =
- typeof this.directionLinks !== 'undefined'
+ !!(typeof this.directionLinks !== 'undefined'
? this.directionLinks
- : this.config.directionLinks;
+ : this.config?.directionLinks);
this.pageBtnClass =
typeof this.pageBtnClass !== 'undefined'
? this.pageBtnClass
- : this.config.pageBtnClass;
+ : this.config?.pageBtnClass || '';
// base class
this.itemsPerPage =
typeof this.itemsPerPage !== 'undefined'
? this.itemsPerPage
- : this.config.itemsPerPage;
+ : this.config?.itemsPerPage || 0;*/
this.totalPages = this.calculateTotalPages();
// this class
this.pages = this.getPages(this.page, this.totalPages);
@@ -222,7 +223,7 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
protected makePage(num: number,
text: string,
active: boolean): { number: number; text: string; active: boolean } {
- return {text, number: num, active};
+ return { text, number: num, active };
}
protected getPages(currentPage: number, totalPages: number): PagesModel[] {
@@ -235,7 +236,7 @@ export class PagerComponent implements ControlValueAccessor, OnInit {
typeof this.maxSize !== 'undefined' && this.maxSize < totalPages;
// recompute if maxSize
- if (isMaxSized) {
+ if (isMaxSized && this.maxSize) {
if (this.rotate) {
// Current page is displayed in the middle of the visible ones
startPage = Math.max(currentPage - Math.floor(this.maxSize / 2), 1);
diff --git a/src/pagination/pagination.component.ts b/src/pagination/pagination.component.ts
index 0d21a7d665..c759b869cf 100644
--- a/src/pagination/pagination.component.ts
+++ b/src/pagination/pagination.component.ts
@@ -7,12 +7,13 @@ import {
Input,
OnInit,
Output,
- Provider, TemplateRef
+ Provider,
+ TemplateRef
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { ConfigModel, PagesModel, PaginationLinkContext, PaginationNumberLinkContext } from './models';
import { PaginationConfig } from './pagination.config';
-import { ConfigModel, PagesModel, PaginationLinkContext, PaginationNumberLinkContext } from './models';
export interface PageChangedEvent {
itemsPerPage: number;
@@ -31,49 +32,66 @@ export const PAGINATION_CONTROL_VALUE_ACCESSOR: Provider = {
providers: [PAGINATION_CONTROL_VALUE_ACCESSOR]
})
export class PaginationComponent implements ControlValueAccessor, OnInit {
- config: ConfigModel;
+ config?: Partial;
/** if `true` aligns each link to the sides of pager */
- @Input() align: boolean;
+ @Input() align = true;
/** limit number for page links in pager */
- @Input() maxSize: number;
+ @Input() maxSize?: number;
/** if false first and last buttons will be hidden */
- @Input() boundaryLinks: boolean;
+ @Input() boundaryLinks = false;
/** if false previous and next buttons will be hidden */
- @Input() directionLinks: boolean;
+ @Input() directionLinks = true;
// labels
/** first button text */
- @Input() firstText: string;
+ @Input() firstText = 'First';
/** previous button text */
- @Input() previousText: string;
+ @Input() previousText = 'Previous';
/** next button text */
- @Input() nextText: string;
+ @Input() nextText = 'Next';
/** last button text */
- @Input() lastText: string;
+ @Input() lastText = 'Last';
/** if true current page will in the middle of pages list */
- @Input() rotate: boolean;
+ @Input() rotate = true;
// css
/** add class to */
- @Input() pageBtnClass: string;
+ @Input() pageBtnClass = '';
/** if true pagination component will be disabled */
- @Input() disabled: boolean;
+ @Input() disabled = false;
/** custom template for page link */
- @Input() customPageTemplate: TemplateRef;
+ @Input() customPageTemplate?: TemplateRef;
/** custom template for next link */
- @Input() customNextTemplate: TemplateRef;
+ @Input() customNextTemplate?: TemplateRef;
/** custom template for previous link */
- @Input() customPreviousTemplate: TemplateRef;
+ @Input() customPreviousTemplate?: TemplateRef;
/** custom template for first link */
- @Input() customFirstTemplate: TemplateRef;
+ @Input() customFirstTemplate?: TemplateRef;
/** custom template for last link */
- @Input() customLastTemplate: TemplateRef;
+ @Input() customLastTemplate?: TemplateRef;
/** fired when total pages count changes, $event:number equals to total pages count */
- @Output() numPages: EventEmitter = new EventEmitter();
+ @Output() numPages = new EventEmitter();
/** fired when page was changed, $event:{page, itemsPerPage} equals to object
* with current page index and number of items per page
*/
- @Output()
- pageChanged = new EventEmitter();
+ @Output() pageChanged = new EventEmitter();
+ onChange = Function.prototype;
+ onTouched = Function.prototype;
+ classMap = '';
+ pages?: PagesModel[];
+ protected inited = false;
+
+ constructor(
+ private elementRef: ElementRef,
+ paginationConfig: PaginationConfig,
+ private changeDetection: ChangeDetectorRef
+ ) {
+ this.elementRef = elementRef;
+ if (!this.config) {
+ this.configureOptions(paginationConfig.main);
+ }
+ }
+
+ protected _itemsPerPage = 10;
/** maximum number of items per page. If value less than 1 will display all items on one page */
@Input()
@@ -86,6 +104,8 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
this.totalPages = this.calculateTotalPages();
}
+ protected _totalItems = 0;
+
/** total number of items in all pages */
@Input()
get totalItems(): number {
@@ -97,6 +117,8 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
this.totalPages = this.calculateTotalPages();
}
+ protected _totalPages = 0;
+
get totalPages(): number {
return this._totalPages;
}
@@ -109,6 +131,12 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
}
}
+ protected _page = 1;
+
+ get page(): number {
+ return this._page;
+ }
+
set page(value: number) {
const _previous = this._page;
this._page = value > this.totalPages ? this.totalPages : value || 1;
@@ -124,34 +152,7 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
});
}
- get page(): number {
- return this._page;
- }
-
- onChange = Function.prototype;
- onTouched = Function.prototype;
-
- classMap: string;
- pages: PagesModel[];
-
- protected _itemsPerPage: number;
- protected _totalItems: number;
- protected _totalPages: number;
- protected inited = false;
- protected _page = 1;
-
- constructor(
- private elementRef: ElementRef,
- paginationConfig: PaginationConfig,
- private changeDetection: ChangeDetectorRef
- ) {
- this.elementRef = elementRef;
- if (!this.config) {
- this.configureOptions(paginationConfig.main);
- }
- }
-
- configureOptions(config: ConfigModel): void {
+ configureOptions(config: Partial): void {
this.config = Object.assign({}, config);
}
@@ -160,28 +161,28 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
this.classMap = this.elementRef.nativeElement.getAttribute('class') || '';
}
// watch for maxSize
- this.maxSize =
- typeof this.maxSize !== 'undefined' ? this.maxSize : this.config.maxSize;
+/* this.maxSize =
+ typeof this.maxSize !== 'undefined' ? this.maxSize : this.config?.maxSize || 0;
this.rotate =
- typeof this.rotate !== 'undefined' ? this.rotate : this.config.rotate;
+ !!(typeof this.rotate !== 'undefined' ? this.rotate : this.config?.rotate);
this.boundaryLinks =
- typeof this.boundaryLinks !== 'undefined'
+ !!(typeof this.boundaryLinks !== 'undefined'
? this.boundaryLinks
- : this.config.boundaryLinks;
+ : this.config?.boundaryLinks);
this.directionLinks =
- typeof this.directionLinks !== 'undefined'
+ !!(typeof this.directionLinks !== 'undefined'
? this.directionLinks
- : this.config.directionLinks;
+ : this.config?.directionLinks);
this.pageBtnClass =
typeof this.pageBtnClass !== 'undefined'
? this.pageBtnClass
- : this.config.pageBtnClass;
+ : this.config?.pageBtnClass || '';*/
// base class
- this.itemsPerPage =
+/* this.itemsPerPage =
typeof this.itemsPerPage !== 'undefined'
? this.itemsPerPage
- : this.config.itemsPerPage;
+ : this.config?.itemsPerPage || 0;*/
this.totalPages = this.calculateTotalPages();
// this class
this.pages = this.getPages(this.page, this.totalPages);
@@ -249,7 +250,7 @@ export class PaginationComponent implements ControlValueAccessor, OnInit {
typeof this.maxSize !== 'undefined' && this.maxSize < totalPages;
// recompute if maxSize
- if (isMaxSized) {
+ if (isMaxSized && this.maxSize) {
if (this.rotate) {
// Current page is displayed in the middle of the visible ones
startPage = Math.max(currentPage - Math.floor(this.maxSize / 2), 1);
diff --git a/src/pagination/pagination.config.ts b/src/pagination/pagination.config.ts
index e7e9faef48..04c476df82 100644
--- a/src/pagination/pagination.config.ts
+++ b/src/pagination/pagination.config.ts
@@ -8,8 +8,7 @@ import { ConfigModel, PagerModel } from './models';
providedIn: 'root'
})
export class PaginationConfig {
- main: ConfigModel = {
- maxSize: void 0,
+ main: Partial = {
itemsPerPage: 10,
boundaryLinks: false,
directionLinks: true,
diff --git a/src/pagination/testing/pagination.component.spec.ts b/src/pagination/testing/pagination.component.spec.ts
index 180e9f09ee..f8766f8d73 100644
--- a/src/pagination/testing/pagination.component.spec.ts
+++ b/src/pagination/testing/pagination.component.spec.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import {
ComponentFixture,
TestBed,
@@ -8,8 +9,8 @@ import { PaginationModule, PaginationComponent } from '../index';
describe('Component: Pagination:', () => {
let fixture: ComponentFixture;
- let context;
- let element;
+ let context: PaginationComponent;
+ let element: HTMLElement;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -37,7 +38,7 @@ describe('Component: Pagination:', () => {
expect(links[1].innerHTML.replace(/
: |
@@ -57,7 +57,7 @@
(wheel)="prevDef($event);changeMinutes(minuteStep * wheelSign($event), 'wheel')"
(keydown.ArrowUp)="changeMinutes(minuteStep, 'key')"
(keydown.ArrowDown)="changeMinutes(-minuteStep, 'key')"
- (change)="updateMinutes($event.target.value)" [attr.aria-label]="labelMinutes">
+ (change)="updateMinutes($event.target)" [attr.aria-label]="labelMinutes">
|
: |
@@ -73,7 +73,7 @@
(wheel)="prevDef($event);changeSeconds(secondsStep * wheelSign($event), 'wheel')"
(keydown.ArrowUp)="changeSeconds(secondsStep, 'key')"
(keydown.ArrowDown)="changeSeconds(-secondsStep, 'key')"
- (change)="updateSeconds($event.target.value)" [attr.aria-label]="labelSeconds">
+ (change)="updateSeconds($event.target)" [attr.aria-label]="labelSeconds">
|
diff --git a/src/timepicker/timepicker.component.ts b/src/timepicker/timepicker.component.ts
index 0140f9f721..33143a175c 100644
--- a/src/timepicker/timepicker.component.ts
+++ b/src/timepicker/timepicker.component.ts
@@ -13,35 +13,31 @@ import {
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { Subscription } from 'rxjs';
+
+import { ControlValueAccessorModel } from './models';
+
import { TimepickerActions } from './reducer/timepicker.actions';
import { TimepickerStore } from './reducer/timepicker.store';
import { getControlsValue } from './timepicker-controls.util';
import { TimepickerConfig } from './timepicker.config';
-import {
- TimeChangeSource,
- TimepickerComponentState,
- TimepickerControls
-} from './timepicker.models';
+import { TimeChangeSource, TimepickerComponentState, TimepickerControls } from './timepicker.models';
import {
- isValidDate,
- padNumber,
- parseTime,
- isInputValid,
isHourInputValid,
+ isInputLimitValid,
+ isInputValid,
isMinuteInputValid,
isSecondInputValid,
- isInputLimitValid
+ isValidDate,
+ padNumber,
+ parseTime
} from './timepicker.utils';
-import { Subscription } from 'rxjs';
-
-import { ControlValueAccessorModel } from './models';
-
export const TIMEPICKER_CONTROL_VALUE_ACCESSOR: ControlValueAccessorModel = {
provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TimepickerComponent),
+ useExisting: forwardRef(() => TimepickerComponent),
multi: true
};
@@ -86,85 +82,70 @@ export class TimepickerComponent
OnChanges,
OnDestroy {
/** hours change step */
- @Input() hourStep: number;
+ @Input() hourStep = 1;
/** minutes change step */
- @Input() minuteStep: number;
+ @Input() minuteStep = 5;
/** seconds change step */
- @Input() secondsStep: number;
+ @Input() secondsStep = 10;
/** if true hours and minutes fields will be readonly */
- @Input() readonlyInput: boolean;
+ @Input() readonlyInput = false;
/** if true hours and minutes fields will be disabled */
- @Input() disabled: boolean;
+ @Input() disabled = false;
/** if true scroll inside hours and minutes inputs will change time */
- @Input() mousewheel: boolean;
+ @Input() mousewheel = true;
/** if true the values of hours and minutes can be changed using the up/down arrow keys on the keyboard */
- @Input() arrowkeys: boolean;
+ @Input() arrowkeys = true;
/** if true spinner arrows above and below the inputs will be shown */
- @Input() showSpinners: boolean;
+ @Input() showSpinners = true;
/** if true meridian button will be shown */
- @Input() showMeridian: boolean;
+ @Input() showMeridian = true;
/** show minutes in timepicker */
- @Input() showMinutes: boolean;
+ @Input() showMinutes = true;
/** show seconds in timepicker */
- @Input() showSeconds: boolean;
+ @Input() showSeconds = false;
/** meridian labels based on locale */
- @Input() meridians: string[];
+ @Input() meridians: string[] = ['AM', 'PM'];
/** minimum time user can select */
- @Input() min: Date;
+ @Input() min?: Date;
/** maximum time user can select */
- @Input() max: Date;
+ @Input() max?: Date;
/** placeholder for hours field in timepicker */
- @Input() hoursPlaceholder: string;
+ @Input() hoursPlaceholder = 'HH';
/** placeholder for minutes field in timepicker */
- @Input() minutesPlaceholder: string;
+ @Input() minutesPlaceholder = 'MM';
/** placeholder for seconds field in timepicker */
- @Input() secondsPlaceholder: string;
+ @Input() secondsPlaceholder = 'SS';
/** emits true if value is a valid date */
@Output() isValid = new EventEmitter();
// ui variables
- hours: string;
- minutes: string;
- seconds: string;
- meridian: string;
-
- /** @deprecated - please use `isEditable` instead */
- get isSpinnersVisible(): boolean {
- return this.showSpinners && !this.readonlyInput;
- }
-
- get isEditable(): boolean {
- return !(this.readonlyInput || this.disabled);
- }
-
+ hours = '';
+ minutes = '';
+ seconds = '';
+ meridian = '';
// min\max validation for input fields
invalidHours = false;
invalidMinutes = false;
invalidSeconds = false;
-
// aria-label variables
- labelHours: string;
- labelMinutes: string;
- labelSeconds: string;
-
+ labelHours = 'hours';
+ labelMinutes = 'minutes';
+ labelSeconds = 'seconds';
// time picker controls state
- canIncrementHours: boolean;
- canIncrementMinutes: boolean;
- canIncrementSeconds: boolean;
-
- canDecrementHours: boolean;
- canDecrementMinutes: boolean;
- canDecrementSeconds: boolean;
-
- canToggleMeridian: boolean;
-
- // control value accessor methods
+ canIncrementHours = true;
+ canIncrementMinutes = true;
+ canIncrementSeconds = true;
+ canDecrementHours = true;
+ canDecrementMinutes = true;
+ canDecrementSeconds = true;
+ canToggleMeridian = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange = Function.prototype;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onTouched = Function.prototype;
+ // control value accessor methods
timepickerSub: Subscription;
constructor(
@@ -177,7 +158,7 @@ export class TimepickerComponent
this.timepickerSub = _store
.select(state => state.value)
- .subscribe((value: Date) => {
+ .subscribe((value: Date | undefined) => {
// update UI values if date changed
this._renderTime(value);
this.onChange(value);
@@ -196,6 +177,15 @@ export class TimepickerComponent
});
}
+ /** @deprecated - please use `isEditable` instead */
+ get isSpinnersVisible(): boolean {
+ return this.showSpinners && !this.readonlyInput;
+ }
+
+ get isEditable(): boolean {
+ return !(this.readonlyInput || this.disabled);
+ }
+
resetValidation(): void {
this.invalidHours = false;
this.invalidMinutes = false;
@@ -211,7 +201,7 @@ export class TimepickerComponent
}
wheelSign($event: WheelEventInit): number {
- return Math.sign($event.deltaY) * -1;
+ return Math.sign($event.deltaY || 0) * -1;
}
ngOnChanges(): void {
@@ -239,9 +229,9 @@ export class TimepickerComponent
);
}
- updateHours(hours: string): void {
+ updateHours(target?: Partial | null): void {
this.resetValidation();
- this.hours = hours;
+ this.hours = (target as HTMLInputElement).value;
const isValid = isHourInputValid(this.hours, this.isPM()) && this.isValidLimit();
@@ -256,9 +246,9 @@ export class TimepickerComponent
this._updateTime();
}
- updateMinutes(minutes: string) {
+ updateMinutes(target: Partial | null) {
this.resetValidation();
- this.minutes = minutes;
+ this.minutes = (target as HTMLInputElement).value;
const isValid = isMinuteInputValid(this.minutes) && this.isValidLimit();
@@ -273,9 +263,9 @@ export class TimepickerComponent
this._updateTime();
}
- updateSeconds(seconds: string) {
+ updateSeconds(target: Partial | null) {
this.resetValidation();
- this.seconds = seconds;
+ this.seconds = (target as HTMLInputElement).value;
const isValid = isSecondInputValid(this.seconds) && this.isValidLimit();
@@ -336,11 +326,11 @@ export class TimepickerComponent
/**
* Write a new value to the element.
*/
- writeValue(obj: string | null | undefined | Date): void {
+ writeValue(obj?: string | Date): void {
if (isValidDate(obj)) {
this._store.dispatch(this._timepickerActions.writeValue(parseTime(obj)));
} else if (obj == null) {
- this._store.dispatch(this._timepickerActions.writeValue(null));
+ this._store.dispatch(this._timepickerActions.writeValue());
}
}
@@ -374,8 +364,8 @@ export class TimepickerComponent
this.timepickerSub.unsubscribe();
}
- private _renderTime(value: string | Date): void {
- if (!isValidDate(value)) {
+ private _renderTime(value?: string | Date): void {
+ if (!value || !isValidDate(value)) {
this.hours = '';
this.minutes = '';
this.seconds = '';
@@ -385,6 +375,10 @@ export class TimepickerComponent
}
const _value = parseTime(value);
+ if (!_value) {
+ return;
+ }
+
const _hoursPerDayHalf = 12;
let _hours = _value.getHours();
diff --git a/src/timepicker/timepicker.config.ts b/src/timepicker/timepicker.config.ts
index f7d8d587cf..1ae962d3db 100644
--- a/src/timepicker/timepicker.config.ts
+++ b/src/timepicker/timepicker.config.ts
@@ -30,9 +30,9 @@ export class TimepickerConfig {
/** show minutes in timepicker */
showMinutes = true;
/** minimum time user can select */
- min: Date;
+ min?: Date;
/** maximum time user can select */
- max: Date;
+ max?: Date;
/** placeholder for hours field in timepicker */
hoursPlaceholder = 'HH';
/** placeholder for minutes field in timepicker */
diff --git a/src/timepicker/timepicker.models.ts b/src/timepicker/timepicker.models.ts
index 5c4e131a1f..5d04dc6c8a 100644
--- a/src/timepicker/timepicker.models.ts
+++ b/src/timepicker/timepicker.models.ts
@@ -18,8 +18,8 @@ export interface TimepickerControls {
}
export interface TimepickerComponentState {
- min: Date;
- max: Date;
+ min?: Date;
+ max?: Date;
hourStep: number;
minuteStep: number;
diff --git a/src/timepicker/timepicker.utils.ts b/src/timepicker/timepicker.utils.ts
index 5000d70a19..18518af1c3 100644
--- a/src/timepicker/timepicker.utils.ts
+++ b/src/timepicker/timepicker.utils.ts
@@ -34,7 +34,11 @@ export function isValidLimit(controls: TimepickerComponentState, newDate: Date):
return true;
}
-export function toNumber(value: string | number): number {
+export function toNumber(value?: string | number): number {
+ if (typeof value === 'undefined') {
+ return NaN;
+ }
+
if (typeof value === 'number') {
return value;
}
@@ -47,7 +51,7 @@ export function isNumber(value: string | number): value is number {
}
export function parseHours(
- value: string | number,
+ value?: string | number,
isPM = false
): number {
const hour = toNumber(value);
@@ -62,7 +66,7 @@ export function parseHours(
return hour;
}
-export function parseMinutes(value: string | number): number {
+export function parseMinutes(value?: string | number): number {
const minute = toNumber(value);
if (isNaN(minute) || minute < 0 || minute > minutesPerHour) {
return NaN;
@@ -71,7 +75,7 @@ export function parseMinutes(value: string | number): number {
return minute;
}
-export function parseSeconds(value: string | number): number {
+export function parseSeconds(value?: string | number): number {
const seconds = toNumber(value);
if (isNaN(seconds) || seconds < 0 || seconds > secondsPerMinute) {
return NaN;
@@ -80,7 +84,7 @@ export function parseSeconds(value: string | number): number {
return seconds;
}
-export function parseTime(value: string | Date): Date {
+export function parseTime(value?: string | Date): Date | undefined {
if (typeof value === 'string') {
return new Date(value);
}
@@ -88,11 +92,15 @@ export function parseTime(value: string | Date): Date {
return value;
}
-export function changeTime(value: Date, diff: Time): Date {
+export function changeTime(value?: Date, diff?: Time): Date {
if (!value) {
return changeTime(createDate(new Date(), 0, 0, 0), diff);
}
+ if (!diff) {
+ return value;
+ }
+
let hour = value.getHours();
let minutes = value.getMinutes();
let seconds = value.getSeconds();
@@ -112,7 +120,7 @@ export function changeTime(value: Date, diff: Time): Date {
return createDate(value, hour, minutes, seconds);
}
-export function setTime(value: Date, opts: Time): Date {
+export function setTime(value: Date | undefined, opts: Time): Date | undefined {
let hour = parseHours(opts.hour);
const minute = parseMinutes(opts.minute);
const seconds = parseSeconds(opts.seconds) || 0;
@@ -180,9 +188,13 @@ export function isSecondInputValid(seconds: string): boolean {
return !isNaN(parseSeconds(seconds));
}
-export function isInputLimitValid(diff: Time, max: Date, min: Date): boolean {
+export function isInputLimitValid(diff: Time, max?: Date, min?: Date): boolean {
const newDate = setTime(new Date(), diff);
+ if (!newDate) {
+ return false;
+ }
+
if (max && newDate > max) {
return false;
}
diff --git a/src/timepicker/tsconfig.json b/src/timepicker/tsconfig.json
index a8648cfa21..8bcea89917 100644
--- a/src/timepicker/tsconfig.json
+++ b/src/timepicker/tsconfig.json
@@ -15,12 +15,12 @@
],
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
- "strict": false,
+ "strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
- "strictTemplates": false
+ "strictTemplates": true
}
}
diff --git a/src/tooltip/testing/tooltip.directive.spec.ts b/src/tooltip/testing/tooltip.directive.spec.ts
index 9032e079de..14cab4f81c 100644
--- a/src/tooltip/testing/tooltip.directive.spec.ts
+++ b/src/tooltip/testing/tooltip.directive.spec.ts
@@ -16,7 +16,7 @@ const overTemplate = `
+ [delay] = "delay" tooltip="See? Now click away..." triggers="focus mouseenter" class="form-control" />
| |