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

Update Date/Time Selector #793

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
97 changes: 38 additions & 59 deletions packages/trip-form/src/DateTimeSelector/index.tsx
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,
Expand All @@ -28,6 +19,8 @@ const {
OTP_API_TIME_FORMAT
} = coreUtils.time;

const { blue } = colors;

type DepartArriveValue = "NOW" | "DEPART" | "ARRIVE";

interface DateTimeSelectorProps {
Expand Down Expand Up @@ -78,7 +71,7 @@ interface DateTimeSelectorProps {

interface DepartArriveOption {
isSelected?: boolean;
text: ReactNode;
text: string;
type: DepartArriveValue;
}

Expand Down Expand Up @@ -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;
});
Comment on lines +150 to +152
Copy link
Collaborator

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?


const handleQueryParamChange = useCallback(
(queryParam: QueryParamChangeEvent): void => {
Expand All @@ -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");
Expand Down Expand Up @@ -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;

Expand All @@ -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" }}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we make the default gray instead of blue?

Copy link
Collaborator

Choose a reason for hiding this comment

The 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>
Expand All @@ -272,7 +252,6 @@ export default function DateTimeSelector({
/>
</div>
</S.DateTimeSelector.DateTimeRow>
)}

{/* Backup controls (for older browsers) */}
{departArrive !== "NOW" && isLegacy && (
Expand Down
5 changes: 3 additions & 2 deletions packages/trip-form/src/__mocks__/trimet-styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ const TriMetStyled = styled.div`
text-decoration: underline;
}
}
${TripFormClasses.DateTimeSelector} {
max-width: 550px;
}
${TripFormClasses.DateTimeSelector.DateTimeRow} {
margin: 15px 0px;
input {
padding: 6px 12px;
text-align: center;
font-size: inherit;
font-family: inherit;
Expand Down
44 changes: 24 additions & 20 deletions packages/trip-form/src/styled.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import styled from "styled-components";
import colors from "@opentripplanner/building-blocks"

// eslint-disable-next-line prettier/prettier
import type {
Expand All @@ -8,6 +9,12 @@ import type {
SubmodeSelectorAndSubComponents
} from "./types";

const { blue } = colors;

export const baseColor = () => getComputedStyle(document.documentElement).getPropertyValue(
"--main-base-color"
)

export const SettingsHeader = styled.div``;

export const SettingsSection = styled.div``;
Expand All @@ -21,29 +28,26 @@ export const FloatingSettingLabel = styled(SettingLabel)`
float: left;
`;

export const DateTimeSelector: DateTimeSelectorAndSubComponents = styled.div``;

DateTimeSelector.DepartureRow = styled.div`
box-sizing: border-box;
> * {
box-sizing: border-box;
width: 33.333333%;
padding: 0px 5px;
}
export const DateTimeSelector: DateTimeSelectorAndSubComponents = styled.div<{ baseColor: string | undefined }>`
border: 2px solid ${(props) => props.baseColor || blue[900]};
border-radius: 5px;
height: 45px;
`;

DateTimeSelector.DateTimeRow = styled.div`
box-sizing: border-box;
> * {
box-sizing: border-box;
width: 50%;
padding: 0px 5px;
display: inline-block;
}
input {
box-sizing: border-box;
width: 100%;
}
display: flex;
justify-content: space-around;
align-items: center;
height: 100%;
padding: 0 0%;
background-color: white;
border-radius: 0 3px 3px 0;

input {
border: 0px;
height: 100%;
width: 100%;
}
`;

export const ModeSelector: ModeSelectorAndSubComponents = styled.div``;
Expand Down
7 changes: 5 additions & 2 deletions packages/trip-form/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,14 @@ export interface ModeSelectorOptionSet {
tertiary?: ModeSelectorOption[];
}

type SimpleStyledDiv = StyledComponent<"div", any>;
type SimpleStyledDiv = StyledComponent<
"div",
any,
{ baseColor?: string | undefined }
>;

export type DateTimeSelectorAndSubComponents = SimpleStyledDiv & {
DateTimeRow?: SimpleStyledDiv;
DepartureRow?: SimpleStyledDiv;
};

export type ModeSelectorAndSubComponents = SimpleStyledDiv & {
Expand Down
Loading