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

feat: Elevator Closures list #2288

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0c790aa
API additions needed for Elevators.
cmaddox5 Oct 29, 2024
e0ea48f
Added new CG.
cmaddox5 Oct 29, 2024
3fcea52
Return maps with all info client will need.
cmaddox5 Oct 29, 2024
211314e
Added mock support for tests.
cmaddox5 Oct 29, 2024
fd6d0ec
Added CG tests.
cmaddox5 Oct 29, 2024
997144a
Fixed reference to key.
cmaddox5 Oct 29, 2024
2d82e11
Improved test.
cmaddox5 Oct 29, 2024
d8fbca4
Credo.
cmaddox5 Oct 29, 2024
cfcda68
Logger error if one is returned.
cmaddox5 Oct 29, 2024
0672aff
Address PR feedback.
cmaddox5 Nov 4, 2024
c952f3a
Simplify business logic so it all lives in CG.
cmaddox5 Nov 4, 2024
8591033
Credo.
cmaddox5 Nov 4, 2024
f134826
Fix serialization.
cmaddox5 Nov 4, 2024
fd5d802
Implement list without paging.
cmaddox5 Nov 5, 2024
3e649d6
Add paging.
cmaddox5 Nov 5, 2024
3803e25
Added route pills.
cmaddox5 Nov 5, 2024
928bbef
Fix horizontal rules.
cmaddox5 Nov 5, 2024
2cb6c73
Generate pages before rendering any alerts.
cmaddox5 Nov 5, 2024
9fad0bc
Added paging indicators.
cmaddox5 Nov 5, 2024
43cbbf4
Fix tests.
cmaddox5 Nov 5, 2024
98455cd
CSS styles.
cmaddox5 Nov 5, 2024
6ae4646
Drop refresh rate to 7 seconds.
cmaddox5 Nov 5, 2024
b290119
Simplify paging.
cmaddox5 Nov 6, 2024
8f43924
Fix horizontal rules.
cmaddox5 Nov 6, 2024
b5214b8
Fixed paging logic.
cmaddox5 Nov 6, 2024
7dce38a
Group alerts by station.
cmaddox5 Nov 6, 2024
09e652d
Renamed variable.
cmaddox5 Nov 6, 2024
1a0ee4b
Added a test.
cmaddox5 Nov 6, 2024
0c630ae
Update refresh interval.
cmaddox5 Nov 6, 2024
f2b8184
Various spacing/sizing fixes.
cmaddox5 Nov 6, 2024
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: 1 addition & 0 deletions assets/css/colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $line-color-ferry: #008eaa;

$alert-yellow: #fd0;

$true-grey-45: #737373;
$true-grey-70: #b2b1af;

$warm-neutral-80: #cccbc8;
Expand Down
1 change: 1 addition & 0 deletions assets/css/elevator_v2.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import "v2/lcd_common/simulation";
@import "v2/elevator/header";
@import "v2/elevator/footer";
@import "v2/lcd_common/route_pill";

