Skip to content

Commit

Permalink
Fix DateRangeInput3 focus with timePrecision
Browse files Browse the repository at this point in the history
  • Loading branch information
evansjohnson committed Jun 27, 2024
1 parent 0d36ba1 commit ec0666b
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
DateUtils,
Errors,
type NonNullDateRange,
TimePrecision,
} from "@blueprintjs/datetime";

import { Classes } from "../../classes";
Expand Down Expand Up @@ -288,6 +289,15 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
// Callbacks - DateRangePicker3
// ===========================

private getTimePrecision = () => {
// timePrecision may be set as a root prop or as a property inside timePickerProps, so we need to check both
const { timePickerProps, timePrecision } = this.props;
if(timePickerProps === DateRangePicker3.defaultProps.timePickerProps) {
return undefined;
}
return timePickerProps?.precision ?? timePrecision;
}

private handleDateRangePickerChange = (selectedRange: DateRange, didSubmitWithEnter = false) => {
// ignore mouse events in the date-range picker if the popover is animating closed.
if (!this.state.isOpen) {
Expand All @@ -306,9 +316,11 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr

let boundaryToModify: Boundary | undefined;

const timePrecision = this.getTimePrecision();

if (selectedStart == null) {
// focus the start field by default or if only an end date is specified
if (this.props.timePrecision == null) {
if (timePrecision == null) {
isStartInputFocused = true;
isEndInputFocused = false;
} else {
Expand All @@ -321,7 +333,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
startHoverString = null;
} else if (selectedEnd == null) {
// focus the end field if a start date is specified
if (this.props.timePrecision == null) {
if (timePrecision == null) {
isStartInputFocused = false;
isEndInputFocused = true;
} else {
Expand All @@ -335,7 +347,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
isOpen = this.getIsOpenValueWhenDateChanges(selectedStart, selectedEnd);
isStartInputFocused = false;

if (this.props.timePrecision == null && didSubmitWithEnter) {
if (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
// the flag to false won't have an effect anyway, so leave it true.
Expand All @@ -346,15 +358,15 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
}
} else if (this.state.lastFocusedField === Boundary.START) {
// keep the start field focused
if (this.props.timePrecision == null) {
if (timePrecision == null) {
isStartInputFocused = true;
isEndInputFocused = false;
} else {
isStartInputFocused = false;
isEndInputFocused = false;
boundaryToModify = Boundary.START;
}
} else if (this.props.timePrecision == null) {
} else if (timePrecision == null) {
// keep the end field focused
isStartInputFocused = false;
isEndInputFocused = true;
Expand Down Expand Up @@ -560,6 +572,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
isValueControlled ? values.controlledValue : values.selectedValue,
this.props,
this.state.locale,
this.getTimePrecision(),
true,
);

Expand Down Expand Up @@ -593,7 +606,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
if (isValueControlled) {
nextState = {
...nextState,
[keys.inputString]: formatDateString(values.controlledValue, this.props, this.state.locale),
[keys.inputString]: formatDateString(values.controlledValue, this.props, this.state.locale, this.getTimePrecision()),
};
} else {
nextState = {
Expand Down Expand Up @@ -680,7 +693,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
private getIsOpenValueWhenDateChanges = (nextSelectedStart: Date, nextSelectedEnd: Date): boolean => {
if (this.props.closeOnSelection) {
// trivial case when TimePicker is not shown
if (this.props.timePrecision == null) {
if (this.getTimePrecision() == null) {
return false;
}

Expand Down Expand Up @@ -747,7 +760,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
} else if (this.doesEndBoundaryOverlapStartBoundary(selectedValue, boundary)) {
return this.props.overlappingDatesMessage;
} else {
return formatDateString(selectedValue, this.props, this.state.locale);
return formatDateString(selectedValue, this.props, this.state.locale, this.getTimePrecision());
}
};

Expand Down Expand Up @@ -893,7 +906,7 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr
// N.B. this.state will be undefined in the constructor, so we need a fallback in that case
const maybeLocale = this.state?.locale ?? typeof props.locale === "string" ? undefined : props.locale;

return formatDateString(date ?? defaultDate, this.props, maybeLocale);
return formatDateString(date ?? defaultDate, this.props, maybeLocale, this.getTimePrecision());
};

private parseDate = (dateString: string | undefined): Date | null => {
Expand All @@ -907,7 +920,8 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr

// HACKHACK: this code below is largely copied from the `useDateParser()` hook, which is the preferred
// implementation that we can migrate to once DateRangeInput3 is a function component.
const { dateFnsFormat, locale: localeFromProps, parseDate, timePickerProps, timePrecision } = this.props;
const { dateFnsFormat, locale: localeFromProps, parseDate, timePickerProps } = this.props;
const timePrecision = this.getTimePrecision();
const { locale } = this.state;
let newDate: false | Date | null = null;

Expand All @@ -931,7 +945,8 @@ export class DateRangeInput3 extends DateFnsLocalizedComponent<DateRangeInput3Pr

// HACKHACK: the code below is largely copied from the `useDateFormatter()` hook, which is the preferred
// implementation that we can migrate to once DateRangeInput3 is a function component.
const { dateFnsFormat, formatDate, locale: localeFromProps, timePickerProps, timePrecision } = this.props;
const { dateFnsFormat, formatDate, locale: localeFromProps, timePickerProps } = this.props;
const timePrecision = this.getTimePrecision();
const { locale } = this.state;

if (formatDate !== undefined) {
Expand All @@ -950,6 +965,7 @@ function formatDateString(
date: Date | false | null | undefined,
props: DateRangeInput3Props,
locale: Locale | undefined,
timePrecision: TimePrecision | undefined,
ignoreRange = false,
) {
const { invalidDateMessage, maxDate, minDate, outOfRangeMessage } = props as DateRangeInput3PropsWithDefaults;
Expand All @@ -961,7 +977,7 @@ function formatDateString(
} else if (ignoreRange || DateUtils.isDayInRange(date, [minDate, maxDate])) {
// HACKHACK: the code below is largely copied from the `useDateFormatter()` hook, which is the preferred
// implementation that we can migrate to once DateRangeInput3 is a function component.
const { dateFnsFormat, formatDate, locale: localeFromProps, timePickerProps, timePrecision } = props;
const { dateFnsFormat, formatDate, locale: localeFromProps, timePickerProps } = props;
if (formatDate !== undefined) {
// user-provided date formatter
return formatDate(date, locale?.code ?? getLocaleCodeFromProps(localeFromProps));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export class DateRangePicker3 extends DateFnsLocalizedComponent<DateRangePicker3
}

const { selectedShortcutIndex } = this.state;
const { allowSingleDayRange, maxDate, minDate, timePrecision } = this.props;
const { allowSingleDayRange, maxDate, minDate } = this.props;
return [
<DatePickerShortcutMenu
key="shortcuts"
Expand All @@ -218,23 +218,31 @@ export class DateRangePicker3 extends DateFnsLocalizedComponent<DateRangePicker3
minDate,
selectedShortcutIndex,
shortcuts,
timePrecision,
timePrecision: this.getTimePrecision(),
}}
onShortcutClick={this.handleShortcutClick}
/>,
<Divider key="div" />,
];
}

private maybeRenderTimePickers(isShowingOneMonth: boolean) {
private getTimePrecision = () => {
// timePrecision may be set as a root prop or as a property inside timePickerProps, so we need to check both
const { timePickerProps, timePrecision = timePickerProps?.precision } = this.props;
if (timePrecision == null && timePickerProps === DateRangePicker3.defaultProps.timePickerProps) {
const { timePickerProps, timePrecision } = this.props;
if(timePickerProps === DateRangePicker3.defaultProps.timePickerProps) {
return undefined;
}
return timePickerProps?.precision ?? timePrecision;
}

private maybeRenderTimePickers(isShowingOneMonth: boolean) {
const timePrecision = this.getTimePrecision();
if (timePrecision == null) {
return null;
}

const isLongTimePicker =
timePickerProps?.useAmPm ||
this.props.timePickerProps?.useAmPm ||
timePrecision === TimePrecision.SECOND ||
timePrecision === TimePrecision.MILLISECOND;

Expand All @@ -246,13 +254,13 @@ export class DateRangePicker3 extends DateFnsLocalizedComponent<DateRangePicker3
>
<TimePicker
precision={timePrecision}
{...timePickerProps}
{...this.props.timePickerProps}
onChange={this.handleTimeChangeLeftCalendar}
value={this.state.time[0]}
/>
<TimePicker
precision={timePrecision}
{...timePickerProps}
{...this.props.timePickerProps}
onChange={this.handleTimeChangeRightCalendar}
value={this.state.time[1]}
/>
Expand Down

1 comment on commit ec0666b

@svc-palantir-github
Copy link

Choose a reason for hiding this comment

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

Fix DateRangeInput3 focus with timePrecision

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.