Date: Fri, 11 Oct 2024 15:41:31 -0400
Subject: [PATCH 04/10] feat(ts/components/reactDivIcon): create custom
`DivIcon` for React portals [optional] (#2848)
* feat(ts/components/reactDivIcon): create custom `DivIcon` for React portals
* chore(ts/components/reactDivIcon): update snapshots
---
.../components/map/utilities/reactDivIcon.tsx | 65 +-
.../__snapshots__/mapPage.test.tsx.snap | 338 +++---
.../shuttleMapPage.test.tsx.snap | 1080 +++++++----------
.../detoursListPage.openDetour.test.tsx.snap | 308 +++--
.../map/utilities/reactMarker.test.tsx | 4 +-
5 files changed, 793 insertions(+), 1002 deletions(-)
diff --git a/assets/src/components/map/utilities/reactDivIcon.tsx b/assets/src/components/map/utilities/reactDivIcon.tsx
index f99a662fd..a2b3c6abf 100644
--- a/assets/src/components/map/utilities/reactDivIcon.tsx
+++ b/assets/src/components/map/utilities/reactDivIcon.tsx
@@ -1,6 +1,6 @@
import { useMemo } from "react"
-import L, { DivIconOptions as LeafletDivIconOptions } from "leaflet"
+import { DivIcon, DivIconOptions as LeafletDivIconOptions } from "leaflet"
// Prevent user from setting parameters we intend to provide
export type DivIconOptions = Omit
@@ -8,6 +8,61 @@ export type DivIconOptions = Omit
// Prevent useEffect from triggering by providing stable default reference
const defaultOptions = {}
+// DefinitelyTyped definitions _seem_ to allow us to use a "real" class instead of `.extend`?
+// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/3923
+// https://leafletjs.com/examples/extending/extending-1-classes.html
+/**
+ * A Leaflet {@linkcode DivIcon} which uses a provided {@linkcode HTMLElement}
+ * as the marker element returned to leaflet when the associated Marker is added
+ * to the map.
+ *
+ * The provided {@linkcode HTMLElement} is a stable element that is created
+ * before the {@linkcode DivIcon} is added to a Map via a Marker. This is
+ * required so that the {@linkcode HTMLElement} reference can be created before
+ * the Marker and Icon are added to the `Map`
+ */
+class DivIconReactPortal extends DivIcon {
+ /**
+ * Stable element reference for React Portals.
+ */
+ element: HTMLElement
+
+ constructor(element: HTMLElement, options?: DivIconOptions) {
+ super(options)
+ this.element = element
+ }
+
+ /**
+ * Overridden function which returns our stable {@linkcode HTMLElement}
+ * reference and configures the {@linkcode DivIconReactPortal.element}
+ * attributes according to Leaflet.
+ *
+ * ---
+ *
+ * {@linkcode _oldIcon} is the {@linkcode HTMLElement} reference that a
+ * Leaflet Marker stores between `onAdd` and `onRemove` calls.
+ * Because we _always_ want Leaflet to use our stable element reference,
+ * {@linkcode DivIconReactPortal.element} is always returned.
+ */
+ createIcon(_oldIcon?: HTMLElement): HTMLElement {
+ this.setElementIconStyles()
+ return this.element
+ }
+
+ // The whole reason we have to recreate the `DivIcon` entirely is because we
+ // need to call `_setIconStyles` on the element, when the `DivIcon` `options`
+ // change. Pulling this out into it's own known function may allow us to
+ // avoid recreating the DivIcon entirely in the future, and instead update it
+ // when the `options` change.
+ setElementIconStyles() {
+ // We KNOW that this function exists, but the `DefinitelyTyped` definitions
+ // do not include it.
+ if ("_setIconStyles" in this && typeof this._setIconStyles === "function") {
+ this._setIconStyles(this.element, "icon")
+ }
+ }
+}
+
/**
* Hook to create a `divIcon` compatible with {@link ReactDOM.createPortal}, by
* creating a stable {@link HTMLDivElement container} for React to use for
@@ -28,7 +83,7 @@ export function useReactDivIcon(options?: DivIconOptions) {
// To ensure that the `divIcon` updates when `opts` change
// regenerate the `divIcon` with the portal element and provided `opts`
const divIcon = useMemo(
- () => L.divIcon({ ...opts, html: iconContainer }),
+ () => new DivIconReactPortal(iconContainer, opts),
[iconContainer, opts]
)
@@ -40,8 +95,4 @@ export function useReactDivIcon(options?: DivIconOptions) {
// Extend this function or add more parameters to `useReactDivIcon` to override
// portal element creation
-const createPortalElement = () => {
- const element = document.createElement("div")
- element.classList.add("w-100", "h-100")
- return element
-}
+const createPortalElement = () => document.createElement("div")
diff --git a/assets/tests/components/__snapshots__/mapPage.test.tsx.snap b/assets/tests/components/__snapshots__/mapPage.test.tsx.snap
index 7824800e9..aec1bb37f 100644
--- a/assets/tests/components/__snapshots__/mapPage.test.tsx.snap
+++ b/assets/tests/components/__snapshots__/mapPage.test.tsx.snap
@@ -1517,21 +1517,17 @@ exports[` Snapshot renders vehicle data 1`] = `
style="margin-left: 14px; margin-top: -25px; width: 12px; height: 12px; left: -57px; top: 1295px; z-index: 1295;"
tabindex="0"
>
-
diff --git a/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap b/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
index 63bcb2e6d..4dbe2b0ef 100644
--- a/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
+++ b/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
@@ -573,30 +573,26 @@ exports[`Detours Page: Open a Detour renders detour details in an open drawer on
tabindex="0"
title="Detour Start"
>
-
-
-
+
+
+
-
-
-
+
+
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
-
+
{
expect(icon).toBeEmptyDOMElement()
- expect(icon.parentElement?.parentElement).toHaveStyle({
+ expect(icon.parentElement).toHaveStyle({
width: `${initialSizeX}px`,
height: `${initialSizeY}px`,
})
@@ -91,7 +91,7 @@ describe("ReactMarker", () => {
/>
)
- expect(icon.parentElement?.parentElement).toHaveStyle({
+ expect(icon.parentElement).toHaveStyle({
width: `${newSizeX}px`,
height: `${newSizeY}px`,
})
From 52b5eee7d01fb4a69171c49da5902aafe5f6444b Mon Sep 17 00:00:00 2001
From: Kayla Firestack
Date: Tue, 15 Oct 2024 10:36:17 -0400
Subject: [PATCH 05/10] fix: disable distributed elixir connections in
production (#2854)
---
config/runtime.exs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/config/runtime.exs b/config/runtime.exs
index d41f217f6..cfd6e3e63 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -105,5 +105,7 @@ if config_env() == :prod do
config :skate, Skate.Detours.TripModificationPublisher, start: true
end
- config :skate, DNSCluster, query: System.get_env("DNS_CLUSTER_QUERY") || :ignore
+ # There are currently issues with Distributed Elixir and our Data Pipelines.
+ # So we need to disable clustering in prod until we figure this out
+ # config :skate, DNSCluster, query: System.get_env("DNS_CLUSTER_QUERY") || :ignore
end
From 08dc1eb885c1f0a3573610450c63e982829ad378 Mon Sep 17 00:00:00 2001
From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:04:29 -0400
Subject: [PATCH 06/10] tweak: Detour notifications behind test group (#2855)
Co-authored-by: Kayla Firestack
---
assets/src/components/notificationBellIcon.tsx | 7 +++++--
assets/src/components/notificationCard.tsx | 5 ++++-
assets/src/userInTestGroup.ts | 1 +
assets/tests/components/notificationBellIcon.test.tsx | 7 ++++++-
.../tests/components/notificationCard.openDetour.test.tsx | 6 +++++-
assets/tests/components/notificationCard.test.tsx | 4 +++-
6 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/assets/src/components/notificationBellIcon.tsx b/assets/src/components/notificationBellIcon.tsx
index 4dc2a1dc2..eb83cd228 100644
--- a/assets/src/components/notificationBellIcon.tsx
+++ b/assets/src/components/notificationBellIcon.tsx
@@ -17,12 +17,15 @@ const NotificationBellIcon = ({
} = usePanelStateFromStateDispatchContext()
const { notifications } = useContext(NotificationsContext)
- const inDetoursList = inTestGroup(TestGroups.DetoursList)
+ const inDetoursNotificationGroup =
+ inTestGroup(TestGroups.DetoursList) &&
+ inTestGroup(TestGroups.DetoursNotifications)
const unreadNotifications = (notifications || []).filter(
(notification) =>
notification.state === "unread" &&
!(
- notification.content.$type === NotificationType.Detour && !inDetoursList
+ notification.content.$type === NotificationType.Detour &&
+ !inDetoursNotificationGroup
)
)
const unreadBadge: boolean = unreadNotifications.length > 0
diff --git a/assets/src/components/notificationCard.tsx b/assets/src/components/notificationCard.tsx
index 3bd961496..aef5de1ea 100644
--- a/assets/src/components/notificationCard.tsx
+++ b/assets/src/components/notificationCard.tsx
@@ -53,7 +53,10 @@ export const NotificationCard = ({
if (
notification.content.$type === NotificationType.Detour &&
- !inTestGroup(TestGroups.DetoursList)
+ !(
+ inTestGroup(TestGroups.DetoursList) &&
+ inTestGroup(TestGroups.DetoursNotifications)
+ )
) {
return null
}
diff --git a/assets/src/userInTestGroup.ts b/assets/src/userInTestGroup.ts
index eeab87498..2228c89ff 100644
--- a/assets/src/userInTestGroup.ts
+++ b/assets/src/userInTestGroup.ts
@@ -4,6 +4,7 @@ export enum TestGroups {
BackwardsDetourPrevention = "backwards-detour-prevention",
DemoMode = "demo-mode",
DetoursList = "detours-list",
+ DetoursNotifications = "detours-notifications",
DetoursPilot = "detours-pilot",
MinimalLadderPage = "minimal-ladder-page",
LateView = "late-view",
diff --git a/assets/tests/components/notificationBellIcon.test.tsx b/assets/tests/components/notificationBellIcon.test.tsx
index 58aae50c0..3032ce815 100644
--- a/assets/tests/components/notificationBellIcon.test.tsx
+++ b/assets/tests/components/notificationBellIcon.test.tsx
@@ -160,7 +160,12 @@ describe("NotificationBellIcon", () => {
},
])("$type detour notification", () => {
test("renders when there are new detour notifications and user is part of DetoursList group", () => {
- jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursList])
+ jest
+ .mocked(getTestGroups)
+ .mockReturnValue([
+ TestGroups.DetoursList,
+ TestGroups.DetoursNotifications,
+ ])
const { baseElement } = render(
{
test("renders detour details modal to match mocked fetchDetour", async () => {
jest
.mocked(getTestGroups)
- .mockReturnValue([TestGroups.DetoursPilot, TestGroups.DetoursList])
+ .mockReturnValue([
+ TestGroups.DetoursPilot,
+ TestGroups.DetoursList,
+ TestGroups.DetoursNotifications,
+ ])
jest.mocked(fetchDetours).mockResolvedValue(Ok(detourListFactory.build()))
jest
diff --git a/assets/tests/components/notificationCard.test.tsx b/assets/tests/components/notificationCard.test.tsx
index c4b42dd9b..175f4040d 100644
--- a/assets/tests/components/notificationCard.test.tsx
+++ b/assets/tests/components/notificationCard.test.tsx
@@ -32,7 +32,9 @@ jest.mock("../../src/helpers/fullStory")
jest.mock("../../src/userTestGroups")
beforeEach(() => {
- jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursList])
+ jest
+ .mocked(getTestGroups)
+ .mockReturnValue([TestGroups.DetoursList, TestGroups.DetoursNotifications])
jest.mocked(fetchDetours).mockResolvedValue(Ok(detourListFactory.build()))
jest
.mocked(fetchDetour)
From 945a2e9ac6dae6b30c107192ddcf9d4039d649ca Mon Sep 17 00:00:00 2001
From: Kayla Firestack
Date: Tue, 15 Oct 2024 15:25:47 -0400
Subject: [PATCH 07/10] fix(ex/Realtime.Server): use `broadcast_local` instead
of `broadcast` (#2856)
* fix(ex/Realtime.Server): use `broadcast_local` instead of `broadcast`
* fix: re-enable `DNSCluster`
Reverts: https://github.com/mbta/skate/pull/2854
---
config/runtime.exs | 4 +---
lib/realtime/server.ex | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/config/runtime.exs b/config/runtime.exs
index cfd6e3e63..d41f217f6 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -105,7 +105,5 @@ if config_env() == :prod do
config :skate, Skate.Detours.TripModificationPublisher, start: true
end
- # There are currently issues with Distributed Elixir and our Data Pipelines.
- # So we need to disable clustering in prod until we figure this out
- # config :skate, DNSCluster, query: System.get_env("DNS_CLUSTER_QUERY") || :ignore
+ config :skate, DNSCluster, query: System.get_env("DNS_CLUSTER_QUERY") || :ignore
end
diff --git a/lib/realtime/server.ex b/lib/realtime/server.ex
index 91cc60add..3cde364c8 100644
--- a/lib/realtime/server.ex
+++ b/lib/realtime/server.ex
@@ -510,7 +510,7 @@ defmodule Realtime.Server do
:alerts -> "realtime_alerts"
end
- Phoenix.PubSub.broadcast(pubsub_name(), topic, {:new_realtime_data, state.ets})
+ Phoenix.PubSub.local_broadcast(pubsub_name(), topic, {:new_realtime_data, state.ets})
end
@spec block_is_active?(VehicleOrGhost.t()) :: boolean
From ba6bd8e14df9db18a9bea9bfa193330aa55545e7 Mon Sep 17 00:00:00 2001
From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com>
Date: Wed, 16 Oct 2024 14:45:57 -0400
Subject: [PATCH 08/10] tweak: Make the missed stops count badge round (#2857)
Co-authored-by: Kayla Firestack
---
assets/src/components/detours/detourPanelComponents.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/assets/src/components/detours/detourPanelComponents.tsx b/assets/src/components/detours/detourPanelComponents.tsx
index accd3ff45..384f96138 100644
--- a/assets/src/components/detours/detourPanelComponents.tsx
+++ b/assets/src/components/detours/detourPanelComponents.tsx
@@ -13,7 +13,10 @@ export const MissedStops = ({ missedStops }: MissedStopsProps) => (
{missedStops && (
diff --git a/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap b/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
index 4dbe2b0ef..3ebde0096 100644
--- a/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
+++ b/assets/tests/components/detours/__snapshots__/detoursListPage.openDetour.test.tsx.snap
@@ -405,7 +405,7 @@ exports[`Detours Page: Open a Detour renders detour details in an open drawer on