From f023a106205f01d79855294743cc33fc9ac8dd35 Mon Sep 17 00:00:00 2001 From: Yixun Xu Date: Mon, 10 Dec 2018 13:00:16 -0500 Subject: [PATCH 1/3] Allow date range shortcuts to change time --- packages/datetime/src/dateRangePicker.tsx | 22 ++++++++++++++++++- packages/datetime/src/shortcuts.tsx | 9 ++++---- .../datetime/test/dateRangePickerTests.tsx | 19 +++++++++++++++- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/datetime/src/dateRangePicker.tsx b/packages/datetime/src/dateRangePicker.tsx index a1ca53f5910..80359c82619 100644 --- a/packages/datetime/src/dateRangePicker.tsx +++ b/packages/datetime/src/dateRangePicker.tsx @@ -35,6 +35,14 @@ import { TimePicker } from "./timePicker"; export interface IDateRangeShortcut { label: string; dateRange: DateRange; + + /** + * By default, clicking a shortcut does not change the time of the date range picker, but instead + * takes the date components of the `dateRange` and combines it with the currently selected time. + * Setting `shouldChangeTime` to `true` will override this behavior and allow shortcuts to change + * the selected time as well. + */ + shouldChangeTime?: boolean; } export interface IDateRangePickerProps extends IDatePickerBaseProps, IProps { @@ -282,7 +290,7 @@ export class DateRangePicker extends AbstractPureComponent, , ]; @@ -495,6 +503,18 @@ export class DateRangePicker extends AbstractPureComponent { + const { dateRange, shouldChangeTime } = shortcut; + if (shouldChangeTime) { + const newDateRange: DateRange = [dateRange[0], dateRange[1]]; + const newTimeRange: DateRange = [dateRange[0], dateRange[1]]; + Utils.safeInvoke(this.props.onChange, newDateRange); + this.setState({ value: newDateRange, time: newTimeRange }); + } else { + this.handleNextState(dateRange); + } + }; + private handleNextState = (nextValue: DateRange) => { const { value } = this.state; nextValue[0] = DateUtils.getDateTime(nextValue[0], this.state.time[0]); diff --git a/packages/datetime/src/shortcuts.tsx b/packages/datetime/src/shortcuts.tsx index b668a5b7a5b..d7aeed10e53 100644 --- a/packages/datetime/src/shortcuts.tsx +++ b/packages/datetime/src/shortcuts.tsx @@ -12,6 +12,7 @@ import { clone, DateRange, isDayRangeInRange } from "./common/dateUtils"; export interface IDateRangeShortcut { label: string; dateRange: DateRange; + shouldChangeTime?: boolean; } export interface IShortcutsProps { @@ -19,7 +20,7 @@ export interface IShortcutsProps { minDate: Date; maxDate: Date; shortcuts: IDateRangeShortcut[] | true; - onShortcutClick: (shortcut: DateRange) => void; + onShortcutClick: (shortcut: IDateRangeShortcut) => void; } export class Shortcuts extends React.PureComponent { @@ -34,7 +35,7 @@ export class Shortcuts extends React.PureComponent { className={Classes.POPOVER_DISMISS_OVERRIDE} disabled={!this.isShortcutInRange(s.dateRange)} key={i} - onClick={this.getShorcutClickHandler(s.dateRange)} + onClick={this.getShorcutClickHandler(s)} text={s.label} /> )); @@ -42,8 +43,8 @@ export class Shortcuts extends React.PureComponent { return {shortcutElements}; } - private getShorcutClickHandler(nextValue: DateRange) { - return () => this.props.onShortcutClick(nextValue); + private getShorcutClickHandler(shortcut: IDateRangeShortcut) { + return () => this.props.onShortcutClick(shortcut); } private isShortcutInRange(shortcutDateRange: DateRange) { diff --git a/packages/datetime/test/dateRangePickerTests.tsx b/packages/datetime/test/dateRangePickerTests.tsx index cb84cf1c92c..ce11635e303 100644 --- a/packages/datetime/test/dateRangePickerTests.tsx +++ b/packages/datetime/test/dateRangePickerTests.tsx @@ -1147,11 +1147,28 @@ describe("", () => { assert.isTrue(DateUtils.areSameDay(onChangeSpy.firstCall.args[0][0] as Date, new Date())); }); - it("clicking a shortcut doesn't change time", () => { + it("clicking a shortcut with shouldChangeTime=false doesn't change time", () => { render({ timePrecision: "minute", defaultValue: defaultRange }).clickShortcut(); assert.isTrue(DateUtils.areSameTime(onChangeSpy.firstCall.args[0][0] as Date, defaultRange[0])); }); + it("clicking a shortcut with shouldChangeTime=true changes time", () => { + const endTime = defaultRange[1]; + const startTime = new Date(defaultRange[1].getTime()); + startTime.setHours(startTime.getHours() - 2); + + const shortcuts = [ + { + dateRange: [startTime, endTime] as DateRange, + label: "custom shortcut", + shouldChangeTime: true, + }, + ]; + + render({ timePrecision: "minute", defaultValue: defaultRange, shortcuts }).clickShortcut(); + assert.equal(onChangeSpy.firstCall.args[0][0] as Date, startTime); + }); + it("selecting and unselecting a day doesn't change time", () => { const leftDatePicker = render({ timePrecision: "minute", defaultValue: defaultRange }).left; leftDatePicker.clickDay(5); From 6cbd4640a17f134d6add0b3f8b70d36c42467948 Mon Sep 17 00:00:00 2001 From: Yixun Xu Date: Tue, 11 Dec 2018 13:40:49 -0500 Subject: [PATCH 2/3] Address PR comments --- packages/datetime/src/dateRangePicker.tsx | 9 +- .../datetime/test/dateRangePickerTests.tsx | 85 ++++++++++++------- 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/packages/datetime/src/dateRangePicker.tsx b/packages/datetime/src/dateRangePicker.tsx index 80359c82619..9af60d3d259 100644 --- a/packages/datetime/src/dateRangePicker.tsx +++ b/packages/datetime/src/dateRangePicker.tsx @@ -41,6 +41,7 @@ export interface IDateRangeShortcut { * takes the date components of the `dateRange` and combines it with the currently selected time. * Setting `shouldChangeTime` to `true` will override this behavior and allow shortcuts to change * the selected time as well. + * @default false */ shouldChangeTime?: boolean; } @@ -508,8 +509,14 @@ export class DateRangePicker extends AbstractPureComponent", () => { }); it("custom shortcuts set the displayed months correctly when start month changes", () => { - const dateRange = [new Date(2016, Months.JANUARY, 1), new Date(2016, Months.DECEMBER, 31)] as DateRange; - const { left, right } = render({ - initialMonth: new Date(2015, Months.JANUARY, 1), - shortcuts: [{ label: "custom shortcut", dateRange }], - }).clickShortcut(); - assert.isTrue(onChangeSpy.calledOnce); - left.assertMonthYear(Months.JANUARY, 2016); - right.assertMonthYear(Months.FEBRUARY, 2016); - }); + const dateRange = [ + new Date(2016, Months.JANUARY, 1, 10, 20, 30), + new Date(2016, Months.DECEMBER, 31, 10, 20, 30), + ] as DateRange; - it( - "custom shortcuts set the displayed months correctly when start month changes " + - "and contiguousCalendarMonths is false", - () => { - const dateRange = [new Date(2016, Months.JANUARY, 1), new Date(2016, Months.DECEMBER, 31)] as DateRange; + const test = (shouldChangeTime: boolean) => { const { left, right } = render({ - contiguousCalendarMonths: false, initialMonth: new Date(2015, Months.JANUARY, 1), - shortcuts: [{ label: "custom shortcut", dateRange }], + shortcuts: [{ label: "custom shortcut", dateRange, shouldChangeTime }], }).clickShortcut(); assert.isTrue(onChangeSpy.calledOnce); left.assertMonthYear(Months.JANUARY, 2016); - right.assertMonthYear(Months.DECEMBER, 2016); + right.assertMonthYear(Months.FEBRUARY, 2016); + }; + + test(true); + test(false); + }); + + it( + "custom shortcuts set the displayed months correctly when start month changes " + + "and contiguousCalendarMonths is false", + () => { + const dateRange = [ + new Date(2016, Months.JANUARY, 1, 10, 20, 30), + new Date(2016, Months.DECEMBER, 31, 10, 20, 30), + ] as DateRange; + + const test = (shouldChangeTime: boolean) => { + const { left, right } = render({ + contiguousCalendarMonths: false, + initialMonth: new Date(2015, Months.JANUARY, 1), + shortcuts: [{ label: "custom shortcut", dateRange, shouldChangeTime }], + }).clickShortcut(); + assert.isTrue(onChangeSpy.calledOnce); + left.assertMonthYear(Months.JANUARY, 2016); + right.assertMonthYear(Months.DECEMBER, 2016); + }; + + test(true); + test(false); }, ); it("custom shortcuts set the displayed months correctly when start month stays the same", () => { - const dateRange = [new Date(2016, Months.JANUARY, 1), new Date(2016, Months.DECEMBER, 31)] as DateRange; - const { clickShortcut, left, right } = render({ - initialMonth: new Date(2016, Months.JANUARY, 1), - shortcuts: [{ label: "custom shortcut", dateRange }], - }); + const dateRange = [ + new Date(2016, Months.JANUARY, 1, 10, 20, 30), + new Date(2016, Months.DECEMBER, 31, 10, 20, 30) + ] as DateRange; + + const test = (shouldChangeTime: boolean) => { + const { clickShortcut, left, right } = render({ + initialMonth: new Date(2016, Months.JANUARY, 1), + shortcuts: [{ label: "custom shortcut", dateRange, shouldChangeTime }], + }); - clickShortcut(); - assert.isTrue(onChangeSpy.calledOnce); - left.assertMonthYear(Months.JANUARY, 2016); - right.assertMonthYear(Months.FEBRUARY, 2016); + clickShortcut(); + assert.isTrue(onChangeSpy.calledOnce); + left.assertMonthYear(Months.JANUARY, 2016); + right.assertMonthYear(Months.FEBRUARY, 2016); + + clickShortcut(); + left.assertMonthYear(Months.JANUARY, 2016); + right.assertMonthYear(Months.FEBRUARY, 2016); + }; - clickShortcut(); - left.assertMonthYear(Months.JANUARY, 2016); - right.assertMonthYear(Months.FEBRUARY, 2016); + test(true); + test(false); }); }); From badae4f420158bcf51f6655eecc2877c43f98b57 Mon Sep 17 00:00:00 2001 From: Yixun Xu Date: Tue, 11 Dec 2018 13:43:06 -0500 Subject: [PATCH 3/3] lint --- packages/datetime/test/dateRangePickerTests.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/datetime/test/dateRangePickerTests.tsx b/packages/datetime/test/dateRangePickerTests.tsx index aea3e03b6f5..5a0c8fccc2f 100644 --- a/packages/datetime/test/dateRangePickerTests.tsx +++ b/packages/datetime/test/dateRangePickerTests.tsx @@ -951,7 +951,7 @@ describe("", () => { it("custom shortcuts set the displayed months correctly when start month stays the same", () => { const dateRange = [ new Date(2016, Months.JANUARY, 1, 10, 20, 30), - new Date(2016, Months.DECEMBER, 31, 10, 20, 30) + new Date(2016, Months.DECEMBER, 31, 10, 20, 30), ] as DateRange; const test = (shouldChangeTime: boolean) => {