Skip to content

Commit

Permalink
refactor(Components.RouteSymbols): move route_pills to subway_route_p…
Browse files Browse the repository at this point in the history
…ill (#2386)

* refactor: Green line branches and Mattapan as routes for Route Pills

* fix: Use assigns @route_id instead of local variable route_id

* feat(Dotcom.Routes): add some more Dotcom stuff

* refactor(Components.RouteSymbols): move route_pills to subway_route_pill

* use the new component

* add tests

* bugfix: only use the negative right margin when there are branches to show

---------

Co-authored-by: Josh Larson <[email protected]>
  • Loading branch information
thecristen and joshlarson authored Feb 18, 2025
1 parent d6e8243 commit 237ceaf
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 90 deletions.
50 changes: 46 additions & 4 deletions lib/dotcom/routes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,53 @@ defmodule Dotcom.Routes do
A collection of functions that help to work with routes in a unified way.
"""

@subway_route_ids ["Blue", "Green", "Orange", "Red"]
alias Routes.Route

@subway_branch_ids GreenLine.branch_ids() ++ ["Mattapan"]
@subway_line_names ["Blue", "Green", "Orange", "Red"]

# Association of subway line names to all their respective subway routes.
# This could later be derived from the GTFS line/route relationships.
@subway_line_route_map %{
"Blue" => ["Blue"],
"Green" => GreenLine.branch_ids(),
"Orange" => ["Orange"],
"Red" => ["Red", "Mattapan"]
}

@doc """
For a given route ID, return the relevant subway line name.
```elixir
line_name_for_subway_route("Green-B") == "Green"
line_name_for_subway_route("CR-Greenbush") == nil
```
"""
@spec line_name_for_subway_route(Route.id_t()) :: String.t() | Route.id_t() | nil
def line_name_for_subway_route(route_id) do
with {line_name, _} <-
Enum.find(@subway_line_route_map, fn {_, route_ids} ->
route_id in route_ids
end) do
line_name
end
end

@doc """
Returns a list of all subway route IDs which we'd like to show as branches.
"""
@spec subway_branch_ids() :: [Route.id_t()]
def subway_branch_ids, do: @subway_branch_ids

@doc """
Returns a list of all subway line names.
"""
@spec subway_line_names() :: [Route.id_t() | String.t()]
def subway_line_names, do: @subway_line_names

@doc """
Returns a list of route ids for all subway routes.
Returns the list of all subway route IDs.
"""
@spec subway_route_ids() :: [String.t()]
def subway_route_ids(), do: @subway_route_ids
@spec subway_route_ids() :: [Route.id_t()]
def subway_route_ids, do: Map.values(@subway_line_route_map) |> List.flatten()
end
73 changes: 0 additions & 73 deletions lib/dotcom_web/components/route_pills.ex

This file was deleted.

88 changes: 88 additions & 0 deletions lib/dotcom_web/components/route_symbols.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ defmodule DotcomWeb.Components.RouteSymbols do

@logan_express_icon_names Route.logan_express_icon_names()
@massport_icon_names Route.massport_icon_names()
@subway_branch_ids Dotcom.Routes.subway_branch_ids()
@subway_line_names Dotcom.Routes.subway_line_names()

variant(
:size,
Expand Down Expand Up @@ -178,6 +180,85 @@ defmodule DotcomWeb.Components.RouteSymbols do
"""
end

attr :class, :string, default: ""

attr :route_ids, :list,
doc: "A list of route IDs. These should all be associated with subway routes.",
examples: [["Blue"], ["Green-B", "Green-C"]]

@doc """
Renders a symbol for one or more subway routes, consisting of a colored pill
with optionally a number of circles representing branches of a subway line.
"""
def subway_route_pill(%{route_ids: [route_id]} = assigns) when route_id in @subway_line_names do
assigns =
assign(assigns, %{
bg_color_class: "bg-#{String.downcase(route_id)}-line",
route_abbreviation: String.at(route_id, 0) <> "L"
})

~H"""
<div class={[
@bg_color_class,
@class,
"w-[3.125rem] h-6 rounded-full ring-2 ring-white",
"flex items-center justify-center",
"text-white font-bold font-heading leading-none"
]}>
{@route_abbreviation}
</div>
"""
end

# Add the subway line name to the list of subway branch route_ids, since it is
# needed to render the larger pill
def subway_route_pill(%{route_ids: [route_id]} = assigns) when route_id in @subway_branch_ids do
assigns = assign(assigns, :route_ids, [subway_line_name(route_id) | assigns.route_ids])

~H"""
<.subway_route_pill {assigns} />
"""
end

# A list of any length - find the relevant subway line. If there's any number
# of subway lines represented here other than one, fall back to subway icon.
def subway_route_pill(%{route_ids: route_ids} = assigns) when is_list(route_ids) do
with subway_line_names <- Enum.map(route_ids, &subway_line_name/1),
[subway_line_name] <- Enum.uniq(subway_line_names) |> Enum.reject(&is_nil/1) do
branch_ids = Enum.reject(assigns.route_ids, &(&1 == subway_line_name)) |> Enum.sort()

if branch_ids == GreenLine.branch_ids() do
assigns = assign(assigns, :route_ids, ["Green"])

~H"""
<.subway_route_pill {assigns} />
"""
else
assigns =
assign(assigns, %{
route_ids: [subway_line_name],
branch_ids: branch_ids
})

~H"""
<span class="flex items-center">
<.subway_route_pill route_ids={@route_ids} class={"#{@class} -mr-1"} />
<.route_symbol
:for={route_id <- @branch_ids}
route={%Routes.Route{id: route_id}}
class={"#{@class} rounded-full ring-white ring-2 mr-0.5"}
/>
</span>
"""
end
else
_ ->
~H"""
<.icon type="icon-svg" name="icon-mode-subway-default" class="h-6 w-6" />
"""
end
end

# Given a route, return a machine-readable label.
defp route_label(%Route{type: 2}), do: "Commuter Rail"
defp route_label(%Route{type: 4}), do: "Ferry"
Expand All @@ -202,4 +283,11 @@ defmodule DotcomWeb.Components.RouteSymbols do
end

defp route_label(%Route{long_name: long_name}), do: long_name

defp subway_line_name(route_id) when route_id in @subway_branch_ids do
Dotcom.Routes.line_name_for_subway_route(route_id)
end

defp subway_line_name(route_id) when route_id in @subway_line_names, do: route_id
defp subway_line_name(_), do: nil
end
9 changes: 4 additions & 5 deletions lib/dotcom_web/components/system_status/subway_status.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
use DotcomWeb, :component

import DotcomWeb.Components, only: [bordered_container: 1, lined_list: 1]
import DotcomWeb.Components.RoutePills
import DotcomWeb.Components.RouteSymbols, only: [subway_route_pill: 1]
import DotcomWeb.Components.SystemStatus.StatusLabel

@max_rows 5
Expand Down Expand Up @@ -35,10 +35,9 @@ defmodule DotcomWeb.Components.SystemStatus.SubwayStatus do
]}
>
<div class={["pl-2 py-3", row.style.hide_route_pill && "invisible"]}>
<.route_pill
route_id={row.route_info.route_id}
modifier_ids={row.route_info.branch_ids}
modifier_class="group-hover/row:ring-brand-primary-lightest"
<.subway_route_pill
class="group-hover/row:ring-brand-primary-lightest"
route_ids={[row.route_info.route_id | row.route_info.branch_ids]}
/>
</div>
<div class={[
Expand Down
25 changes: 17 additions & 8 deletions lib/dotcom_web/live/system_status.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule DotcomWeb.Live.SystemStatus do

use DotcomWeb, :live_view

import DotcomWeb.Components.RoutePills
import DotcomWeb.Components.RouteSymbols, only: [subway_route_pill: 1]
import DotcomWeb.Components.SystemStatus.StatusLabel
import DotcomWeb.Components.SystemStatus.SubwayStatus

Expand Down Expand Up @@ -68,13 +68,22 @@ defmodule DotcomWeb.Live.SystemStatus do
</div>
<h2>Route Pills</h2>
<div class="flex flex-col gap-2">
<.route_pill route_id="Blue" />
<.route_pill route_id="Green" />
<.route_pill route_id="Orange" />
<.route_pill route_id="Red" />
<.route_pill route_id="Green" modifier_ids={["Green-B", "Green-C"]} />
</div>
<%= for ids <- [
["Blue"],
["Green"],
["Orange"],
["Red"],
["Mattapan"],
["Red", "Mattapan"],
["Orange", "Mattapan"],
["Green-E", "Green-B", "Green-D"],
["Fake News"],
["Green-B", "Green-C", "Green-D", "Green-E"]
] do %>
<div class="flex gap-sm p-2 items-center hover:bg-slate-600 hover:text-white cursor-pointer group/row">
<.subway_route_pill route_ids={ids} class="group-hover/row:ring-slate-600" /> {inspect(ids)}
</div>
<% end %>
"""
end

Expand Down
56 changes: 56 additions & 0 deletions test/dotcom_web/components/route_symbols_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,62 @@ defmodule DotcomWeb.Components.RouteSymbolsTest do
end
end

describe "subway_route_pill/1" do
test "Subway lines render one element" do
for route_id <- Dotcom.Routes.subway_line_names() do
html =
render_component(&subway_route_pill/1, %{
route_ids: [route_id]
})
|> Floki.parse_fragment!()
|> List.first()

assert html
assert Floki.children(html, include_text: false) == []
end
end

test "Mattapan renders pill + icon" do
html =
render_component(&subway_route_pill/1, %{
route_ids: ["Mattapan"]
})
|> Floki.parse_fragment!()
|> List.first()

assert [{"div", _, _}, {"svg", _, _}] = Floki.children(html, include_text: false)
end

test "Multiple branches render pill + multiple icons" do
num_branches = Faker.Util.pick([2, 3])

html =
render_component(&subway_route_pill/1, %{
route_ids: Enum.take(GreenLine.branch_ids(), num_branches)
})
|> Floki.parse_fragment!()
|> List.first()

assert [{"div", _, _} | icons] = Floki.children(html, include_text: false)
assert [{"svg", _, _} | _] = icons
assert Enum.count(icons) == num_branches
end

test "List of all Green Line branches renders one pill" do
all_gl_branches_pill =
render_component(&subway_route_pill/1, %{
route_ids: GreenLine.branch_ids()
})

green_line_pill =
render_component(&subway_route_pill/1, %{
route_ids: ["Green"]
})

assert all_gl_branches_pill == green_line_pill
end
end

defp matches_title?(html, text) do
title =
html
Expand Down

0 comments on commit 237ceaf

Please sign in to comment.