diff --git a/assets/css/colors.scss b/assets/css/colors.scss index b4b0fb251..36a4f2b44 100644 --- a/assets/css/colors.scss +++ b/assets/css/colors.scss @@ -19,3 +19,5 @@ $cool-black-15: #171f26; $cool-black-30: #2e3e4d; $normal-service-green: #145a06; + +$accessibility-blue: #165c96; diff --git a/assets/css/elevator_v2.scss b/assets/css/elevator_v2.scss index 5a0ea16c4..06f089c72 100644 --- a/assets/css/elevator_v2.scss +++ b/assets/css/elevator_v2.scss @@ -1,13 +1,17 @@ +// Our Mimo elevator screens run Webview 83. +// When writing styles for widgets on these screens, +// verify browser compatibility in the MDN web docs. + @import "https://rsms.me/inter/inter.css"; @import "colors"; @import "v2/lcd_common/screen_container"; -@import "v2/elevator/elevator_closures"; @import "v2/elevator/normal"; @import "v2/simulation_common"; @import "v2/lcd_common/simulation"; @import "v2/elevator/header"; @import "v2/elevator/footer"; @import "v2/lcd_common/route_pill"; +@import "v2/arrow"; .evergreen-content-image__container, .evergreen-content-image__image { @@ -23,3 +27,61 @@ body { .multi-screen-page { grid-auto-rows: 1920px; } + +*, +*::before, +*::after { + box-sizing: border-box; +} + +.normal-header, +.footer { + &--closed { + color: white; + background-color: $accessibility-blue; + } +} + +.outside-elevator-closures, +.current-elevator-closed { + position: relative; + display: flex; + flex-direction: column; + height: 100%; + color: $cool-black-15; + background-color: $warm-neutral-90; + + hr.thick { + min-height: 24px; + margin: 0; + background-color: $cool-black-15; + border: none; + } + + hr.thin { + min-height: 2px; + margin: 24px 0; + background-color: $true-grey-45; + border: none; + opacity: 0.5; + } + + .subheading { + font-size: 80px; + font-weight: 700; + line-height: 80px; + } +} + +.paging-indicators { + display: flex; + align-items: center; + margin-right: 66px; + + .paging-indicator:first-child:not(:only-child) { + margin-right: 27px; + } +} + +@import "v2/elevator/current_elevator_closed"; +@import "v2/elevator/outside_elevator_closures"; diff --git a/assets/css/v2/elevator/current_elevator_closed.scss b/assets/css/v2/elevator/current_elevator_closed.scss new file mode 100644 index 000000000..acaa23595 --- /dev/null +++ b/assets/css/v2/elevator/current_elevator_closed.scss @@ -0,0 +1,129 @@ +.current-elevator-closed { + position: relative; + height: 100%; + + .notch { + position: absolute; + top: 0; + right: 0; + display: inline-block; + border-right: 345px solid $accessibility-blue; + border-bottom: 300px solid transparent; + } + + .header { + display: flex; + flex-direction: column; + justify-content: center; + height: 528px; + padding-left: 48px; + font-weight: 700; + + .icons { + margin-bottom: 21px; + + .no-service-icon { + margin-right: 16px; + } + } + + .closed-text { + margin-bottom: 18px; + font-size: 150px; + line-height: 150px; + } + } + + hr.thin { + margin: 0 20px; + } + + .accessible-path-container { + height: 984px; + padding: 48px; + + .subheading-container { + display: flex; + align-items: center; + justify-content: space-between; + height: 142px; + + .subheading { + align-self: center; + } + + .arrow { + width: 100px; + height: 100px; + } + } + + .alternate-direction-text { + color: $cool-black-30; + + &.small { + font-size: 48px; + line-height: 64px; + } + + &.medium { + font-size: 62px; + line-height: 80px; + } + + &.large { + font-size: 80px; + line-height: 96px; + } + } + + .map-container { + position: relative; + margin-top: 26px; + margin-left: -48px; + + .map { + width: 1080px; + } + } + } + + .paging-indicators { + position: absolute; + right: 0; + bottom: 0; + height: 120px; + } +} + +.marker-container { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + width: 116px; + height: 116px; + + .marker-background, + .marker { + position: absolute; + } + + .marker-background { + animation: pulse-ring 1.5s infinite; + } +} + +@keyframes pulse-ring { + 0% { + transform: scale(0.5); + } + + 60% { + opacity: 0.25; + } + + 100% { + opacity: 0; + } +} diff --git a/assets/css/v2/elevator/footer.scss b/assets/css/v2/elevator/footer.scss index 77d4264eb..86a964854 100644 --- a/assets/css/v2/elevator/footer.scss +++ b/assets/css/v2/elevator/footer.scss @@ -1,5 +1,4 @@ .footer { - box-sizing: border-box; height: 100%; padding: 26px 48px; font-size: 48px; diff --git a/assets/css/v2/elevator/elevator_closures.scss b/assets/css/v2/elevator/outside_elevator_closures.scss similarity index 79% rename from assets/css/v2/elevator/elevator_closures.scss rename to assets/css/v2/elevator/outside_elevator_closures.scss index 0ce4ea92a..077f6ce2a 100644 --- a/assets/css/v2/elevator/elevator_closures.scss +++ b/assets/css/v2/elevator/outside_elevator_closures.scss @@ -1,9 +1,7 @@ -.elevator-closures { - position: relative; +.outside-elevator-closures { display: flex; + flex: 1; flex-direction: column; - height: 100%; - background-color: $warm-neutral-90; .in-station-summary { display: flex; @@ -19,23 +17,7 @@ } } - hr.thick { - min-height: 24px; - margin: 0; - background-color: $cool-black-15; - border: none; - } - - hr.thin { - min-height: 2px; - margin: 24px 0; - background-color: $true-grey-45; - border: none; - opacity: 0.5; - } - .outside-closure-list { - position: relative; height: 100%; background-color: $warm-neutral-90; @@ -67,7 +49,6 @@ transform: translateX(calc(-100% * var(--closure-list-offset))); .closure-row { - box-sizing: border-box; width: 1080px; padding: 0 48px; @@ -129,16 +110,6 @@ align-self: center; padding: 0 48px 20px; } - - .paging-indicators { - display: flex; - align-items: center; - margin-right: 66px; - - .paging-indicator:first-child:not(:only-child) { - margin-right: 27px; - } - } } .paging-info-container, diff --git a/assets/src/apps/v2/elevator.tsx b/assets/src/apps/v2/elevator.tsx index cea5a1e47..b43342a35 100644 --- a/assets/src/apps/v2/elevator.tsx +++ b/assets/src/apps/v2/elevator.tsx @@ -14,14 +14,16 @@ import EvergreenContent from "Components/v2/evergreen_content"; import ScreenPage from "Components/v2/screen_page"; import { MappingContext } from "Components/v2/widget"; import MultiScreenPage from "Components/v2/multi_screen_page"; -import ElevatorClosures from "Components/v2/elevator/elevator_closures"; +import OutsideElevatorClosures from "Components/v2/elevator/outside_elevator_closures"; +import CurrentElevatorClosed from "Components/v2/elevator/current_elevator_closed"; import SimulationScreenPage from "Components/v2/simulation_screen_page"; import Footer from "Components/v2/elevator/footer"; import NormalHeader from "Components/v2/normal_header"; const TYPE_TO_COMPONENT = { normal: NormalScreen, - elevator_closures: ElevatorClosures, + outside_elevator_closures: OutsideElevatorClosures, + current_elevator_closed: CurrentElevatorClosed, evergreen_content: EvergreenContent, footer: Footer, normal_header: NormalHeader, diff --git a/assets/src/components/v2/elevator/current_elevator_closed.tsx b/assets/src/components/v2/elevator/current_elevator_closed.tsx new file mode 100644 index 000000000..ad06cd3f6 --- /dev/null +++ b/assets/src/components/v2/elevator/current_elevator_closed.tsx @@ -0,0 +1,98 @@ +import React, { ComponentType } from "react"; +import cx from "classnames"; +import Arrow, { Direction } from "Components/v2/arrow"; +import makePersistent, { + WrappedComponentProps, +} from "Components/v2/persistent_wrapper"; +import PagingIndicators from "Components/v2/elevator/paging_indicators"; +import { type Closure } from "Components/v2/elevator/types"; +import useClientPaging from "Hooks/v2/use_client_paging"; +import useTextResizer from "Hooks/v2/use_text_resizer"; +import CurrentLocationMarker from "Images/svgr_bundled/current-location-marker.svg"; +import CurrentLocationBackground from "Images/svgr_bundled/current-location-background.svg"; +import NoService from "Images/svgr_bundled/no-service-black.svg"; +import ElevatorWayfinding from "Images/svgr_bundled/elevator-wayfinding.svg"; +import IsaNegative from "Images/svgr_bundled/isa-negative.svg"; + +type Coordinates = { + x: number; + y: number; +}; + +const PulsatingDot = ({ x, y }: Coordinates) => { + return ( +
+ + +
+ ); +}; + +interface Props extends WrappedComponentProps { + closure: Closure; + alternate_direction_text: string; + accessible_path_direction_arrow: Direction; + accessible_path_image_url: string | null; + accessible_path_image_here_coordinates: Coordinates; +} + +const CurrentElevatorClosed = ({ + alternate_direction_text: alternateDirectionText, + accessible_path_direction_arrow: accessiblePathDirectionArrow, + accessible_path_image_url: accessiblePathImageUrl, + accessible_path_image_here_coordinates: accessiblePathImageHereCoordinates, + onFinish, + lastUpdate, +}: Props) => { + const numPages = accessiblePathImageUrl ? 2 : 1; + const pageIndex = useClientPaging({ numPages, onFinish, lastUpdate }); + const { ref, size } = useTextResizer({ + sizes: ["small", "medium", "large"], + maxHeight: 746, + resetDependencies: [alternateDirectionText], + }); + + return ( +
+
+
+
+ + +
+
Closed
+
Until further notice
+
+
+
+
+
Accessible Path
+
+ + +
+
+ {pageIndex === 0 ? ( +
+ {alternateDirectionText} +
+ ) : ( +
+ + +
+ )} +
+ {numPages === 2 && ( + + )} +
+ ); +}; + +export default makePersistent( + CurrentElevatorClosed as ComponentType, +); diff --git a/assets/src/components/v2/elevator/footer.tsx b/assets/src/components/v2/elevator/footer.tsx index b9eeff968..a6d0675a3 100644 --- a/assets/src/components/v2/elevator/footer.tsx +++ b/assets/src/components/v2/elevator/footer.tsx @@ -1,8 +1,9 @@ import React from "react"; +import { classWithModifier } from "Util/util"; -const Footer = () => { +const Footer = ({ variant }: { variant: string | null }) => { return ( -
+
For more info and alternate paths: mbta.com/alerts/access or{" "} 617-222-2828
diff --git a/assets/src/components/v2/elevator/elevator_closures.tsx b/assets/src/components/v2/elevator/outside_elevator_closures.tsx similarity index 66% rename from assets/src/components/v2/elevator/elevator_closures.tsx rename to assets/src/components/v2/elevator/outside_elevator_closures.tsx index 4da4503e1..2618911ae 100644 --- a/assets/src/components/v2/elevator/elevator_closures.tsx +++ b/assets/src/components/v2/elevator/outside_elevator_closures.tsx @@ -1,28 +1,18 @@ import React, { ComponentType, useLayoutEffect, useRef, 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"; import _ from "lodash"; +import RoutePill, { routePillKey } from "Components/v2/departures/route_pill"; +import makePersistent, { + WrappedComponentProps, +} from "Components/v2/persistent_wrapper"; +import PagingIndicators from "Components/v2/elevator/paging_indicators"; +import { + type StationWithClosures, + type Closure, +} from "Components/v2/elevator/types"; import useClientPaging from "Hooks/v2/use_client_paging"; - -type StationWithClosures = { - id: string; - name: string; - route_icons: Pill[]; - closures: ElevatorClosure[]; -}; - -type ElevatorClosure = { - id: string; - elevator_name: string; - elevator_id: string; - description: string; - header_text: string; -}; +import NormalService from "Images/svgr_bundled/normal-service.svg"; +import AccessibilityAlert from "Images/svgr_bundled/accessibility-alert.svg"; interface ClosureRowProps { station: StationWithClosures; @@ -54,19 +44,13 @@ const ClosureRow = ({ station }: ClosureRowProps) => { ); }; -interface InStationSummaryProps { - closures: ElevatorClosure[]; -} - -const InStationSummary = ({ closures }: InStationSummaryProps) => { - const summaryText = closures.length - ? "" - : "All elevators at this station are currently working"; - +const InStationSummary = () => { return ( <>
- {summaryText} + + All elevators at this station are currently working + @@ -78,7 +62,6 @@ const InStationSummary = ({ closures }: InStationSummaryProps) => { interface OutsideClosureListProps extends WrappedComponentProps { stations: StationWithClosures[]; - lastUpdate: number | null; } const OutsideClosureList = ({ @@ -119,31 +102,6 @@ const OutsideClosureList = ({ setRowCounts(rowCounts); }, [stations]); - const getPagingIndicators = (num: number) => { - const indicators: JSX.Element[] = []; - for (let i = 0; i < num; i++) { - const indicator = - pageIndex === i ? ( - - ) : ( - - ); - indicators.push(indicator); - } - - return indicators; - }; - return (
@@ -177,9 +135,7 @@ const OutsideClosureList = ({
+{numOffsetRows} more elevators
-
- {getPagingIndicators(numPages)} -
+
)}
@@ -188,21 +144,20 @@ const OutsideClosureList = ({ interface Props extends WrappedComponentProps { id: string; - in_station_closures: ElevatorClosure[]; + in_station_closures: Closure[]; other_stations_with_closures: StationWithClosures[]; } -const ElevatorClosures: React.ComponentType = ({ - other_stations_with_closures: otherStationsWithClosures, - in_station_closures: inStationClosures, +const OutsideElevatorClosures = ({ + other_stations_with_closures: stations, lastUpdate, onFinish, }: Props) => { return ( -
- +
+ @@ -211,5 +166,5 @@ const ElevatorClosures: React.ComponentType = ({ }; export default makePersistent( - ElevatorClosures as ComponentType, + OutsideElevatorClosures as ComponentType, ); diff --git a/assets/src/components/v2/elevator/paging_indicators.tsx b/assets/src/components/v2/elevator/paging_indicators.tsx new file mode 100644 index 000000000..406e68602 --- /dev/null +++ b/assets/src/components/v2/elevator/paging_indicators.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import PagingDotUnselected from "Images/svgr_bundled/paging_dot_unselected.svg"; +import PagingDotSelected from "Images/svgr_bundled/paging_dot_selected.svg"; + +interface PagingIndicatorsProps { + numPages: number; + pageIndex: number; +} + +const PagingIndicators = ({ numPages, pageIndex }: PagingIndicatorsProps) => { + const indicators: JSX.Element[] = []; + for (let i = 0; i < numPages; i++) { + const indicator = + pageIndex === i ? ( + + ) : ( + + ); + indicators.push(indicator); + } + + return
{indicators}
; +}; + +export default PagingIndicators; diff --git a/assets/src/components/v2/elevator/types.ts b/assets/src/components/v2/elevator/types.ts new file mode 100644 index 000000000..2d21013ba --- /dev/null +++ b/assets/src/components/v2/elevator/types.ts @@ -0,0 +1,16 @@ +import { type Pill } from "Components/v2/departures/route_pill"; + +export type StationWithClosures = { + id: string; + name: string; + route_icons: Pill[]; + closures: Closure[]; +}; + +export type Closure = { + id: string; + elevator_name: string; + elevator_id: string; + description: string; + header_text: string; +}; diff --git a/assets/src/components/v2/normal_header.tsx b/assets/src/components/v2/normal_header.tsx index da8397786..c0a69ec25 100644 --- a/assets/src/components/v2/normal_header.tsx +++ b/assets/src/components/v2/normal_header.tsx @@ -1,15 +1,9 @@ -import useTextResizer from "Hooks/v2/use_text_resizer"; import React, { forwardRef, ComponentType } from "react"; -import { getDatasetValue } from "Util/dataset"; import LiveDataSvg from "Images/svgr_bundled/live-data-small.svg"; - -import { - classWithModifier, - classWithModifiers, - formatTimeString, - imagePath, -} from "Util/util"; +import { getDatasetValue } from "Util/dataset"; +import { classWithModifiers, formatTimeString, imagePath } from "Util/util"; +import useTextResizer from "Hooks/v2/use_text_resizer"; enum Icon { green_b = "green_b", @@ -154,6 +148,7 @@ interface Props { fullName?: boolean; classModifiers?: string; accentPattern?: string; + variant?: string | null; } const NormalHeader: ComponentType = ({ @@ -167,6 +162,7 @@ const NormalHeader: ComponentType = ({ fullName = false, classModifiers, accentPattern, + variant, }) => { const { ref: headerRef, size: headerSize } = useTextResizer({ sizes: Object.keys(TitleSize), @@ -174,7 +170,9 @@ const NormalHeader: ComponentType = ({ resetDependencies: [text], }); return ( -
+
{ if (modifiers.length === 0) { return baseClass; } else { - return ( - `${baseClass} ` + modifiers.map((m) => `${baseClass}--${m}`).join(" ") + return cx( + baseClass, + ...modifiers.filter((m) => m).map((m) => `${baseClass}--${m}`), ); } }; diff --git a/assets/static/images/svgr_bundled/current-location-background.svg b/assets/static/images/svgr_bundled/current-location-background.svg new file mode 100644 index 000000000..4c6354c40 --- /dev/null +++ b/assets/static/images/svgr_bundled/current-location-background.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/static/images/svgr_bundled/current-location-marker.svg b/assets/static/images/svgr_bundled/current-location-marker.svg new file mode 100644 index 000000000..430431a4f --- /dev/null +++ b/assets/static/images/svgr_bundled/current-location-marker.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/static/images/svgr_bundled/elevator-wayfinding.svg b/assets/static/images/svgr_bundled/elevator-wayfinding.svg new file mode 100644 index 000000000..3f3c5d4e7 --- /dev/null +++ b/assets/static/images/svgr_bundled/elevator-wayfinding.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/static/images/svgr_bundled/no-service-black.svg b/assets/static/images/svgr_bundled/no-service-black.svg new file mode 100644 index 000000000..969faac38 --- /dev/null +++ b/assets/static/images/svgr_bundled/no-service-black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/screens/v2/candidate_generator/elevator.ex b/lib/screens/v2/candidate_generator/elevator.ex index 8de4a1e8c..01d6bcdaa 100644 --- a/lib/screens/v2/candidate_generator/elevator.ex +++ b/lib/screens/v2/candidate_generator/elevator.ex @@ -28,13 +28,15 @@ defmodule Screens.V2.CandidateGenerator.Elevator do def candidate_instances( config, now \\ DateTime.utc_now(), - elevator_closure_instances_fn \\ &ElevatorClosures.elevator_status_instances/1, + elevator_closure_instances_fn \\ &ElevatorClosures.elevator_status_instances/3, evergreen_content_instances_fn \\ &Evergreen.evergreen_content_instances/2 ) do Enum.concat([ - [header_instance(config, now)], - [footer_instance(config)], - elevator_closure_instances_fn.(config), + elevator_closure_instances_fn.( + config, + header_instance(config, now), + footer_instance(config) + ), evergreen_content_instances_fn.(config, now) ]) end diff --git a/lib/screens/v2/candidate_generator/elevator/closures.ex b/lib/screens/v2/candidate_generator/elevator/closures.ex index 127564cba..a3b28a5e7 100644 --- a/lib/screens/v2/candidate_generator/elevator/closures.ex +++ b/lib/screens/v2/candidate_generator/elevator/closures.ex @@ -6,7 +6,16 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do alias Screens.Alerts.{Alert, InformedEntity} alias Screens.Routes.Route alias Screens.Stops.Stop - alias Screens.V2.WidgetInstance.ElevatorClosures + alias Screens.V2.WidgetInstance + + alias Screens.V2.WidgetInstance.{ + CurrentElevatorClosed, + Footer, + NormalHeader, + OutsideElevatorClosures + } + + alias Screens.V2.WidgetInstance.Elevator.Closure alias Screens.V2.WidgetInstance.Serializer.RoutePill alias ScreensConfig.Screen alias ScreensConfig.V2.Elevator @@ -18,8 +27,13 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do @route injected(Route) @stop injected(Stop) - @spec elevator_status_instances(Screen.t()) :: list(ElevatorClosures.t()) - def elevator_status_instances(%Screen{app_params: %Elevator{elevator_id: elevator_id}}) do + @spec elevator_status_instances(Screen.t(), NormalHeader.t(), Footer.t()) :: + list(WidgetInstance.t()) + def elevator_status_instances( + %Screen{app_params: %Elevator{elevator_id: elevator_id} = config}, + header_instance, + footer_instance + ) do with {:ok, %Stop{id: stop_id}} <- @facility.fetch_stop_for_facility(elevator_id), {:ok, parent_station_map} <- @stop.fetch_parent_station_name_map(), {:ok, alerts} <- @alert.fetch_elevator_alerts_with_facilities() do @@ -29,13 +43,27 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do {in_station_alerts, outside_alerts} = split_closures_by_location(elevator_alerts, stop_id) + in_station_closures = + Enum.map(in_station_alerts, &alert_to_elevator_closure/1) + + current_elevator_closure = Enum.find(in_station_closures, &(&1.elevator_id == elevator_id)) + + {elevator_widget_instance, header_footer_variant} = + if is_nil(current_elevator_closure) do + {%OutsideElevatorClosures{ + in_station_closures: in_station_closures, + other_stations_with_closures: + format_outside_closures(outside_alerts, parent_station_map, routes_map), + app_params: config + }, nil} + else + {%CurrentElevatorClosed{closure: current_elevator_closure, app_params: config}, :closed} + end + [ - %ElevatorClosures{ - id: elevator_id, - in_station_closures: Enum.map(in_station_alerts, &alert_to_elevator_closure/1), - other_stations_with_closures: - format_outside_closures(outside_alerts, parent_station_map, routes_map) - } + %NormalHeader{header_instance | variant: header_footer_variant}, + elevator_widget_instance, + %Footer{footer_instance | variant: header_footer_variant} ] else :error -> @@ -106,7 +134,7 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do }) do facility = Enum.find_value(entities, fn %{facility: facility} -> facility end) - %ElevatorClosures.Closure{ + %Closure{ id: id, elevator_name: facility.name, elevator_id: facility.id, @@ -126,7 +154,7 @@ defmodule Screens.V2.CandidateGenerator.Elevator.Closures do |> Map.fetch!(parent_station_id) |> Enum.map(&RoutePill.serialize_icon/1) - %ElevatorClosures.Station{ + %OutsideElevatorClosures.Station{ id: parent_station_id, name: Map.fetch!(station_id_to_name, parent_station_id), route_icons: route_pills, diff --git a/lib/screens/v2/widget_instance/blue_bikes.ex b/lib/screens/v2/widget_instance/blue_bikes.ex index d46bb0cf3..017c514e0 100644 --- a/lib/screens/v2/widget_instance/blue_bikes.ex +++ b/lib/screens/v2/widget_instance/blue_bikes.ex @@ -4,8 +4,7 @@ defmodule Screens.V2.WidgetInstance.BlueBikes do """ alias Screens.BlueBikes alias Screens.BlueBikes.StationStatus - alias ScreensConfig.Screen - alias ScreensConfig.V2.BlueBikes.Station + alias ScreensConfig.{Arrow, Screen} alias ScreensConfig.V2.PreFare @type t :: %__MODULE__{ @@ -24,7 +23,7 @@ defmodule Screens.V2.WidgetInstance.BlueBikes do @type normal_station :: %{ status: :normal, id: String.t(), - arrow: Station.arrow(), + arrow: Arrow.t(), walk_distance_minutes: non_neg_integer(), walk_distance_feet: non_neg_integer(), name: String.t(), @@ -35,7 +34,7 @@ defmodule Screens.V2.WidgetInstance.BlueBikes do @type special_station :: %{ status: :valet | :out_of_service, id: String.t(), - arrow: Station.arrow(), + arrow: Arrow.t(), walk_distance_minutes: non_neg_integer(), walk_distance_feet: non_neg_integer(), name: String.t() diff --git a/lib/screens/v2/widget_instance/current_elevator_closed.ex b/lib/screens/v2/widget_instance/current_elevator_closed.ex new file mode 100644 index 000000000..8729c12dc --- /dev/null +++ b/lib/screens/v2/widget_instance/current_elevator_closed.ex @@ -0,0 +1,51 @@ +defmodule Screens.V2.WidgetInstance.CurrentElevatorClosed do + @moduledoc false + + alias Screens.Util.Assets + alias Screens.V2.WidgetInstance.Elevator.Closure + alias ScreensConfig.V2.Elevator + + defstruct ~w[app_params closure]a + + @type t :: %__MODULE__{ + app_params: Elevator.t(), + closure: Closure.t() + } + + def serialize(%__MODULE__{ + app_params: %Elevator{ + elevator_id: id, + alternate_direction_text: alternate_direction_text, + accessible_path_direction_arrow: accessible_path_direction_arrow, + accessible_path_image_url: accessible_path_image_url, + accessible_path_image_here_coordinates: accessible_path_image_here_coordinates + }, + closure: closure + }), + do: %{ + id: id, + closure: closure, + alternate_direction_text: alternate_direction_text, + accessible_path_direction_arrow: accessible_path_direction_arrow, + accessible_path_image_url: + if(is_nil(accessible_path_image_url), + do: nil, + else: Assets.s3_asset_url(accessible_path_image_url) + ), + accessible_path_image_here_coordinates: accessible_path_image_here_coordinates + } + + defimpl Screens.V2.WidgetInstance do + alias Screens.V2.WidgetInstance.CurrentElevatorClosed + + def priority(_instance), do: [1] + def serialize(instance), do: CurrentElevatorClosed.serialize(instance) + def slot_names(_instance), do: [:main_content] + def widget_type(_instance), do: :current_elevator_closed + def valid_candidate?(_instance), do: true + def audio_serialize(_instance), do: %{} + def audio_sort_key(_instance), do: [0] + def audio_valid_candidate?(_instance), do: false + def audio_view(_instance), do: ScreensWeb.V2.Audio.CurrentElevatorClosedView + end +end diff --git a/lib/screens/v2/widget_instance/elevator/closure.ex b/lib/screens/v2/widget_instance/elevator/closure.ex new file mode 100644 index 000000000..88990fe1d --- /dev/null +++ b/lib/screens/v2/widget_instance/elevator/closure.ex @@ -0,0 +1,17 @@ +defmodule Screens.V2.WidgetInstance.Elevator.Closure do + @moduledoc """ + Represents a serializable closure to be displayed on elevator widgets. + """ + + @derive Jason.Encoder + + defstruct ~w[id elevator_name elevator_id description header_text]a + + @type t :: %__MODULE__{ + id: String.t(), + elevator_name: String.t(), + elevator_id: String.t(), + description: String.t(), + header_text: String.t() + } +end diff --git a/lib/screens/v2/widget_instance/footer.ex b/lib/screens/v2/widget_instance/footer.ex index 9a5f54904..a273c228e 100644 --- a/lib/screens/v2/widget_instance/footer.ex +++ b/lib/screens/v2/widget_instance/footer.ex @@ -3,16 +3,23 @@ defmodule Screens.V2.WidgetInstance.Footer do alias ScreensConfig.Screen - defstruct screen: nil + defstruct screen: nil, variant: nil @type t :: %__MODULE__{ - screen: Screen.t() + screen: Screen.t(), + variant: atom() | nil } + def serialize(%__MODULE__{variant: variant}) do + %{variant: variant} + end + defimpl Screens.V2.WidgetInstance do + alias Screens.V2.WidgetInstance.Footer + def priority(_instance), do: [1] - def serialize(_instance), do: %{} + def serialize(instance), do: Footer.serialize(instance) def slot_names(_instance), do: [:footer] diff --git a/lib/screens/v2/widget_instance/normal_header.ex b/lib/screens/v2/widget_instance/normal_header.ex index 90ba5ad88..ecdcfe9d5 100644 --- a/lib/screens/v2/widget_instance/normal_header.ex +++ b/lib/screens/v2/widget_instance/normal_header.ex @@ -8,14 +8,16 @@ defmodule Screens.V2.WidgetInstance.NormalHeader do defstruct screen: nil, icon: nil, text: nil, - time: nil + time: nil, + variant: nil @type icon :: :logo | :green_b | :green_c | :green_d | :green_e @type t :: %__MODULE__{ screen: ScreensConfig.Screen.t(), icon: icon | nil, text: String.t(), - time: DateTime.t() + time: DateTime.t(), + variant: atom() | nil } # Mercury adds their own time so we omit the time in the response. @@ -24,8 +26,14 @@ defmodule Screens.V2.WidgetInstance.NormalHeader do %{icon: icon, text: text, show_to: showing_destination?(t)} end - def serialize(%__MODULE__{icon: icon, text: text, time: time} = t) do - %{icon: icon, text: text, time: DateTime.to_iso8601(time), show_to: showing_destination?(t)} + def serialize(%__MODULE__{icon: icon, text: text, time: time, variant: variant} = t) do + %{ + icon: icon, + text: text, + time: DateTime.to_iso8601(time), + show_to: showing_destination?(t), + variant: variant + } end def slot_names(%__MODULE__{screen: %Screen{app_id: :dup_v2}}) do diff --git a/lib/screens/v2/widget_instance/elevator_closures.ex b/lib/screens/v2/widget_instance/outside_elevator_closures.ex similarity index 55% rename from lib/screens/v2/widget_instance/elevator_closures.ex rename to lib/screens/v2/widget_instance/outside_elevator_closures.ex index a819ca248..bcc2f12e1 100644 --- a/lib/screens/v2/widget_instance/elevator_closures.ex +++ b/lib/screens/v2/widget_instance/outside_elevator_closures.ex @@ -1,13 +1,15 @@ -defmodule Screens.V2.WidgetInstance.ElevatorClosures do +defmodule Screens.V2.WidgetInstance.OutsideElevatorClosures do @moduledoc false alias Screens.Stops.Stop + alias Screens.V2.WidgetInstance.Elevator.Closure + alias ScreensConfig.V2.Elevator - defstruct ~w[id in_station_closures other_stations_with_closures]a + defstruct ~w[app_params in_station_closures other_stations_with_closures]a @type t :: %__MODULE__{ - id: String.t(), - in_station_closures: list(__MODULE__.Closure.t()), + app_params: Elevator.t(), + in_station_closures: list(Closure.t()), other_stations_with_closures: list(__MODULE__.Station.t()) } @@ -15,7 +17,7 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosures do @moduledoc false alias Screens.Routes.Route - alias Screens.V2.WidgetInstance.ElevatorClosures.Closure + alias Screens.V2.WidgetInstance.Elevator.Closure @derive Jason.Encoder @@ -29,24 +31,8 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosures do } end - defmodule Closure do - @moduledoc false - - @derive Jason.Encoder - - defstruct ~w[id elevator_name elevator_id description header_text]a - - @type t :: %__MODULE__{ - id: String.t(), - elevator_name: String.t(), - elevator_id: String.t(), - description: String.t(), - header_text: String.t() - } - end - def serialize(%__MODULE__{ - id: id, + app_params: %Elevator{elevator_id: id}, in_station_closures: in_station_closures, other_stations_with_closures: other_stations_with_closures }), @@ -57,16 +43,16 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosures do } defimpl Screens.V2.WidgetInstance do - alias Screens.V2.WidgetInstance.ElevatorClosures + alias Screens.V2.WidgetInstance.OutsideElevatorClosures def priority(_instance), do: [1] - def serialize(instance), do: ElevatorClosures.serialize(instance) + def serialize(instance), do: OutsideElevatorClosures.serialize(instance) def slot_names(_instance), do: [:main_content] - def widget_type(_instance), do: :elevator_closures + def widget_type(_instance), do: :outside_elevator_closures def valid_candidate?(_instance), do: true def audio_serialize(_instance), do: %{} def audio_sort_key(_instance), do: [0] def audio_valid_candidate?(_instance), do: false - def audio_view(_instance), do: ScreensWeb.V2.Audio.ElevatorClosuresView + def audio_view(_instance), do: ScreensWeb.V2.Audio.OutsideElevatorClosuresView end end diff --git a/lib/screens_web/views/v2/audio/elevator_closures_view.ex b/lib/screens_web/views/v2/audio/current_elevator_closed_view.ex similarity index 57% rename from lib/screens_web/views/v2/audio/elevator_closures_view.ex rename to lib/screens_web/views/v2/audio/current_elevator_closed_view.ex index 7bd732227..8060c80da 100644 --- a/lib/screens_web/views/v2/audio/elevator_closures_view.ex +++ b/lib/screens_web/views/v2/audio/current_elevator_closed_view.ex @@ -1,4 +1,4 @@ -defmodule ScreensWeb.V2.Audio.ElevatorClosuresView do +defmodule ScreensWeb.V2.Audio.CurrentElevatorClosedView do use ScreensWeb, :view def render("_widget.ssml", _) do diff --git a/lib/screens_web/views/v2/audio/outside_elevator_closures_view.ex b/lib/screens_web/views/v2/audio/outside_elevator_closures_view.ex new file mode 100644 index 000000000..bfee0f336 --- /dev/null +++ b/lib/screens_web/views/v2/audio/outside_elevator_closures_view.ex @@ -0,0 +1,7 @@ +defmodule ScreensWeb.V2.Audio.OutsideElevatorClosuresView do + use ScreensWeb, :view + + def render("_widget.ssml", _) do + ~E"" + end +end diff --git a/mix.exs b/mix.exs index 496808d1b..c6818aba8 100644 --- a/mix.exs +++ b/mix.exs @@ -94,7 +94,7 @@ defmodule Screens.MixProject do {:telemetry_metrics, "~> 0.4"}, {:screens_config, git: "https://github.com/mbta/screens-config-lib.git", - ref: "c0db78ecd43c633a55a1f24b268ae7b8a5214748"}, + ref: "bbc9575e375b28b0489732eefa66c2ca3f100d0e"}, {:nebulex, "~> 2.6"}, {:remote_ip, "~> 1.2"}, {:hackney_telemetry, "~> 0.2.0"}, diff --git a/mix.lock b/mix.lock index e27785181..68b6eb004 100644 --- a/mix.lock +++ b/mix.lock @@ -62,7 +62,7 @@ "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "retry": {:hex, :retry, "0.18.0", "dc58ebe22c95aa00bc2459f9e0c5400e6005541cf8539925af0aa027dc860543", [:mix], [], "hexpm", "9483959cc7bf69c9e576d9dfb2b678b71c045d3e6f39ab7c9aa1489df4492d73"}, - "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "c0db78ecd43c633a55a1f24b268ae7b8a5214748", [ref: "c0db78ecd43c633a55a1f24b268ae7b8a5214748"]}, + "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "bbc9575e375b28b0489732eefa66c2ca3f100d0e", [ref: "bbc9575e375b28b0489732eefa66c2ca3f100d0e"]}, "sentry": {:hex, :sentry, "10.7.1", "33392222d80ccff99c503f972998d2858b4c1e5aca2219a34269b68dacba8e7d", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "56291312397bf2b6afab6cf4f7aa1f27413b0eb2ceeb63b8aab2d7658aaea882"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "stream_data": {:hex, :stream_data, "1.1.2", "05499eaec0443349ff877aaabc6e194e82bda6799b9ce6aaa1aadac15a9fdb4d", [:mix], [], "hexpm", "129558d2c77cbc1eb2f4747acbbea79e181a5da51108457000020a906813a1a9"}, diff --git a/test/screens/v2/candidate_generator/elevator/closures_test.exs b/test/screens/v2/candidate_generator/elevator/closures_test.exs index 9dd6582ed..31aa6f5dc 100644 --- a/test/screens/v2/candidate_generator/elevator/closures_test.exs +++ b/test/screens/v2/candidate_generator/elevator/closures_test.exs @@ -1,10 +1,19 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do use ExUnit.Case, async: true + alias Screens.V2.WidgetInstance.Elevator.Closure alias Screens.Alerts.Alert alias Screens.Routes.Route alias Screens.Stops.Stop alias Screens.V2.CandidateGenerator.Elevator.Closures, as: ElevatorClosures + + alias Screens.V2.WidgetInstance.{ + CurrentElevatorClosed, + Footer, + NormalHeader, + OutsideElevatorClosures + } + alias ScreensConfig.Screen alias ScreensConfig.V2.Elevator @@ -17,8 +26,31 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do @route injected(Route) @stop injected(Stop) - describe "elevator_status_instances/1" do - test "Only returns alerts with effect of :elevator_closure" do + describe "elevator_status_instances/3" do + setup do + config = %Elevator{ + elevator_id: "111", + accessible_path_direction_arrow: :n, + alternate_direction_text: "Test" + } + + %{ + config: config, + header_instance: %NormalHeader{ + screen: config, + icon: nil, + text: "Elevator 1", + time: ~U[2020-04-06T10:00:00Z] + }, + footer_instance: %Footer{screen: config} + } + end + + test "Only returns alerts with effect of :elevator_closure", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) expect(@stop, :fetch_parent_station_name_map, fn -> @@ -50,8 +82,9 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do end) [ - %Screens.V2.WidgetInstance.ElevatorClosures{ - id: "111", + ^header_instance, + %OutsideElevatorClosures{ + app_params: ^config, in_station_closures: [ %{ id: "1", @@ -62,14 +95,21 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do } ], other_stations_with_closures: [] - } + }, + ^footer_instance ] = ElevatorClosures.elevator_status_instances( - struct(Screen, app_id: :elevator_v2, app_params: %Elevator{elevator_id: "111"}) + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance ) end - test "Groups outside closures by station" do + test "Groups outside closures by station", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) expect(@stop, :fetch_parent_station_name_map, fn -> @@ -106,8 +146,9 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do end) [ - %Screens.V2.WidgetInstance.ElevatorClosures{ - id: "111", + ^header_instance, + %OutsideElevatorClosures{ + app_params: ^config, in_station_closures: [], other_stations_with_closures: [ %{ @@ -132,14 +173,21 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do ] } ] - } + }, + ^footer_instance ] = ElevatorClosures.elevator_status_instances( - struct(Screen, app_id: :elevator_v2, app_params: %Elevator{elevator_id: "111"}) + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance ) end - test "Filters alerts with no facilities or more than one facility" do + test "Filters alerts with no facilities or more than one facility", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) expect(@stop, :fetch_parent_station_name_map, fn -> @@ -171,18 +219,85 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do end) [ - %Screens.V2.WidgetInstance.ElevatorClosures{ - id: "111", + ^header_instance, + %OutsideElevatorClosures{ + app_params: ^config, in_station_closures: [], other_stations_with_closures: [] - } + }, + ^footer_instance + ] = + ElevatorClosures.elevator_status_instances( + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance + ) + end + + test "Returns CurrentElevatorClosed if configured elevator is closed", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do + expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) + + expect(@stop, :fetch_parent_station_name_map, fn -> + {:ok, %{"place-test" => "Place Test"}} + end) + + expect(@route, :fetch, fn %{stop_id: "place-test"} -> + {:ok, [%Route{id: "Red", type: :subway}]} + end) + + expect(@alert, :fetch_elevator_alerts_with_facilities, fn -> + alerts = [ + struct(Alert, + id: "1", + effect: :elevator_closure, + informed_entities: [ + %{stop: "place-test", facility: %{name: "Test", id: "111"}} + ] + ), + struct(Alert, + effect: :detour, + informed_entities: [ + %{stop: "place-test", facility: %{name: "Test 2", id: "facility-test2"}} + ] + ) + ] + + {:ok, alerts} + end) + + closed_header_instance = %{header_instance | variant: :closed} + closed_footer_instance = %{footer_instance | variant: :closed} + + [ + ^closed_header_instance, + %CurrentElevatorClosed{ + app_params: ^config, + closure: %Closure{ + id: "1", + elevator_name: "Test", + elevator_id: "111", + description: nil, + header_text: nil + } + }, + ^closed_footer_instance ] = ElevatorClosures.elevator_status_instances( - struct(Screen, app_id: :elevator_v2, app_params: %Elevator{elevator_id: "111"}) + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance ) end - test "Return empty routes on API error" do + test "Return empty routes on API error", %{ + config: config, + header_instance: header_instance, + footer_instance: footer_instance + } do expect(@facility, :fetch_stop_for_facility, fn "111" -> {:ok, %Stop{id: "place-test"}} end) expect(@stop, :fetch_parent_station_name_map, fn -> @@ -208,8 +323,9 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do end) [ - %Screens.V2.WidgetInstance.ElevatorClosures{ - id: "111", + ^header_instance, + %OutsideElevatorClosures{ + app_params: ^config, in_station_closures: [ %{ id: "1", @@ -220,10 +336,13 @@ defmodule Screens.V2.CandidateGenerator.Elevator.ClosuresTest do } ], other_stations_with_closures: [] - } + }, + ^footer_instance ] = ElevatorClosures.elevator_status_instances( - struct(Screen, app_id: :elevator_v2, app_params: %Elevator{elevator_id: "111"}) + struct(Screen, app_id: :elevator_v2, app_params: config), + header_instance, + footer_instance ) end end diff --git a/test/screens/v2/candidate_generator/elevator_test.exs b/test/screens/v2/candidate_generator/elevator_test.exs index 979843fcf..555459187 100644 --- a/test/screens/v2/candidate_generator/elevator_test.exs +++ b/test/screens/v2/candidate_generator/elevator_test.exs @@ -9,7 +9,9 @@ defmodule Screens.V2.CandidateGenerator.ElevatorTest do config = %Screen{ app_id: :elevator_v2, app_params: %V2.Elevator{ - elevator_id: "1" + elevator_id: "1", + alternate_direction_text: "Test", + accessible_path_direction_arrow: :n }, device_id: "TEST", name: "TEST", @@ -31,8 +33,6 @@ defmodule Screens.V2.CandidateGenerator.ElevatorTest do describe "candidate_instances/4" do test "returns expected header and footer", %{config: config} do now = ~U[2020-04-06T10:00:00Z] - elevator_closure_instances_fn = fn _ -> [] end - evergreen_content_instances_fn = fn _, _ -> [] end expected_header = %NormalHeader{ screen: config, @@ -42,6 +42,8 @@ defmodule Screens.V2.CandidateGenerator.ElevatorTest do } expected_footer = %Footer{screen: config} + elevator_closure_instances_fn = fn _, _, _ -> [expected_header, expected_footer] end + evergreen_content_instances_fn = fn _, _ -> [] end actual_instances = Elevator.candidate_instances( diff --git a/test/screens/v2/widget_instance/current_elevator_closed_test.exs b/test/screens/v2/widget_instance/current_elevator_closed_test.exs new file mode 100644 index 000000000..bbba11a43 --- /dev/null +++ b/test/screens/v2/widget_instance/current_elevator_closed_test.exs @@ -0,0 +1,85 @@ +defmodule Screens.V2.WidgetInstance.CurrentElevatorClosedTest do + use ExUnit.Case, async: true + + alias Screens.V2.WidgetInstance + alias Screens.V2.WidgetInstance.CurrentElevatorClosed + alias Screens.V2.WidgetInstance.Elevator.Closure + alias ScreensConfig.V2.Elevator + + setup do + %{ + instance: %CurrentElevatorClosed{ + app_params: + struct(Elevator, + elevator_id: "111", + alternate_direction_text: "Test", + accessible_path_direction_arrow: :n + ), + closure: %Closure{ + description: "Test Alert Description", + elevator_name: "Test Elevator", + elevator_id: "111", + id: "1", + header_text: "Test Alert Header" + } + } + } + end + + describe "priority/1" do + test "returns 1", %{instance: instance} do + assert [1] == WidgetInstance.priority(instance) + end + end + + describe "serialize/1" do + test "returns map with id, closure, and alternate direction info", %{instance: instance} do + assert %{ + closure: instance.closure, + accessible_path_direction_arrow: + instance.app_params.accessible_path_direction_arrow, + accessible_path_image_here_coordinates: + instance.app_params.accessible_path_image_here_coordinates, + accessible_path_image_url: instance.app_params.accessible_path_image_url, + alternate_direction_text: instance.app_params.alternate_direction_text, + id: instance.app_params.elevator_id + } == WidgetInstance.serialize(instance) + end + end + + describe "slot_names/1" do + test "returns main_content", %{instance: instance} do + assert [:main_content] == WidgetInstance.slot_names(instance) + end + end + + describe "widget_type/1" do + test "returns current_elevator_closed", %{instance: instance} do + assert :current_elevator_closed == WidgetInstance.widget_type(instance) + end + end + + describe "audio_serialize/1" do + test "returns empty map", %{instance: instance} do + assert %{} == WidgetInstance.audio_serialize(instance) + end + end + + describe "audio_sort_key/1" do + test "returns [0]", %{instance: instance} do + assert [0] == WidgetInstance.audio_sort_key(instance) + end + end + + describe "audio_valid_candidate?/1" do + test "returns false", %{instance: instance} do + refute WidgetInstance.audio_valid_candidate?(instance) + end + end + + describe "audio_view/1" do + test "returns CurrentElevatorClosedView", %{instance: instance} do + assert ScreensWeb.V2.Audio.CurrentElevatorClosedView == WidgetInstance.audio_view(instance) + end + end +end diff --git a/test/screens/v2/widget_instance/normal_header_test.exs b/test/screens/v2/widget_instance/normal_header_test.exs index 1e828eaff..6fbc15408 100644 --- a/test/screens/v2/widget_instance/normal_header_test.exs +++ b/test/screens/v2/widget_instance/normal_header_test.exs @@ -52,7 +52,8 @@ defmodule Screens.V2.WidgetInstance.NormalHeaderTest do icon: :logo, text: "Ruggles", time: "2021-03-04T11:00:00Z", - show_to: false + show_to: false, + variant: nil } == WidgetInstance.serialize(instance) end end diff --git a/test/screens/v2/widget_instance/elevator_closures_test.exs b/test/screens/v2/widget_instance/outside_elevator_closures_test.exs similarity index 60% rename from test/screens/v2/widget_instance/elevator_closures_test.exs rename to test/screens/v2/widget_instance/outside_elevator_closures_test.exs index 553ea625a..ee8397206 100644 --- a/test/screens/v2/widget_instance/elevator_closures_test.exs +++ b/test/screens/v2/widget_instance/outside_elevator_closures_test.exs @@ -1,15 +1,22 @@ -defmodule Screens.V2.WidgetInstance.ElevatorClosuresTest do +defmodule Screens.V2.WidgetInstance.OutsideElevatorClosuresTest do use ExUnit.Case, async: true alias Screens.V2.WidgetInstance - alias Screens.V2.WidgetInstance.ElevatorClosures + alias Screens.V2.WidgetInstance.Elevator.Closure + alias Screens.V2.WidgetInstance.OutsideElevatorClosures + alias ScreensConfig.V2.Elevator setup do %{ - instance: %ElevatorClosures{ - id: "111", + instance: %OutsideElevatorClosures{ + app_params: + struct(Elevator, + elevator_id: "1", + alternate_direction_text: "Test", + accessible_path_direction_arrow: :n + ), in_station_closures: [ - %ElevatorClosures.Closure{ + %Closure{ description: "Test Alert Description", elevator_name: "Test Elevator", elevator_id: "111", @@ -18,11 +25,11 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosuresTest do } ], other_stations_with_closures: [ - %ElevatorClosures.Station{ + %OutsideElevatorClosures.Station{ name: "Forest Hills", route_icons: ["Orange"], closures: [ - %ElevatorClosures.Closure{ + %Closure{ description: "FH Alert Description", elevator_name: "FH Elevator", elevator_id: "222", @@ -44,7 +51,11 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosuresTest do describe "serialize/1" do test "returns map with id and closures", %{instance: instance} do - assert Map.from_struct(instance) == WidgetInstance.serialize(instance) + assert %{ + in_station_closures: instance.in_station_closures, + other_stations_with_closures: instance.other_stations_with_closures, + id: instance.app_params.elevator_id + } == WidgetInstance.serialize(instance) end end @@ -55,8 +66,8 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosuresTest do end describe "widget_type/1" do - test "returns elevator_closures", %{instance: instance} do - assert :elevator_closures == WidgetInstance.widget_type(instance) + test "returns outside_elevator_closures", %{instance: instance} do + assert :outside_elevator_closures == WidgetInstance.widget_type(instance) end end @@ -79,8 +90,9 @@ defmodule Screens.V2.WidgetInstance.ElevatorClosuresTest do end describe "audio_view/1" do - test "returns ElevatorClosuresView", %{instance: instance} do - assert ScreensWeb.V2.Audio.ElevatorClosuresView == WidgetInstance.audio_view(instance) + test "returns OutsideElevatorClosuresView", %{instance: instance} do + assert ScreensWeb.V2.Audio.OutsideElevatorClosuresView == + WidgetInstance.audio_view(instance) end end end