Skip to content

Commit

Permalink
[datetime] fix(DRI): do not close on selection when time inputs recei…
Browse files Browse the repository at this point in the history
…ve key presses (#3658)
  • Loading branch information
vedadeepta authored and adidahiya committed Jul 18, 2019
1 parent 7b5592b commit 8c40112
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 10 deletions.
35 changes: 30 additions & 5 deletions packages/datetime/src/dateRangeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
Utils,
} from "@blueprintjs/core";

import { DateRange, isDateValid, isDayInRange } from "./common/dateUtils";
import { areSameTime, DateRange, isDateValid, isDayInRange } from "./common/dateUtils";
import * as Errors from "./common/errors";
import { getFormattedDateString, IDateFormatProps } from "./dateFormat";
import { getDefaultMaxDate, getDefaultMinDate, IDatePickerBaseProps } from "./datePickerCore";
Expand Down Expand Up @@ -410,8 +410,9 @@ export class DateRangeInput extends AbstractPureComponent<IDateRangeInputProps,

endHoverString = null;
} else if (this.props.closeOnSelection) {
isOpen = false;
isOpen = this.getIsOpenValueWhenDateChanges(selectedStart, selectedEnd);
isStartInputFocused = false;

if (this.props.timePrecision == null && didSubmitWithEnter) {
// if we submit via click or Tab, the focus will have moved already.
// it we submit with Enter, the focus won't have moved, and setting
Expand Down Expand Up @@ -735,6 +736,29 @@ export class DateRangeInput extends AbstractPureComponent<IDateRangeInputProps,
return isFocused && inputRef !== undefined && document.activeElement !== inputRef;
}

private getIsOpenValueWhenDateChanges = (nextSelectedStart: Date, nextSelectedEnd: Date): boolean => {
if (this.props.closeOnSelection) {
// trivial case when TimePicker is not shown
if (this.props.timePrecision == null) {
return false;
}

const fallbackDate = new Date(new Date().setHours(0, 0, 0, 0));
const [selectedStart, selectedEnd] = this.getSelectedRange([fallbackDate, fallbackDate]);

// case to check if the user has changed TimePicker values
if (
areSameTime(selectedStart, nextSelectedStart) === true &&
areSameTime(selectedEnd, nextSelectedEnd) === true
) {
return false;
}
return true;
}

return true;
};

private getInitialRange = (props = this.props): DateRange => {
const { defaultValue, value } = props;
if (value != null) {
Expand All @@ -746,7 +770,7 @@ export class DateRangeInput extends AbstractPureComponent<IDateRangeInputProps,
}
};

private getSelectedRange = () => {
private getSelectedRange = (fallbackRange?: [Date, Date]) => {
let selectedStart: Date;
let selectedEnd: Date;

Expand All @@ -763,8 +787,9 @@ export class DateRangeInput extends AbstractPureComponent<IDateRangeInputProps,
const doBoundaryDatesOverlap = this.doBoundaryDatesOverlap(selectedStart, Boundary.START);
const dateRange = [selectedStart, doBoundaryDatesOverlap ? undefined : selectedEnd];

return dateRange.map((selectedBound: Date | undefined) => {
return this.isDateValidAndInRange(selectedBound) ? selectedBound : undefined;
return dateRange.map((selectedBound: Date | undefined, index: number) => {
const fallbackDate = fallbackRange != null ? fallbackRange[index] : undefined;
return this.isDateValidAndInRange(selectedBound) ? selectedBound : fallbackDate;
}) as DateRange;
};

Expand Down
54 changes: 49 additions & 5 deletions packages/datetime/test/dateRangeInputTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,50 @@ describe("<DateRangeInput>", () => {
expect(isEndInputFocused(root), "end input focus to be false").to.be.false;
});

after(() => {
it("when timePrecision != null && closeOnSelection=true && <TimePicker /> values is changed popover should not close", () => {
const { root, getDayElement } = wrap(
<DateRangeInput {...DATE_FORMAT} timePrecision={TimePrecision.MINUTE} />,
testsContainerElement,
);

root.setState({ isOpen: true });

getDayElement(1).simulate("click");
getDayElement(10).simulate("click");

root.setState({ isOpen: true });

keyDownOnInput(DateClasses.TIMEPICKER_HOUR, Keys.ARROW_UP);
root.update();
expect(root.find(Popover).prop("isOpen")).to.be.true;
});

it("when timePrecision != null && closeOnSelection=true && end <TimePicker /> values is changed directly (without setting the selectedEnd date) - popover should not close", () => {
const { root } = wrap(
<DateRangeInput {...DATE_FORMAT} timePrecision={TimePrecision.MINUTE} />,
testsContainerElement,
);

root.setState({ isOpen: true });
keyDownOnInput(DateClasses.TIMEPICKER_HOUR, Keys.ARROW_UP);
root.update();
keyDownOnInput(DateClasses.TIMEPICKER_HOUR, Keys.ARROW_UP, 1);
root.update();
expect(root.find(Popover).prop("isOpen")).to.be.true;
});

afterEach(() => {
ReactDOM.unmountComponentAtNode(testsContainerElement);
});

function keyDownOnInput(className: string, key: number) {
TestUtils.Simulate.keyDown(findTimePickerInputElement(className), { which: key });
function keyDownOnInput(className: string, key: number, inputElementIndex: number = 0) {
TestUtils.Simulate.keyDown(findTimePickerInputElement(className, inputElementIndex), { which: key });
}

function findTimePickerInputElement(className: string) {
return document.querySelector(`.${DateClasses.TIMEPICKER_INPUT}.${className}`) as HTMLInputElement;
function findTimePickerInputElement(className: string, inputElementIndex: number = 0) {
return document.querySelectorAll(`.${DateClasses.TIMEPICKER_INPUT}.${className}`)[
inputElementIndex
] as HTMLInputElement;
}
});

Expand Down Expand Up @@ -335,6 +369,16 @@ describe("<DateRangeInput>", () => {
getDayElement(10).simulate("click");
expect(root.state("isOpen")).to.be.false;
});

it("if closeOnSelection=true && timePrecision != null, popover closes when full date range is selected", () => {
const { root, getDayElement } = wrap(
<DateRangeInput {...DATE_FORMAT} timePrecision={TimePrecision.MINUTE} />,
);
root.setState({ isOpen: true });
getDayElement(1).simulate("click");
getDayElement(10).simulate("click");
expect(root.state("isOpen")).to.be.false;
});
});

it("accepts contiguousCalendarMonths prop and passes it to the date range picker", () => {
Expand Down

1 comment on commit 8c40112

@blueprint-bot
Copy link

Choose a reason for hiding this comment

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

[datetime] fix(DRI): do not close on selection when time inputs receive key presses (#3658)

Previews: documentation | landing | table

Please sign in to comment.