From 200e8f9e88d893aed8d1ad5c37e4a8643a9fa593 Mon Sep 17 00:00:00 2001 From: Ershova Irina Date: Tue, 30 Jan 2018 16:53:41 +0200 Subject: [PATCH] feat(timepicker): add validation for timepicker Add validation for timepicker based on ui-bootstrap validation. Fix min-max validation issue. close #3549 #3288 --- .../timepicker-controls.util.spec.ts | 215 ++++++++++------- .../timepicker/timepicker.component.spec.ts | 217 +++++++++++++----- src/spec/timepicker/timepicker.utils.spec.ts | 186 +++++++++++---- src/timepicker/reducer/timepicker.actions.ts | 2 +- src/timepicker/reducer/timepicker.reducer.ts | 22 +- src/timepicker/reducer/timepicker.store.ts | 4 +- src/timepicker/timepicker-controls.util.ts | 13 +- src/timepicker/timepicker.component.html | 15 +- src/timepicker/timepicker.component.ts | 85 ++++++- src/timepicker/timepicker.models.ts | 2 + src/timepicker/timepicker.utils.ts | 58 ++++- 11 files changed, 601 insertions(+), 218 deletions(-) diff --git a/src/spec/timepicker/timepicker-controls.util.spec.ts b/src/spec/timepicker/timepicker-controls.util.spec.ts index 4d9118392b..e3f0497432 100644 --- a/src/spec/timepicker/timepicker-controls.util.spec.ts +++ b/src/spec/timepicker/timepicker-controls.util.spec.ts @@ -1,22 +1,16 @@ -import { - canChangeHours, - canChangeMinutes, - canChangeSeconds, - canChangeValue, - getControlsValue, - timepickerControls -} from '../../timepicker/timepicker-controls.util'; import { TimeChangeEvent, TimepickerComponentState, TimepickerControls } from '../../timepicker/timepicker.models'; +import * as timepickerControlsUtil from '../../timepicker/timepicker-controls.util'; function testTime(hours?: number, minutes?: number, seconds?: number) { const time = new Date(); time.setHours(hours || 0); time.setMinutes(minutes || 0); time.setSeconds(seconds || 0); + return time; } -describe('Runtime coverage. Util: Timepicker-controls', () => { +describe('Util: Timepicker-controls', () => { let state: TimepickerComponentState; let controls: TimepickerControls; let event: TimeChangeEvent; @@ -38,12 +32,13 @@ describe('Runtime coverage. Util: Timepicker-controls', () => { }; controls = { - canIncrementHours: false, - canIncrementMinutes: false, - canIncrementSeconds: false, - canDecrementHours: false, - canDecrementMinutes: false, - canDecrementSeconds: false + canIncrementHours: true, + canIncrementMinutes: true, + canIncrementSeconds: true, + canDecrementHours: true, + canDecrementMinutes: true, + canDecrementSeconds: true, + canToggleMeridian: true }; event = { @@ -52,123 +47,185 @@ describe('Runtime coverage. Util: Timepicker-controls', () => { }; }); - it('should can change value read only', () => { - canChangeValue(state, event); - + it('canChangeValue method should return false if readonlyInput is true', () => { state.readonlyInput = true; - canChangeValue(state, event); - }); - it('should can change value event', () => { - canChangeValue(state); - canChangeValue(state, event); + const result = timepickerControlsUtil.canChangeValue(state, event); + + expect(result).toEqual(false); }); - it('should can change value event source and wheel', () => { + it('canChangeValue method should return false if source is wheel and no mousewheel', () => { event.source = 'wheel'; state.mousewheel = false; - canChangeValue(state, event); + const result = timepickerControlsUtil.canChangeValue(state, event); + + expect(result).toEqual(false); }); - it('should can change value event source and key', () => { + it('canChangeValue method should return false if source is key and no arrowkeys', () => { event.source = 'key'; state.arrowkeys = false; - canChangeValue(state, event); + const result = timepickerControlsUtil.canChangeValue(state, event); + + expect(result).toEqual(false); }); - it('should change Hours', () => { - canChangeHours(event, controls); + it('canChangeValue method should return true if readonlyInput is false and event is empty', () => { + const result = timepickerControlsUtil.canChangeValue(state, event); + + expect(result).toEqual(true); + }); + + it('canChangeValue method should return true if readonlyInput is false and no event', () => { + const result = timepickerControlsUtil.canChangeValue(state); + + expect(result).toEqual(true); }); - it('should change Hours no step', () => { + it('canChangeHours method should validate ability to change Hours and return true', () => { + const result = timepickerControlsUtil.canChangeHours(event, controls); + + expect(result).toEqual(true); + }); + + it('canChangeHours method should validate and return false if no step', () => { event.step = null; - canChangeHours(event, controls); + + const result = timepickerControlsUtil.canChangeHours(event, controls); + + expect(result).toEqual(false); }); - it('should change Hours step is -1', () => { - event.step = -1; - canChangeHours(event, controls); + it('canChangeHours method should validate and return false if canIncrementHours is false', () => { + controls.canIncrementHours = false; + + const result = timepickerControlsUtil.canChangeHours(event, controls); + + expect(result).toEqual(false); }); - it('should change Hours can increment', () => { - controls.canIncrementHours = true; - canChangeHours(event, controls); + it('canChangeHours method should validate and return false if step < 0 and canDecrementHours is false', () => { + controls.canDecrementHours = false; + event.step = -2; + + const result = timepickerControlsUtil.canChangeHours(event, controls); + + expect(result).toEqual(false); }); - it('should change Minutes', () => { - canChangeMinutes(event, controls); + it('canChangeMinutes method should validate ability to change Minutes and return true', () => { + const result = timepickerControlsUtil.canChangeMinutes(event, controls); + + expect(result).toEqual(true); }); - it('should change Minutes no step', () => { + it('canChangeMinutes method should validate and return false if no step', () => { event.step = null; - canChangeMinutes(event, controls); + + const result = timepickerControlsUtil.canChangeMinutes(event, controls); + + expect(result).toEqual(false); }); - it('should change Minutes step is -1', () => { - event.step = -1; - canChangeMinutes(event, controls); + it('canChangeMinutes method should validate and return false if canIncrementMinutes is false', () => { + controls.canIncrementMinutes = false; + + const result = timepickerControlsUtil.canChangeMinutes(event, controls); + + expect(result).toEqual(false); }); - it('should change Minutes can increment', () => { - controls.canIncrementMinutes = true; - canChangeMinutes(event, controls); + it('canChangeMinutes method should validate and return false if step < 0 and canDecrementMinutes is false', () => { + controls.canDecrementMinutes = false; + event.step = -2; + + const result = timepickerControlsUtil.canChangeMinutes(event, controls); + + expect(result).toEqual(false); }); - it('should change Seconds', () => { - canChangeSeconds(event, controls); + it('canChangeSeconds method should validate ability to change Seconds and return true', () => { + const result = timepickerControlsUtil.canChangeSeconds(event, controls); + + expect(result).toEqual(true); }); - it('should change Seconds no step', () => { + it('canChangeSeconds method should validate and return false if no step', () => { event.step = null; - canChangeSeconds(event, controls); + + const result = timepickerControlsUtil.canChangeSeconds(event, controls); + + expect(result).toEqual(false); }); - it('should change Seconds step is -1', () => { - event.step = -1; - canChangeSeconds(event, controls); + it('canChangeSeconds method should validate and return false if canIncrementSeconds is false', () => { + controls.canIncrementSeconds = false; + + const result = timepickerControlsUtil.canChangeSeconds(event, controls); + + expect(result).toEqual(false); }); - it('should change Seconds can increment', () => { - controls.canIncrementSeconds = true; - canChangeSeconds(event, controls); + it('canChangeSeconds method should validate and return false if step < 0 and canDecrementSeconds is false', () => { + controls.canDecrementSeconds = false; + event.step = -2; + + const result = timepickerControlsUtil.canChangeSeconds(event, controls); + + expect(result).toEqual(false); }); - it('should get controls value', () => { - getControlsValue(state); + it('getControlsValue method should return TimepickerComponentState', () => { + const result = timepickerControlsUtil.getControlsValue(state); + + expect(result).toEqual(state); }); - it('should set data in timepicker controls', () => { - timepickerControls(new Date(), state); + it('timepickerControls method should return default data if no value', () => { + const result = timepickerControlsUtil.timepickerControls(null, state); + + expect(result).toEqual(controls); }); - it('should set data in timepicker controls without date', () => { - // unreachable code + it('timepickerControls method should change canIncrementHours to true', () => { + state.max = testTime(14); + + const result = timepickerControlsUtil.timepickerControls(testTime(11), state); + + expect(result.canIncrementHours).toEqual(true); + expect(result.canToggleMeridian).toEqual(false); }); - it('should set data in timepicker controls without showSeconds', () => { + it('timepickerControls method should change canIncrementHours to false', () => { + state.max = testTime(14); state.showSeconds = true; - timepickerControls(new Date(), state); - }); - it('should set data in timepicker controls with max', () => { - state.max = new Date(); - timepickerControls(new Date(), state); - }); + const result = timepickerControlsUtil.timepickerControls(testTime(17), state); - it('should set data in timepicker controls with max greater to control time', () => { - state.max = testTime(1); - timepickerControls(testTime(), state); + expect(result.canIncrementHours).toEqual(false); + expect(result.canIncrementMinutes).toEqual(false); }); - it('should set data in timepicker controls with min', () => { - state.min = new Date(); - timepickerControls(new Date(), state); + it('timepickerControls method should change canDecrementHours to true', () => { + state.min = testTime(10); + + const result = timepickerControlsUtil.timepickerControls(testTime(13), state); + + expect(result.canDecrementHours).toEqual(true); + expect(result.canToggleMeridian).toEqual(false); }); - it('should set data in timepicker controls with min greater to control time', () => { - state.min = testTime(1); - timepickerControls(testTime(), state); + it('timepickerControls method should change canIncrementHours to false', () => { + state.min = testTime(10); + state.showSeconds = true; + + const result = timepickerControlsUtil.timepickerControls(testTime(9), state); + + expect(result.canDecrementHours).toEqual(false); + expect(result.canDecrementMinutes).toEqual(false); + }); }); diff --git a/src/spec/timepicker/timepicker.component.spec.ts b/src/spec/timepicker/timepicker.component.spec.ts index 7eb6a767e2..c5104b923a 100644 --- a/src/spec/timepicker/timepicker.component.spec.ts +++ b/src/spec/timepicker/timepicker.component.spec.ts @@ -4,11 +4,12 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { fireEvent } from '../../../scripts/helpers'; -import { TimepickerActions } from '../../timepicker/reducer/timepicker.actions'; -import { TimepickerComponent } from '../../timepicker/timepicker.component'; - -import { TimepickerConfig } from '../../timepicker/timepicker.config'; -import { TimepickerModule } from '../../timepicker/timepicker.module'; +import { + TimepickerActions, + TimepickerComponent, + TimepickerConfig, + TimepickerModule +} from '../../timepicker'; function getInputElements(fixture: any) { return fixture.nativeElement.querySelectorAll('input') as HTMLInputElement; @@ -31,7 +32,7 @@ function testTime(hours?: number, minutes?: number, seconds?: number) { return time; } -describe('Component: timepicker', () => { +describe('Component: TimepickerComponent', () => { let fixture: ComponentFixture; let component: TimepickerComponent; let inputHours: HTMLInputElement; @@ -63,25 +64,24 @@ describe('Component: timepicker', () => { buttonMeridian = getElements(fixture, 'button'); }); - // поле часы и минуты отображаются it('should seconds fields is not display', () => { expect(inputHours).toBeTruthy(); expect(inputMinutes).toBeTruthy(); }); - // поле секунды не отображается + it('should seconds fields is not display', () => { expect(inputSeconds).toBeFalsy(); }); - // поле часы и минуты должно быть пустым + it('should be empty inputs fields hours and minutes', () => { expect(inputHours.value).toBeFalsy(); expect(inputMinutes.value).toBeFalsy(); }); - // должны отображаться кнопки изменения времени + it('should visible change buttons', () => { expect(buttonChanges).toBeTruthy(); }); - // должна отображаться кнопка меридиана + it('should visible meridian button', () => { expect(buttonMeridian).toBeTruthy(); }); @@ -98,7 +98,6 @@ describe('Component: timepicker', () => { buttonChanges = getElements(fixture, 'a.btn'); }); - // проверить данные в поле минуты корректные данные it('should validate the data in the minutes input with valid data', () => { fireEvent(inputMinutes, 'change'); @@ -108,14 +107,14 @@ describe('Component: timepicker', () => { expect(inputMinutes.value).toEqual('12'); }); - // проверить данные в поле минуты корректные данные с неполным значением + it('should validate the data in the minutes input with valid data with half value', () => { component.writeValue(testTime(0, 2, 0)); fixture.detectChanges(); expect(inputMinutes.value).toEqual('02'); }); - // установить время путем нажатия на кнопку изменения времени + it('should set time in a input field after click on input change button', () => { expect(inputHours.value).toBeFalsy(); expect(inputMinutes.value).toBeFalsy(); @@ -140,11 +139,10 @@ describe('Component: timepicker', () => { buttonDebugMeridian = getDebugElements(fixture, 'button')[0]; }); - // отобразить кнопку AM/PM при состоянии showMeridian по умолчанию it('should default state showMeridian display AM/PM button', () => { expect(buttonMeridian).toBeTruthy(); }); - // проверить данные в поле ввода Часы при вормате времени 12h + it('should validate the data in the hours input at time format 12h', () => { fireEvent(inputHours, 'change'); @@ -154,7 +152,7 @@ describe('Component: timepicker', () => { expect(inputHours.value).toEqual('10'); }); - // изменить временной период после клика на кнопку AM/PM + it('should change time period after click on AM/PM button', () => { expect(buttonMeridian.textContent.trim()).toBe(component.meridians[0]); @@ -165,7 +163,7 @@ describe('Component: timepicker', () => { expect(buttonMeridian.textContent.trim()).toBe(component.meridians[1]); }); }); - // изменить временной период после клика на кнопку AM/PM без readonlyInput + it('should change time period after click on AM/PM button without readonlyInput', () => { component.readonlyInput = false; component.showMeridian = false; @@ -179,7 +177,7 @@ describe('Component: timepicker', () => { expect(buttonMeridian.textContent.trim()).toBe(component.meridians[0]); }); }); - // изменить временной период после клика на кнопку AM/PM с readonlyInput + it('should change time period after click on AM/PM button with readonlyInput', () => { component.readonlyInput = false; component.showMeridian = true; @@ -205,7 +203,6 @@ describe('Component: timepicker', () => { inputHours = getInputElements(fixture)[0]; }); - // не отобразить кнопку AM/PM если showMeridian выключен it('should not display AM/PM button if showMeridian switch off', () => { expect(buttonMeridian).toBeTruthy(); @@ -218,7 +215,7 @@ describe('Component: timepicker', () => { expect(buttonMeridian).toBeFalsy(); }); }); - // проверить данные в поле часы при формате времени 24h + it('should validate the data in the hours input at time format 24h', () => { component.showMeridian = false; @@ -242,7 +239,6 @@ describe('Component: timepicker', () => { buttonChanges = getElements(fixture, 'a.btn'); }); - // заблокировать кнопку увеличения часов it('should block the hours / minutes increment button if clicking on it will cause exceeding the max value', () => { component.max = testTime(18); component.writeValue(testTime(17, 50)); @@ -270,7 +266,6 @@ describe('Component: timepicker', () => { buttonChanges = getElements(fixture, 'a.btn'); }); - // заблокировать кнопку уменьшения часов it('should block the hours / minutes decrement button if clicking on it will cause exceeding the min value', () => { component.min = testTime(13); component.writeValue(testTime(13, 22)); @@ -310,7 +305,6 @@ describe('Component: timepicker', () => { inputSeconds = getInputElements(fixture)[2]; }); - // отображать поле секунды если showSeconds включен it('should display seconds field if showMeridian switch on', () => { component.showSeconds = true; @@ -325,7 +319,7 @@ describe('Component: timepicker', () => { expect(inputSeconds).toBeTruthy(); }); }); - // проверить данные в поле секунды + it('should validate the data in the seconds input', () => { component.showSeconds = true; @@ -353,7 +347,7 @@ describe('Component: timepicker', () => { inputSeconds = getInputElements(fixture)[2]; buttonChanges = getElements(fixture, 'a.btn'); }); - // должна быть возможность ввода значений + it('should be possible to enter values', () => { expect(inputHours.getAttribute('readonly')).toBeFalsy(); expect(inputMinutes.getAttribute('readonly')).toBeFalsy(); @@ -369,11 +363,11 @@ describe('Component: timepicker', () => { expect(inputSeconds.getAttribute('readonly')).toBeFalsy(); }); }); - // должна отображать кнопки изменения времени + it('should be display is time change buttons', () => { expect(buttonChanges).toBeTruthy(); }); - // не должно быть возможности ввода значений + it('should be impossible to enter values', () => { component.readonlyInput = true; component.showSeconds = true; @@ -390,7 +384,7 @@ describe('Component: timepicker', () => { expect(inputSeconds.getAttribute('readonly')).toBe(''); }); }); - // не должны отображаться кнопки изменения времени + it('should not display is time change buttons', () => { expect(buttonChanges).toBeTruthy(); @@ -419,7 +413,7 @@ describe('Component: timepicker', () => { inputSeconds = getInputElements(fixture)[2]; buttonChanges = getElements(fixture, 'a.btn'); }); - // добавить в поле ввода часы значение с учетом hourStep инкримент + it('should add to the hour input field value, hourStep value increment', () => { component.hourStep = 2; @@ -432,7 +426,7 @@ describe('Component: timepicker', () => { expect(inputHours.value).toBe('02'); }); }); - // добавить в поле ввода часы значение с учетом hourStep декримент + it('should add to the hour input field value, hourStep value decrement', () => { component.hourStep = 2; @@ -445,7 +439,7 @@ describe('Component: timepicker', () => { expect(inputHours.value).toBe('04'); }); }); - // вычесть в поле ввода часы значение с учетом minuteStep инкримент + it('should input field value, minuteStep value increment', () => { component.minuteStep = 12; @@ -458,7 +452,7 @@ describe('Component: timepicker', () => { expect(inputMinutes.value).toBe('34'); }); }); - // вычесть в поле ввода часы значение с учетом minuteStep декримент + it('should input field value, minuteStep value decrement', () => { component.minuteStep = 12; @@ -471,7 +465,7 @@ describe('Component: timepicker', () => { expect(inputMinutes.value).toBe('10'); }); }); - // вычесть в поле ввода часы значение с учетом secondsStep инкримент + it('should input field value, secondsStep value increment', () => { component.showSeconds = true; component.secondsStep = 10; @@ -489,7 +483,7 @@ describe('Component: timepicker', () => { expect(inputSeconds.value).toBe('40'); }); }); - // вычесть в поле ввода часы значение с учетом secondsStep декримент + it('should input field value, secondsStep value decrement', () => { component.showSeconds = true; component.secondsStep = 10; @@ -517,7 +511,6 @@ describe('Component: timepicker', () => { buttonChanges = getElements(fixture, 'a.btn'); }); - // скрыть кнопки изменения времени it('should hide change button', () => { component.showSpinners = false; @@ -547,7 +540,6 @@ describe('Component: timepicker', () => { inputDebugSeconds = getDebugElements(fixture, 'input')[2]; }); - // измененить часы колесом мыши инкремент it('should can change hours value with the mouse wheel increment', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -568,7 +560,7 @@ describe('Component: timepicker', () => { ); }); }); - // измененить минуты колесом мыши инкремент + it('should can change minutes value with the mouse wheel increment', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -589,7 +581,7 @@ describe('Component: timepicker', () => { ); }); }); - // измененить секунды колесом мыши инкремент + it('should can change seconds value with the mouse wheel increment', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); @@ -616,7 +608,7 @@ describe('Component: timepicker', () => { ); }); }); - // измененить часы колесом мыши декремент + it('should can change hours value with the mouse wheel decrement', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -637,7 +629,7 @@ describe('Component: timepicker', () => { ); }); }); - // измененить минуты колесом мыши декремент + it('should can change minutes value with the mouse wheel decrement', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -658,7 +650,7 @@ describe('Component: timepicker', () => { ); }); }); - // измененить секунды колесом мыши декремент + it('should can change seconds value with the mouse wheel decrement', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); component.secondsStep = 3; @@ -684,7 +676,7 @@ describe('Component: timepicker', () => { ); }); }); - // отключить изменение часы колесом мыши + it('should can not change hours value with the mouse wheel', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -706,7 +698,7 @@ describe('Component: timepicker', () => { ); }); }); - // отключить изменение минуты колесом мыши + it('should can not change minutes value with the mouse wheel', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -728,7 +720,7 @@ describe('Component: timepicker', () => { ); }); }); - // отключить изменение секунды колесом мыши + it('should can not change seconds value with the mouse wheel', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); component.showSeconds = true; @@ -771,7 +763,6 @@ describe('Component: timepicker', () => { inputDebugSeconds = getDebugElements(fixture, 'input')[2]; }); - // изменить часы кнопками вверх it('should can change hours value with the arrow keys up', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -787,7 +778,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(component.hourStep, 'key'); }); }); - // изменить минуты кнопками вверх + it('should can change minutes value with the arrow keys up', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -803,7 +794,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(component.minuteStep, 'key'); }); }); - // изменить секунды кнопками вверх + it('should can change seconds value with the arrow keys up', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); component.showSeconds = true; @@ -824,7 +815,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(component.secondsStep, 'key'); }); }); - // изменить часы кнопками вниз + it('should can not change hours value with the arrow keys down', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -840,7 +831,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(-component.hourStep, 'key'); }); }); - // изменить минуты кнопками вниз + it('should can not change minutes value with the arrow keys down', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -856,7 +847,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(-component.minuteStep, 'key'); }); }); - // изменить секунды кнопками вниз + it('should can not change seconds value with the arrow keys down', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); @@ -878,7 +869,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(-component.secondsStep, 'key'); }); }); - // отключить часы времени кнопками + it('should can not change hours value with the arrow keys', () => { const methodSpy = spyOn(component, 'changeHours').and.callThrough(); component.hourStep = 3; @@ -895,7 +886,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(component.hourStep, 'key'); }); }); - // отключить минуты времени кнопками + it('should can not change minutes value with the arrow keys', () => { const methodSpy = spyOn(component, 'changeMinutes').and.callThrough(); component.minuteStep = 3; @@ -912,7 +903,7 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(component.minuteStep, 'key'); }); }); - // отключить секунды времени кнопками + it('should can not change seconds value with the arrow keys', () => { const methodSpy = spyOn(component, 'changeSeconds').and.callThrough(); @@ -948,7 +939,6 @@ describe('Component: timepicker', () => { inputSeconds = getInputElements(fixture)[2]; }); - // отставить поля не заполненными it('should leave the input fields not specified', () => { expect(inputHours.value).toBe(''); expect(inputMinutes.value).toBe(''); @@ -964,7 +954,6 @@ describe('Component: timepicker', () => { expect(methodSpy).not.toHaveBeenCalled(); }); - // не верное значение поля должно сбрасывать время it('should clear model if values are invalid', () => { component.showSeconds = true; component.writeValue(testTime(12, 12, 12)); @@ -984,7 +973,121 @@ describe('Component: timepicker', () => { expect(methodSpy).toHaveBeenCalledWith(null); }); - // верное значение поля + + it('should clear model if hour input is invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.hours = '10'; + component.showMeridian = false; + + component.updateHours('99'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + expect(component.invalidHours).toEqual(true); + }); + + it('should clear model if hour limits are invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.hours = '11'; + component.showMeridian = false; + component.max = testTime(15); + component.min = testTime(10); + + component.updateHours('19'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + expect(component.invalidHours).toEqual(true); + }); + + it('should update time if hour is valid', () => { + spyOn(component, '_updateTime').and.stub(); + component.hours = '10'; + component.showMeridian = false; + + component.updateHours('17'); + + expect(component.invalidHours).toEqual(false); + expect(component._updateTime).toHaveBeenCalled(); + }); + + it('should clear model if minute input is invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.minutes = '10'; + + component.updateMinutes('99'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + }); + + it('should clear model if minute limits are invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.hours = '17'; + component.minutes = '10'; + component.showMeridian = false; + component.max = testTime(15); + component.min = testTime(10); + + component.updateMinutes('30'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + }); + + it('should update time if minute is valid', () => { + spyOn(component, '_updateTime').and.stub(); + component.minutes = '10'; + + component.updateMinutes('30'); + + expect(component.invalidMinutes).toEqual(false); + expect(component._updateTime).toHaveBeenCalled(); + }); + + it('should clear model if second input is invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.showSeconds = true; + component.seconds = '10'; + + component.updateSeconds('99'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + }); + + it('should clear model if second limits are invalid', () => { + const methodSpy = spyOn(component, 'onChange').and.callThrough(); + spyOn(component.isValid, 'emit').and.stub(); + component.hours = '17'; + component.minutes = '10'; + component.seconds = '10'; + component.showMeridian = false; + component.showSeconds = true; + component.max = testTime(17, 10, 30); + component.min = testTime(10); + + component.updateSeconds('50'); + + expect(methodSpy).toHaveBeenCalledWith(null); + expect(component.isValid.emit).toHaveBeenCalledWith(false); + }); + + it('should update time if second is valid', () => { + spyOn(component, '_updateTime').and.stub(); + component.seconds = '10'; + + component.updateSeconds('30'); + + expect(component.invalidSeconds).toEqual(false); + expect(component._updateTime).toHaveBeenCalled(); + }); + it('should valid value in input fields', () => { component.showSeconds = true; component.showMeridian = false; diff --git a/src/spec/timepicker/timepicker.utils.spec.ts b/src/spec/timepicker/timepicker.utils.spec.ts index 9b056a4260..6bc9f2288c 100644 --- a/src/spec/timepicker/timepicker.utils.spec.ts +++ b/src/spec/timepicker/timepicker.utils.spec.ts @@ -1,18 +1,22 @@ -import { - changeTime, - createDate, - isNumber, - isValidDate, - padNumber, - parseHours, - parseMinutes, - parseSeconds, - parseTime, - setTime, - toNumber -} from '../../timepicker/timepicker.utils'; - -function testTime(hours?: number, minutes?: number, seconds?: number) { +import { Time, TimepickerComponentState } from '../../timepicker/timepicker.models'; +import * as timepickerUtils from '../../timepicker/timepicker.utils'; + +const controls: TimepickerComponentState = { + min: testTime(3, 0, 0), + max: testTime(17, 0, 0), + hourStep: 1, + minuteStep: 1, + secondsStep: 1, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: false, + showMeridian: true, + showSeconds: true, + meridians: ['AM', 'PM'] +}; + +function testTime(hours?: number, minutes?: number, seconds?: number): Date { const time = new Date(); time.setHours(hours || 0); time.setMinutes(minutes || 0); @@ -24,117 +28,211 @@ function testTime(hours?: number, minutes?: number, seconds?: number) { function modelTime(hours: string | number, minutes: string | number, second: string | number, - PM: boolean) { - const time = { + PM: boolean): Time { + return { hour: hours || null, minute: minutes || null, seconds: second || null, isPM: PM || null }; - - return time; } describe('Runtime coverage. Utils: Timepicker', () => { it('should is not empty', () => { - isValidDate(); + timepickerUtils.isValidDate(); }); it('should is empty', () => { - isValidDate(testTime()); + timepickerUtils.isValidDate(testTime()); }); it('should date is interface Data', () => { const time = new Date(); time.setHours(NaN); - isValidDate(time); + timepickerUtils.isValidDate(time); }); it('should date is string', () => { - isValidDate('123'); + timepickerUtils.isValidDate('123'); }); it('should to number', () => { - toNumber(12); + timepickerUtils.toNumber(12); }); it('should to string', () => { - toNumber('12'); + timepickerUtils.toNumber('12'); }); it('should date is string', () => { - isNumber('12'); + timepickerUtils.isNumber('12'); }); it('should parse hours valid value', () => { - parseHours(12); + timepickerUtils.parseHours(12); }); it('should parse hours invalid value', () => { - parseHours('q'); + timepickerUtils.parseHours('q'); }); it('should parse minutes valid value', () => { - parseMinutes(12); + timepickerUtils.parseMinutes(12); }); it('should parse minutes invalid value', () => { - parseMinutes('q'); + timepickerUtils.parseMinutes('q'); }); it('should parse seconds valid value', () => { - parseSeconds(12); + timepickerUtils.parseSeconds(12); }); it('should parse seconds invalid value', () => { - parseSeconds('q'); + timepickerUtils.parseSeconds('q'); }); it('should parse time string value', () => { - parseTime('12'); + timepickerUtils.parseTime('12'); }); it('should parse time date value', () => { - parseTime(testTime()); + timepickerUtils.parseTime(testTime()); }); it('should change time valid value', () => { - changeTime(testTime(), modelTime(1, 2, 3, true)); + timepickerUtils.changeTime(testTime(), modelTime(1, 2, 3, true)); }); it('should change time invalid diff', () => { - changeTime(testTime(), modelTime(-1, 0, 0, false)); + timepickerUtils.changeTime(testTime(), modelTime(-1, 0, 0, false)); }); it('should change time invalid diff hour NaN', () => { - changeTime(testTime(), modelTime(NaN, 0, 0, false)); + timepickerUtils.changeTime(testTime(), modelTime(NaN, 0, 0, false)); }); it('should set time opts true', () => { - setTime(testTime(), modelTime(0, 0, 0, true)); + timepickerUtils.setTime(testTime(), modelTime(0, 0, 0, true)); }); it('should set time opts false', () => { - setTime(testTime(), modelTime(0, 0, 0, false)); + timepickerUtils.setTime(testTime(), modelTime(0, 0, 0, false)); }); it('should set time opts hours NaN', () => { - setTime(testTime(), modelTime(1, 1, 0, false)); + timepickerUtils.setTime(testTime(), modelTime(1, 1, 0, false)); }); it('should create date', () => { - createDate(testTime(), 10, 20, 30); + timepickerUtils.createDate(testTime(), 10, 20, 30); }); it('should create date false', () => { - createDate(testTime(), 10, 20, 30); + timepickerUtils.createDate(testTime(), 10, 20, 30); }); it('should pad number', () => { - padNumber(10); + timepickerUtils.padNumber(10); }); it('should pad number length', () => { - padNumber(1); + timepickerUtils.padNumber(1); + }); + + it('isValidLimit method should validate the date according to the max limit and return false', () => { + const date = testTime(18, 0, 0); + + const result = timepickerUtils.isValidLimit(controls, date); + + expect(result).toEqual(false); + }); + + it('isValidLimit method should validate the date according to the min limit and return false', () => { + const date = testTime(2, 0, 0); + + const result = timepickerUtils.isValidLimit(controls, date); + + expect(result).toEqual(false); + }); + + it('isValidLimit method should validate the date according to the limits and return true', () => { + const date = testTime(4, 0, 0); + + const result = timepickerUtils.isValidLimit(controls, date); + + expect(result).toEqual(true); + }); + + it('isHourInputValid method should validate hour and return true', () => { + const result = timepickerUtils.isHourInputValid('3', true); + + expect(result).toEqual(true); + }); + + it('isHourInputValid method should validate hour and return false', () => { + const result = timepickerUtils.isHourInputValid('78', false); + + expect(result).toEqual(false); + }); + + it('isMinuteInputValid method should validate minutes and return true', () => { + const result = timepickerUtils.isMinuteInputValid('56'); + + expect(result).toEqual(true); + }); + + it('isMinuteInputValid method should validate minutes and return false', () => { + const result = timepickerUtils.isMinuteInputValid('78'); + + expect(result).toEqual(false); + }); + + it('isSecondInputValid method should validate seconds and return true', () => { + const result = timepickerUtils.isSecondInputValid('56'); + + expect(result).toEqual(true); + }); + + it('isSecondInputValid method should validate seconds and return false', () => { + const result = timepickerUtils.isSecondInputValid('78'); + + expect(result).toEqual(false); + }); + + it('isInputValid method should validate time and return false', () => { + const result = timepickerUtils.isInputValid('78', undefined, undefined, false); + + expect(result).toEqual(false); + }); + + it('isInputValid method should validate time and return true', () => { + const result = timepickerUtils.isInputValid('5', '12', '30', true); + + expect(result).toEqual(true); + }); + + it('isInputLimitValid method should validate input according to the max limit and return false', () => { + const date = modelTime(2, 0, 0, true); + const max = timepickerUtils.changeTime(new Date(), modelTime(1, 0, 0, true)); + + const result = timepickerUtils.isInputLimitValid(date, max, null); + + expect(result).toEqual(false); + }); + + it('isInputLimitValid method should validate input according to the min limit and return false', () => { + const date = modelTime(1, 0, 0, true); + const min = timepickerUtils.changeTime(new Date(), modelTime(3, 0, 0, true)); + + const result = timepickerUtils.isInputLimitValid(date, null, min); + + expect(result).toEqual(false); + }); + + it('isInputLimitValid method should validate input according to the limits and return true', () => { + const result = timepickerUtils.isInputLimitValid(modelTime(1, 0, 0, true), null, null); + + expect(result).toEqual(true); }); }); diff --git a/src/timepicker/reducer/timepicker.actions.ts b/src/timepicker/reducer/timepicker.actions.ts index 7c5e698070..7aa774f949 100644 --- a/src/timepicker/reducer/timepicker.actions.ts +++ b/src/timepicker/reducer/timepicker.actions.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Action } from '../../mini-ngrx/index'; +import { Action } from '../../mini-ngrx'; import { TimeChangeEvent, TimepickerComponentState, diff --git a/src/timepicker/reducer/timepicker.reducer.ts b/src/timepicker/reducer/timepicker.reducer.ts index 413f5cd667..bf36f937e6 100644 --- a/src/timepicker/reducer/timepicker.reducer.ts +++ b/src/timepicker/reducer/timepicker.reducer.ts @@ -1,4 +1,4 @@ -import { Action } from '../../mini-ngrx/index'; +import { Action } from '../../mini-ngrx'; import { canChangeHours, canChangeMinutes, @@ -11,7 +11,7 @@ import { TimepickerComponentState, TimepickerControls } from '../timepicker.models'; -import { changeTime, setTime } from '../timepicker.utils'; +import { changeTime, setTime, isValidLimit } from '../timepicker.utils'; import { TimepickerActions } from './timepicker.actions'; export class TimepickerState { @@ -30,7 +30,9 @@ export const initialState: TimepickerState = { canDecrementHours: true, canDecrementMinutes: true, - canDecrementSeconds: true + canDecrementSeconds: true, + + canToggleMeridian: true } }; @@ -50,6 +52,10 @@ export function timepickerReducer(state = initialState, action: Action) { const _newTime = changeTime(state.value, { hour: action.payload.step }); + if ((state.config.max || state.config.min) && !isValidLimit(state.config, _newTime)) { + return state; + } + return Object.assign({}, state, { value: _newTime }); } @@ -63,6 +69,10 @@ export function timepickerReducer(state = initialState, action: Action) { const _newTime = changeTime(state.value, { minute: action.payload.step }); + if ((state.config.max || state.config.min) && !isValidLimit(state.config, _newTime)) { + return state; + } + return Object.assign({}, state, { value: _newTime }); } @@ -78,6 +88,10 @@ export function timepickerReducer(state = initialState, action: Action) { seconds: action.payload.step }); + if ((state.config.max || state.config.min) && !isValidLimit(state.config, _newTime)) { + return state; + } + return Object.assign({}, state, { value: _newTime }); } @@ -100,7 +114,7 @@ export function timepickerReducer(state = initialState, action: Action) { }; if (state.config.showMeridian !== _newState.config.showMeridian) { - if(state.value){ + if (state.value) { _newState.value = new Date(state.value); } } diff --git a/src/timepicker/reducer/timepicker.store.ts b/src/timepicker/reducer/timepicker.store.ts index 695fcd129a..83b0ce82c2 100644 --- a/src/timepicker/reducer/timepicker.store.ts +++ b/src/timepicker/reducer/timepicker.store.ts @@ -6,9 +6,7 @@ import { } from './timepicker.reducer'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { Action } from '../../mini-ngrx/index'; -import { MiniStore } from '../../mini-ngrx/store.class'; -import { MiniState } from '../../mini-ngrx/state.class'; +import { Action, MiniStore, MiniState } from '../../mini-ngrx'; @Injectable() export class TimepickerStore extends MiniStore { diff --git a/src/timepicker/timepicker-controls.util.ts b/src/timepicker/timepicker-controls.util.ts index c5d31c1629..5373860fb5 100644 --- a/src/timepicker/timepicker-controls.util.ts +++ b/src/timepicker/timepicker-controls.util.ts @@ -117,6 +117,7 @@ export function timepickerControls( value: Date, state: TimepickerComponentState ): TimepickerControls { + const hoursPerDayHalf = 12; const { min, max, hourStep, minuteStep, secondsStep, showSeconds } = state; const res: TimepickerControls = { canIncrementHours: true, @@ -125,7 +126,9 @@ export function timepickerControls( canDecrementHours: true, canDecrementMinutes: true, - canDecrementSeconds: true + canDecrementSeconds: true, + + canToggleMeridian: true }; if (!value) { @@ -148,6 +151,10 @@ export function timepickerControls( const _newSeconds = changeTime(value, { seconds: secondsStep }); res.canIncrementSeconds = max >= _newSeconds; } + + if (value.getHours() < hoursPerDayHalf) { + res.canToggleMeridian = changeTime(value, { hour: hoursPerDayHalf }) < max; + } } if (min) { @@ -165,6 +172,10 @@ export function timepickerControls( const _newSeconds = changeTime(value, { seconds: -secondsStep }); res.canDecrementSeconds = min <= _newSeconds; } + + if (value.getHours() >= hoursPerDayHalf) { + res.canToggleMeridian = changeTime(value, { hour: -hoursPerDayHalf }) > min; + } } return res; diff --git a/src/timepicker/timepicker.component.html b/src/timepicker/timepicker.component.html index 0a685e2429..b6a1a61344 100644 --- a/src/timepicker/timepicker.component.html +++ b/src/timepicker/timepicker.component.html @@ -32,7 +32,7 @@ -  :  - - +  :  - - + diff --git a/src/timepicker/timepicker.component.ts b/src/timepicker/timepicker.component.ts index fe27e361a2..c4b210135e 100644 --- a/src/timepicker/timepicker.component.ts +++ b/src/timepicker/timepicker.component.ts @@ -7,6 +7,7 @@ import { forwardRef, Input, OnChanges, + OnDestroy, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; @@ -25,9 +26,13 @@ import { isValidDate, padNumber, parseTime, - isInputValid, parseSeconds + isInputValid, + isHourInputValid, + isMinuteInputValid, + isSecondInputValid, + isInputLimitValid } from './timepicker.utils'; -import { fakeAsync } from '@angular/core/testing'; +import { Subscription } from 'rxjs/Subscription'; export const TIMEPICKER_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, @@ -70,7 +75,8 @@ export class TimepickerComponent implements ControlValueAccessor, TimepickerComponentState, TimepickerControls, - OnChanges { + OnChanges, + OnDestroy { /** hours change step */ @Input() hourStep: number; /** hours change step */ @@ -99,7 +105,7 @@ export class TimepickerComponent @Input() max: Date; /** emits true if value is a valid date */ - @Output() isValid: EventEmitter = new EventEmitter(); + @Output() isValid = new EventEmitter(); // ui variables hours: string; @@ -125,10 +131,14 @@ export class TimepickerComponent canDecrementMinutes: boolean; canDecrementSeconds: boolean; + canToggleMeridian: boolean; + // control value accessor methods onChange: any = Function.prototype; onTouched: any = Function.prototype; + timepickerSub: Subscription; + constructor( _config: TimepickerConfig, _cd: ChangeDetectorRef, @@ -136,8 +146,8 @@ export class TimepickerComponent private _timepickerActions: TimepickerActions ) { Object.assign(this, _config); - // todo: add unsubscribe - _store.select(state => state.value).subscribe(value => { + + this.timepickerSub = _store.select(state => state.value).subscribe(value => { // update UI values if date changed this._renderTime(value); this.onChange(value); @@ -148,14 +158,18 @@ export class TimepickerComponent }); _store.select(state => state.controls).subscribe(controlsState => { - this.isValid.emit( - isInputValid(this.hours, this.minutes, this.seconds, this.isPM()) - ); + this.isValid.emit(isInputValid(this.hours, this.minutes, this.seconds, this.isPM())); Object.assign(this, controlsState); _cd.markForCheck(); }); } + resetValidation(): void { + this.invalidHours = false; + this.invalidMinutes = false; + this.invalidSeconds = false; + } + isPM(): boolean { return this.showMeridian && this.meridian === this.meridians[1]; } @@ -175,36 +189,84 @@ export class TimepickerComponent } changeHours(step: number, source: TimeChangeSource = ''): void { + this.resetValidation(); this._store.dispatch(this._timepickerActions.changeHours({ step, source })); } changeMinutes(step: number, source: TimeChangeSource = ''): void { + this.resetValidation(); this._store.dispatch( this._timepickerActions.changeMinutes({ step, source }) ); } changeSeconds(step: number, source: TimeChangeSource = ''): void { + this.resetValidation(); this._store.dispatch( this._timepickerActions.changeSeconds({ step, source }) ); } updateHours(hours: string): void { + this.resetValidation(); this.hours = hours; + + const isValid = isHourInputValid(this.hours, this.isPM()) && this.isValidLimit(); + + if (!isValid) { + this.invalidHours = true; + this.isValid.emit(false); + this.onChange(null); + + return; + } + this._updateTime(); } updateMinutes(minutes: string) { + this.resetValidation(); this.minutes = minutes; + + const isValid = isMinuteInputValid(this.minutes) && this.isValidLimit(); + + if (!isValid) { + this.invalidMinutes = true; + this.isValid.emit(false); + this.onChange(null); + + return; + } + this._updateTime(); } updateSeconds(seconds: string) { + this.resetValidation(); this.seconds = seconds; + + const isValid = isSecondInputValid(this.seconds) && this.isValidLimit(); + + if (!isValid) { + this.invalidSeconds = true; + this.isValid.emit(false); + this.onChange(null); + + return; + } + this._updateTime(); } + isValidLimit(): boolean { + return isInputLimitValid({ + hour: this.hours, + minute: this.minutes, + seconds: this.seconds, + isPM: this.isPM() + }, this.max, this.min); + } + _updateTime() { const _seconds = this.showSeconds ? this.seconds : void 0; const _minutes = this.showMinutes ? this.minutes : void 0; @@ -214,6 +276,7 @@ export class TimepickerComponent return; } + this._store.dispatch( this._timepickerActions.setTime({ hour: this.hours, @@ -273,6 +336,10 @@ export class TimepickerComponent this.readonlyInput = isDisabled; } + ngOnDestroy(): void { + this.timepickerSub.unsubscribe(); + } + private _renderTime(value: string | Date): void { if (!isValidDate(value)) { this.hours = ''; diff --git a/src/timepicker/timepicker.models.ts b/src/timepicker/timepicker.models.ts index 69f8ba5a20..cdb8943910 100644 --- a/src/timepicker/timepicker.models.ts +++ b/src/timepicker/timepicker.models.ts @@ -13,6 +13,8 @@ export interface TimepickerControls { canDecrementHours: boolean; canDecrementMinutes: boolean; canDecrementSeconds: boolean; + + canToggleMeridian: boolean; } export interface TimepickerComponentState { diff --git a/src/timepicker/timepicker.utils.ts b/src/timepicker/timepicker.utils.ts index a9bdaee9dd..ed99fa1cef 100644 --- a/src/timepicker/timepicker.utils.ts +++ b/src/timepicker/timepicker.utils.ts @@ -1,4 +1,4 @@ -import { Time } from './timepicker.models'; +import { Time, TimepickerComponentState } from './timepicker.models'; const dex = 10; const hoursPerDay = 24; @@ -22,6 +22,18 @@ export function isValidDate(value?: string | Date): boolean { return true; } +export function isValidLimit(controls: TimepickerComponentState, newDate: Date): boolean { + if (controls.min && newDate < controls.min) { + return false; + } + + if (controls.max && newDate > controls.max) { + return false; + } + + return true; +} + export function toNumber(value: string | number): number { if (typeof value === 'number') { return value; @@ -112,7 +124,6 @@ export function setTime(value: Date, opts: Time): Date { hour += hoursPerDayHalf; } - // fixme: unreachable code, value is mandatory if (!value) { if (!isNaN(hour) && !isNaN(minute)) { return createDate(new Date(), hour, minute, seconds); @@ -134,17 +145,14 @@ export function createDate( minutes: number, seconds: number ): Date { - // fixme: unreachable code, value is mandatory - const _value = value || new Date(); - return new Date( - _value.getFullYear(), - _value.getMonth(), - _value.getDate(), + value.getFullYear(), + value.getMonth(), + value.getDate(), hours, minutes, seconds, - _value.getMilliseconds() + value.getMilliseconds() ); } @@ -157,13 +165,39 @@ export function padNumber(value: number): string { return `0${_value}`; } +export function isHourInputValid(hours: string, isPM: boolean): boolean { + return !isNaN(parseHours(hours, isPM)); +} + +export function isMinuteInputValid(minutes: string): boolean { + return !isNaN(parseMinutes(minutes)); +} + +export function isSecondInputValid(seconds: string): boolean { + return !isNaN(parseSeconds(seconds)); +} + +export function isInputLimitValid(diff: Time, max: Date, min: Date): boolean { + const newDate = changeTime(new Date(), diff); + + if (max && newDate > max) { + return false; + } + + if (min && newDate < min) { + return false; + } + + return true; +} + export function isInputValid( hours: string, minutes = '0', seconds = '0', isPM: boolean ): boolean { - return !(isNaN(parseHours(hours, isPM)) - || isNaN(parseMinutes(minutes)) - || isNaN(parseSeconds(seconds))); + return isHourInputValid(hours, isPM) + && isMinuteInputValid(minutes) + && isSecondInputValid(seconds); }