Skip to content

Commit

Permalink
[datetime2] feat(TimezoneSelect): support valueDisplayFormat
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya committed Oct 18, 2022
1 parent 73514d6 commit 9fe34e6
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/datetime2/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ import * as DateUtils from "./dateUtils";

export { Classes, DateUtils };
export { DateRange, NonNullDateRange } from "./dateRange";
export { TimezoneDisplayFormat } from "./timezoneDisplayFormat";
87 changes: 87 additions & 0 deletions packages/datetime2/src/common/timezoneDisplayFormat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2017 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { TimezoneWithNames } from "./timezoneNameUtils";

export type TimezoneDisplayFormat = "offset" | "abbreviation" | "name" | "composite" | "code" | "long-name";
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const TimezoneDisplayFormat = {
/**
* Short name format: "HST", "EDT", etc.
* Falls back to "GMT+/-offset" if there is no commonly used abbreviation.
*/
ABBREVIATION: "abbreviation" as "abbreviation",

/**
* IANA timezone code: "Pacific/Honolulu", "America/New_York", etc.
*/
CODE: "code" as "code",

/**
* Composite format: "Hawaii Time (HST) -10:00", "New York (EDT) -5:00", etc.
* Omits abbreviation if there is no short name (it is redundant with offset).
*/
COMPOSITE: "composite" as "composite",

/**
* Long name format: "Hawaii-Aleutian Standard Time", "Eastern Daylight Time", "Coordinated Universal Time", etc.
*/
LONG_NAME: "long-name" as "long-name",

/**
* @deprecated use {@link TimezoneDisplayFormat.CODE} instead
*/
// eslint-disable-next-line deprecation/deprecation
NAME: "name" as "name",

/**
* Offset format: "-10:00", "-5:00", etc.
*/
OFFSET: "offset" as "offset",
};

