From 272203388451971c12bc4778c5364f46487788c4 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 11 Dec 2024 17:33:27 +0000 Subject: [PATCH] replace show hide buttons with islands. remove old show hide page level island --- dotcom-rendering/src/components/FrontPage.tsx | 8 -- .../src/components/FrontSection.tsx | 9 +- .../src/components/Island.test.tsx | 10 +-- dotcom-rendering/src/components/Section.tsx | 10 ++- .../components/ShowHideButton.importable.tsx | 88 +++++++++++++++++++ .../src/components/ShowHideButton.stories.tsx | 14 +++ .../src/components/ShowHideButton.tsx | 42 --------- .../ShowHideContainers.importable.tsx | 88 ------------------- 8 files changed, 120 insertions(+), 149 deletions(-) create mode 100644 dotcom-rendering/src/components/ShowHideButton.importable.tsx create mode 100644 dotcom-rendering/src/components/ShowHideButton.stories.tsx delete mode 100644 dotcom-rendering/src/components/ShowHideButton.tsx delete mode 100644 dotcom-rendering/src/components/ShowHideContainers.importable.tsx diff --git a/dotcom-rendering/src/components/FrontPage.tsx b/dotcom-rendering/src/components/FrontPage.tsx index 56a573685fa..6aee8c43824 100644 --- a/dotcom-rendering/src/components/FrontPage.tsx +++ b/dotcom-rendering/src/components/FrontPage.tsx @@ -17,7 +17,6 @@ import { Metrics } from './Metrics.importable'; import { ReaderRevenueDev } from './ReaderRevenueDev.importable'; import { SetABTests } from './SetABTests.importable'; import { SetAdTargeting } from './SetAdTargeting.importable'; -import { ShowHideContainers } from './ShowHideContainers.importable'; import { SkipTo } from './SkipTo'; type Props = { @@ -71,13 +70,6 @@ export const FrontPage = ({ front, NAV }: Props) => { tests={front.config.abTests} /> - - - - + + + )} diff --git a/dotcom-rendering/src/components/Island.test.tsx b/dotcom-rendering/src/components/Island.test.tsx index 074c1dc4054..c01bf118336 100644 --- a/dotcom-rendering/src/components/Island.test.tsx +++ b/dotcom-rendering/src/components/Island.test.tsx @@ -31,7 +31,7 @@ import { SendTargetingParams } from './SendTargetingParams.importable'; import { SetABTests } from './SetABTests.importable'; import { SetAdTargeting } from './SetAdTargeting.importable'; import { ShareButton } from './ShareButton.importable'; -import { ShowHideContainers } from './ShowHideContainers.importable'; +import { ShowHideButton } from './ShowHideButton.importable'; import { SignInGateSelector } from './SignInGateSelector.importable'; import { SlotBodyEnd } from './SlotBodyEnd.importable'; import { StickyBottomBanner } from './StickyBottomBanner.importable'; @@ -364,13 +364,9 @@ describe('Island: server-side rendering', () => { ).not.toThrow(); }); - test('ShowHideContainers', () => { + test('ShowHideButton', () => { expect(() => - renderToString( - , - ), + renderToString(), ).not.toThrow(); }); diff --git a/dotcom-rendering/src/components/Section.tsx b/dotcom-rendering/src/components/Section.tsx index d9ca350dfdc..fac51a2ab25 100644 --- a/dotcom-rendering/src/components/Section.tsx +++ b/dotcom-rendering/src/components/Section.tsx @@ -10,8 +10,9 @@ import { ContainerTitle } from './ContainerTitle'; import { ElementContainer } from './ElementContainer'; import { Flex } from './Flex'; import { Hide } from './Hide'; +import { Island } from './Island'; import { LeftColumn } from './LeftColumn'; -import { ShowHideButton } from './ShowHideButton'; +import { ShowHideButton } from './ShowHideButton.importable'; import { Treats } from './Treats'; /** @@ -364,7 +365,12 @@ export const Section = ({ /> {toggleable && !!sectionId && ( - + + + )} {toggleable && sectionId ? ( diff --git a/dotcom-rendering/src/components/ShowHideButton.importable.tsx b/dotcom-rendering/src/components/ShowHideButton.importable.tsx new file mode 100644 index 00000000000..3bada8ef999 --- /dev/null +++ b/dotcom-rendering/src/components/ShowHideButton.importable.tsx @@ -0,0 +1,88 @@ +import { css } from '@emotion/react'; +import { isObject, isString, storage } from '@guardian/libs'; +import { space, textSans14 } from '@guardian/source/foundations'; +import { Button } from '@guardian/source/react-components'; +import { useEffect, useState } from 'react'; +import { useIsSignedIn } from '../lib/useAuthStatus'; +import { palette } from '../palette'; + +type Props = { + sectionId: string; +}; + +const showHideButtonCss = css` + button { + ${textSans14}; + /* margin-top: ${space[2]}px; */ + margin-right: 10px; + margin-bottom: ${space[2]}px; + position: relative; + align-items: bottom; + text-decoration: none; + } +`; + +type ContainerStates = { [id: string]: string }; + +const isContainerStates = (item: unknown): item is ContainerStates => { + if (!isObject(item)) return false; + if (!Object.keys(item).every(isString)) return false; + if (!Object.values(item).every(isString)) return false; + return true; +}; + +const getContainerStates = (): ContainerStates => { + const item = storage.local.get(`gu.prefs.container-states`); + if (!isContainerStates(item)) return {}; + return item; +}; + +/** + * Component to toggle the visibility of a front container. Used within FrontSection. + **/ +export const ShowHideButton = ({ sectionId }: Props) => { + const [isExpanded, setIsExpanded] = useState(true); + const [containerStates, setContainerStates] = useState({}); + const textShowHide = isExpanded ? 'Hide' : 'Show'; + const isSignedIn = useIsSignedIn(); + + const toggleContainer = () => { + const section: Element | null = + window.document.getElementById(sectionId); + + if (isExpanded) { + containerStates[sectionId] = 'closed'; + section?.classList.add('hidden'); + } else { + containerStates[sectionId] = 'opened'; + section?.classList.remove('hidden'); + } + + storage.local.set(`gu.prefs.container-states`, containerStates); + setIsExpanded(!isExpanded); + }; + + useEffect(() => { + setContainerStates(getContainerStates()); + }, []); + + return ( + isSignedIn === true && ( +
+ +
+ ) + ); +}; diff --git a/dotcom-rendering/src/components/ShowHideButton.stories.tsx b/dotcom-rendering/src/components/ShowHideButton.stories.tsx new file mode 100644 index 00000000000..6dff1d1ebc9 --- /dev/null +++ b/dotcom-rendering/src/components/ShowHideButton.stories.tsx @@ -0,0 +1,14 @@ +import type { Meta } from '@storybook/react'; +import { ShowHideButton } from './ShowHideButton.importable'; + +const meta = { + title: 'Components/ShowHideButton', + component: ShowHideButton, + args: { + sectionId: 'sectionId', + }, +} satisfies Meta; + +export default meta; + +export const Default = {}; diff --git a/dotcom-rendering/src/components/ShowHideButton.tsx b/dotcom-rendering/src/components/ShowHideButton.tsx deleted file mode 100644 index 663cb71de40..00000000000 --- a/dotcom-rendering/src/components/ShowHideButton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { css } from '@emotion/react'; -import { space, textSans14 } from '@guardian/source/foundations'; -import { ButtonLink } from '@guardian/source/react-components'; -import { palette } from '../palette'; - -type Props = { - sectionId: string; -}; - -const showHideButtonCss = css` - ${textSans14}; - - margin-top: ${space[2]}px; - margin-right: 10px; - margin-bottom: ${space[2]}px; - position: relative; - align-items: bottom; - text-decoration: none; -`; - -/** - * This component creates the styled button for showing & hiding a container, - * The functionality for this is implemented in a single island 'ShownHideContainers.importable' - **/ -export const ShowHideButton = ({ sectionId }: Props) => { - return ( - - Hide - - ); -}; diff --git a/dotcom-rendering/src/components/ShowHideContainers.importable.tsx b/dotcom-rendering/src/components/ShowHideContainers.importable.tsx deleted file mode 100644 index 7ae4c3ca8b2..00000000000 --- a/dotcom-rendering/src/components/ShowHideContainers.importable.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { isObject, isString, storage } from '@guardian/libs'; -import { useEffect } from 'react'; - -type ContainerStates = { [id: string]: string }; - -const isContainerStates = (item: unknown): item is ContainerStates => { - if (!isObject(item)) return false; - if (!Object.keys(item).every(isString)) return false; - if (!Object.values(item).every(isString)) return false; - return true; -}; - -const getContainerStates = (): ContainerStates => { - const item = storage.local.get(`gu.prefs.container-states`); - - if (!isContainerStates(item)) return {}; - - return item; -}; - -type Props = { - /** When in the ON position, we remove the show/hide functionality for all - * containers on the page if the user does not have any hidden containers */ - disableFrontContainerToggleSwitch: boolean; -}; - -export const ShowHideContainers = ({ - disableFrontContainerToggleSwitch, -}: Props) => { - useEffect(() => { - const containerStates = getContainerStates(); - - const toggleContainer = (sectionId: string, element: HTMLElement) => { - const isExpanded = element.getAttribute('aria-expanded') === 'true'; - - const section: Element | null = - window.document.getElementById(sectionId); - - if (isExpanded) { - containerStates[sectionId] = 'closed'; - section?.classList.add('hidden'); - element.innerHTML = 'Show'; - element.setAttribute('aria-expanded', 'false'); - element.setAttribute('data-link-name', 'Show'); - } else { - containerStates[sectionId] = 'opened'; - section?.classList.remove('hidden'); - element.innerHTML = 'Hide'; - element.setAttribute('aria-expanded', 'true'); - element.setAttribute('data-link-name', 'Hide'); - } - - storage.local.set(`gu.prefs.container-states`, containerStates); - }; - - const allShowHideButtons = Array.from( - window.document.querySelectorAll( - '[data-show-hide-button]', - ), - ); - - const allContainersAreExpanded = allShowHideButtons - .map((el) => { - const sectionId = el.getAttribute('data-show-hide-button'); - return sectionId && containerStates[sectionId]; - }) - .every((state) => state !== 'closed'); - - for (const e of allShowHideButtons) { - // We want to remove the ability to toggle front containers between expanded and collapsed states. - // The first part of doing this is removing the feature for those who do not currently use it. - if (disableFrontContainerToggleSwitch && allContainersAreExpanded) { - e.remove(); - } - - const sectionId = e.getAttribute('data-show-hide-button'); - if (!sectionId) continue; - - e.onclick = () => toggleContainer(sectionId, e); - - if (containerStates[sectionId] === 'closed') { - toggleContainer(sectionId, e); - } - } - }, [disableFrontContainerToggleSwitch]); - - return <>; -};