From 2a0164a36d95f79d24ff823b970b53e6dad27d7a Mon Sep 17 00:00:00 2001 From: Ryan Kotval Date: Mon, 17 Jun 2024 11:02:54 -0500 Subject: [PATCH] feat(Alerts): Added back the lifecycle labels (#2095) --- assets/css/_alerts.scss | 14 ++++++++ assets/ts/components/Alerts.tsx | 35 +++++++++++++++++- assets/ts/components/__tests__/AlertsTest.tsx | 32 ++++++++++++++++- .../__snapshots__/AlertsTest.tsx.snap | 5 +++ lib/alerts/alert.ex | 8 +++++ lib/dotcom_web/banneralert.ex | 14 ++++++++ .../templates/alert/_banner.html.eex | 4 +++ lib/dotcom_web/templates/alert/_item.html.eex | 4 +++ lib/dotcom_web/views/alert_view.ex | 20 +++++++++++ test/alerts/alerts_test.exs | 36 +++++++++++++++++++ test/dotcom_web/banneralert_test.exs | 17 +++++++++ 11 files changed, 187 insertions(+), 2 deletions(-) diff --git a/assets/css/_alerts.scss b/assets/css/_alerts.scss index eaec1b8980..20f2987c47 100644 --- a/assets/css/_alerts.scss +++ b/assets/css/_alerts.scss @@ -169,6 +169,20 @@ margin-top: $space-12; } +.c-alert-item__badge { + background-color: $service-alert-yellow-darker; + border-radius: calc(#{$base-spacing} / 4); + padding: calc(#{$base-spacing} / 8) calc(#{$base-spacing} / 2); +} + +.c-alert-item__badge--upcoming { + background-color: $service-alert-gray-darker; +} + +.c-alert-item__badge--system { + background-color: $service-alert-yellow; +} + .m-alerts__mode-buttons { display: flex; margin-left: calc(-#{$grid-gutter-width} / 4); diff --git a/assets/ts/components/Alerts.tsx b/assets/ts/components/Alerts.tsx index 767f4dba2a..f36fb26d6e 100644 --- a/assets/ts/components/Alerts.tsx +++ b/assets/ts/components/Alerts.tsx @@ -1,5 +1,5 @@ import React, { ReactElement, useState } from "react"; -import { Alert as AlertType } from "../__v3api"; +import { Alert as AlertType, Lifecycle } from "../__v3api"; import { handleReactEnterKeyPress } from "../helpers/keyboard-events-react"; import { caret } from "../helpers/icon"; import renderSVG from "../helpers/render-svg"; @@ -47,6 +47,38 @@ export const iconForAlert = ({ } }; +export const humanLifecycle = (lifecycle: Lifecycle): string | null => { + switch (lifecycle) { + case "upcoming": + case "ongoing_upcoming": + return "Upcoming"; + case "ongoing": + return "Ongoing"; + case "new": + case "unknown": + default: + return null; + } +}; + +export const alertLabel = (alert: AlertType): ReactElement => { + const alertClasses = ["u-small-caps", "c-alert-item__badge"]; + if (alert.priority === "system") { + alertClasses.push("c-alert-item__badge--system"); + } + if ( + alert.lifecycle === "upcoming" || + alert.lifecycle === "ongoing_upcoming" + ) { + alertClasses.push("c-alert-item__badge--upcoming"); + } + return ( + + {humanLifecycle(alert.lifecycle)} + + ); +}; + export const effectNameForAlert = (alert: AlertType): string => alert.effect .split("_") @@ -195,6 +227,7 @@ export const Alert = ({
{`${effectNameForAlert(alert)} `} + {humanLifecycle(alert.lifecycle) ? alertLabel(alert) : null}
{/* eslint-disable-next-line react/no-danger */}
diff --git a/assets/ts/components/__tests__/AlertsTest.tsx b/assets/ts/components/__tests__/AlertsTest.tsx index f7bf062e15..2874ce42e2 100644 --- a/assets/ts/components/__tests__/AlertsTest.tsx +++ b/assets/ts/components/__tests__/AlertsTest.tsx @@ -1,7 +1,7 @@ import React from "react"; import { mount } from "enzyme"; import renderer from "react-test-renderer"; -import Alerts, { iconForAlert } from "../Alerts"; +import Alerts, { alertLabel, iconForAlert, humanLifecycle } from "../Alerts"; import { enzymeToJsonWithoutProps } from "../../app/helpers/testUtils"; import { Alert, InformedEntitySet } from "../../__v3api"; import { isAmenityAlert } from "../../models/alert"; @@ -168,6 +168,36 @@ describe("iconForAlert", () => { ); }); + describe("alertLabel", () => { + test("it returns a system label for system alerts", () => { + const label = alertLabel({ + ...highAlert, + priority: "system", + lifecycle: "ongoing_upcoming" + }); + const labelComponent = JSON.stringify(renderer.create(label!).toJSON()); + expect(labelComponent).toMatch("badge--system"); + expect(labelComponent).toMatch("badge--upcoming"); + }); + }); + + describe("humanLifecycle", () => { + it("returns correct value for others", () => { + let label = humanLifecycle("ongoing_upcoming"); + expect(label).toMatch("Upcoming"); + label = humanLifecycle("ongoing"); + expect(label).toMatch("Ongoing"); + label = humanLifecycle("upcoming"); + expect(label).toMatch("Upcoming"); + label = humanLifecycle("new"); + expect(label).toBeNull(); + label = humanLifecycle("unknown"); + expect(label).toBeNull(); + label = humanLifecycle("something" as any); + expect(label).toBeNull(); + }); + }); + test("renders alert icon for system alerts", () => { const icon = iconForAlert({ ...highAlert, diff --git a/assets/ts/components/__tests__/__snapshots__/AlertsTest.tsx.snap b/assets/ts/components/__tests__/__snapshots__/AlertsTest.tsx.snap index f1c1f50645..ef2c760c65 100644 --- a/assets/ts/components/__tests__/__snapshots__/AlertsTest.tsx.snap +++ b/assets/ts/components/__tests__/__snapshots__/AlertsTest.tsx.snap @@ -125,6 +125,11 @@ exports[`it renders 1`] = ` className="c-alert-item__effect" > Other + + Upcoming +
Alerts.Alert.human_effect() end + def human_label(banner) do + alert_from_banner(banner) + |> Alerts.Alert.human_label() + end + def icon(banner) do alert_from_banner(banner) |> Alerts.Alert.icon() end + def label_class(banner) do + alert_from_banner(banner) + |> DotcomWeb.AlertView.alert_label_class() + end + defp alert_from_banner(banner) do Alerts.Alert |> struct(Map.from_struct(banner)) diff --git a/lib/dotcom_web/templates/alert/_banner.html.eex b/lib/dotcom_web/templates/alert/_banner.html.eex index 78d3d593bf..f77ac22483 100644 --- a/lib/dotcom_web/templates/alert/_banner.html.eex +++ b/lib/dotcom_web/templates/alert/_banner.html.eex @@ -1,3 +1,4 @@ +<% label = BannerAlert.human_label(@alert) %>
<%= @alert |> BannerAlert.icon() |> alert_icon() %> @@ -6,6 +7,9 @@
<%= "#{BannerAlert.human_effect(@alert)} " %> + <%= unless label == "" do + content_tag(:span, [label], class: BannerAlert.label_class(@alert)) + end %>
<%= replace_urls_with_links(BannerAlert.header(@alert)) %> diff --git a/lib/dotcom_web/templates/alert/_item.html.eex b/lib/dotcom_web/templates/alert/_item.html.eex index 7f92c9d666..372ce44379 100644 --- a/lib/dotcom_web/templates/alert/_item.html.eex +++ b/lib/dotcom_web/templates/alert/_item.html.eex @@ -1,3 +1,4 @@ +<% label = Alerts.Alert.human_label(@alert) %> <% alert_icon_type = Alerts.Alert.icon(@alert) %>
  • <%= "#{effect_name(@alert)} " %> + <%= unless label == "" do + content_tag(:span, [label], class: alert_label_class(@alert)) + end %>
    <%= replace_urls_with_links(@alert.header) %> diff --git a/lib/dotcom_web/views/alert_view.ex b/lib/dotcom_web/views/alert_view.ex index 51411ad1f8..cfe774cb2c 100644 --- a/lib/dotcom_web/views/alert_view.ex +++ b/lib/dotcom_web/views/alert_view.ex @@ -145,6 +145,26 @@ defmodule DotcomWeb.AlertView do Alert.human_effect(alert) end + def alert_label_class(%Alert{} = alert) do + ["u-small-caps", "c-alert-item__badge"] + |> do_alert_label_class(alert) + |> Enum.join(" ") + end + + defp do_alert_label_class(class_list, %Alert{priority: priority}) + when priority == :system do + ["c-alert-item__badge--system" | class_list] + end + + defp do_alert_label_class(class_list, %Alert{lifecycle: lifecycle}) + when lifecycle in [:upcoming, :ongoing_upcoming] do + ["c-alert-item__badge--upcoming" | class_list] + end + + defp do_alert_label_class(class_list, _) do + class_list + end + def alert_updated(%Alert{updated_at: updated_at}, relative_to) do alert_updated(updated_at, relative_to) end diff --git a/test/alerts/alerts_test.exs b/test/alerts/alerts_test.exs index 33ee9c9407..edb60d9cf6 100644 --- a/test/alerts/alerts_test.exs +++ b/test/alerts/alerts_test.exs @@ -65,6 +65,42 @@ defmodule AlertsTest do end end + describe "human_label/1" do + @future_active_period [{Timex.shift(@now, days: 8), Timex.shift(@now, days: 20)}] + + test "returns Ongoing when lifecycle is ongoing and time not in active period" do + assert human_label(%Alert{ + effect: :cancellation, + active_period: @future_active_period, + lifecycle: :ongoing + }) == "Ongoing" + end + + test "returns Upcoming when lifecycle is ongoing and time not in active period" do + assert human_label(%Alert{ + effect: :cancellation, + active_period: @future_active_period, + lifecycle: :upcoming + }) == "Upcoming" + end + + test "returns empty string when lifecycle is new and time not in active period" do + assert human_label(%Alert{ + effect: :cancellation, + active_period: @future_active_period, + lifecycle: :new + }) == "" + end + + test "returns Upcoming when lifecycle is upcoming and active period is empty" do + assert human_label(%Alert{ + effect: :cancellation, + active_period: [], + lifecycle: :upcoming + }) == "Upcoming" + end + end + describe "icon/1" do test "return :none, :cancel, :snow, :shuttle or :alert" do assert icon(%Alert{effect: :detour, priority: :low}) == :none diff --git a/test/dotcom_web/banneralert_test.exs b/test/dotcom_web/banneralert_test.exs index 38af33bdda..f5aa0762c8 100644 --- a/test/dotcom_web/banneralert_test.exs +++ b/test/dotcom_web/banneralert_test.exs @@ -27,6 +27,12 @@ defmodule BannerAlertTest do end end + describe "BannerAlert.human_label" do + test "for Alerts.Banner" do + assert BannerAlert.human_label(@banner) == "" + end + end + describe "BannerAlert.icon" do test "for Alerts.Alert" do assert BannerAlert.icon(@alert) == :none @@ -36,4 +42,15 @@ defmodule BannerAlertTest do assert BannerAlert.icon(@banner) == :alert end end + + describe "BannerAlert.label_class" do + test "for Alerts.Alert" do + assert BannerAlert.label_class(@alert) == "u-small-caps c-alert-item__badge" + end + + test "for Alerts.Banner" do + assert BannerAlert.label_class(@banner) == + "c-alert-item__badge--system u-small-caps c-alert-item__badge" + end + end end