Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes / improvements for datetime2 #5370

Merged
merged 5 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/datetime2/src/common/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ export const DATE_INPUT_TIMEZONE_SELECT = `${NS}-date-input-timezone-select`;

export const TIMEZONE_SELECT = `${NS}-timezone-select`;
export const TIMEZONE_SELECT_POPOVER = `${TIMEZONE_SELECT}-popover`;
export const TIMEZONE_SELECT_TARGET = `${TIMEZONE_SELECT}-target`;
32 changes: 18 additions & 14 deletions packages/datetime2/src/components/date-input2/dateInput2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ export interface DateInput2Props extends Omit<DateInputProps, "onChange" | "valu
value: string | null;

/**
* Whether to completely hide timezone elements.
* If `timePrecision` is undefined, this will always be true.
* Whether to show the timezone select dropdown on the right side of the input.
* If `timePrecision` is undefined, this will always be false.
*
* @default false
*/
hideTimezone?: boolean;
showTimezoneSelect?: boolean;

/**
* Whether to disable the timezone picker.
* Whether to disable the timezone select.
*
* @default false
*/
Expand All @@ -60,23 +62,23 @@ const timezoneSelectButtonProps: Partial<ButtonProps> = {
export const DateInput2: React.FC<DateInput2Props> = React.memo(function _DateInput2(props) {
const {
defaultTimezone,
value,
disabled,
disableTimezoneSelect,
onChange,
showTimezoneSelect,
timePrecision,
disableTimezoneSelect,
hideTimezone: hideTimezoneProp,
disabled,
...passThroughToDateInputProps
value,
...dateInputProps
} = props;

const [timezoneValue, updateTimezoneValue] = React.useState(defaultTimezone ?? getCurrentTimezone());
const hideTimezone = timePrecision === undefined ? true : hideTimezoneProp;
const dateValue = React.useMemo(() => getDateObjectFromIsoString(value, timezoneValue), [timezoneValue, value]);

const handleTimezoneChange = React.useCallback(
(newTimezone: string) => {
if (dateValue != null) {
onChange?.(getIsoEquivalentWithUpdatedTimezone(dateValue, newTimezone, timePrecision));
const newDateString = getIsoEquivalentWithUpdatedTimezone(dateValue, newTimezone, timePrecision);
onChange?.(newDateString);
}
updateTimezoneValue(newTimezone);
},
Expand All @@ -88,7 +90,8 @@ export const DateInput2: React.FC<DateInput2Props> = React.memo(function _DateIn
if (newDate == null) {
return;
}
onChange?.(getIsoEquivalentWithUpdatedTimezone(newDate, timezoneValue, timePrecision), isUserChange);
const newDateString = getIsoEquivalentWithUpdatedTimezone(newDate, timezoneValue, timePrecision);
onChange?.(newDateString, isUserChange);
},
[onChange, timezoneValue, timePrecision],
);
Expand All @@ -103,9 +106,10 @@ export const DateInput2: React.FC<DateInput2Props> = React.memo(function _DateIn
[timezoneValue, dateValue],
);

const isTimezoneSelectHidden = timePrecision === undefined || showTimezoneSelect === false;
const isTimezoneSelectDisabled = disabled || disableTimezoneSelect;

const maybeTimezonePicker = hideTimezone ? undefined : (
const maybeTimezonePicker = isTimezoneSelectHidden ? undefined : (
<TimezoneSelect
value={timezoneValue}
onChange={handleTimezoneChange}
Expand All @@ -126,7 +130,7 @@ export const DateInput2: React.FC<DateInput2Props> = React.memo(function _DateIn

return (
<DateInput
{...passThroughToDateInputProps}
{...dateInputProps}
value={dateValue}
onChange={handleDateChange}
timePrecision={timePrecision}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ import {
MenuItem,
Props,
} from "@blueprintjs/core";
import type { Popover2Props } from "@blueprintjs/popover2";
import { ItemListPredicate, ItemRenderer, Select2 } from "@blueprintjs/select";
import { ItemListPredicate, ItemRenderer, Select2, SelectPopoverProps } from "@blueprintjs/select";

import * as Classes from "../../common/classes";
import { TIMEZONE_ITEMS } from "../../common/timezoneItems";
Expand Down Expand Up @@ -111,7 +110,7 @@ export interface TimezoneSelectProps extends Props {
inputProps?: InputGroupProps2;

/** Props to spread to `Popover2`. Note that `content` cannot be changed. */
popoverProps?: Partial<Omit<Popover2Props, "content">>;
popoverProps?: SelectPopoverProps["popoverProps"];
}

export interface TimezoneSelectState {
Expand Down Expand Up @@ -169,7 +168,6 @@ export class TimezoneSelect extends AbstractPureComponent2<TimezoneSelectProps,
...popoverProps,
popoverClassName: classNames(Classes.TIMEZONE_SELECT_POPOVER, popoverProps?.popoverClassName),
}}
popoverTargetProps={{ className: Classes.TIMEZONE_SELECT_TARGET }}
resetOnClose={true}
resetOnSelect={true}
>
Expand Down
18 changes: 9 additions & 9 deletions packages/datetime2/test/components/dateInput2Tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,41 +53,41 @@ describe("<DateInput2>", () => {
it("Correctly passes on the default selected timezone", () => {
const defaultTimezone = "Europe/Paris";
const wrapper = mount(<DateInput2 {...DEFAULT_PROPS} defaultTimezone={defaultTimezone} />);
const timezonePicker = wrapper.find(TimezoneSelect);
const timezoneSelect = wrapper.find(TimezoneSelect);

assert.strictEqual(timezonePicker.prop("value"), defaultTimezone);
assert.strictEqual(timezoneSelect.prop("value"), defaultTimezone);
});

it("It updates the passed back string when timezone is changed", () => {
const wrapper = mount(<DateInput2 {...DEFAULT_PROPS} />);
const timezonePicker = wrapper.find(TimezoneSelect);
const timezoneSelect = wrapper.find(TimezoneSelect);
const newTimezone = "Europe/Paris";
timezonePicker.prop("onChange")(newTimezone);
timezoneSelect.prop("onChange")(newTimezone);

assert.isTrue(onChange.calledOnce);
assert.deepEqual(onChange.firstCall.args, ["2021-11-29T10:30:00.000+01:00"]);
});

it("It formats the string based on the time precision when timezone is changed", () => {
const wrapper = mount(<DateInput2 {...DEFAULT_PROPS} timePrecision={TimePrecision.MINUTE} />);
const timezonePicker = wrapper.find(TimezoneSelect);
const timezoneSelect = wrapper.find(TimezoneSelect);
const newTimezone = "Europe/Paris";
timezonePicker.prop("onChange")(newTimezone);
timezoneSelect.prop("onChange")(newTimezone);

assert.isTrue(onChange.calledOnce);
assert.deepEqual(onChange.firstCall.args, ["2021-11-29T10:30+01:00"]);
});

it("It updates the passed back string when time is changed", () => {
const wrapper = mount(<DateInput2 {...DEFAULT_PROPS} />);
const timezonePicker = wrapper.find(DateInput);
timezonePicker.prop("onChange")!(new Date("2021-11-29T11:30:00.000"), true);
const timezoneSelect = wrapper.find(DateInput);
timezoneSelect.prop("onChange")!(new Date("2021-11-29T11:30:00.000"), true);

assert.isTrue(onChange.calledOnce);
assert.deepEqual(onChange.firstCall.args, ["2021-11-29T11:30:00.000+00:00", true]);
});

it("Does not render a timezone picker not passed a precision", () => {
it("Does not render a timezone select if timePrecision is undefined", () => {
const wrapper = mount(<DateInput2 {...DEFAULT_PROPS} timePrecision={undefined} />);
assert.isFalse(wrapper.find(TimezoneSelect).exists());
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,26 @@ export interface DateInput2ExampleState {
closeOnSelection: boolean;
date: string | null;
disabled: boolean;
disableTimezoneSelect: boolean;
fill: boolean;
format: DateFormatProps;
reverseMonthAndYearMenus: boolean;
shortcuts: boolean;
showTimezoneSelect: boolean;
timePrecision: TimePrecision | undefined;
disableTimezonePicker: boolean;
hideTimezonePicker: boolean;
}

export class DateInput2Example extends React.PureComponent<IExampleProps, DateInput2ExampleState> {
public state: DateInput2ExampleState = {
closeOnSelection: true,
date: null,
disableTimezonePicker: false,
disableTimezoneSelect: false,
disabled: false,
fill: false,
format: FORMATS[0],
hideTimezonePicker: false,
reverseMonthAndYearMenus: false,
shortcuts: false,
showTimezoneSelect: true,
timePrecision: TimePrecision.MINUTE,
};

Expand All @@ -57,22 +57,22 @@ export class DateInput2Example extends React.PureComponent<IExampleProps, DateIn

private toggleDisabled = handleBooleanChange(disabled => this.setState({ disabled }));

private toggleHideTimezonePicker = handleBooleanChange(hideTimezonePicker => this.setState({ hideTimezonePicker }));
private toggleShowTimezoneSelect = handleBooleanChange(showTimezoneSelect => this.setState({ showTimezoneSelect }));

private toggleDisableTimezonePicker = handleBooleanChange(disableTimezonePicker =>
this.setState({ disableTimezonePicker }),
private toggleDisableTimezoneSelect = handleBooleanChange(disableTimezoneSelect =>
this.setState({ disableTimezoneSelect }),
);

private toggleFill = handleBooleanChange(fill => this.setState({ fill }));

private toggleReverseMenus = handleBooleanChange(reverse => this.setState({ reverseMonthAndYearMenus: reverse }));

private toggleTimePrecision = handleValueChange((timePrecision: TimePrecision | "none") =>
private handleTimePrecisionChange = handleValueChange((timePrecision: TimePrecision | "none") =>
this.setState({ timePrecision: timePrecision === "none" ? undefined : timePrecision }),
);

public render() {
const { date, format, timePrecision, hideTimezonePicker, disableTimezonePicker, ...spreadProps } = this.state;
const { date, format, timePrecision, ...spreadProps } = this.state;
return (
<Example options={this.renderOptions()} {...this.props}>
<DateInput2
Expand All @@ -82,8 +82,6 @@ export class DateInput2Example extends React.PureComponent<IExampleProps, DateIn
popoverProps={{ position: Position.BOTTOM }}
timePrecision={timePrecision}
value={date}
hideTimezone={hideTimezonePicker}
disableTimezoneSelect={disableTimezonePicker}
/>
{date}
</Example>
Expand All @@ -99,34 +97,37 @@ export class DateInput2Example extends React.PureComponent<IExampleProps, DateIn
format,
timePrecision,
shortcuts,
disableTimezonePicker,
hideTimezonePicker,
disableTimezoneSelect,
showTimezoneSelect,
} = this.state;
return (
<>
<H5>Props</H5>
<Switch label="Close on selection" checked={closeOnSelection} onChange={this.toggleSelection} />
<Switch checked={shortcuts} label="Show shortcuts" onChange={this.toggleShortcuts} />
<PrecisionSelect
allowNone={true}
label="Time precision"
onChange={this.handleTimePrecisionChange}
value={timePrecision}
/>

<H5>Appearance props</H5>
<Switch label="Disabled" checked={disabled} onChange={this.toggleDisabled} />
<Switch label="Fill" checked={fill} onChange={this.toggleFill} />
<Switch label="Reverse month and year menus" checked={reverse} onChange={this.toggleReverseMenus} />
<FormatSelect format={format} onChange={this.handleFormatChange} />

<H5>Timezone props</H5>
<Switch
label="Disable timezone picker"
checked={disableTimezonePicker}
onChange={this.toggleDisableTimezonePicker}
label="Disable timezone select"
checked={disableTimezoneSelect}
onChange={this.toggleDisableTimezoneSelect}
/>
<Switch
label="Hide timezone picker"
checked={hideTimezonePicker}
onChange={this.toggleHideTimezonePicker}
/>

<FormatSelect format={format} onChange={this.handleFormatChange} />
<PrecisionSelect
allowNone={true}
label="Time precision"
onChange={this.toggleTimePrecision}
value={timePrecision}
label="Show timezone select"
checked={showTimezoneSelect}
onChange={this.toggleShowTimezoneSelect}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class TimezonePickerExample extends React.PureComponent<IExampleProps, IT
</>
);

/* eslint-disable deprecation/deprecation */
return (
<Example options={options} {...this.props}>
<TimezonePicker
Expand All @@ -86,6 +87,7 @@ export class TimezonePickerExample extends React.PureComponent<IExampleProps, IT
</TimezonePicker>
</Example>
);
/* eslint-enable deprecation/deprecation */
}

private renderCustomTarget() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export interface ITimezonePickerState {
// eslint-disable-next-line deprecation/deprecation
const TypedSelect = Select.ofType<TimezoneItem>();

/** @deprecated use { TimezoneSelect } from "@blueprintjs/datetime2" */
export class TimezonePicker extends AbstractPureComponent2<TimezonePickerProps, ITimezonePickerState> {
public static displayName = `${DISPLAYNAME_PREFIX}.TimezonePicker`;

Expand Down
7 changes: 2 additions & 5 deletions packages/timezone/test/timezonePickerTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const shallow = (
options?: ShallowRendererProps,
): TimezonePickerShallowWrapper => untypedShallow<TimezonePicker>(el, options);

const VALUE = "America/Los_Angeles";
/* eslint-disable deprecation/deprecation */

describe("<TimezonePicker>", () => {
const onChange = sinon.spy();
Expand All @@ -60,7 +60,7 @@ describe("<TimezonePicker>", () => {
isOpen: true,
usePortal: false,
},
value: VALUE,
value: "America/Los_Angeles",
};

afterEach(() => onChange.resetHistory());
Expand All @@ -69,7 +69,6 @@ describe("<TimezonePicker>", () => {
// remove isOpen from popoverProps so it's
const timezonePicker = mount(<TimezonePicker {...DEFAULT_PROPS} popoverProps={{ usePortal: false }} />);
timezonePicker.find(Button).simulate("click");
/* eslint-disable-next-line deprecation/deprecation */
assert.isTrue(timezonePicker.find(Popover).prop("isOpen"));
});

Expand All @@ -78,7 +77,6 @@ describe("<TimezonePicker>", () => {
<TimezonePicker {...DEFAULT_PROPS} disabled={true} popoverProps={{ usePortal: false }} />,
);
timezonePicker.find(Button).simulate("click");
/* eslint-disable-next-line deprecation/deprecation */
assert.isFalse(timezonePicker.find(Popover).prop("isOpen"));
});

Expand Down Expand Up @@ -244,7 +242,6 @@ describe("<TimezonePicker>", () => {
}

function findPopover(timezonePicker: TimezonePickerShallowWrapper) {
/* eslint-disable-next-line deprecation/deprecation */
return findQueryList(timezonePicker).shallow().find(Popover);
}

Expand Down