-
Notifications
You must be signed in to change notification settings - Fork 35
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
Update Date/Time Selector #793
base: master
Are you sure you want to change the base?
Changes from all commits
75fc672
ea03d07
d2855f5
7fc56a4
352d394
1e24618
44a98bd
f92704b
97e88b0
1b8539c
8c96fc8
43e2d1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,15 @@ | ||
import CSS from "csstype"; | ||
import { format, parse } from "date-fns"; | ||
import flatten from "flat"; | ||
import coreUtils from "@opentripplanner/core-utils"; | ||
import React, { ChangeEvent, ReactElement, ReactNode, useCallback } from "react"; | ||
import { FormattedMessage, useIntl } from "react-intl"; | ||
import React, { ChangeEvent, ReactElement, useCallback } from "react"; | ||
import { useIntl } from "react-intl"; | ||
|
||
import ModeButton from "../ModeButton"; | ||
import colors, { Dropdown } from "@opentripplanner/building-blocks"; | ||
import * as S from "../styled"; | ||
|
||
// eslint-disable-next-line prettier/prettier | ||
import type { QueryParamChangeEvent } from "../types"; | ||
|
||
// Load the default messages. | ||
import defaultEnglishMessages from "../../i18n/en-US.yml"; | ||
|
||
// HACK: We should flatten the messages loaded above because | ||
// the YAML loaders behave differently between webpack and our version of jest: | ||
// - the yaml loader for webpack returns a nested object, | ||
// - the yaml loader for jest returns messages with flattened ids. | ||
const defaultMessages: Record<string, string> = flatten(defaultEnglishMessages); | ||
|
||
const { | ||
getCurrentDate, | ||
|
@@ -28,6 +19,8 @@ const { | |
OTP_API_TIME_FORMAT | ||
} = coreUtils.time; | ||
|
||
const { grey } = colors; | ||
|
||
type DepartArriveValue = "NOW" | "DEPART" | "ARRIVE"; | ||
|
||
interface DateTimeSelectorProps { | ||
|
@@ -77,8 +70,7 @@ interface DateTimeSelectorProps { | |
} | ||
|
||
interface DepartArriveOption { | ||
isSelected?: boolean; | ||
text: ReactNode; | ||
text: string; | ||
type: DepartArriveValue; | ||
} | ||
|
||
|
@@ -99,6 +91,8 @@ function isInputTypeSupported(type: string): boolean { | |
|
||
const supportsDateTimeInputs = isInputTypeSupported("date") && isInputTypeSupported("time"); | ||
|
||
const buttonStyle = { backgroundColor: S.baseColor() || grey[700], border: "0px", borderRadius: "3px 0px 0px 3px", color: "white", height: "45px" } | ||
|
||
/** | ||
* Reference date for parsing. | ||
*/ | ||
|
@@ -137,6 +131,23 @@ export default function DateTimeSelector({ | |
timeZone = getUserTimezone() | ||
}: DateTimeSelectorProps): ReactElement { | ||
const intl = useIntl() | ||
const baseColor = S.baseColor() | ||
|
||
const departureOptions: DepartArriveOption[] = [ | ||
{ | ||
// Default option. | ||
type: "NOW", | ||
text: intl.formatMessage({ id: "otpUi.DateTimeSelector.now" }) | ||
}, | ||
{ | ||
type: "DEPART", | ||
text: intl.formatMessage({ id: "otpUi.DateTimeSelector.depart" }) | ||
}, | ||
{ | ||
type: "ARRIVE", | ||
text: intl.formatMessage({ id: "otpUi.DateTimeSelector.arrive" }) | ||
} | ||
]; | ||
|
||
const handleQueryParamChange = useCallback( | ||
(queryParam: QueryParamChangeEvent): void => { | ||
|
@@ -150,8 +161,12 @@ export default function DateTimeSelector({ | |
const handleInputChange = (key: string) => useCallback( | ||
(evt: ChangeEvent<HTMLInputElement>): void => { | ||
handleQueryParamChange({ [key]: evt.target.value }); | ||
// If the user changes the time, it doesn't make sense for them to be departing now. | ||
if (departArrive === "NOW") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: if departureOptions were an enum then we could reuse that instead of using a magic string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you change
And then render the text strings dynamically? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes! but it's such a nitpick |
||
handleQueryParamChange({ departArrive: "DEPART" }); | ||
} | ||
}, | ||
[onQueryParamChange, key] | ||
[onQueryParamChange, key, departArrive] | ||
); | ||
|
||
const handleDateChange = handleInputChange("date"); | ||
|
@@ -182,51 +197,16 @@ export default function DateTimeSelector({ | |
departArrive: "NOW", | ||
time: getCurrentTime(timeZone) | ||
}); | ||
} else if (!option.isSelected) { | ||
} else if (!(option.type === departArrive)) { | ||
handleQueryParamChange({ | ||
departArrive: option.type | ||
}); | ||
} | ||
}, | ||
[onQueryParamChange, option.type, option.isSelected, timeZone] | ||
[onQueryParamChange, option.type, timeZone] | ||
); | ||
|
||
const departureOptions: DepartArriveOption[] = [ | ||
{ | ||
// Default option. | ||
type: "NOW", | ||
text: ( | ||
<FormattedMessage | ||
defaultMessage={defaultMessages["otpUi.DateTimeSelector.now"]} | ||
description="Text indicating that the traveler wants to depart as soon as possible (i.e. 'now')" | ||
id="otpUi.DateTimeSelector.now" | ||
/> | ||
) | ||
}, | ||
{ | ||
type: "DEPART", | ||
text: ( | ||
<FormattedMessage | ||
defaultMessage={defaultMessages["otpUi.DateTimeSelector.depart"]} | ||
description="Text indicating that the traveler wants to depart at a given date/time" | ||
id="otpUi.DateTimeSelector.depart" | ||
/> | ||
) | ||
}, | ||
{ | ||
type: "ARRIVE", | ||
text: ( | ||
<FormattedMessage | ||
defaultMessage={defaultMessages["otpUi.DateTimeSelector.arrive"]} | ||
description="Text indicating that the traveler wants to arrive by a certain date/time" | ||
id="otpUi.DateTimeSelector.arrive" | ||
/> | ||
) | ||
} | ||
]; | ||
departureOptions.forEach(opt => { | ||
opt.isSelected = departArrive === opt.type; | ||
}); | ||
|
||
|
||
const isLegacy = forceLegacy || !supportsDateTimeInputs; | ||
|
||
|
@@ -236,21 +216,19 @@ export default function DateTimeSelector({ | |
className={className} | ||
role="group" | ||
style={style} | ||
baseColor={baseColor} | ||
> | ||
<S.DateTimeSelector.DepartureRow> | ||
<Dropdown alignMenuLeft id="date-time-depart-arrive" text={departureOptions.find(opt => opt.type === departArrive).text} buttonStyle={buttonStyle}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Breakup this long line. |
||
{departureOptions.map(opt => ( | ||
<ModeButton | ||
aria-pressed={opt.isSelected} | ||
<button | ||
aria-pressed={opt.type === departArrive} | ||
key={opt.type} | ||
onClick={setDepartArrive(opt)} | ||
selected={opt.isSelected} | ||
type="button" | ||
> | ||
{opt.text} | ||
</ModeButton> | ||
))} | ||
</S.DateTimeSelector.DepartureRow> | ||
|
||
{departArrive !== "NOW" && !isLegacy && ( | ||
</button>))} | ||
</Dropdown> | ||
<S.DateTimeSelector.DateTimeRow> | ||
{/* The <div> elements below are used for layout, see S.DateTimeSelector. */} | ||
<div> | ||
|
@@ -272,7 +250,6 @@ export default function DateTimeSelector({ | |
/> | ||
</div> | ||
</S.DateTimeSelector.DateTimeRow> | ||
)} | ||
|
||
{/* Backup controls (for older browsers) */} | ||
{departArrive !== "NOW" && isLegacy && ( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Break up this long line