From 4230fbe5d952cc864b7bec63ae2d597777c8814f Mon Sep 17 00:00:00 2001 From: Thomas Jeffery Date: Fri, 30 May 2025 18:52:33 -0600 Subject: [PATCH 1/4] feat(#2687): notification banners too noisy --- src/App.tsx | 73 +++++++++---------- .../version-language-switcher/HelpButton.tsx | 38 ++++++++++ .../SiteWideNotification.tsx | 23 ++++++ .../VersionLanguageSwitcher.tsx | 7 +- .../VersionUpdateNotification.tsx | 48 ++++++++++++ .../VersionUpdateNotificationContext.tsx | 55 ++++++++++++++ src/contexts/LanguageVersionContext.tsx | 2 +- src/contexts/SiteWideNotificationContext.tsx | 39 ++++++++++ src/routes/components/Components.tsx | 15 +--- src/routes/root.tsx | 49 +++++++------ 10 files changed, 274 insertions(+), 75 deletions(-) create mode 100644 src/components/version-language-switcher/HelpButton.tsx create mode 100644 src/components/version-language-switcher/SiteWideNotification.tsx create mode 100644 src/components/version-language-switcher/VersionUpdateNotification.tsx create mode 100644 src/components/version-language-switcher/VersionUpdateNotificationContext.tsx create mode 100644 src/contexts/SiteWideNotificationContext.tsx diff --git a/src/App.tsx b/src/App.tsx index b360027f3..eba771d81 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,12 +12,16 @@ import Root from "@routes/root"; import { DeviceWidthProvider } from "@contexts/DeviceWidthContext"; import "./index.css"; -// Support +import { + VersionUpdateNotificationProvider +} from "@components/version-language-switcher/VersionUpdateNotificationContext"; +import { SiteWideNotificationProvider } from "@contexts/SiteWideNotificationContext"; + +// (Your full route definitions below this remain exactly the same...) import HomePage from "@routes/home"; // Design Tokens - import DesignTokensOverviewPage from "@routes/design-tokens/overview/Overview"; import BorderRadiusPage from "@routes/design-tokens/border-radius/BorderRadius"; import BorderWidthPage from "@routes/design-tokens/border-width/BorderWidth"; @@ -30,7 +34,6 @@ import SpacingPage from "@routes/design-tokens/spacing/Spacing"; import TypographyPage from "@routes/design-tokens/typography/Typography"; // Get Started - import DevelopersOverviewPage from "@routes/get-started/developers/DevelopersOverview"; import DevelopersSetupPage from "@routes/get-started/developers/DevelopersSetup"; import DevelopersTechnologiesPage from "@routes/get-started/developers/DevelopersTechnologies"; @@ -49,18 +52,16 @@ import UxDesignerPage from "@routes/get-started/designers/UxDesigner"; import { LtsPolicyPage } from "@routes/get-started/LtsPolicyPage.tsx"; // Content Pages - -import ContentLayout from "@routes/content/ContentLayout"; -import CapitalizationPage from "@routes/content/Capitalization"; -import DateFormatPage from "@routes/content/DateFormat"; -import ErrorMessagesPage from "@routes/content/ErrorMessages"; -import HelperTextPage from "@routes/content/HelperText"; +import CapitalizationPage from "@routes/content/Capitalization.tsx"; +import DateFormatPage from "@routes/content/DateFormat.tsx"; +import ErrorMessagesPage from "@routes/content/ErrorMessages.tsx"; +import HelperTextPage from "@routes/content/HelperText.tsx"; import UserExperienceGuidelinesPage from "@routes/get-started/UserExperienceGuidelines"; import { VersionFromUrlProvider } from "@contexts/VersionFromUrlContext.tsx"; import { ComponentsRouter, PatternsRouter } from "./versioned-router"; import ComponentNotFound from "@routes/not-found/NotFound.tsx"; -import { LanguageVersionProvider } from "@contexts/LanguageVersionContext.tsx"; +import { LanguageVersionContext, LanguageVersionProvider } from "@contexts/LanguageVersionContext.tsx"; import DevelopersUpgradePage from "@routes/get-started/developers/upgrade-guide/DevelopersUpgrade.tsx"; // Foundations Pages @@ -79,7 +80,7 @@ const router = createBrowserRouter( createRoutesFromElements( }> } /> - }> + } /> } errorElement={}> } /> } /> @@ -94,9 +95,7 @@ const router = createBrowserRouter( }> } /> - - } /> - + } /> } /> } /> @@ -106,25 +105,17 @@ const router = createBrowserRouter( } /> } /> - - } /> - - - } /> - - - } /> - - - } /> - } /> - } /> - + } /> + } /> + } /> + } /> + } /> + } /> } /> } /> - }> + }> } /> } /> } /> @@ -134,17 +125,17 @@ const router = createBrowserRouter( } /> } /> } /> - - - }> - - } /> - + } /> } /> } /> } /> + } /> + } /> + } /> + } /> + }> ) @@ -155,9 +146,17 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + + {({ version }) => ( + + + + + + )} + -); +); \ No newline at end of file diff --git a/src/components/version-language-switcher/HelpButton.tsx b/src/components/version-language-switcher/HelpButton.tsx new file mode 100644 index 000000000..88d888f26 --- /dev/null +++ b/src/components/version-language-switcher/HelpButton.tsx @@ -0,0 +1,38 @@ +import { useContext } from "react"; +import { useLocation } from "react-router-dom"; +import { GoabIconButton, GoabTooltip } from "@abgov/react-components"; + +import { useVersionUpdateNotification } from "@components/version-language-switcher/VersionUpdateNotificationContext"; +import { useSiteWideNotification } from "@contexts/SiteWideNotificationContext"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext"; + +export function HelpButton() { + const { reset: resetVersion } = useVersionUpdateNotification(); + const { reset: resetSiteWideNotification } = useSiteWideNotification(); + useContext(LanguageVersionContext); + const location = useLocation(); + + const handleHelpClick = () => { + const isComponentOrExamplePage = + location.pathname.startsWith("/components") || location.pathname.startsWith("/examples"); + + if (isComponentOrExamplePage) { + resetVersion(); + } else { + resetSiteWideNotification(); + } + }; + + return ( + + + + ); +} \ No newline at end of file diff --git a/src/components/version-language-switcher/SiteWideNotification.tsx b/src/components/version-language-switcher/SiteWideNotification.tsx new file mode 100644 index 000000000..5609808ec --- /dev/null +++ b/src/components/version-language-switcher/SiteWideNotification.tsx @@ -0,0 +1,23 @@ +import { GoabNotification } from "@abgov/react-components"; +import { useLocation } from "react-router-dom"; +import { useSiteWideNotification } from "@contexts/SiteWideNotificationContext"; +import { MAX_CONTENT_WIDTH } from "../../global-constants.ts"; + +export function SiteWideNotification() { + const { isDismissed, dismiss } = useSiteWideNotification(); + const location = useLocation(); + + const isComponentOrExamplePage = + location.pathname.startsWith("/components") || location.pathname.startsWith("/examples"); + + if (isComponentOrExamplePage || isDismissed) return null; + + return ( + + Select your development framework to see all code in your development languages. You can change this in the top + right of the screen. + + ); +} + +export default SiteWideNotification; \ No newline at end of file diff --git a/src/components/version-language-switcher/VersionLanguageSwitcher.tsx b/src/components/version-language-switcher/VersionLanguageSwitcher.tsx index 843b86f3d..1308e8246 100644 --- a/src/components/version-language-switcher/VersionLanguageSwitcher.tsx +++ b/src/components/version-language-switcher/VersionLanguageSwitcher.tsx @@ -1,6 +1,6 @@ import { GoabIcon, - GoabPopover, + GoabPopover, GoabTooltip } from "@abgov/react-components"; import { ANGULAR_VERSIONS, getVersionedUrlPath, Language, LanguageVersion, @@ -103,6 +103,7 @@ export const VersionLanguageSwitcher = () => { return ( <> + openLanguagePopOver(e)}> @@ -118,7 +119,9 @@ export const VersionLanguageSwitcher = () => { } + + openVersionPopOver(e)}> @@ -133,7 +136,7 @@ export const VersionLanguageSwitcher = () => { ))} - + ); } diff --git a/src/components/version-language-switcher/VersionUpdateNotification.tsx b/src/components/version-language-switcher/VersionUpdateNotification.tsx new file mode 100644 index 000000000..a6156a5a6 --- /dev/null +++ b/src/components/version-language-switcher/VersionUpdateNotification.tsx @@ -0,0 +1,48 @@ +import { useEffect } from "react"; +import { GoabNotification } from "@abgov/react-components"; +import { MAX_CONTENT_WIDTH } from "../../global-constants"; +import { useVersionUpdateNotification } from "./VersionUpdateNotificationContext"; +import type { LanguageVersion } from "@components/version-language-switcher/version-language-constants"; + +interface VersionUpdateNotificationProps { + version: LanguageVersion; +} + +export function VersionUpdateNotification({ version }: VersionUpdateNotificationProps) { + const { isDismissed, dismiss, oldLinkRef, newLinkRef } = useVersionUpdateNotification(); + + useEffect(() => { + const el = document.querySelector("goa-notification"); + if (!el) return; + + const handleDismiss = () => dismiss(); + el.addEventListener("_dismiss", handleDismiss as EventListener); + + return () => el.removeEventListener("_dismiss", handleDismiss as EventListener); + }, [dismiss]); + + if (isDismissed) return null; + + return ( + + {version === "old" ? ( + <> + Support for the Long Term Support (LTS) version of the Design system will be available until September + 2025.{" "} + + View the upgrade guide + + + ) : ( + <> + Upgrading to the latest version of the design system?{" "} + + View the upgrade guide + + + )} + + ); +} + +export default VersionUpdateNotification; \ No newline at end of file diff --git a/src/components/version-language-switcher/VersionUpdateNotificationContext.tsx b/src/components/version-language-switcher/VersionUpdateNotificationContext.tsx new file mode 100644 index 000000000..315a68811 --- /dev/null +++ b/src/components/version-language-switcher/VersionUpdateNotificationContext.tsx @@ -0,0 +1,55 @@ +import { createContext, useContext, useEffect, useRef, useState } from "react"; +import type { LanguageVersion } from "@components/version-language-switcher/version-language-constants"; + +interface VersionUpdateNotificationContextType { + isDismissed: boolean; + dismiss: () => void; + reset: () => void; + oldLinkRef: React.RefObject; + newLinkRef: React.RefObject; +} + +const VersionUpdateNotificationContext = createContext(undefined); + +export const VersionUpdateNotificationProvider = ({ + version, + children + }: { + version: LanguageVersion; + children: React.ReactNode; +}) => { + const storageKey = `versionUpdateNotificationDismissed-${version}`; + const [isDismissed, setIsDismissed] = useState(false); + + const oldLinkRef = useRef(null); + const newLinkRef = useRef(null); + + useEffect(() => { + const storedValue = localStorage.getItem(storageKey); + setIsDismissed(storedValue === "true"); + }, [version]); + + const dismiss = () => { + localStorage.setItem(storageKey, "true"); + setIsDismissed(true); + }; + + const reset = () => { + localStorage.removeItem(storageKey); + setIsDismissed(false); + }; + + return ( + + {children} + + ); +}; + +export const useVersionUpdateNotification = () => { + const context = useContext(VersionUpdateNotificationContext); + if (!context) { + throw new Error("useVersionUpdateNotification must be used within VersionUpdateNotificationProvider"); + } + return context; +}; \ No newline at end of file diff --git a/src/contexts/LanguageVersionContext.tsx b/src/contexts/LanguageVersionContext.tsx index 212edbf4f..6efeaf7fc 100644 --- a/src/contexts/LanguageVersionContext.tsx +++ b/src/contexts/LanguageVersionContext.tsx @@ -7,7 +7,7 @@ import { } from "@components/version-language-switcher/version-language-constants.ts"; import { DEFAULT_LANGUAGE, DEFAULT_VERSION } from "../global-constants"; -interface LanguageVersionContextProps { +export interface LanguageVersionContextProps { language: Language; version: LanguageVersion; setLanguage: (lang: Language) => void; diff --git a/src/contexts/SiteWideNotificationContext.tsx b/src/contexts/SiteWideNotificationContext.tsx new file mode 100644 index 000000000..ff6ac6c7e --- /dev/null +++ b/src/contexts/SiteWideNotificationContext.tsx @@ -0,0 +1,39 @@ +import { createContext, useContext, useState } from "react"; + +interface SiteWideNotificationContextType { + isDismissed: boolean; + dismiss: () => void; + reset: () => void; +} + +const SiteWideNotificationContext = createContext(undefined); + +export const SiteWideNotificationProvider = ({ children }: { children: React.ReactNode }) => { + const [isDismissed, setIsDismissed] = useState(() => { + const stored = localStorage.getItem("siteWideDismissed"); + return stored === "true"; + }); + const dismiss = () => { + localStorage.setItem("siteWideDismissed", "true"); + setIsDismissed(true); + }; + + const reset = () => { + localStorage.removeItem("siteWideDismissed"); + setIsDismissed(false); + }; + + return ( + + {children} + + ); +}; + +export const useSiteWideNotification = () => { + const context = useContext(SiteWideNotificationContext); + if (!context) { + throw new Error("useSiteWideNotification must be used within SiteWideNotificationProvider"); + } + return context; +}; \ No newline at end of file diff --git a/src/routes/components/Components.tsx b/src/routes/components/Components.tsx index 7fa5fcc0f..197fe1405 100644 --- a/src/routes/components/Components.tsx +++ b/src/routes/components/Components.tsx @@ -1,7 +1,6 @@ import { GoabBadge, GoabBlock, - GoabNotification, GoabSideMenu, GoabSideMenuGroup, GoabSpacer @@ -11,7 +10,7 @@ import { SupportInfo } from "@components/support-info/SupportInfo.tsx"; import { useContext } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { getVersionedUrlPath, ANGULAR_VERSIONS, REACT_VERSIONS } from "@components/version-language-switcher/version-language-constants.ts"; -import { MAX_CONTENT_WIDTH } from "../../global-constants.ts"; + export function Components() { const { language, version } = useContext(LanguageVersionContext); @@ -39,18 +38,6 @@ export function Components() { return ( <> - {version === "old" && ( - - Support for the Long Term Support (LTS) version of the Design system will be available until September - 2025. View the upgrade guide - - )} - {version === "new" && ( - - Upgrading to the latest version of the design system?{" "} - View the upgrade guide - - )}
diff --git a/src/routes/root.tsx b/src/routes/root.tsx index 7102b8d85..fdc7fc4b0 100644 --- a/src/routes/root.tsx +++ b/src/routes/root.tsx @@ -3,20 +3,28 @@ import { GoabAppFooterMetaSection, GoabAppFooterNavSection, GoabAppHeader, - GoabMicrositeHeader, GoabNotification, + GoabMicrositeHeader, GoabOneColumnLayout } from "@abgov/react-components"; import { useEffect, useState } from "react"; -import { Link, Outlet } from "react-router-dom"; -import Cookies from "js-cookie"; +import { Link, Outlet, useLocation } from "react-router-dom"; import "./root.css"; -import { useLocation } from "react-router-dom"; import { MAX_CONTENT_WIDTH, } from "../global-constants.ts"; -import { VersionLanguageSwitcher } from "@components/version-language-switcher/VersionLanguageSwitcher.tsx"; + + +import VersionUpdateNotification from "@components/version-language-switcher/VersionUpdateNotification"; +import { HelpButton } from "@components/version-language-switcher/HelpButton"; +import { + VersionLanguageSwitcher +} from "@components/version-language-switcher/VersionLanguageSwitcher"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext"; +import { useContext } from "react"; +import SiteWideNotification from "@components/version-language-switcher/SiteWideNotification"; + function ScrollToTop() { const { pathname } = useLocation(); @@ -29,7 +37,10 @@ function ScrollToTop() { } export default function Root() { - const isFirstVisit = Cookies.get("hasVisited"); + const { version } = useContext(LanguageVersionContext); + const location = useLocation(); + const showNotification = + location.pathname.startsWith("/components") || location.pathname.startsWith("/examples"); const [visible, setVisibility] = useState(false); @@ -39,12 +50,6 @@ export default function Root() { }, 50); }); - useEffect(() => { - setTimeout(() => { - Cookies.set("hasVisited", "true", {expires: 3650}); // increase the time everytime ppl land on so it won't expire - }, 600); // update later - }, []); - return (
@@ -54,9 +59,14 @@ export default function Root() { type={"live"} feedbackUrl="https://forms.microsoft.com/r/8Zp7zSJS6W" maxContentWidth={MAX_CONTENT_WIDTH} - version={} - /> - + version={ + <> + + + + } /> + Get started Foundations Patterns @@ -65,13 +75,10 @@ export default function Root() { Content Get support -
- - {isFirstVisit == null && - Select your development framework to see all code in your development languages. You can change this in the top right of the screen. - } - + {showNotification && } + +
From e9e1ef7d440beed16ba512521422c86a9c2bbaf9 Mon Sep 17 00:00:00 2001 From: Thomas Jeffery Date: Mon, 2 Jun 2025 12:12:43 -0600 Subject: [PATCH 2/4] chore: content update --- .../version-language-switcher/SiteWideNotification.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/version-language-switcher/SiteWideNotification.tsx b/src/components/version-language-switcher/SiteWideNotification.tsx index 5609808ec..e97172d45 100644 --- a/src/components/version-language-switcher/SiteWideNotification.tsx +++ b/src/components/version-language-switcher/SiteWideNotification.tsx @@ -14,8 +14,8 @@ export function SiteWideNotification() { return ( - Select your development framework to see all code in your development languages. You can change this in the top - right of the screen. + Select your development framework to see all code in your development language. Change framework and version at + the top right of the screen. ); } From 0e84cbfcdd080f7085e0a20c253c59346c75432a Mon Sep 17 00:00:00 2001 From: Thomas Jeffery Date: Mon, 9 Jun 2025 09:56:27 -0600 Subject: [PATCH 3/4] update from feedback --- src/App.tsx | 13 +++++++------ .../version-language-switcher/HelpButton.tsx | 2 +- .../VersionLanguageSwitcher.tsx | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index eba771d81..e39a53993 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,8 +17,6 @@ import { } from "@components/version-language-switcher/VersionUpdateNotificationContext"; import { SiteWideNotificationProvider } from "@contexts/SiteWideNotificationContext"; -// (Your full route definitions below this remain exactly the same...) - import HomePage from "@routes/home"; // Design Tokens @@ -52,6 +50,7 @@ import UxDesignerPage from "@routes/get-started/designers/UxDesigner"; import { LtsPolicyPage } from "@routes/get-started/LtsPolicyPage.tsx"; // Content Pages +import ContentLayout from "@routes/content/ContentLayout"; import CapitalizationPage from "@routes/content/Capitalization.tsx"; import DateFormatPage from "@routes/content/DateFormat.tsx"; import ErrorMessagesPage from "@routes/content/ErrorMessages.tsx"; @@ -131,10 +130,12 @@ const router = createBrowserRouter( } /> - } /> - } /> - } /> - } /> + }> + } /> + } /> + } /> + } /> + }> diff --git a/src/components/version-language-switcher/HelpButton.tsx b/src/components/version-language-switcher/HelpButton.tsx index 88d888f26..60eb0424a 100644 --- a/src/components/version-language-switcher/HelpButton.tsx +++ b/src/components/version-language-switcher/HelpButton.tsx @@ -24,7 +24,7 @@ export function HelpButton() { }; return ( - + { return ( <> - + openLanguagePopOver(e)}> From 2d2c3949a732f5292a874011e931b0ae73a8720d Mon Sep 17 00:00:00 2001 From: Thomas Jeffery Date: Mon, 9 Jun 2025 10:36:29 -0600 Subject: [PATCH 4/4] update from feedback --- src/routes/content/ErrorMessages.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/content/ErrorMessages.tsx b/src/routes/content/ErrorMessages.tsx index 4d3ddd312..3a46c7525 100644 --- a/src/routes/content/ErrorMessages.tsx +++ b/src/routes/content/ErrorMessages.tsx @@ -46,8 +46,7 @@ export default function ErrorMessagesPage() {