/**
* Formats a timezone according to the specified display format to show in the default `<Button>` rendered as the
* `<TimezoneSelect>` target element.
*/
export function formatTimezone(
timezone: TimezoneWithNames | undefined,
displayFormat: TimezoneDisplayFormat,
): string | undefined {
if (timezone === undefined) {
return undefined;
}

switch (displayFormat) {
case TimezoneDisplayFormat.ABBREVIATION:
return timezone.shortName;
// eslint-disable-next-line deprecation/deprecation
case TimezoneDisplayFormat.NAME:
return timezone.ianaCode;
case TimezoneDisplayFormat.OFFSET:
return timezone.offset;
case TimezoneDisplayFormat.CODE:
return timezone.ianaCode;
case TimezoneDisplayFormat.LONG_NAME:
return timezone.longName;
case TimezoneDisplayFormat.COMPOSITE:
const { shortName } = timezone;
// if the short name is just an offset (contains + or -) or equal to the label, omit it
return /[-\+]/.test(shortName) || shortName === timezone.label
? `${timezone.label} ${timezone.offset}`
: `${timezone.label} (${timezone.shortName}) ${timezone.offset}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import classNames from "classnames";
import { formatInTimeZone } from "date-fns-tz";
import * as React from "react";

import {
Expand All @@ -31,6 +30,7 @@ import {
import { ItemListPredicate, ItemRenderer, Select2, SelectPopoverProps } from "@blueprintjs/select";

import * as Classes from "../../common/classes";
import { formatTimezone, TimezoneDisplayFormat } from "../../common/timezoneDisplayFormat";
import { TIMEZONE_ITEMS } from "../../common/timezoneItems";
import { getInitialTimezoneItems, mapTimezonesWithNames, TimezoneWithNames } from "../../common/timezoneNameUtils";

Expand Down Expand Up @@ -111,6 +111,14 @@ export interface TimezoneSelectProps extends Props {

/** Props to spread to `Popover2`. Note that `content` cannot be changed. */
popoverProps?: SelectPopoverProps["popoverProps"];

/**
* Format to use when displaying the selected (or default) timezone within the target element.
* This prop will be ignored if `children` is provided.
*
* @default TimezoneDisplayFormat.COMPOSITE
*/
valueDisplayFormat?: TimezoneDisplayFormat;
}

export interface TimezoneSelectState {
Expand Down Expand Up @@ -188,13 +196,14 @@ export class TimezoneSelect extends AbstractPureComponent2<TimezoneSelectProps,
}

private renderButton() {
const { buttonProps = {}, disabled, fill, placeholder, value } = this.props;
const { buttonProps = {}, disabled, fill, placeholder, value, valueDisplayFormat } = this.props;
const selectedTimezone = this.timezoneItems.find(tz => tz.ianaCode === value);
const buttonContent = selectedTimezone ? (
`${selectedTimezone.label} ${formatInTimeZone(this.props.date!, selectedTimezone.ianaCode, "xxx")}`
) : (
<span className={CoreClasses.TEXT_MUTED}>{placeholder}</span>
);
const buttonContent =
selectedTimezone !== undefined ? (
formatTimezone(selectedTimezone, valueDisplayFormat ?? TimezoneDisplayFormat.COMPOSITE)
) : (
<span className={CoreClasses.TEXT_MUTED}>{placeholder}</span>
);
return <Button rightIcon="caret-down" disabled={disabled} text={buttonContent} fill={fill} {...buttonProps} />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

import * as React from "react";

import { H5, Position, Switch } from "@blueprintjs/core";
import { TimezoneSelect } from "@blueprintjs/datetime2";
import { Example, ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme";
import { H5, Position, Radio, RadioGroup, Switch } from "@blueprintjs/core";
import { TimezoneDisplayFormat, TimezoneSelect } from "@blueprintjs/datetime2";
import { Example, ExampleProps, handleBooleanChange, handleValueChange } from "@blueprintjs/docs-theme";

export interface TimezoneSelectExampleState {
disabled: boolean;
displayFormat: TimezoneDisplayFormat;
fill: boolean;
showCustomTarget: boolean;
showLocalTimezone: boolean;
Expand All @@ -31,6 +32,7 @@ export interface TimezoneSelectExampleState {
export class TimezoneSelectExample extends React.PureComponent<ExampleProps, TimezoneSelectExampleState> {
public state: TimezoneSelectExampleState = {
disabled: false,
displayFormat: TimezoneDisplayFormat.COMPOSITE,
fill: false,
showCustomTarget: false,
showLocalTimezone: true,
Expand All @@ -39,19 +41,34 @@ export class TimezoneSelectExample extends React.PureComponent<ExampleProps, Tim

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

private handleDisplayFormatChange = handleValueChange((displayFormat: TimezoneDisplayFormat) =>
this.setState({ displayFormat }),
);

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

private handleShowLocalChange = handleBooleanChange(showLocalTimezone => this.setState({ showLocalTimezone }));

public render() {
const { timezone, disabled, fill, showLocalTimezone } = this.state;
const { timezone, disabled, displayFormat, fill, showLocalTimezone } = this.state;

const options = (
<>
<H5>Props</H5>
<Switch checked={showLocalTimezone} label="Show local timezone" onChange={this.handleShowLocalChange} />
<Switch checked={disabled} label="Disabled" onChange={this.handleDisabledChange} />
<Switch label="Fill container width" checked={this.state.fill} onChange={this.handleFillChange} />
<RadioGroup
label="Display format"
onChange={this.handleDisplayFormatChange}
selectedValue={this.state.displayFormat}
>
<Radio label="Composite" value={TimezoneDisplayFormat.COMPOSITE} />
<Radio label="Abbreviation" value={TimezoneDisplayFormat.ABBREVIATION} />
<Radio label="Long Name" value={TimezoneDisplayFormat.LONG_NAME} />
<Radio label="IANA Code" value={TimezoneDisplayFormat.CODE} />
<Radio label="Offset" value={TimezoneDisplayFormat.OFFSET} />
</RadioGroup>
</>
);

Expand All @@ -64,6 +81,7 @@ export class TimezoneSelectExample extends React.PureComponent<ExampleProps, Tim
popoverProps={{ position: Position.BOTTOM }}
showLocalTimezone={showLocalTimezone}
value={timezone}
valueDisplayFormat={displayFormat}
/>
</Example>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@
* limitations under the License.
*/

/**
* @fileoverview These APIs are DEPRECATED and the code is frozen.
* All changes & bugfixes should be made to @blueprintjs/datetime2 instead.
*/

/* eslint-disable deprecation/deprecation */

import * as moment from "moment-timezone";

import { getTimezoneMetadata } from "./timezoneMetadata";

/** @deprecated use @blueprintjs/datetime2 */
export type TimezoneDisplayFormat = "offset" | "abbreviation" | "name" | "composite";
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const TimezoneDisplayFormat = {
Expand All @@ -31,6 +39,7 @@ export const TimezoneDisplayFormat = {
OFFSET: "offset" as "offset",
};

/** @deprecated use @blueprintjs/datetime2 */
export function formatTimezone(timezone: string, date: Date, displayFormat: TimezoneDisplayFormat): string | undefined {
if (!timezone || !moment.tz.zone(timezone)) {
return undefined;
Expand Down

1 comment on commit 9fe34e6

@blueprint-bot
Copy link

Choose a reason for hiding this comment

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

[datetime2] feat(TimezoneSelect): support valueDisplayFormat

Previews: documentation | landing | table | demo

Please sign in to comment.