-
Notifications
You must be signed in to change notification settings - Fork 34
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 3 commits
75fc672
ea03d07
d2855f5
7fc56a4
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 { blue } = colors; | ||
|
||
type DepartArriveValue = "NOW" | "DEPART" | "ARRIVE"; | ||
|
||
interface DateTimeSelectorProps { | ||
|
@@ -78,7 +71,7 @@ interface DateTimeSelectorProps { | |
|
||
interface DepartArriveOption { | ||
isSelected?: boolean; | ||
text: ReactNode; | ||
text: string; | ||
type: DepartArriveValue; | ||
} | ||
|
||
|
@@ -137,6 +130,26 @@ 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" }) | ||
} | ||
]; | ||
departureOptions.forEach(opt => { | ||
opt.isSelected = departArrive === opt.type; | ||
}); | ||
|
||
const handleQueryParamChange = useCallback( | ||
(queryParam: QueryParamChangeEvent): void => { | ||
|
@@ -150,8 +163,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") { | ||
handleQueryParamChange({ departArrive: "DEPART" }); | ||
} | ||
}, | ||
[onQueryParamChange, key] | ||
[onQueryParamChange, key, departArrive] | ||
); | ||
|
||
const handleDateChange = handleInputChange("date"); | ||
|
@@ -191,42 +208,7 @@ export default function DateTimeSelector({ | |
[onQueryParamChange, option.type, option.isSelected, 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 +218,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.isSelected).text} buttonStyle={{ backgroundColor: S.baseColor() || blue[900], borderRadius: "3px 0px 0px 3px", color: "white", height: "45px", border: "0px", padding:"5px 7px" }}> | ||
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. Can we make the default gray instead of blue? 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. Also some of these values are pretty fixed and are causing layout problems in some implementations. Any way we can make this more reactive to its environment? |
||
{departureOptions.map(opt => ( | ||
<ModeButton | ||
<button | ||
aria-pressed={opt.isSelected} | ||
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 +252,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.
This works but it's pretty inefficient... Anyway we can replace/generate
isSelected
when we're iterating through the array anyway?