body {
margin: 0;
Expand Down
116 changes: 105 additions & 11 deletions assets/css/v2/elevator/elevator_closures.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,126 @@

.in-station-summary {
display: flex;
gap: 82px;
justify-content: space-between;
padding: 24px 58px;
padding: 24px 48px;
font-size: 48px;
font-weight: 400;
line-height: 64px;
color: $cool-black-30;
}

hr {
width: 100%;
height: 24px;
margin-top: 0;
margin-bottom: 0;
hr.thick {
min-height: 24px;
margin: 0;
background-color: $cool-black-15;
border: none;
}

hr.thin {
min-height: 2px;
margin: 24px 0 0;
background-color: $true-grey-45;
border: none;
opacity: 0.5;
}

.outside-alert-list {
position: relative;
height: 100%;
font-family: Inter;
background-color: $warm-neutral-90;

.header {
.header-container {
margin: 48px;
margin-bottom: 0;

.header {
display: flex;
max-height: 432px;
font-size: 112px;
font-weight: 700;
line-height: 112px;

&__title {
word-spacing: 9999px;
}
}
}

.alert-list-container {
overflow: hidden;

.alert-list {
display: flex;
flex-flow: column wrap;
height: 904px;
transform: translateX(calc(-100% * var(--alert-list-offset)));

.alert-row {
margin: 24px 48px 0;

&__station-name {
font-size: 62px;
font-weight: 600;
line-height: 80px;
color: $cool-black-15;
}

&__name-and-pills {
display: flex;
gap: 24px;
align-items: center;
margin-bottom: 14px;

.route-pill {
width: 132px;
height: 68.13px;

& > * {
height: 100%;
}
}
}

&__elevator-name {
line-height: 64px;
}

&__elevator-name.list-item {
display: list-item;
margin-bottom: 8px;
margin-left: 48px;
}
}
}
}

.paging-info-container {
position: absolute;
bottom: 0;
display: flex;
padding: 48px;
font-size: 150px;
font-weight: 700;
line-height: 150px;
justify-content: space-between;
width: 100%;
height: 72px;

& > * {
margin: 0 48px 12px;
}

.paging-indicators {
display: flex;
gap: 27px;
align-items: center;
margin-right: 66px;
}
}

.paging-info-container,
.alert-row__elevator-name {
font-size: 48px;
font-weight: 400;
color: $cool-black-30;
}
}
}
184 changes: 167 additions & 17 deletions assets/src/components/v2/elevator/elevator_closures.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,65 @@
import React from "react";
import React, {
ComponentType,
useEffect,
useLayoutEffect,
useMemo,
useState,
} from "react";
import cx from "classnames";
import NormalService from "Images/svgr_bundled/normal-service.svg";
import AccessibilityAlert from "Images/svgr_bundled/accessibility-alert.svg";
import PagingDotUnselected from "Images/svgr_bundled/paging_dot_unselected.svg";
import PagingDotSelected from "Images/svgr_bundled/paging_dot_selected.svg";
import makePersistent, { WrappedComponentProps } from "../persistent_wrapper";
import RoutePill, { routePillKey, type Pill } from "../departures/route_pill";

type StationWithAlert = {
id: string;
name: string;
routes: Pill[];
alerts: ElevatorClosure[];
};

type ElevatorClosure = {
id: string;
elevator_name: string;
elevator_id: string;
description: string;
header_text: string;
};

interface AlertRowProps {
station: StationWithAlert;
}

const AlertRow = ({ station }: AlertRowProps) => {
const { name, alerts, routes, id } = station;

return (
<div className="alert-row">
<div className="alert-row__name-and-pills">
{routes.map((route) => (
<RoutePill pill={route} key={`${routePillKey(route)}-${id}`} />
))}
<div className="alert-row__station-name">{name}</div>
</div>
{alerts.map((alert) => (
<div
key={alert.id}
className={cx("alert-row__elevator-name", {
"list-item": alerts.length > 1,
})}
>
{alert.elevator_name} ({alert.elevator_id})
</div>
))}
<hr className="thin" />
</div>
);
};

interface InStationSummaryProps {
alerts: string[];
alerts: ElevatorClosure[];
}

const InStationSummary = ({ alerts }: InStationSummaryProps) => {
Expand All @@ -19,44 +75,138 @@ const InStationSummary = ({ alerts }: InStationSummaryProps) => {
<NormalService height={72} width={72} fill="#145A06" />
</span>
</div>
<hr />
<hr className="thick" />
</>
);
};

interface OutsideAlertListProps {
alerts: string[];
interface OutsideAlertListProps extends WrappedComponentProps {
stations: StationWithAlert[];
lastUpdate: number | null;
}

const OutsideAlertList = (_props: OutsideAlertListProps) => {
const OutsideAlertList = ({
stations,
lastUpdate,
onFinish,
}: OutsideAlertListProps) => {
const [isFirstRender, setIsFirstRender] = useState(true);
const [pageIndex, setPageIndex] = useState(0);

// Each value represents the pageIndex the row is visible on
const [rowPageIndexes, setRowPageIndexes] = useState<number[]>([]);

const [numPages, numOffsetRows] = useMemo(
() => [
rowPageIndexes.filter((val, i, self) => self.indexOf(val) === i).length,
rowPageIndexes.filter((offset) => offset !== pageIndex).length,
],
[rowPageIndexes],
);

useEffect(() => {
if (lastUpdate != null) {
if (isFirstRender) {
setIsFirstRender(false);
} else {
setPageIndex((i) => i + 1);
}
}
}, [lastUpdate]);

useEffect(() => {
if (pageIndex === numPages - 1) {
onFinish();
}
}, [pageIndex]);

useLayoutEffect(() => {
const alertRows = Array.from(document.getElementsByClassName("alert-row"));
const screenWidth = 1080;
const totalXMargins = 48;

const rowPageIndexes = alertRows.map((alert) => {
const val = (alert as HTMLDivElement).offsetLeft - totalXMargins;
return val / screenWidth;
});

setRowPageIndexes(rowPageIndexes);
}, [stations]);

const getPagingIndicators = (num: number) => {
const indicators: JSX.Element[] = [];
for (let i = 0; i < num; i++) {
const indicator =
pageIndex === i ? (
<PagingDotSelected height={40} width={40} key={i} />
) : (
<PagingDotUnselected height={28} width={28} key={i} />
);
indicators.push(indicator);
}

return indicators;
};

return (
<div className="outside-alert-list">
<div className="header">
<span>MBTA Elevator Closures</span>
<span>
<AccessibilityAlert height={128} width={128} />
</span>
<div className="header-container">
<div className="header">
<div className="header__title">MBTA Elevator Closures</div>
<div>
<AccessibilityAlert height={128} width={155} />
</div>
</div>
<hr className="thin" />
</div>
<div className="alert-list-container">
{
<div
className="alert-list"
style={
{
"--alert-list-offset": pageIndex,
} as React.CSSProperties
}
>
{stations.map((station) => (
<AlertRow station={station} key={station.id} />
))}
</div>
}
</div>
<div className="paging-info-container">
<div>+{numOffsetRows} more elevators</div>
<div className="paging-indicators">{getPagingIndicators(numPages)}</div>
</div>
</div>
);
};

interface Props {
interface Props extends WrappedComponentProps {
id: string;
in_station_alerts: string[];
outside_alerts: string[];
in_station_alerts: ElevatorClosure[];
other_stations_with_alerts: StationWithAlert[];
}

const ElevatorClosures: React.ComponentType<Props> = ({
other_stations_with_alerts: otherStationsWithAlerts,
in_station_alerts: inStationAlerts,
outside_alerts: outsideAlerts,
lastUpdate,
onFinish,
}: Props) => {
return (
<div className="elevator-closures">
<InStationSummary alerts={inStationAlerts} />
<OutsideAlertList alerts={outsideAlerts} />
<OutsideAlertList
stations={otherStationsWithAlerts}
lastUpdate={lastUpdate}
onFinish={onFinish}
/>
</div>
);
};

export default ElevatorClosures;
export default makePersistent(
ElevatorClosures as ComponentType<WrappedComponentProps>,
);
3 changes: 3 additions & 0 deletions assets/static/images/svgr_bundled/paging_dot_selected.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions assets/static/images/svgr_bundled/paging_dot_unselected.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ config :screens, Screens.V2.RDS,
route_pattern_module: Screens.RoutePatterns.MockRoutePattern,
stop_module: Screens.Stops.MockStop

config :screens, Screens.V2.CandidateGenerator.Elevator.Closures,
stop_module: Screens.Stops.MockStop,
facility_module: Screens.Facilities.MockFacility,
alert_module: Screens.Alerts.MockAlert,
route_module: Screens.Routes.MockRoute

config :screens, Screens.LastTrip,
trip_updates_adapter: Screens.LastTrip.TripUpdates.Noop,
vehicle_positions_adapter: Screens.LastTrip.VehiclePositions.Noop
Loading
Loading