From 344d00c9422e2e36c77702022c2855e19c5f0b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLAKSHMIRPILLAI=E2=80=9D?= <“luxmi.r.pillai@gmail.com”> Date: Thu, 19 Dec 2024 12:56:58 +0000 Subject: [PATCH 01/19] Change the highlight text color and CTA hoover color --- .../src/components/UsEoy2024Wrapper.importable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx index d96f9653f8..9e10212c6c 100644 --- a/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx +++ b/dotcom-rendering/src/components/UsEoy2024Wrapper.importable.tsx @@ -185,7 +185,7 @@ const stylesSubCampaign = { color: ${'#1A2835'}; `, highlight: css` - background-color: ${'#670055'}; + background-color: ${'#016D67'}; color: ${'#F6F6F6'}; padding-left: 2px; `, @@ -413,7 +413,7 @@ export const UsEoy2024: ReactComponent = ({ }, hover: { backgroundColour: isSubCampaign - ? '#891414' + ? '#01544F' : '#C41C1C', textColour: '#FFFFFF', }, From f43b035adaced75a744067e0cbba8b1277b47499 Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 19 Dec 2024 14:48:12 +0000 Subject: [PATCH 02/19] Add 1:1 padding option for card picture (#13039) --- dotcom-rendering/src/components/CardPicture.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotcom-rendering/src/components/CardPicture.tsx b/dotcom-rendering/src/components/CardPicture.tsx index 772d9ec599..9b251c2dc9 100644 --- a/dotcom-rendering/src/components/CardPicture.tsx +++ b/dotcom-rendering/src/components/CardPicture.tsx @@ -104,6 +104,8 @@ const getAspectRatioPadding = (aspectRatio?: AspectRatio): string => { return '80%'; case '4:5': return '125%'; + case '1:1': + return '100%'; case '5:3': default: return '60%'; From e603a00c046629a3533deda9013effa48aee111c Mon Sep 17 00:00:00 2001 From: Anna Beddow Date: Thu, 19 Dec 2024 14:48:42 +0000 Subject: [PATCH 03/19] Media card alignment (#13040) * Always position image on the top if its a media card * Always inner headline if its a media card * remove src from path * fix linting --- dotcom-rendering/src/components/Card/Card.tsx | 18 ++++++----- .../src/components/FlexibleGeneral.tsx | 21 ++++++++----- .../src/components/FlexibleSpecial.tsx | 31 +++++++++++++------ .../ScrollableMedium.importable.tsx | 8 +++-- .../src/components/StaticMediumFour.tsx | 5 ++- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx index 568562a1b4..7b0679ac80 100644 --- a/dotcom-rendering/src/components/Card/Card.tsx +++ b/dotcom-rendering/src/components/Card/Card.tsx @@ -7,7 +7,7 @@ import { } from '@guardian/source/foundations'; import { Hide, Link } from '@guardian/source/react-components'; import { ArticleDesign, type ArticleFormat } from '../../lib/articleFormat'; -import { isMediaCard } from '../../lib/cardHelpers'; +import { isMediaCard as isAMediaCard } from '../../lib/cardHelpers'; import { getZIndex } from '../../lib/getZIndex'; import { DISCUSSION_ID_DATA_ATTRIBUTE } from '../../lib/useCommentCount'; import { palette } from '../../palette'; @@ -260,11 +260,14 @@ const getHeadlinePosition = ({ isFlexSplash, containerType, showLivePlayable, + isMediaCard, }: { containerType?: DCRContainerType; isFlexSplash?: boolean; showLivePlayable: boolean; + isMediaCard: boolean; }) => { + if (isMediaCard) return 'inner'; if (containerType === 'flexible/special' && isFlexSplash) { return 'outer'; } @@ -450,9 +453,9 @@ export const Card = ({ - * Media cards have contrasting background colours. We add additional * padding to these cards to keep the text readable. - */ - const hasBackgroundColour = isMediaCard(format); + const isMediaCard = isAMediaCard(format); - const backgroundColour = hasBackgroundColour + const backgroundColour = isMediaCard ? palette('--card-media-background') : palette('--card-background'); @@ -479,6 +482,7 @@ export const Card = ({ containerType, isFlexSplash, showLivePlayable, + isMediaCard, }); const hideTrailTextUntil = () => { @@ -498,7 +502,7 @@ export const Card = ({ /** Determines the gap of between card components based on card properties */ const getGapSize = (): GapSize => { if (isOnwardContent) return 'none'; - if (hasBackgroundColour && !isFlexibleContainer) return 'tiny'; + if (isMediaCard && !isFlexibleContainer) return 'tiny'; if (!!isFlexSplash || (isFlexibleContainer && imageSize === 'jumbo')) { return 'small'; } @@ -812,7 +816,7 @@ export const Card = ({ imageType={media?.type} imageSize={imageSize} imagePositionOnDesktop={imagePositionOnDesktop} - hasBackgroundColour={hasBackgroundColour} + hasBackgroundColour={isMediaCard} isOnwardContent={isOnwardContent} isFlexibleContainer={isFlexibleContainer} > @@ -943,9 +947,7 @@ export const Card = ({
{showLivePlayable && liveUpdatesPosition === 'outer' && ( diff --git a/dotcom-rendering/src/components/FlexibleGeneral.tsx b/dotcom-rendering/src/components/FlexibleGeneral.tsx index d296ff707c..c13a3a5cd2 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import { palette } from '../palette'; import type { BoostLevel } from '../types/content'; import type { @@ -86,6 +87,7 @@ type BoostedSplashProperties = { const decideSplashCardProperties = ( boostLevel: BoostLevel, supportingContentLength: number, + mediaCard: boolean, ): BoostedSplashProperties => { switch (boostLevel) { // boostedfont sizing @@ -98,7 +100,7 @@ const decideSplashCardProperties = ( mobile: 'medium', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'large', supportingContentAlignment: supportingContentLength >= 4 ? 'horizontal' : 'vertical', @@ -113,7 +115,7 @@ const decideSplashCardProperties = ( mobile: 'large', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: supportingContentLength >= 4 ? 'horizontal' : 'vertical', @@ -127,8 +129,8 @@ const decideSplashCardProperties = ( tablet: 'xlarge', mobile: 'xlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -141,8 +143,8 @@ const decideSplashCardProperties = ( tablet: 'xlarge', mobile: 'xxlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -182,6 +184,7 @@ export const SplashCardLayout = ({ } = decideSplashCardProperties( card.boostLevel ?? 'default', card.supportingContent?.length ?? 0, + isMediaCard(card.format), ); return ( @@ -312,8 +315,10 @@ export const BoostedCardLayout = ({ showAge={showAge} absoluteServerTimes={absoluteServerTimes} headlineSizes={headlineSizes} - imagePositionOnDesktop={'right'} - imagePositionOnMobile={'bottom'} + imagePositionOnDesktop="right" + imagePositionOnMobile={ + isMediaCard(card.format) ? 'top' : 'bottom' + } imageSize={imageSize} trailText={card.trailText} supportingContent={card.supportingContent} diff --git a/dotcom-rendering/src/components/FlexibleSpecial.tsx b/dotcom-rendering/src/components/FlexibleSpecial.tsx index ad29cdc47d..9b09bb367a 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import type { BoostLevel } from '../types/content'; import type { AspectRatio, @@ -42,6 +43,7 @@ type BoostProperties = { const determineCardProperties = ( boostLevel: BoostLevel, supportingContentLength: number, + mediaCard: boolean, ): BoostProperties => { switch (boostLevel) { // The default boost level is equal to no boost. It is the same as the default card layout. @@ -53,7 +55,7 @@ const determineCardProperties = ( mobile: 'medium', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'large', supportingContentAlignment: supportingContentLength >= 3 ? 'horizontal' : 'vertical', @@ -69,7 +71,7 @@ const determineCardProperties = ( mobile: 'large', }, imagePositionOnDesktop: 'right', - imagePositionOnMobile: 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: supportingContentLength >= 3 ? 'horizontal' : 'vertical', @@ -83,8 +85,8 @@ const determineCardProperties = ( tablet: 'xlarge', mobile: 'xlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -97,8 +99,8 @@ const determineCardProperties = ( tablet: 'xxlarge', mobile: 'xxlarge', }, - imagePositionOnDesktop: 'bottom', - imagePositionOnMobile: 'bottom', + imagePositionOnDesktop: mediaCard ? 'top' : 'bottom', + imagePositionOnMobile: mediaCard ? 'top' : 'bottom', imageSize: 'jumbo', supportingContentAlignment: 'horizontal', liveUpdatesAlignment: 'horizontal', @@ -137,6 +139,7 @@ export const OneCardLayout = ({ } = determineCardProperties( card.boostLevel ?? 'default', card.supportingContent?.length ?? 0, + isMediaCard(card.format), ); return (
    @@ -170,6 +173,15 @@ export const OneCardLayout = ({ ); }; +const getImagePosition = ( + hasTwoOrFewerCards: boolean, + isAMediaCard: boolean, +) => { + if (isAMediaCard && !hasTwoOrFewerCards) return 'top'; + if (hasTwoOrFewerCards) return 'left'; + return 'bottom'; +}; + const TwoCardOrFourCardLayout = ({ cards, containerPalette, @@ -208,9 +220,10 @@ const TwoCardOrFourCardLayout = ({ absoluteServerTimes={absoluteServerTimes} image={showImage ? card.image : undefined} imageLoading={imageLoading} - imagePositionOnDesktop={ - hasTwoOrFewerCards ? 'left' : 'bottom' - } + imagePositionOnDesktop={getImagePosition( + hasTwoOrFewerCards, + isMediaCard(card.format), + )} /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} imageSize={'medium'} diff --git a/dotcom-rendering/src/components/ScrollableMedium.importable.tsx b/dotcom-rendering/src/components/ScrollableMedium.importable.tsx index c1797103f4..7045ef4a38 100644 --- a/dotcom-rendering/src/components/ScrollableMedium.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableMedium.importable.tsx @@ -1,3 +1,4 @@ +import { isMediaCard } from '../lib/cardHelpers'; import type { AspectRatio, DCRContainerPalette, @@ -40,6 +41,9 @@ export const ScrollableMedium = ({ visibleCardsOnTablet={4} > {trails.map((trail) => { + const imagePosition = isMediaCard(trail.format) + ? 'top' + : 'bottom'; return ( Date: Thu, 19 Dec 2024 22:03:24 +0000 Subject: [PATCH 04/19] december updates to all newsletters page --- dotcom-rendering/src/model/newsletter-grouping.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/src/model/newsletter-grouping.ts b/dotcom-rendering/src/model/newsletter-grouping.ts index 3c379e1d55..e8901b167b 100644 --- a/dotcom-rendering/src/model/newsletter-grouping.ts +++ b/dotcom-rendering/src/model/newsletter-grouping.ts @@ -13,7 +13,7 @@ export const groups: Partial> = { newsletters: [ 'the-long-wave', 'morning-briefing', // First Edition - 'fighting-back', + 'the-overwhelm', 'saturday-edition', 'word-of-mouth', // Feast 'the-guide-staying-in', @@ -39,9 +39,9 @@ export const groups: Partial> = { 'sleeve-notes', 'whats-on', 'bookmarks', - 'hear-here', 'art-weekly', 'design-review', + 'documentaries', ], }, { @@ -63,11 +63,11 @@ export const groups: Partial> = { newsletters: [ 'this-is-europe', 'the-stakes-us-election-edition', // The Stakes — Presidential Transition + 'fighting-back', 'follow-margaret-sullivan', 'follow-robert-reich', 'follow-mehdi-hasan', 'global-dispatch', - 'documentaries', 'her-stage', 'trump-on-trial', ], @@ -115,6 +115,7 @@ export const groups: Partial> = { title: 'Get started', newsletters: [ 'fighting-back', + 'the-overwhelm', 'the-long-wave', 'the-stakes-us-election-edition', 'us-morning-newsletter', // First Thing @@ -214,7 +215,7 @@ export const groups: Partial> = { title: 'Get started', newsletters: [ 'breaking-news-australia', - 'the-long-wave', + 'the-overwhelm', 'morning-mail', 'afternoon-update', 'the-crunch', @@ -266,6 +267,7 @@ export const groups: Partial> = { { title: 'The world explained', newsletters: [ + 'the-long-wave', 'trump-on-trial', 'global-dispatch', 'patriarchy', From c46b8974b44326f3b8ee968cb1ccd91e0e7c996c Mon Sep 17 00:00:00 2001 From: Daniel Clifton <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:24:23 +0000 Subject: [PATCH 05/19] Fix asset url path for development (#13038) * fix/asset-url-path --- dotcom-rendering/src/lib/assets.test.ts | 7 ++++--- dotcom-rendering/src/lib/assets.ts | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dotcom-rendering/src/lib/assets.test.ts b/dotcom-rendering/src/lib/assets.test.ts index da813843b9..53f7afde8c 100644 --- a/dotcom-rendering/src/lib/assets.test.ts +++ b/dotcom-rendering/src/lib/assets.test.ts @@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'; import { BUILD_VARIANT } from '../../webpack/bundles'; import { APPS_SCRIPT, + BASE_URL_DEV, decideAssetOrigin, getModulesBuild, getPathFromManifest, @@ -27,9 +28,9 @@ describe('decideAssetOrigin for stage', () => { ); }); it('DEV', () => { - expect(decideAssetOrigin('DEV')).toEqual('/'); - expect(decideAssetOrigin('dev')).toEqual('/'); - expect(decideAssetOrigin(undefined)).toEqual('/'); + expect(decideAssetOrigin('DEV', true)).toEqual(BASE_URL_DEV); + expect(decideAssetOrigin('dev', true)).toEqual(BASE_URL_DEV); + expect(decideAssetOrigin(undefined, false)).toEqual('/'); }); }); diff --git a/dotcom-rendering/src/lib/assets.ts b/dotcom-rendering/src/lib/assets.ts index 43c92234c6..3f585f0916 100644 --- a/dotcom-rendering/src/lib/assets.ts +++ b/dotcom-rendering/src/lib/assets.ts @@ -9,18 +9,28 @@ interface AssetHash { [key: string]: string; } +export const BASE_URL_DEV = 'http://localhost:3030/'; + export type AssetOrigin = | 'https://assets.guim.co.uk/' | 'https://assets-code.guim.co.uk/' + | typeof BASE_URL_DEV | '/'; /** * Decides the url to use for fetching assets * * @param {'PROD' | 'CODE' | undefined} stage the environment code is executing in - * @returns {string} + * @param {boolean} isDev whether the environment is development + * @returns {AssetOrigin} */ -export const decideAssetOrigin = (stage: string | undefined): AssetOrigin => { +export const decideAssetOrigin = ( + stage: string | undefined, + isDev?: boolean, +): AssetOrigin => { + if (isDev) { + return BASE_URL_DEV; + } switch (stage?.toUpperCase()) { case 'PROD': return 'https://assets.guim.co.uk/'; @@ -33,7 +43,7 @@ export const decideAssetOrigin = (stage: string | undefined): AssetOrigin => { const isDev = process.env.NODE_ENV === 'development'; -export const ASSET_ORIGIN = decideAssetOrigin(process.env.GU_STAGE); +export const ASSET_ORIGIN = decideAssetOrigin(process.env.GU_STAGE, isDev); const isAssetHash = (manifest: unknown): manifest is AssetHash => isObject(manifest) && From f713c1bf33a0fe09e8ee5d651f4f18e40f806181 Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Mon, 23 Dec 2024 11:48:55 +0000 Subject: [PATCH 06/19] Convert DocumentBlockComponent to CSF v3 --- .../DocumentBlockComponent.stories.tsx | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx b/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx index 553c62258b..5470010aaa 100644 --- a/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx @@ -1,45 +1,47 @@ import { css } from '@emotion/react'; +import type { Meta, StoryObj } from '@storybook/react'; import { DocumentBlockComponent } from './DocumentBlockComponent.importable'; -export default { +const meta = { component: DocumentBlockComponent, - title: 'Components/DocumentBlockComponent', -}; + title: 'Components/Document Block Component', + decorators: [ + (Story) => ( +
    + +
    + ), + ], +} satisfies Meta; + +export default meta; -const Wrapper = ({ children }: { children: React.ReactNode }) => ( -
    - {children} -
    -); +type Story = StoryObj; + +export const ScribdDocument: Story = { + args: { + embedUrl: 'https://www.scribd.com/embeds/431975393/content', + height: 613, + width: 460, + title: '', + isTracking: false, + isMainMedia: false, + }, +}; -export const documentEmbed = () => { - return ( - -

    Scribd Document

    - -

    DocumentCloud Document

    - -
    - ); +export const DocumentCloudDocument: Story = { + args: { + embedUrl: 'https://embed.documentcloud.org/documents/20417938-test-pdf', + height: 700, + width: 990, + title: 'TEST PDF (Hosted by DocumentCloud)', + source: 'DocumentCloud', + isTracking: false, + isMainMedia: false, + }, }; -documentEmbed.storyName = 'document embed'; From f24feb49a1a13daa60e4c60ec270710d94dda8fe Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Mon, 23 Dec 2024 12:24:10 +0000 Subject: [PATCH 07/19] Convert Comments to CSF v3 --- .../Discussion/Comments.stories.tsx | 566 ++++++------------ 1 file changed, 181 insertions(+), 385 deletions(-) diff --git a/dotcom-rendering/src/components/Discussion/Comments.stories.tsx b/dotcom-rendering/src/components/Discussion/Comments.stories.tsx index 00c46b6ec2..84a5b2b0b0 100644 --- a/dotcom-rendering/src/components/Discussion/Comments.stories.tsx +++ b/dotcom-rendering/src/components/Discussion/Comments.stories.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; +import type { Meta, StoryObj } from '@storybook/react'; import { parse } from 'valibot'; import { splitTheme } from '../../../.storybook/decorators/splitThemeDecorator'; -import { lightDecorator } from '../../../.storybook/decorators/themeDecorator'; import { discussion } from '../../../fixtures/manual/discussion'; import { discussionWithTwoComments as discussionWithTwoCommentsMock } from '../../../fixtures/manual/discussionWithTwoComments'; import { legacyDiscussionWithoutThreading } from '../../../fixtures/manual/legacyDiscussionWithoutThreading'; @@ -15,7 +15,26 @@ import { discussionApiResponseSchema } from '../../lib/discussion'; import { ok } from '../../lib/result'; import { Comments } from './Comments'; -export default { component: Comments, title: 'Discussion/App' }; +const meta = { + component: Comments, + title: 'Discussion/App', + decorators: [ + (Story) => ( +
    + +
    + ), + ], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; const discussionMock = parse(discussionApiResponseSchema, discussion); if (discussionMock.status !== 'ok') throw new Error('Invalid mock'); @@ -71,387 +90,164 @@ const defaultCommentForm = { previewBody: '', } satisfies CommentFormProps; -export const LoggedOutHiddenPicks = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); -LoggedOutHiddenPicks.storyName = 'when logged out, unexpanded and with picks'; -LoggedOutHiddenPicks.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Culture, - }, - ]), -]; - -export const InitialPage = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={1} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); -InitialPage.storyName = 'with initial page set to 1'; -InitialPage.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Lifestyle, - }, - ]), -]; - -export const LoggedInHiddenNoPicks = () => { - return ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={{ ...defaultCommentForm }} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    - ); -}; -LoggedInHiddenNoPicks.storyName = - 'when logged in, with no picks and not expanded'; -LoggedInHiddenNoPicks.decorators = [splitTheme([format])]; - -export const LoggedIn = () => { - return ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    - ); -}; -LoggedIn.storyName = 'when logged in and expanded'; -LoggedIn.decorators = [lightDecorator([format])]; - -export const LoggedInShortDiscussion = () => { - return ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionWithTwoComments.discussion.topLevelCommentCount - } - loading={false} - comments={discussionWithTwoComments.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    - ); -}; -LoggedInShortDiscussion.decorators = [splitTheme([format])]; - -export const LoggedOutHiddenNoPicks = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); - -LoggedOutHiddenNoPicks.storyName = - 'when logged out, with no picks and not expanded'; -LoggedOutHiddenNoPicks.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Sport, - }, - ]), -]; - -export const Closed = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={ - discussionMock.discussion.topLevelCommentCount - } - loading={false} - comments={discussionMock.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); -Closed.storyName = 'Logged in but closed for comments'; -Closed.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Lifestyle, - }, - ]), -]; - -export const NoComments = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={3} - filters={filters} - topLevelCommentCount={0} - loading={false} - comments={[]} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); -NoComments.storyName = 'when no comments have been made'; -NoComments.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Culture, +export const LoggedOutHiddenPicks = { + name: 'When logged out, unexpanded and with picks', + args: { + shortUrl: discussionMock.discussion.key, + baseUrl: 'https://discussion.theguardian.com/discussion-api', + isClosedForComments: false, + isClosedForRecommendations: false, + additionalHeaders: { + 'D2-X-UID': 'testD2Header', + 'GU-Client': 'testClientHeader', }, - ]), -]; - -export const LegacyDiscussion = () => ( -
    - {}} - apiKey="" - idApiUrl="https://idapi.theguardian.com" - page={2} - filters={filters} - topLevelCommentCount={ - legacyDiscussionWithoutThreading.discussion.commentCount - } - loading={false} - comments={legacyDiscussionWithoutThreading.discussion.comments} - topForm={defaultCommentForm} - replyForm={defaultCommentForm} - bottomForm={defaultCommentForm} - reportAbuse={() => Promise.resolve(ok(true))} - /> -
    -); -LegacyDiscussion.storyName = "a legacy discussion that doesn't allow threading"; -LegacyDiscussion.decorators = [ - splitTheme([ - { - ...format, - theme: Pillar.Culture, - }, - ]), -]; + expanded: false, + onPermalinkClick: () => {}, + apiKey: '', + idApiUrl: 'https://idapi.theguardian.com', + page: 3, + filters, + topLevelCommentCount: discussionMock.discussion.topLevelCommentCount, + loading: false, + comments: discussionMock.discussion.comments, + topForm: defaultCommentForm, + replyForm: defaultCommentForm, + bottomForm: defaultCommentForm, + reportAbuse: () => Promise.resolve(ok(true)), + }, + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Culture, + }, + ]), + ], +} satisfies Story; + +export const InitialPage = { + ...LoggedOutHiddenPicks, + name: 'With initial page set to 1', + args: { + ...LoggedOutHiddenPicks.args, + expanded: true, + page: 1, + }, + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Lifestyle, + }, + ]), + ], +} satisfies Story; + +export const LoggedInHiddenNoPicks = { + ...LoggedOutHiddenPicks, + name: 'When logged in, with no picks and not expanded', + args: { + ...LoggedOutHiddenPicks.args, + shortUrl: 'p/abc123', + user: aUser, + }, + decorators: [splitTheme([format])], +} satisfies Story; + +export const LoggedIn = { + ...LoggedInHiddenNoPicks, + name: 'When logged in and expanded', + args: { + ...LoggedOutHiddenPicks.args, + expanded: true, + }, + decorators: [splitTheme([format])], +} satisfies Story; + +export const LoggedInShortDiscussion = { + ...LoggedInHiddenNoPicks, + args: { + ...LoggedInHiddenNoPicks.args, + shortUrl: discussionWithTwoComments.discussion.key, + expanded: true, + topLevelCommentCount: + discussionWithTwoComments.discussion.topLevelCommentCount, + comments: discussionWithTwoComments.discussion.comments, + }, + decorators: [splitTheme([format])], +} satisfies Story; + +export const LoggedOutHiddenNoPicks = { + ...LoggedOutHiddenPicks, + args: { + ...LoggedOutHiddenPicks.args, + shortUrl: 'p/abc123', + }, + name: 'When logged out, with no picks and not expanded', + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Sport, + }, + ]), + ], +} satisfies Story; + +export const Closed = { + ...LoggedOutHiddenPicks, + args: { + ...LoggedOutHiddenPicks.args, + isClosedForComments: true, + user: aUser, + expanded: true, + }, + name: 'Logged in but closed for comments', + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Lifestyle, + }, + ]), + ], +} satisfies Story; + +export const NoComments = { + ...LoggedOutHiddenPicks, + args: { + ...LoggedOutHiddenPicks.args, + shortUrl: 'p/39f5x', + topLevelCommentCount: 0, + comments: [], + }, + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Culture, + }, + ]), + ], +} satisfies Story; + +export const LegacyDiscussion = { + ...LoggedOutHiddenPicks, + args: { + ...LoggedOutHiddenPicks.args, + shortUrl: legacyDiscussionWithoutThreading.discussion.key, + page: 2, + topLevelCommentCount: + legacyDiscussionWithoutThreading.discussion.commentCount, + comments: legacyDiscussionWithoutThreading.discussion.comments, + }, + name: "A legacy discussion that doesn't allow threading", + decorators: [ + splitTheme([ + { + ...format, + theme: Pillar.Culture, + }, + ]), + ], +} satisfies Story; From b74fa53cc45018b777b002adbbf4c0af5e7808cd Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Mon, 23 Dec 2024 11:25:19 +0000 Subject: [PATCH 08/19] Disable chromatic snapshots on flaky tests. --- .../src/components/ArticleHeadline.stories.tsx | 11 +++++++++++ .../src/components/Discussion/Comments.stories.tsx | 11 +++++++++++ .../components/DocumentBlockComponent.stories.tsx | 12 ++++++++++++ .../src/components/VimeoBlockComponent.stories.tsx | 11 +++++++++++ .../src/layouts/DecideLayout.stories.tsx | 13 ++++++++++++- 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/ArticleHeadline.stories.tsx b/dotcom-rendering/src/components/ArticleHeadline.stories.tsx index a6a408f90f..df0741d615 100644 --- a/dotcom-rendering/src/components/ArticleHeadline.stories.tsx +++ b/dotcom-rendering/src/components/ArticleHeadline.stories.tsx @@ -253,6 +253,14 @@ export const EditorialDesign = { }, } satisfies Story; +/** + * Skipped (flaky). + * + * This story fails intermittently. It is unclear to whether it is the icon, the + * image, the font, or something else that is causing the issue. + * + * Example: https://www.chromatic.com/test?appId=637e406971a9af18ddba0505&id=665eecc338adeba89d31f92b + */ export const MatchReportDesignSportTheme = { args: { ...StandardDesign.args, @@ -263,6 +271,9 @@ export const MatchReportDesignSportTheme = { }, }, name: 'MatchReport Design, Sport Theme', + parameters: { + chromatic: { disableSnapshot: true }, + }, } satisfies Story; export const LiveBlogDesign = { diff --git a/dotcom-rendering/src/components/Discussion/Comments.stories.tsx b/dotcom-rendering/src/components/Discussion/Comments.stories.tsx index 84a5b2b0b0..e367dda8e7 100644 --- a/dotcom-rendering/src/components/Discussion/Comments.stories.tsx +++ b/dotcom-rendering/src/components/Discussion/Comments.stories.tsx @@ -143,6 +143,14 @@ export const InitialPage = { ], } satisfies Story; +/** + * Skipped (flaky). + * + * This story fails intermittently. The monospaced + * text: "code" is often different in the snapshot. + * + * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=6659d8e7fde909fdd4dbf8b9 + */ export const LoggedInHiddenNoPicks = { ...LoggedOutHiddenPicks, name: 'When logged in, with no picks and not expanded', @@ -152,6 +160,9 @@ export const LoggedInHiddenNoPicks = { user: aUser, }, decorators: [splitTheme([format])], + parameters: { + chromatic: { disableSnapshot: true }, + }, } satisfies Story; export const LoggedIn = { diff --git a/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx b/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx index 5470010aaa..d8b1e4c32d 100644 --- a/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/DocumentBlockComponent.stories.tsx @@ -34,6 +34,15 @@ export const ScribdDocument: Story = { }, }; +/** + * Skipped (flaky). + * + * This story fails intermittently. The text: "fit width" in + * the button in the bottom-right sometimes causes a difference. + * often different in the snapshot. + * + * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=676405bf6014bfa8ccddc7be + */ export const DocumentCloudDocument: Story = { args: { embedUrl: 'https://embed.documentcloud.org/documents/20417938-test-pdf', @@ -44,4 +53,7 @@ export const DocumentCloudDocument: Story = { isTracking: false, isMainMedia: false, }, + parameters: { + chromatic: { disableSnapshot: true }, + }, }; diff --git a/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx b/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx index 766f5e37d3..58f0ad0480 100644 --- a/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx @@ -69,6 +69,14 @@ largeAspectRatio.story = { chromatic: { disable: true }, }; +/** + * Skipped (flaky). + * + * This story fails intermittently. Sometimes the video doesn't load + * with the error message: "We couldn't verify the security of your connection." + * + * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=6762b11e0dbf28eb24702ff1 + */ export const verticalAspectRatio = () => { return ( @@ -92,3 +100,6 @@ export const verticalAspectRatio = () => { ); }; verticalAspectRatio.storyName = 'with vertical aspect ratio'; +verticalAspectRatio.story = { + chromatic: { disableSnapshot: true }, +}; diff --git a/dotcom-rendering/src/layouts/DecideLayout.stories.tsx b/dotcom-rendering/src/layouts/DecideLayout.stories.tsx index ab9681a96f..f9b284eca6 100644 --- a/dotcom-rendering/src/layouts/DecideLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/DecideLayout.stories.tsx @@ -222,12 +222,23 @@ export const AppsPictureShowcaseOpinionDark: Story = { parameters: appsParameters, }; +/** + * Skipped (flaky). + * + * This story fails intermittently as an iframe is inserted into the page + * which is sometimes registered by the snapshot. + * + * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=675aaa4f3aa384bd64bde3a1 + */ export const WebPhotoEssayImmersiveLabsLight: Story = { args: { article: enhanceArticleType(PhotoEssayImmersiveLabsFixture, 'Web') .frontendData, }, - parameters: webParameters, + parameters: { + ...webParameters, + chromatic: { disableSnapshot: true }, + }, }; const standardStandardLabsWebFixture: ArticleDeprecated = { From 72efd4c8fb941db93321b8e3b5dbb2720ade835b Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Mon, 23 Dec 2024 15:11:38 +0000 Subject: [PATCH 09/19] Convert VimeoBlockComponent to CSF v3 --- .../VimeoBlockComponent.stories.tsx | 160 +++++++----------- 1 file changed, 64 insertions(+), 96 deletions(-) diff --git a/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx b/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx index 58f0ad0480..b400b3df9c 100644 --- a/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/VimeoBlockComponent.stories.tsx @@ -1,105 +1,73 @@ import { css } from '@emotion/react'; +import type { Meta, StoryObj } from '@storybook/react'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { VimeoBlockComponent } from './VimeoBlockComponent'; -export default { +const meta = { component: VimeoBlockComponent, - title: 'Components/VimeoComponent', -}; + title: 'Components/Vimeo Component', + decorators: [ + (Story) => ( +
    +

    abc

    + +

    abc

    +
    + ), + ], + /** + * Skipped (flaky). + * + * This story fails intermittently. Sometimes the video doesn't load + * with the error message: "We couldn't verify the security of your connection." + * + * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=6762b11e0dbf28eb24702ff1 + */ + parameters: { + chromatic: { disableSnapshot: true }, + }, +} satisfies Meta; -const Wrapper = ({ children }: { children: React.ReactNode }) => ( -
    - {children} -
    -); +export default meta; -export const smallAspectRatio = () => { - return ( - -

    abc

    - -

    abc

    -
    - ); -}; -smallAspectRatio.storyName = 'with small aspect ratio'; +type Story = StoryObj; -export const largeAspectRatio = () => { - return ( - -

    abc

    - -

    abc

    -
    - ); -}; -largeAspectRatio.storyName = 'with large aspect ratio'; -largeAspectRatio.story = { - chromatic: { disable: true }, -}; +export const SmallAspectRatio = { + args: { + embedUrl: 'https://player.vimeo.com/video/327310297?app_id=122963', + height: 250, + width: 250, + caption: 'blah', + credit: '', + title: '', + format: { + display: ArticleDisplay.Standard, + design: ArticleDesign.Standard, + theme: Pillar.News, + }, + isMainMedia: false, + }, +} satisfies Story; -/** - * Skipped (flaky). - * - * This story fails intermittently. Sometimes the video doesn't load - * with the error message: "We couldn't verify the security of your connection." - * - * Example: https://www.chromatic.com/test?appId=63e251470cfbe61776b0ef19&id=6762b11e0dbf28eb24702ff1 - */ -export const verticalAspectRatio = () => { - return ( - -

    abc

    - -

    abc

    -
    - ); -}; -verticalAspectRatio.storyName = 'with vertical aspect ratio'; -verticalAspectRatio.story = { - chromatic: { disableSnapshot: true }, -}; +export const LargeAspectRatio = { + ...SmallAspectRatio, + args: { + ...SmallAspectRatio.args, + height: 259, + width: 460, + }, +} satisfies Story; + +export const VerticalAspectRatio = { + ...SmallAspectRatio, + args: { + ...SmallAspectRatio.args, + height: 818, + width: 460, + }, +} satisfies Story; From 0a8b4bcf5572b0d9a1ae6bf24aac66557b1a410a Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Tue, 24 Dec 2024 11:15:07 +0000 Subject: [PATCH 10/19] Fix disable chromatic for video facebook component --- .../src/components/VideoFacebookBlockComponent.stories.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/VideoFacebookBlockComponent.stories.tsx b/dotcom-rendering/src/components/VideoFacebookBlockComponent.stories.tsx index e8fd63072a..df95c1240f 100644 --- a/dotcom-rendering/src/components/VideoFacebookBlockComponent.stories.tsx +++ b/dotcom-rendering/src/components/VideoFacebookBlockComponent.stories.tsx @@ -43,7 +43,9 @@ export const largeAspectRatio = () => { }; largeAspectRatio.storyName = 'with large aspect ratio'; largeAspectRatio.story = { - chromatic: { disable: true }, + parameters: { + chromatic: { disable: true }, + }, }; export const verticalAspectRatio = () => { From 28b19430d87722073f51a8db5883afae11c6a3d1 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:20:16 +0000 Subject: [PATCH 11/19] Use share button Use share button with quiz results --- .../fixtures/manual/sharingUrls.ts | 48 -------- .../KnowledgeQuizAtom.importable.tsx | 46 +++---- .../components/KnowledgeQuizAtom.stories.tsx | 11 +- .../src/components/KnowledgeQuizAtom.test.tsx | 58 ++++++--- .../PersonalityQuizAtom.importable.tsx | 48 ++++---- .../PersonalityQuizAtom.stories.tsx | 11 +- .../components/PersonalityQuizAtom.test.tsx | 53 +++++--- .../src/components/SharingIcons.stories.tsx | 27 ---- .../src/components/SharingIcons.tsx | 115 ------------------ dotcom-rendering/src/lib/renderElement.tsx | 11 +- dotcom-rendering/src/types/content.ts | 12 +- 11 files changed, 151 insertions(+), 289 deletions(-) delete mode 100644 dotcom-rendering/fixtures/manual/sharingUrls.ts delete mode 100644 dotcom-rendering/src/components/SharingIcons.stories.tsx delete mode 100644 dotcom-rendering/src/components/SharingIcons.tsx diff --git a/dotcom-rendering/fixtures/manual/sharingUrls.ts b/dotcom-rendering/fixtures/manual/sharingUrls.ts deleted file mode 100644 index fcd740cb5f..0000000000 --- a/dotcom-rendering/fixtures/manual/sharingUrls.ts +++ /dev/null @@ -1,48 +0,0 @@ -const title = 'Sports quiz: football in the 1980s'; -const articleUrl = - 'https://www.theguardian.com/football/that-1980s-sports-blog/2020/jun/12/sports-quiz-football-in-the-1980s'; - -export const sharingUrls = { - facebook: { - userMessage: 'Share on Facebook', - url: `https://www.facebook.com/dialog/share?app_id=202314643182694&href=${encodeURIComponent( - `${articleUrl}?CMP=share_btn_fb`, - )}`, - }, - twitter: { - userMessage: 'Share on Twitter', - url: `https://twitter.com/intent/tweet?text=${encodeURIComponent( - title, - )}&url=${encodeURIComponent(`${articleUrl}?CMP=share_btn_tw`)}`, - }, - email: { - userMessage: 'Share via Email', - url: `mailto:?subject=${title}&body=${encodeURIComponent( - `${articleUrl}?CMP=share_btn_link`, - )}`, - }, - whatsApp: { - userMessage: 'Share on WhatsApp', - url: `whatsapp://send?text=${encodeURIComponent( - `"${title}" ${articleUrl}?CMP=share_btn_wa`, - )}`, - }, - messenger: { - userMessage: 'Share on Messanger', - url: `fb-messenger://share?link=${encodeURIComponent( - `${articleUrl}?CMP=share_btn_wa`, - )}&app_id=180444840287`, - }, - linkedIn: { - userMessage: 'Share on LinkedIn', - url: `http://www.linkedin.com/shareArticle?mini=true&title=${encodeURIComponent( - title, - )}&url=${encodeURIComponent(`${articleUrl}?CMP=share_btn_wa`)}`, - }, - pinterest: { - userMessage: 'Share on Pinterest', - url: `http://www.pinterest.com/pin/find/?url=${encodeURIComponent( - `${articleUrl}?CMP=share_btn_wa`, - )}`, - }, -}; diff --git a/dotcom-rendering/src/components/KnowledgeQuizAtom.importable.tsx b/dotcom-rendering/src/components/KnowledgeQuizAtom.importable.tsx index 9b720a9684..930c5ebf4e 100644 --- a/dotcom-rendering/src/components/KnowledgeQuizAtom.importable.tsx +++ b/dotcom-rendering/src/components/KnowledgeQuizAtom.importable.tsx @@ -10,14 +10,14 @@ import { } from '@guardian/source/foundations'; import { Button, Radio, RadioGroup } from '@guardian/source/react-components'; import { Fragment, useEffect, useState } from 'react'; -import { ArticleSpecial, type ArticleTheme } from '../lib/articleFormat'; +import { ArticleSpecial } from '../lib/articleFormat'; +import type { ArticleFormat, ArticleTheme } from '../lib/articleFormat'; import type { AnswerType, KnowledgeQuizAtomType, QuestionType, QuizSelectionType, ResultGroupsType, - SharingUrlsType, } from '../types/content'; import { CorrectSelectedAnswer, @@ -26,7 +26,7 @@ import { radioButtonWrapperStyles, UnselectedAnswer, } from './Answers'; -import { SharingIcons } from './SharingIcons'; +import { ShareButton } from './ShareButton.importable'; const fieldsetStyle = css` margin-bottom: 12px; @@ -42,10 +42,12 @@ export const KnowledgeQuizAtom = ({ id, questions, resultGroups, - sharingUrls, - theme, + pageId, + webTitle, + format, }: KnowledgeQuizAtomType) => { const [quizSelection, setQuizSelection] = useState({}); + const theme = format.theme; const haveAllQuestionsBeenAnswered = Object.keys(quizSelection).length === questions.length; @@ -57,7 +59,9 @@ export const KnowledgeQuizAtom = ({
)} @@ -81,7 +85,9 @@ export const KnowledgeQuizAtom = ({ )} @@ -182,7 +188,7 @@ export const Question = ({ const spaceKey = 32; const enterKey = 13; if ( - e.keyCode === spaceKey ?? + e.keyCode === spaceKey || e.keyCode === enterKey ) { setHasSubmitted(true); @@ -319,11 +325,15 @@ const resultHeaderStyles = css` export const Result = ({ quizSelection, resultGroups, - sharingUrls, + pageId, + webTitle, + format, }: { quizSelection: Record; resultGroups: ResultGroupsType[]; - sharingUrls: SharingUrlsType; + pageId: string; + webTitle: string; + format: ArticleFormat; }) => { const totalNumberOfQuestions = Object.keys(quizSelection).length; const numberOfCorrectAnswers = Object.keys(quizSelection).filter( @@ -387,17 +397,11 @@ export const Result = ({
Challenge your friends
- ); diff --git a/dotcom-rendering/src/components/KnowledgeQuizAtom.stories.tsx b/dotcom-rendering/src/components/KnowledgeQuizAtom.stories.tsx index 71cce5f162..1570944226 100644 --- a/dotcom-rendering/src/components/KnowledgeQuizAtom.stories.tsx +++ b/dotcom-rendering/src/components/KnowledgeQuizAtom.stories.tsx @@ -6,7 +6,6 @@ import { natureResultGroups, resultGroups, } from '../../fixtures/manual/knowledgeQuizAtom'; -import { sharingUrls } from '../../fixtures/manual/sharingUrls'; import { ArticleDesign, ArticleDisplay, @@ -29,8 +28,13 @@ export const Default = { id: '2c6bf552-2827-4256-b3a0-f557d215c394', questions: exampleKnowledgeQuestions, resultGroups, - sharingUrls, - theme: Pillar.News, + pageId: '/', + webTitle: 'Quiz title', + format: { + display: ArticleDisplay.Standard, + design: ArticleDesign.Comment, + theme: Pillar.News, + }, }, decorators: [ splitTheme([ @@ -63,7 +67,6 @@ export const BatchedResults = { export const LabsTheme = { args: { ...Default.args, - theme: ArticleSpecial.Labs, }, decorators: [ splitTheme([ diff --git a/dotcom-rendering/src/components/KnowledgeQuizAtom.test.tsx b/dotcom-rendering/src/components/KnowledgeQuizAtom.test.tsx index 3a1b47c5a0..75f0a26009 100644 --- a/dotcom-rendering/src/components/KnowledgeQuizAtom.test.tsx +++ b/dotcom-rendering/src/components/KnowledgeQuizAtom.test.tsx @@ -4,10 +4,19 @@ import { exampleKnowledgeQuestions, resultGroups, } from '../../fixtures/manual/knowledgeQuizAtom'; -import { sharingUrls } from '../../fixtures/manual/sharingUrls'; -import { Pillar } from '../lib/articleFormat'; +import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { KnowledgeQuizAtom } from './KnowledgeQuizAtom.importable'; +jest.mock('../lib/useMatchMedia', () => ({ + useMatchMedia: jest.fn(() => true), +})); + +const defaultFormat = { + design: ArticleDesign.Standard, + display: ArticleDisplay.Standard, + theme: Pillar.News, +}; + const questionOne = ensure(exampleKnowledgeQuestions.find((x) => x)); describe('KnowledgeQuiz', () => { @@ -17,8 +26,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); expect(getByText(questionOne.text)).toBeInTheDocument(); @@ -53,8 +63,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -64,8 +75,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -80,8 +92,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -92,8 +105,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -108,8 +122,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -120,8 +135,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -141,8 +157,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -159,8 +176,9 @@ describe('KnowledgeQuiz', () => { id="123abc" questions={[questionOne]} resultGroups={resultGroups} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); diff --git a/dotcom-rendering/src/components/PersonalityQuizAtom.importable.tsx b/dotcom-rendering/src/components/PersonalityQuizAtom.importable.tsx index 8cf9767320..f653c300b4 100644 --- a/dotcom-rendering/src/components/PersonalityQuizAtom.importable.tsx +++ b/dotcom-rendering/src/components/PersonalityQuizAtom.importable.tsx @@ -10,16 +10,16 @@ import { import { Button, Radio, RadioGroup } from '@guardian/source/react-components'; import type { KeyboardEvent, MouseEvent } from 'react'; import { memo, useEffect, useState } from 'react'; -import { ArticleSpecial, type ArticleTheme } from '../lib/articleFormat'; +import { ArticleSpecial } from '../lib/articleFormat'; +import type { ArticleFormat, ArticleTheme } from '../lib/articleFormat'; import type { AnswerType, PersonalityQuizAtomType, QuestionType, ResultsBucketType, - SharingUrlsType, } from '../types/content'; import { radioButtonWrapperStyles } from './Answers'; -import { SharingIcons } from './SharingIcons'; +import { ShareButton } from './ShareButton.importable'; const answersWrapperStyle = (theme: ArticleTheme) => css` margin-bottom: 12px; @@ -90,8 +90,9 @@ export const PersonalityQuizAtom = ({ id, questions, resultBuckets, - sharingUrls, - theme, + pageId, + webTitle, + format, }: PersonalityQuizAtomType) => { const [selectedGlobalAnswers, setSelectedGlobalAnswers] = useState< Record @@ -140,6 +141,7 @@ export const PersonalityQuizAtom = ({ resultBuckets, questions, ]); + const theme = format.theme; return (
@@ -147,7 +149,9 @@ export const PersonalityQuizAtom = ({
)} @@ -183,7 +187,9 @@ export const PersonalityQuizAtom = ({
)} @@ -202,7 +208,7 @@ export const PersonalityQuizAtom = ({ onKeyDown={(e: React.KeyboardEvent) => { const spaceKey = 32; const enterKey = 13; - if (e.keyCode === spaceKey ?? e.keyCode === enterKey) { + if (e.keyCode === spaceKey || e.keyCode === enterKey) { onSubmit(e); } }} @@ -220,7 +226,7 @@ export const PersonalityQuizAtom = ({ onKeyDown={(e: React.KeyboardEvent) => { const spaceKey = 32; const enterKey = 13; - if (e.keyCode === spaceKey ?? e.keyCode === enterKey) { + if (e.keyCode === spaceKey || e.keyCode === enterKey) { setSelectedGlobalAnswers({}); setHasSubmittedAnswers(false); setTopSelectedResult(null); @@ -401,27 +407,25 @@ const resultDescriptionStyles = css` export const Result = ({ resultBuckets, - sharingUrls, + pageId, + webTitle, + format, }: { resultBuckets: ResultsBucketType; - sharingUrls: SharingUrlsType; + pageId: string; + webTitle: string; + format: ArticleFormat; }) => (
{resultBuckets.title}
{resultBuckets.description}

Challenge your friends
-
); diff --git a/dotcom-rendering/src/components/PersonalityQuizAtom.stories.tsx b/dotcom-rendering/src/components/PersonalityQuizAtom.stories.tsx index 8721b8e3f4..efe0c21980 100644 --- a/dotcom-rendering/src/components/PersonalityQuizAtom.stories.tsx +++ b/dotcom-rendering/src/components/PersonalityQuizAtom.stories.tsx @@ -4,7 +4,6 @@ import { examplePersonalityQuestions, exampleResultBuckets, } from '../../fixtures/manual/personalityQuizAtom'; -import { sharingUrls } from '../../fixtures/manual/sharingUrls'; import { ArticleDesign, ArticleDisplay, @@ -27,8 +26,13 @@ export const Default = { id: 'quiz-id', questions: examplePersonalityQuestions, resultBuckets: exampleResultBuckets, - sharingUrls, - theme: Pillar.News, + pageId: '/', + webTitle: 'Quiz title', + format: { + display: ArticleDisplay.Standard, + design: ArticleDesign.Comment, + theme: Pillar.News, + }, }, decorators: [ splitTheme([ @@ -45,7 +49,6 @@ export const LabsTheme = { args: { ...Default.args, id: '2c6bf552-2827-4256-b3a0-f557d215c394', - theme: ArticleSpecial.Labs, }, decorators: [ splitTheme([ diff --git a/dotcom-rendering/src/components/PersonalityQuizAtom.test.tsx b/dotcom-rendering/src/components/PersonalityQuizAtom.test.tsx index 0c3cf3adbc..33ae64929d 100644 --- a/dotcom-rendering/src/components/PersonalityQuizAtom.test.tsx +++ b/dotcom-rendering/src/components/PersonalityQuizAtom.test.tsx @@ -5,13 +5,22 @@ import { examplePersonalityQuestions, exampleResultBuckets, } from '../../fixtures/manual/personalityQuizAtom'; -import { sharingUrls } from '../../fixtures/manual/sharingUrls'; -import { Pillar } from '../lib/articleFormat'; +import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import { findMostReferredToBucketId, PersonalityQuizAtom, } from './PersonalityQuizAtom.importable'; +jest.mock('../lib/useMatchMedia', () => ({ + useMatchMedia: jest.fn(() => true), +})); + +const defaultFormat = { + design: ArticleDesign.Standard, + display: ArticleDisplay.Standard, + theme: Pillar.News, +}; + describe('PersonalityQuiz', () => { it('should render', () => { const { getByText } = render( @@ -19,8 +28,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -37,8 +47,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -48,8 +59,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -64,8 +76,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -75,8 +88,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -91,8 +105,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -107,8 +122,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); @@ -124,8 +140,9 @@ describe('PersonalityQuiz', () => { id="123abc" questions={examplePersonalityQuestions} resultBuckets={exampleResultBuckets} - sharingUrls={sharingUrls} - theme={Pillar.News} + pageId={'/'} + webTitle={'Quiz title'} + format={defaultFormat} />, ); diff --git a/dotcom-rendering/src/components/SharingIcons.stories.tsx b/dotcom-rendering/src/components/SharingIcons.stories.tsx deleted file mode 100644 index fdbaaebc77..0000000000 --- a/dotcom-rendering/src/components/SharingIcons.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { sharingUrls } from '../../fixtures/manual/sharingUrls'; -import { SharingIcons as SharingIconsComponent } from './SharingIcons'; - -const meta = { - title: 'Components/SharingIcons', - component: SharingIconsComponent, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const SharingIcons = { - args: { - sharingUrls, - displayIcons: [ - 'facebook', - 'twitter', - 'email', - 'whatsApp', - 'messenger', - 'pinterest', - 'linkedIn', - ], - }, -} satisfies Story; diff --git a/dotcom-rendering/src/components/SharingIcons.tsx b/dotcom-rendering/src/components/SharingIcons.tsx deleted file mode 100644 index b7155d3a0d..0000000000 --- a/dotcom-rendering/src/components/SharingIcons.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { css } from '@emotion/react'; -import { from } from '@guardian/source/foundations'; -import { - LinkButton, - SvgEnvelope, - SvgFacebook, - SvgFacebookMessenger, - SvgLinkedIn, - SvgPinterest, - SvgTwitter, - SvgWhatsApp, -} from '@guardian/source/react-components'; -import type { IconProps } from '@guardian/source/react-components'; -import type { SharePlatformType } from '../types/content'; - -const shareIconList = css` - display: flex; - flex-direction: row; - ${from.wide} { - flex: auto; - } - - a { - margin-right: 5px; - } -`; - -const mobileOnlyShareIconsListItem = css` - ${from.phablet} { - display: none; - } -`; - -export interface ShareListItemType { - id: SharePlatformType; - Icon: React.ComponentType; - url: string; - userMessage: string; - mobileOnly: boolean; -} - -export const SharingIcons = ({ - sharingUrls, - displayIcons, -}: { - sharingUrls: { - [K in SharePlatformType]?: { - url: string; - userMessage: string; - }; - }; - displayIcons: SharePlatformType[]; -}) => { - const icons: { - [K in SharePlatformType]?: React.ComponentType; - } = { - facebook: SvgFacebook, - twitter: SvgTwitter, - email: SvgEnvelope, - whatsApp: SvgWhatsApp, - messenger: SvgFacebookMessenger, - linkedIn: SvgLinkedIn, - pinterest: SvgPinterest, - }; - - const mobileOnlyIcons: SharePlatformType[] = ['whatsApp', 'messenger']; - - const shareList = displayIcons.reduce((list: ShareListItemType[], id) => { - const icon = icons[id]; - const sharingUrl = sharingUrls[id]; - - if (icon && sharingUrl) { - const listItem: ShareListItemType = { - id, - Icon: icon, - mobileOnly: mobileOnlyIcons.includes(id), - ...sharingUrl, - }; - list.push(listItem); - } - - return list; - }, []); - - return ( -
- {shareList.map((shareListItem) => { - const { Icon, id, url, userMessage, mobileOnly } = - shareListItem; - - return ( -
- } - priority="tertiary" - size="small" - data-ignore="global-link-styling" - > - {userMessage} - -
- ); - })} -
- ); -}; diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index cd0241d8f2..7208c5bd60 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -64,7 +64,6 @@ import { interactiveLegacyFigureClasses, isInteractive, } from '../layouts/lib/interactiveLegacyStyling'; -import { getSharingUrls } from '../lib/sharing-urls'; import type { ServerSideTests, Switches } from '../types/config'; import type { FEElement, RoleType, StarRating } from '../types/content'; import { ArticleDesign, type ArticleFormat } from './articleFormat'; @@ -583,8 +582,9 @@ export const renderElement = ({ id={element.id} questions={element.questions} resultBuckets={element.resultBuckets} - sharingUrls={getSharingUrls(pageId, webTitle)} - theme={format.theme} + pageId={pageId} + webTitle={webTitle} + format={format} /> )} @@ -597,8 +597,9 @@ export const renderElement = ({ id={element.id} questions={element.questions} resultGroups={element.resultGroups} - sharingUrls={getSharingUrls(pageId, webTitle)} - theme={format.theme} + pageId={pageId} + webTitle={webTitle} + format={format} /> )} diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index d481eacf9c..0a6fa8a662 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -1,4 +1,4 @@ -import type { ArticleTheme } from '../lib/articleFormat'; +import type { ArticleFormat } from '../lib/articleFormat'; export type StarRating = 0 | 1 | 2 | 3 | 4 | 5; @@ -1008,16 +1008,18 @@ export type KnowledgeQuizAtomType = { id: string; questions: QuestionType[]; resultGroups: ResultGroupsType[]; - sharingUrls: SharingUrlsType; - theme: ArticleTheme; + pageId: string; + webTitle: string; + format: ArticleFormat; }; export type PersonalityQuizAtomType = { id: string; questions: QuestionType[]; resultBuckets: ResultsBucketType[]; - sharingUrls: SharingUrlsType; - theme: ArticleTheme; + pageId: string; + webTitle: string; + format: ArticleFormat; }; export type QuizSelectionType = Record; From d7a5b6dd470bb934f8d5e18b8e591e3afc52a41a Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Tue, 31 Dec 2024 13:52:38 +0000 Subject: [PATCH 12/19] Remove superfluous space from headlines container --- dotcom-rendering/src/components/FrontSection.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dotcom-rendering/src/components/FrontSection.tsx b/dotcom-rendering/src/components/FrontSection.tsx index b7fa9b4cdf..e107b28198 100644 --- a/dotcom-rendering/src/components/FrontSection.tsx +++ b/dotcom-rendering/src/components/FrontSection.tsx @@ -253,12 +253,6 @@ const sectionHeadlineFromLeftCol = (borderColour: string) => css` } `; -const sectionHeadlineHeight = css` - ${until.tablet} { - min-height: 58px; - } -`; - const topPadding = css` padding-top: ${space[2]}px; `; @@ -559,8 +553,6 @@ export const FrontSection = ({ sectionHeadlineFromLeftCol( schemePalette('--section-border'), ), - title?.toLowerCase() === 'headlines' && - sectionHeadlineHeight, ]} > Date: Thu, 2 Jan 2025 11:08:51 +0000 Subject: [PATCH 13/19] Fix targeting by signed in status in Liveblog --- dotcom-rendering/src/components/LiveBlogEpic.importable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx index a834d1f25b..e9b68bb8b7 100644 --- a/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx +++ b/dotcom-rendering/src/components/LiveBlogEpic.importable.tsx @@ -129,6 +129,7 @@ const usePayload = ({ weeklyArticleHistory: articleCounts?.weeklyArticleHistory, hasOptedOutOfArticleCount, url: window.location.origin + window.location.pathname, + isSignedIn, }, }; }; From a6a21462aa7afdec093323970665ad0b42ca0f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLAKSHMIRPILLAI=E2=80=9D?= <“luxmi.r.pillai@gmail.com”> Date: Thu, 2 Jan 2025 12:32:42 +0000 Subject: [PATCH 14/19] Remove Ad free as a benefit on epic and live blog epic choice cards --- .../src/components/marketing/epics/ThreeTierChoiceCardData.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx index 0a3613c64e..340d9e2da8 100644 --- a/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx +++ b/dotcom-rendering/src/components/marketing/epics/ThreeTierChoiceCardData.tsx @@ -39,7 +39,6 @@ export const ChoiceCardTestData_REGULAR: ChoiceInfo[] = [ benefits: () => [ 'Unlimited access to the Guardian app', 'Unlimited access to our new Feast App', - 'Ad-free reading on all your devices', 'Exclusive newsletter for supporters, sent every week from the Guardian newsroom', 'Far fewer asks for support', ], @@ -73,7 +72,6 @@ export const ChoiceCardTestData_US: ChoiceInfo[] = [ benefits: () => [ 'Unlimited access to the Guardian app', 'Unlimited access to our new Feast App', - 'Ad-free reading on all your devices', 'Exclusive newsletter for supporters, sent every week from the Guardian newsroom', 'Far fewer asks for support', ], @@ -118,7 +116,6 @@ export const ChoiceCardTestData_TwoTier_REGULAR: ChoiceInfo[] = [ benefitsLabel: 'All-access digital', benefits: () => [ 'Unlimited access to the Guardian app', - 'Ad-free reading on all your devices', 'Exclusive newsletter for supporters', 'And much more!', ], From 2bc6f6f13cebf49f6dbe0a5865b86f2adb0d2a3a Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:29:20 +0000 Subject: [PATCH 15/19] Added `StackedProgress` election tracker component (#13007) This component is one of several that will allow rendering election trackers within DCAR. It's a "stacked progress bar", representing progress through an election divided up by each group running. It's generic, so the kinds of groups it can represent varies. For example: - Candidates in a US presidential election - Parties in a UK general election - Party groups in an EU parliamentary election These examples are demonstrated in the stories file also included in this change. --- .../StackedProgress.stories.tsx | 152 +++++++++++++ .../ElectionTrackers/StackedProgress.tsx | 207 ++++++++++++++++++ dotcom-rendering/src/paletteDeclarations.ts | 72 ++++++ 3 files changed, 431 insertions(+) create mode 100644 dotcom-rendering/src/components/ElectionTrackers/StackedProgress.stories.tsx create mode 100644 dotcom-rendering/src/components/ElectionTrackers/StackedProgress.tsx diff --git a/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.stories.tsx b/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.stories.tsx new file mode 100644 index 0000000000..deba2d610a --- /dev/null +++ b/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.stories.tsx @@ -0,0 +1,152 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { allModes } from '../../../.storybook/modes'; +import { palette } from '../../palette'; +import { StackedProgress } from './StackedProgress'; + +const meta = { + title: 'Components/Election Trackers/Stacked Progress', + component: StackedProgress, + decorators: (Story) => ( +
+ +
+ ), + parameters: { + viewport: { + defaultViewport: 'mobileLandscape', + }, + chromatic: { + modes: { + 'vertical mobileLandscape': + allModes['vertical mobileLandscape'], + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const UKGeneral = { + args: { + total: 650, + toWinCopy: 'for majority', + sections: [ + { + name: 'Labour', + colour: palette('--uk-elections-labour'), + value: 400, + align: 'left', + }, + { + name: 'Conservative', + colour: palette('--uk-elections-conservative'), + value: 100, + align: 'right', + }, + { + name: 'Lib Dem', + colour: palette('--uk-elections-lib-dem'), + value: 70, + align: 'left', + }, + { + name: 'SNP', + colour: palette('--uk-elections-snp'), + value: 10, + align: 'left', + }, + { + name: 'Reform', + colour: palette('--uk-elections-reform'), + value: 5, + align: 'right', + }, + ], + }, +} satisfies Story; + +export const USPresidential = { + args: { + total: 538, + toWinCopy: 'to win', + sections: [ + { + name: 'Harris', + colour: palette('--us-elections-democrats'), + value: 200, + align: 'left', + }, + { + name: 'Trump', + colour: palette('--us-elections-republicans'), + value: 200, + align: 'right', + }, + ], + }, +} satisfies Story; + +export const EUParliament = { + args: { + total: 720, + toWinCopy: undefined, + sections: [ + { + colour: palette('--eu-parliament-theleft'), + name: 'Left', + value: 40, + align: 'left', + }, + { + name: 'S&D', + colour: palette('--eu-parliament-sd'), + value: 100, + align: 'left', + }, + { + name: 'Grn/EFA', + colour: palette('--eu-parliament-greensefa'), + value: 40, + align: 'left', + }, + { + name: 'Renew', + colour: palette('--eu-parliament-renew'), + value: 60, + align: 'left', + }, + { + name: 'EPP', + colour: palette('--eu-parliament-epp'), + value: 150, + align: 'left', + }, + { + name: 'ECR', + colour: palette('--eu-parliament-ecr'), + value: 60, + align: 'left', + }, + { + name: 'NI', + colour: palette('--eu-parliament-ni'), + value: 30, + align: 'left', + }, + { + name: 'PfE', + colour: palette('--eu-parliament-unknown'), + value: 70, + align: 'left', + }, + { + name: 'ESN', + colour: palette('--eu-parliament-unknown'), + value: 20, + align: 'left', + }, + ], + }, +} satisfies Story; diff --git a/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.tsx b/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.tsx new file mode 100644 index 0000000000..53f4eaa7ff --- /dev/null +++ b/dotcom-rendering/src/components/ElectionTrackers/StackedProgress.tsx @@ -0,0 +1,207 @@ +import { from, textSans12 } from '@guardian/source/foundations'; +import type { ReactNode } from 'react'; +import { palette } from '../../palette'; + +type Props = { + /** + * The sections into which the stacked progress bar will be broken. For more + * information see {@linkcode Section}. + */ + sections: Section[]; + /** + * The maximum number the stacked progress bar can reach. For an election, + * this would be the number of results expected. Must be an integer (a whole + * number). + * + * **Examples:** number of constituencies up for election; total electoral + * college votes. + */ + total: number; + /** + * When this is specified, the bar will include a line down the centre that + * represents a target needed to win the election by achieving a majority. + * The groups being elected can then be arranged on either side of this line + * by setting their {@linkcode Section.align|align} property. The majority + * needed will be calculated automatically based on the + * {@linkcode Props.total|total}. + * + * The copy specified here will be prefixed by the majority number and used + * to label the stacked progress bar, and will appear above the central + * line. + * + * **Examples:** Specify {@linkcode Props.total|total} as 538 and this prop + * as "to win" to get "270 to win"; specify {@linkcode Props.total|total} as + * 650 and this prop as "for majority" to get "326 for majority". + */ + toWinCopy: string | undefined; +}; + +/** + * A section of the stacked progress bar. For an election each section would + * represent a group that's running. Examples: seats won by a party; votes won + * by a candidate. + */ +type Section = { + /** + * The colour used to represent the group in the stacked progress bar. It + * expects a CSS `color` value (e.g. a hex string). To ensure dark mode + * support a {@linkcode palette} colour can be used; i.e. this property + * can be set to the return value of the {@linkcode palette} function. + */ + colour: string; + /** + * The size of a particular section of the progress bar, less than the + * {@linkcode Props.total|total}. For an election, this would be the result + * for the group in question. + * + * **Examples:** seats won by a party; votes won by a candidate. + */ + value: number; + /** + * The name of the section in the stacked progress bar. For an election, + * this would be the name of the group. It will be used to provide an + * accessible description of that section and as a React "key" for the + * element, so each section's `name` should be unique relative to the + * other sections. + * + * **Examples:** name of a candidate; name of a party. + */ + name: string; + /** + * Aligns a section to the left or right side of the stacked progress bar. + * For an election this can be used to represent two or more groups in + * opposition to one another. When used in conjunction with + * {@linkcode Props.toWinCopy|toWinCopy} it can be used to show two or more + * groups competing for a majority. + */ + align: 'left' | 'right'; +}; + +/** + * Represents progress towards a goal divided into groups. Designed to be used + * in election trackers, where it can be used to show progress through an + * election divided up by each group running. + * + * It's generic, so the kinds of groups it can represent varies. For example: + * + * - Candidates in a US presidential election + * - Parties in a UK general election + * - Party groups in an EU parliamentary election + * + * These examples are demonstrated in the stories for this component. + */ +export const StackedProgress = ({ sections, total, toWinCopy }: Props) => { + const value = sections.reduce((acc, section) => acc + section.value, 0); + + return ( + + ); +}; + +type SectionDivProps = { + section: Section; + total: number; +}; + +const SectionDiv = ({ section, total }: SectionDivProps) => ( +
+); + +type LabelProps = { + children: ReactNode; + total: number; + toWinCopy: string | undefined; +}; + +const Label = ({ children, total, toWinCopy }: LabelProps) => + toWinCopy === undefined ? ( + <>{children} + ) : ( + + ); + +const spacer = (total: number, value: number): Section => ({ + colour: palette('--stacked-progress-background'), + value: total - value, + name: 'spacer', + align: 'left', +}); + +const toWin = (total: number): number => Math.floor(total / 2) + 1; + +const valueText = (value: number, sections: Section[]): string => + `Progress so far: ${value}, values: ${sections + .map((section) => `${section.name} ${section.value}`) + .join(', ')}`; diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 30c15bcd8c..0f4bc889cf 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -6378,6 +6378,38 @@ const paletteColours = { light: emailSignupTextSubduedLight, dark: emailSignupTextSubduedDark, }, + '--eu-parliament-ecr': { + light: () => sourcePalette.brand[500], + dark: () => '#009AE1', + }, + '--eu-parliament-epp': { + light: () => '#3DBBE2', + dark: () => '#3DBBE2', + }, + '--eu-parliament-greensefa': { + light: () => '#39A566', + dark: () => '#39A566', + }, + '--eu-parliament-ni': { + light: () => sourcePalette.neutral[20], + dark: () => '#A1A1A1', + }, + '--eu-parliament-renew': { + light: () => '#FF7F0F', + dark: () => '#FF7F0F', + }, + '--eu-parliament-sd': { + light: () => sourcePalette.news[400], + dark: () => '#DC2E1C', + }, + '--eu-parliament-theleft': { + light: () => '#8B0000', + dark: () => '#B23C2D', + }, + '--eu-parliament-unknown': { + light: () => '#848484', + dark: () => sourcePalette.neutral[46], + }, '--expandable-atom-background': { light: expandableAtomBackgroundLight, dark: expandableAtomBackgroundDark, @@ -6995,6 +7027,18 @@ const paletteColours = { light: speechBubbleBackgroundLight, dark: speechBubbleBackgroundLight, }, + '--stacked-progress-background': { + light: () => sourcePalette.neutral[86], + /** + * Custom colour to prevent clashes with the neutral palette, which + * is sometimes used for sections of the stacked progress bar. + */ + dark: () => '#606060', + }, + '--stacked-progress-to-win': { + light: () => sourcePalette.neutral[7], + dark: () => sourcePalette.neutral[86], + }, '--staff-contributor-badge': { light: staffBadgeLight, dark: staffBadgeDark, @@ -7195,6 +7239,34 @@ const paletteColours = { light: () => sourcePalette.neutral[20], dark: () => sourcePalette.neutral[73], }, + '--uk-elections-conservative': { + light: () => sourcePalette.sport[400], + dark: () => '#009AE1', + }, + '--uk-elections-labour': { + light: () => sourcePalette.news[400], + dark: () => '#DC2E1C', + }, + '--uk-elections-lib-dem': { + light: () => sourcePalette.opinion[450], + dark: () => sourcePalette.opinion[500], + }, + '--uk-elections-reform': { + light: () => '#3DBBE2', + dark: () => '#3DBBE2', + }, + '--uk-elections-snp': { + light: () => '#F5DC00', + dark: () => '#F5DC00', + }, + '--us-elections-democrats': { + light: () => '#093CA3', + dark: () => '#3261DB', + }, + '--us-elections-republicans': { + light: () => sourcePalette.news[400], + dark: () => '#DC2E1C', + }, '--weather-icon': { light: () => sourcePalette.neutral[97], dark: () => sourcePalette.neutral[7], From f0b29da30ff87a004e6ab56e69ccb93570b5ff77 Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Fri, 3 Jan 2025 12:41:42 +0000 Subject: [PATCH 16/19] Delete `decidePalette` (#13064) It's no longer used, we've migrated to `palette.ts`. --- dotcom-rendering/src/lib/decidePalette.ts | 433 ---------------------- dotcom-rendering/src/lib/pillars.ts | 5 +- 2 files changed, 3 insertions(+), 435 deletions(-) delete mode 100644 dotcom-rendering/src/lib/decidePalette.ts diff --git a/dotcom-rendering/src/lib/decidePalette.ts b/dotcom-rendering/src/lib/decidePalette.ts deleted file mode 100644 index a7c837d2f0..0000000000 --- a/dotcom-rendering/src/lib/decidePalette.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { brandAltBackground, palette } from '@guardian/source/foundations'; -// Here is the one place where we use `pillarPalette` -import { pillarPalette_DO_NOT_USE as pillarPalette } from '../lib/pillars'; -import type { Palette } from '../types/palette'; -import { - ArticleDesign, - type ArticleFormat, - ArticleSpecial, - Pillar, -} from './articleFormat'; -import { transparentColour } from './transparentColour'; - -const { - brandAlt, - culture, - lifestyle, - news, - neutral, - opinion, - specialReport, - sport, - labs, -} = palette; - -const WHITE = neutral[100]; -const BLACK = neutral[7]; - -const textStandfirst = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.LiveBlog) return WHITE; - if (format.design === ArticleDesign.Picture) return palette.neutral[86]; - if ( - format.theme === ArticleSpecial.SpecialReportAlt && - format.design !== ArticleDesign.DeadBlog - ) { - return palette.specialReportAlt[100]; - } - - return BLACK; -}; - -const textDisclaimerLink = (format: ArticleFormat): string => - pillarPalette[format.theme].dark; - -const textStandfirstLink = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.LiveBlog) return WHITE; - if (format.design === ArticleDesign.DeadBlog) { - switch (format.theme) { - case Pillar.Opinion: - return opinion[200]; - case Pillar.News: - return news[400]; - default: - return pillarPalette[format.theme].dark; - } - } - if (format.design === ArticleDesign.Analysis) { - switch (format.theme) { - case Pillar.Opinion: - return opinion[200]; - case Pillar.Culture: - case Pillar.News: - return news[300]; - default: - return pillarPalette[format.theme].dark; - } - } - - if (format.design === ArticleDesign.Picture) { - return pillarPalette[format.theme].bright; - } - - if (format.theme === ArticleSpecial.SpecialReport) { - return specialReport[400]; - } - - if (format.theme === ArticleSpecial.SpecialReportAlt) { - return palette.specialReportAlt[100]; - } - - switch (format.theme) { - case Pillar.Opinion: - case Pillar.Culture: - return pillarPalette[format.theme].dark; - default: - return pillarPalette[format.theme].main; - } -}; - -const backgroundBulletStandfirst = (format: ArticleFormat): string => { - if ( - format.design === ArticleDesign.DeadBlog || - format.design === ArticleDesign.Analysis - ) { - return neutral[60]; - } - - if (format.design === ArticleDesign.LiveBlog) { - switch (format.theme) { - case Pillar.News: - return news[600]; - case Pillar.Culture: - return culture[400]; - case Pillar.Lifestyle: - return lifestyle[500]; - case Pillar.Sport: - return sport[600]; - case Pillar.Opinion: - return opinion[500]; - case ArticleSpecial.Labs: - return news[600]; - case ArticleSpecial.SpecialReport: - return specialReport[700]; - case ArticleSpecial.SpecialReportAlt: - return news[600]; - } - } - - if (format.theme === ArticleSpecial.SpecialReportAlt) { - return palette.specialReportAlt[100]; - } - - return neutral[86]; // default previously defined in Standfirst.tsx -}; - -const backgroundFilterButtonHover = (format: ArticleFormat): string => { - switch (format.theme) { - case Pillar.News: - return news[200]; - case Pillar.Culture: - return culture[200]; - case Pillar.Lifestyle: - return lifestyle[200]; - case Pillar.Sport: - return sport[200]; - case Pillar.Opinion: - return opinion[200]; - case ArticleSpecial.Labs: - return labs[200]; - case ArticleSpecial.SpecialReport: - return specialReport[200]; - default: - return news[200]; - } -}; - -const backgroundFilterButtonActive = (format: ArticleFormat): string => { - switch (format.theme) { - case Pillar.News: - return news[300]; - case Pillar.Culture: - return culture[300]; - case Pillar.Lifestyle: - return lifestyle[300]; - case Pillar.Sport: - return sport[300]; - case Pillar.Opinion: - return opinion[300]; - case ArticleSpecial.Labs: - return labs[300]; - case ArticleSpecial.SpecialReport: - return specialReport[300]; - default: - return news[300]; - } -}; - -const fillGuardianLogo = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.NewsletterSignup) return WHITE; - - return WHITE; -}; - -const borderStandfirstLink = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.LiveBlog) { - switch (format.theme) { - case Pillar.News: - return news[600]; - case Pillar.Culture: - return culture[400]; - case Pillar.Lifestyle: - return lifestyle[500]; - case Pillar.Sport: - return sport[600]; - case Pillar.Opinion: - return opinion[500]; - case ArticleSpecial.Labs: - return news[600]; - case ArticleSpecial.SpecialReport: - return specialReport[450]; - case ArticleSpecial.SpecialReportAlt: - return news[600]; - } - } - if (format.theme === ArticleSpecial.SpecialReport) { - return specialReport[400]; - } - - if (format.theme === ArticleSpecial.SpecialReportAlt) { - return transparentColour(neutral[60], 0.3); - } - - return palette.neutral[86]; -}; - -const borderHeadline = (format: ArticleFormat): string => { - if (format.design === ArticleDesign.LiveBlog) { - return 'rgba(255,255,255, 0.2)'; - } - if (format.design === ArticleDesign.DeadBlog) return '#CDCDCD'; - return palette.neutral[86]; -}; - -const borderCardSupporting = (format: ArticleFormat): string => { - switch (format.design) { - case ArticleDesign.Comment: - case ArticleDesign.Letter: - switch (format.theme) { - case ArticleSpecial.SpecialReport: - return opinion[550]; - default: - return neutral[86]; - } - case ArticleDesign.LiveBlog: - switch (format.theme) { - case Pillar.News: - return news[600]; - case Pillar.Sport: - return sport[600]; - case Pillar.Opinion: - return WHITE; - case Pillar.Culture: - return culture[600]; - case Pillar.Lifestyle: - return lifestyle[500]; - case ArticleSpecial.SpecialReport: - return brandAlt[400]; - case ArticleSpecial.Labs: - default: - return neutral[86]; - } - case ArticleDesign.Gallery: - case ArticleDesign.Audio: - case ArticleDesign.Video: - switch (format.theme) { - case ArticleSpecial.SpecialReport: - return brandAlt[400]; - case ArticleSpecial.SpecialReportAlt: - return news[600]; - case Pillar.News: - return news[600]; - case Pillar.Sport: - return sport[600]; - case Pillar.Opinion: - return opinion[550]; - case Pillar.Lifestyle: - return lifestyle[500]; - case Pillar.Culture: - return culture[500]; - case ArticleSpecial.Labs: - return labs[400]; - default: - return neutral[86]; - } - default: - switch (format.theme) { - case ArticleSpecial.SpecialReport: - return brandAltBackground.primary; - default: - return neutral[86]; - } - } -}; - -const hoverStandfirstLink = (format: ArticleFormat): string => { - return textStandfirstLink(format); -}; - -const borderNavPillar: (format: ArticleFormat) => string = (format) => - pillarPalette[format.theme].bright; - -const borderLines = (format: ArticleFormat): string => { - if (format.theme === ArticleSpecial.Labs) return neutral[60]; - if ( - format.theme === ArticleSpecial.SpecialReport && - (format.design === ArticleDesign.Comment || - format.design === ArticleDesign.Letter) - ) { - return neutral[46]; - } - - if ( - format.theme === ArticleSpecial.SpecialReportAlt && - (format.design === ArticleDesign.Comment || - format.design === ArticleDesign.Letter) - ) { - return transparentColour(neutral[60], 0.3); - } - if (format.design === ArticleDesign.Picture) { - return transparentColour(neutral[60], 0.5); - } - - return neutral[86]; -}; - -const borderFilterButton = (): string => neutral[60]; - -const backgroundAnalysisContrastColour = (): string => '#F2E8E6'; -const backgroundAnalysisContrastHoverColour = (): string => '#e9d9d5'; - -const textBetaLabel = (): string => neutral[46]; - -const textDateLine = (format: ArticleFormat): string => { - if ( - format.theme === ArticleSpecial.SpecialReportAlt && - format.design !== ArticleDesign.DeadBlog && - format.design !== ArticleDesign.LiveBlog - ) { - return palette.specialReportAlt[100]; - } - - return neutral[46]; -}; - -const textFilterButton = (): string => neutral[7]; - -const textFilterButtonHover = (): string => neutral[100]; - -const textFilterButtonActive = (): string => neutral[100]; - -const backgroundFilterButton = (): string => neutral[100]; - -const backgroundTreat = (format: ArticleFormat): string => { - switch (format.theme) { - case Pillar.News: - return news[300]; - case Pillar.Sport: - return sport[300]; - case Pillar.Lifestyle: - return lifestyle[300]; - case Pillar.Culture: - return culture[300]; - case Pillar.Opinion: - return opinion[300]; - case ArticleSpecial.Labs: - return labs[300]; - case ArticleSpecial.SpecialReport: - return specialReport[300]; - case ArticleSpecial.SpecialReportAlt: - return news[300]; - } -}; - -const textExpandableAtom = (format: ArticleFormat) => { - switch (format.theme) { - case Pillar.News: - return news[300]; - case Pillar.Lifestyle: - return lifestyle[300]; - case Pillar.Sport: - return sport[300]; - case Pillar.Culture: - return culture[300]; - case Pillar.Opinion: - return opinion[300]; - case ArticleSpecial.Labs: - return lifestyle[300]; - case ArticleSpecial.SpecialReport: - return news[300]; - case ArticleSpecial.SpecialReportAlt: - return news[300]; - } -}; - -const textExpandableAtomHover = (format: ArticleFormat) => { - switch (format.theme) { - case Pillar.News: - return news[400]; - case Pillar.Lifestyle: - return lifestyle[400]; - case Pillar.Sport: - return sport[400]; - case Pillar.Culture: - return culture[400]; - case Pillar.Opinion: - return opinion[400]; - case ArticleSpecial.Labs: - return lifestyle[400]; - case ArticleSpecial.SpecialReport: - return news[400]; - case ArticleSpecial.SpecialReportAlt: - return news[400]; - } -}; - -export const decidePalette = (format: ArticleFormat): Palette => { - return { - text: { - standfirst: textStandfirst(format), - standfirstLink: textStandfirstLink(format), - disclaimerLink: textDisclaimerLink(format), - filterButton: textFilterButton(), - filterButtonHover: textFilterButtonHover(), - filterButtonActive: textFilterButtonActive(), - betaLabel: textBetaLabel(), - dateLine: textDateLine(format), - expandableAtom: textExpandableAtom(format), - expandableAtomHover: textExpandableAtomHover(format), - }, - background: { - analysisContrast: backgroundAnalysisContrastColour(), - analysisContrastHover: backgroundAnalysisContrastHoverColour(), - bulletStandfirst: backgroundBulletStandfirst(format), - filterButton: backgroundFilterButton(), - filterButtonHover: backgroundFilterButtonHover(format), - filterButtonActive: backgroundFilterButtonActive(format), - treat: backgroundTreat(format), - }, - fill: { - guardianLogo: fillGuardianLogo(format), - }, - border: { - standfirstLink: borderStandfirstLink(format), - headline: borderHeadline(format), - navPillar: borderNavPillar(format), - lines: borderLines(format), - cardSupporting: borderCardSupporting(format), - filterButton: borderFilterButton(), - }, - hover: { - standfirstLink: hoverStandfirstLink(format), - }, - }; -}; diff --git a/dotcom-rendering/src/lib/pillars.ts b/dotcom-rendering/src/lib/pillars.ts index d2f8ac09a2..cd7e756601 100644 --- a/dotcom-rendering/src/lib/pillars.ts +++ b/dotcom-rendering/src/lib/pillars.ts @@ -61,8 +61,9 @@ type SpecialAltPalette = { 800: ColourType; }; -// pillarPalette_DO_NOT_USE should no longer be used. Use palette from decidePalette instead - +/** + * @deprecated Use `palette` from `palette.ts` instead + */ export const pillarPalette_DO_NOT_USE: Record< ArticleTheme, PillarPalette | SpecialPalette | LabsPalette | SpecialAltPalette From 9361e94f986093e8811c4c37d692b3ec6a7f2d5d Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Mon, 6 Jan 2025 11:29:43 +0000 Subject: [PATCH 17/19] Fix submeta scaling (#13066) The apps include a feature for scaling text and other aspects of the UI by changing the root font size of the page. When this feature is used it causes the fonts in the submeta to scale up and down. However, the dotted lines that appear between the tag links are created using a background image that relies on precise alignment, calculated in pixels. When the fonts scale this alignment is lost, and the lines start to overlap the text in the tag links. To fix this, this change migrates several pixel values to their `rem` equivalents. This allows the lengths in the background image, and other aspects of the submeta design, to scale proportionately with the change in root font size. --- dotcom-rendering/src/components/SubMeta.tsx | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dotcom-rendering/src/components/SubMeta.tsx b/dotcom-rendering/src/components/SubMeta.tsx index 38634acf44..aa1ac68b95 100644 --- a/dotcom-rendering/src/components/SubMeta.tsx +++ b/dotcom-rendering/src/components/SubMeta.tsx @@ -62,34 +62,34 @@ const listStyleNone = css` background-image: repeating-linear-gradient( to bottom, ${palette('--sub-meta-background')} 0px, - ${palette('--sub-meta-background')} 36px, - transparent 36px, - transparent 37px, - ${palette('--sub-meta-background')} 37px, - ${palette('--sub-meta-background')} 48px + ${palette('--sub-meta-background')} 2.25rem, + transparent 2.25rem, + transparent 2.3125rem, + ${palette('--sub-meta-background')} 2.3125rem, + ${palette('--sub-meta-background')} 3rem ), repeating-linear-gradient( to right, ${palette('--article-border')} 0px, - ${palette('--article-border')} 3px, - transparent 3px, - transparent 5px + ${palette('--article-border')} 0.1875rem, + transparent 0.1875rem, + transparent 0.3125rem ); background-position: top; background-repeat: no-repeat; `; const listWrapper = css` - padding-bottom: 12px; + padding-bottom: 0.75rem; margin-bottom: 6px; border-bottom: 1px solid ${palette('--article-border')}; `; const listItemStyles = css` ${textSans14}; - border: 1px solid ${palette('--sub-meta-text')}; - border-radius: 12px; - padding: 2px 9px; + border: 0.0625rem solid ${palette('--sub-meta-text')}; + border-radius: 0.75rem; + padding: 0.125rem 0.5625rem; :hover { background-color: ${palette('--sub-meta-text')}; a { From 034cb8706935bb1f452e9c5fbbba3c694fdd249c Mon Sep 17 00:00:00 2001 From: Dominik Lander Date: Mon, 6 Jan 2025 11:29:55 +0000 Subject: [PATCH 18/19] Remove US Banner AB test --- .../ExpandableMarketingCard.stories.tsx | 69 ---- .../components/ExpandableMarketingCard.tsx | 313 ------------------ .../ExpandableMarketingCardSwipeable.tsx | 306 ----------------- ...andableMarketingCardWrapper.importable.tsx | 171 ---------- dotcom-rendering/src/experiments/ab-tests.ts | 2 - .../tests/usa-expandable-marketing-card.ts | 41 --- dotcom-rendering/src/layouts/AudioLayout.tsx | 113 ++----- .../src/layouts/CommentLayout.tsx | 61 ---- .../src/layouts/ImmersiveLayout.tsx | 60 ---- .../src/layouts/PictureLayout.tsx | 10 +- .../src/layouts/ShowcaseLayout.tsx | 60 ---- .../src/layouts/StandardLayout.tsx | 119 ++----- dotcom-rendering/src/lib/getZIndex.ts | 3 - dotcom-rendering/src/paletteDeclarations.ts | 42 --- .../logos/red-blue-large-banner-bottom.svg | 43 --- .../logos/red-blue-large-banner-faded.svg | 45 --- .../logos/red-blue-large-banner-top.svg | 43 --- .../logos/red-blue-small-banner-bottom.svg | 43 --- .../logos/red-blue-small-banner-faded.svg | 33 -- .../logos/red-blue-small-banner-top.svg | 31 -- 20 files changed, 57 insertions(+), 1551 deletions(-) delete mode 100644 dotcom-rendering/src/components/ExpandableMarketingCard.stories.tsx delete mode 100644 dotcom-rendering/src/components/ExpandableMarketingCard.tsx delete mode 100644 dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx delete mode 100644 dotcom-rendering/src/components/ExpandableMarketingCardWrapper.importable.tsx delete mode 100644 dotcom-rendering/src/experiments/tests/usa-expandable-marketing-card.ts delete mode 100644 dotcom-rendering/src/static/logos/red-blue-large-banner-bottom.svg delete mode 100644 dotcom-rendering/src/static/logos/red-blue-large-banner-faded.svg delete mode 100644 dotcom-rendering/src/static/logos/red-blue-large-banner-top.svg delete mode 100644 dotcom-rendering/src/static/logos/red-blue-small-banner-bottom.svg delete mode 100644 dotcom-rendering/src/static/logos/red-blue-small-banner-faded.svg delete mode 100644 dotcom-rendering/src/static/logos/red-blue-small-banner-top.svg diff --git a/dotcom-rendering/src/components/ExpandableMarketingCard.stories.tsx b/dotcom-rendering/src/components/ExpandableMarketingCard.stories.tsx deleted file mode 100644 index 1725d3ad17..0000000000 --- a/dotcom-rendering/src/components/ExpandableMarketingCard.stories.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { type Meta, type StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; -import { - centreColumnDecorator, - leftColumnDecorator, -} from '../../.storybook/decorators/gridDecorators'; -import { ExpandableMarketingCard } from './ExpandableMarketingCard'; - -const meta = { - component: ExpandableMarketingCard, - title: 'Components/ExpandableMarketingCard', -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default = { - args: { - guardianBaseURL: 'https://www.theguardian.com', - heading: 'Pop your US news bubble', - kicker: 'How the Guardian is different', - isExpanded: false, - setIsClosed: fn(), - }, - decorators: [centreColumnDecorator], - parameters: { - viewport: { - defaultViewport: 'mobileMedium', - }, - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.desktop, - ], - }, - }, -} satisfies Story; - -// LeftCol and larger screen sizes -export const DefaultLargeScreens = { - ...Default, - decorators: [leftColumnDecorator], - parameters: { - viewport: { - defaultViewport: 'leftCol', - }, - chromatic: { - viewports: [breakpoints.leftCol, breakpoints.wide], - }, - }, -} satisfies Story; - -export const Expanded = { - ...Default, - args: { - ...Default.args, - isExpanded: true, - }, -} satisfies Story; - -export const ExpandedLargeScreens = { - ...DefaultLargeScreens, - args: { - ...DefaultLargeScreens.args, - isExpanded: true, - }, -} satisfies Story; diff --git a/dotcom-rendering/src/components/ExpandableMarketingCard.tsx b/dotcom-rendering/src/components/ExpandableMarketingCard.tsx deleted file mode 100644 index dcaaacce00..0000000000 --- a/dotcom-rendering/src/components/ExpandableMarketingCard.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import type { SerializedStyles } from '@emotion/react'; -import { css } from '@emotion/react'; -import { - from, - headlineBold17, - headlineBold20, - neutral, - space, - textSans14, - textSans15, - textSansBold12, - textSansBold14, -} from '@guardian/source/foundations'; -import { - LinkButton, - SvgChevronDownSingle, - SvgCross, -} from '@guardian/source/react-components'; -import type { Dispatch, SetStateAction } from 'react'; -import { palette } from '../palette'; -import { useConfig } from './ConfigContext'; - -interface BannersIllustrationProps { - type: 'faded' | 'top' | 'bottom'; - styles?: SerializedStyles; -} - -const smallIllustrationStyles = css` - display: none; - ${from.phablet} { - display: inline; - } - ${from.leftCol} { - display: none; - } -`; -const largeIllustrationStyles = css` - display: inline; - ${from.phablet} { - display: none; - } - ${from.leftCol} { - display: inline; - } -`; - -const BannersIllustration = ({ type, styles }: BannersIllustrationProps) => { - const { assetOrigin } = useConfig(); - const smallSrc = `${assetOrigin}static/frontend/logos/red-blue-small-banner-${type}.svg`; - const largeSrc = `${assetOrigin}static/frontend/logos/red-blue-large-banner-${type}.svg`; - - return ( - <> - - - - ); -}; - -const fillBarStyles = css` - background-color: ${palette('--expandable-marketing-card-fill-background')}; - width: 100%; - margin-top: -1px; - margin-bottom: -1px; - - height: 6px; - ${from.desktop} { - height: 4px; - } -`; - -const contentStyles = css` - position: relative; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: start; - gap: ${space[4]}px; - height: auto; - width: auto; - padding: ${space[2]}px; - border-radius: 0 0 ${space[2]}px ${space[2]}px; - background-color: ${palette('--expandable-marketing-card-background')}; - color: ${neutral[100]}; -`; - -const summaryStyles = css` - display: flex; - flex-direction: column; - gap: ${space[3]}px; - z-index: 1; - width: 100%; -`; - -const contractedSummaryStyles = css` - ${summaryStyles} - cursor: pointer; -`; - -const headingStyles = css` - display: flex; - gap: ${space[2]}px; - justify-content: space-between; - - ${headlineBold17}; - ${from.wide} { - ${headlineBold20}; - } -`; - -const kickerStyles = css` - ${textSans15}; - ${from.leftCol} { - ${textSans14}; - } - ${from.wide} { - ${textSans15}; - } -`; - -const arrowStyles = css` - flex-shrink: 0; - display: inline-flex; - align-items: center; - border: none; - vertical-align: middle; - justify-content: center; - padding: 0; - width: ${space[6]}px; - height: ${space[6]}px; - background-color: ${palette('--expandable-marketing-card-svg-background')}; - border-radius: 50%; - cursor: pointer; - - svg { - flex: 0 0 auto; - display: block; - fill: ${palette('--expandable-marketing-card-svg-fill')}; - position: relative; - width: ${space[5]}px; - height: auto; - } -`; - -const detailsStyles = css` - display: flex; - flex-direction: column; - gap: ${space[4]}px; - margin-bottom: ${space[2]}px; -`; - -const sectionStyles = css` - display: flex; - flex-direction: column; - gap: ${space[3]}px; - border-top: 1px solid ${neutral[100]}; - padding-top: ${space[2]}px; -`; - -const subheadingStyles = css` - ${headlineBold17}; -`; - -const paragraphStyles = css` - ${textSans15} - margin-right: ${space[4]}px; - z-index: 1; -`; - -const ctaCalloutStyles = css` - ${textSansBold14}; - z-index: 1; -`; - -const imageTopStyles = css` - position: absolute; - top: 0; - right: 0; -`; -const imageBottomStyles = css` - position: absolute; - right: 0; - bottom: 0; -`; - -const buttonStyles = css` - z-index: 1; - background-color: ${palette( - '--expandable-marketing-card-button-background', - )}; - width: fit-content; - - ${textSansBold12}; - ${from.wide} { - ${textSansBold14}; - } -`; - -interface Props { - heading: string; - kicker: string; - guardianBaseURL: string; - isExpanded: boolean; - setIsClosed: Dispatch>; -} - -export const ExpandableMarketingCard = ({ - guardianBaseURL, - heading, - kicker, - isExpanded, - setIsClosed, -}: Props) => { - return ( - <> -
-
- {!isExpanded ? ( - <> - -
-
-

{heading}

-
- -
-
-
{kicker}
-
- - ) : ( - <> - - -
-
-

{heading}

- -
-
{kicker}
-
-
-
-

- We’re independent -

-

- With no billionaire owner or shareholders, - our journalism is funded by readers -

-
-
-

We’re open

-

- With misinformation threatening democracy, - we keep our fact-based news paywall-free -

-
-
-

We’re global

-

- With 200 years of history and staff across - America and the world, we offer an outsider - perspective on US news -

-
-
-

- Sign up for Guardian Headlines US edition -

-
- - Newsletter sign up - -
- - )} -
- - ); -}; diff --git a/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx b/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx deleted file mode 100644 index c56ddf9bcb..0000000000 --- a/dotcom-rendering/src/components/ExpandableMarketingCardSwipeable.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import { css } from '@emotion/react'; -import { type Dispatch, type SetStateAction, useEffect, useState } from 'react'; -import { getOphan, submitComponentEvent } from '../client/ophan/ophan'; -import { getZIndex } from '../lib/getZIndex'; -import { ExpandableMarketingCard } from './ExpandableMarketingCard'; -import type { UsBannerTestVariantName } from './ExpandableMarketingCardWrapper.importable'; - -// The minimum length of the left swipe on the x-axis to close the banner -const LEFT_SWIPE_THRESHOLD_PX = 20; - -const isLeftSwipe = (thisXCoord: number, prevXCoord: number | null) => { - if (prevXCoord === null) { - return false; - } - - return thisXCoord + LEFT_SWIPE_THRESHOLD_PX < prevXCoord; -}; - -const isViewportBelowTopOfBody = (topOfBody: Element) => - topOfBody.getBoundingClientRect().top < 0; - -interface Props { - heading: string; - kicker: string; - guardianBaseURL: string; - isExpanded: boolean; - setIsExpanded: Dispatch>; - setIsClosed: Dispatch>; - abTestVariant: UsBannerTestVariantName; -} - -const hideBannerStyles = css` - left: -1200px; - transition: all 2.5s linear; -`; - -const stickyContainerStyles = css` - position: sticky; - top: 0; - z-index: ${getZIndex('expandableMarketingCardOverlay')}; - - /* The component slides in from the left-hand side */ - animation: slidein 2.4s linear; - - @keyframes slidein { - from { - translate: -1200px 0; - } - - to { - translate: 0 0; - } - } -`; - -const absoluteContainerStyles = css` - position: absolute; - width: 100%; -`; - -const isClick = ( - pointerDownCoords: [number, number], - pointerUpCoords: [number | null, number | null], -) => { - const [lastDownXCoord, lastDownYCoord] = pointerDownCoords; - const [lastUpXCoord, lastUpYCoord] = pointerUpCoords; - - if (lastUpXCoord === null || lastUpYCoord === null) return false; - - /** - * Allow a 5px margin of error to identify a click. We want to - * err on the side of expanding the banner if it is touched. - */ - return ( - Math.abs(lastUpXCoord - lastDownXCoord) < 5 && - Math.abs(lastUpYCoord - lastDownYCoord) < 5 - ); -}; - -/** - * Provides a swipeable wrapper around the ExpandableMarketingCard component. - * This component can be dismissed by swiping it to the left. - */ -export const ExpandableMarketingCardSwipeable = ({ - guardianBaseURL, - heading, - kicker, - isExpanded, - setIsExpanded, - setIsClosed, - abTestVariant, -}: Props) => { - const [lastDownXCoord, setLastDownXCoord] = useState(null); - const [lastDownYCoord, setLastDownYCoord] = useState(null); - const [isDown, setIsDown] = useState(false); - const [hasSwipedLeft, setHasSwipedLeft] = useState(false); - const [topOfBody, setTopOfBody] = useState(null); - - /** - * On screen sizes below leftCol (<1140px), only display the card - * when the user scrolls past the top of the article. - */ - const [shouldDisplayCard, setShouldDisplayCard] = useState(false); - - // The distance to the left from the pointer down and current pointer, whilst the pointer is still down. - const [dragLeftDistance, setDragLeftDistance] = useState(0); - - useEffect(() => { - setTopOfBody(document.querySelector('[data-gu-name="body"]')); - }, []); - - useEffect(() => { - if (!topOfBody) return; - - /** - * It's possible that the viewport does not start at the top of the page. - */ - if (isViewportBelowTopOfBody(topOfBody)) { - setShouldDisplayCard(true); - return; - } - - /** - * Show the card when the top of the body moves out of the viewport. - */ - const observer = new window.IntersectionObserver( - ([entry]) => { - if (!entry) return; - if (entry.isIntersecting) { - setShouldDisplayCard(true); - } - }, - { rootMargin: '0px 0px -100%' }, - ); - - observer.observe(topOfBody); - - return () => { - observer.disconnect(); - }; - }, [topOfBody]); - - useEffect(() => { - const reportView = () => { - void getOphan('Web').then((ophan) => { - ophan.record({ - interaction: { - component: 'us-expandable-marketing-card', - value: 'us-expandable-marketing-card visible', - }, - }); - }); - }; - if (shouldDisplayCard) { - reportView(); - } - }, [shouldDisplayCard]); - - useEffect(() => { - if (isExpanded) { - void submitComponentEvent( - { - component: { - componentType: 'CONTAINER', - id: 'us-expandable-marketing-card', - }, - action: 'EXPAND', - abTest: { - name: 'UsaExpandableMarketingCard', - variant: abTestVariant, - }, - }, - 'Web', - ); - } - }, [abTestVariant, isExpanded]); - - useEffect(() => { - if (hasSwipedLeft) { - void submitComponentEvent( - { - component: { - componentType: 'CONTAINER', - id: 'us-expandable-marketing-card', - }, - action: 'CLOSE', - abTest: { - name: 'UsaExpandableMarketingCard', - variant: abTestVariant, - }, - }, - 'Web', - ); - } - }, [abTestVariant, hasSwipedLeft]); - - if (!shouldDisplayCard) { - return null; - } - - if (!isExpanded) { - return ( -
-
{ - if (event.key === 'Enter') { - setIsExpanded(true); - } - if (event.key === 'Escape') { - setIsClosed(true); - } - }} - onPointerDown={(event) => { - event.preventDefault(); - - setIsDown(true); - - setLastDownXCoord(event.clientX); - setLastDownYCoord(event.clientY); - }} - onPointerUp={(event) => { - event.preventDefault(); - - setIsDown(false); - - if ( - isClick( - [event.clientX, event.clientY], - [lastDownXCoord, lastDownYCoord], - ) - ) { - setIsExpanded(true); - } else if (isLeftSwipe(event.clientX, lastDownXCoord)) { - setHasSwipedLeft(true); - } - }} - onPointerMove={(event) => { - event.preventDefault(); - - if (isDown) { - setDragLeftDistance( - Math.min( - 0, - event.clientX - (lastDownXCoord ?? 0), - ), - ); - } - }} - onTouchStart={(event) => { - event.preventDefault(); - }} - > - -
-
- ); - } - - return ( -
-
{ - if (event.key === 'Escape') { - setIsClosed(true); - } - }} - > - -
-
- ); -}; diff --git a/dotcom-rendering/src/components/ExpandableMarketingCardWrapper.importable.tsx b/dotcom-rendering/src/components/ExpandableMarketingCardWrapper.importable.tsx deleted file mode 100644 index 1699681632..0000000000 --- a/dotcom-rendering/src/components/ExpandableMarketingCardWrapper.importable.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import type { ABTestAPI } from '@guardian/ab-core'; -import { getCookie } from '@guardian/libs'; -import { useEffect, useState } from 'react'; -import type { DailyArticle } from '../lib/dailyArticleCount'; -import { getDailyArticleCount } from '../lib/dailyArticleCount'; -import { getLocaleCode } from '../lib/getCountryCode'; -import { useAB } from '../lib/useAB'; -import { ExpandableMarketingCard } from './ExpandableMarketingCard'; -import { ExpandableMarketingCardSwipeable } from './ExpandableMarketingCardSwipeable'; -import { Hide } from './Hide'; - -export type UsBannerTestVariantName = - | 'variant-free' - | 'variant-bubble' - | 'variant-billionaire'; - -type Variant = { - heading: string; - kicker: string; -}; - -const variantFree: Variant = { - heading: 'Yes, this story is free', - kicker: 'Why the Guardian has no paywall', -}; -const variantBubble: Variant = { - heading: 'Pop your US news bubble', - kicker: 'How the Guardian is different', -}; -const variantBillionaire: Variant = { - heading: 'No billionaire approved this', - kicker: 'How the Guardian is different', -}; - -const getVariant = ( - abTestAPI: ABTestAPI | undefined, -): UsBannerTestVariantName | null => { - if (!abTestAPI) { - return null; - } - - const isInVariantFree = abTestAPI.isUserInVariant( - 'UsaExpandableMarketingCard', - 'variant-free', - ); - const isInVariantBubble = abTestAPI.isUserInVariant( - 'UsaExpandableMarketingCard', - 'variant-bubble', - ); - const isInVariantBillionaire = abTestAPI.isUserInVariant( - 'UsaExpandableMarketingCard', - 'variant-billionaire', - ); - - if (isInVariantFree) { - return 'variant-free'; - } - - if (isInVariantBubble) { - return 'variant-bubble'; - } - - if (isInVariantBillionaire) { - return 'variant-billionaire'; - } - - return null; -}; - -const getVariantCopy = (variant: UsBannerTestVariantName): Variant => { - if (variant === 'variant-free') { - return variantFree; - } - - if (variant === 'variant-bubble') { - return variantBubble; - } - - return variantBillionaire; -}; - -const isFirstOrSecondArticle = () => { - const [dailyCount = {} as DailyArticle] = getDailyArticleCount() ?? []; - return Object.keys(dailyCount).length === 0 || dailyCount.count <= 1; -}; - -const isNewUSUser = async () => { - const isUserInUS = (await getLocaleCode()) === 'US'; - if (!isUserInUS) { - return false; - } - - // Exclude users who have selected a non-US edition. - const editionCookie = getCookie({ name: 'GU_EDITION' }); - const hasUserSelectedNonUSEdition = - !!editionCookie && editionCookie !== 'US'; - - // This check must happen AFTER we've ensured that the user is in the US. - const isNewUser = isFirstOrSecondArticle(); - - return !hasUserSelectedNonUSEdition && isNewUser; -}; - -interface Props { - guardianBaseURL: string; -} - -export const ExpandableMarketingCardWrapper = ({ guardianBaseURL }: Props) => { - const [isExpanded, setIsExpanded] = useState(false); - const [isClosed, setIsClosed] = useState(false); - const [isApplicableUser, setIsApplicableUser] = useState(false); - - const abTestAPI = useAB()?.api; - const abTestVariant = getVariant(abTestAPI); - - useEffect(() => { - void isNewUSUser().then((show) => { - if (show) { - setIsApplicableUser(true); - } - }); - }, []); - - if (!abTestVariant || !isApplicableUser || isClosed) { - return null; - } - - const { heading, kicker } = getVariantCopy(abTestVariant); - - return ( - <> - -
{ - if (event.key === 'Enter' && !isExpanded) { - setIsExpanded(true); - } - if (event.key === 'Escape') { - setIsClosed(true); - } - }} - onClick={() => { - !isExpanded && setIsExpanded(true); - }} - > - -
-
- - - - - ); -}; diff --git a/dotcom-rendering/src/experiments/ab-tests.ts b/dotcom-rendering/src/experiments/ab-tests.ts index f00fb644ad..e0ebcd9f97 100644 --- a/dotcom-rendering/src/experiments/ab-tests.ts +++ b/dotcom-rendering/src/experiments/ab-tests.ts @@ -8,7 +8,6 @@ import { onwardsContentArticle } from './tests/onwards-content-article'; import { optimiseSpacefinderInline } from './tests/optimise-spacefinder-inline'; import { signInGateMainControl } from './tests/sign-in-gate-main-control'; import { signInGateMainVariant } from './tests/sign-in-gate-main-variant'; -import { UsaExpandableMarketingCard } from './tests/usa-expandable-marketing-card'; // keep in sync with ab-tests in frontend // https://github.com/guardian/frontend/tree/main/static/src/javascripts/projects/common/modules/experiments/ab-tests.ts @@ -21,6 +20,5 @@ export const tests: ABTest[] = [ mpuWhenNoEpic, adBlockAsk, optimiseSpacefinderInline, - UsaExpandableMarketingCard, onwardsContentArticle, ]; diff --git a/dotcom-rendering/src/experiments/tests/usa-expandable-marketing-card.ts b/dotcom-rendering/src/experiments/tests/usa-expandable-marketing-card.ts deleted file mode 100644 index 7e1204bebc..0000000000 --- a/dotcom-rendering/src/experiments/tests/usa-expandable-marketing-card.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { ABTest } from '@guardian/ab-core'; - -export const UsaExpandableMarketingCard: ABTest = { - id: 'UsaExpandableMarketingCard', - start: '2024-10-02', - expiry: '2024-12-18', - author: 'dotcom.platform@guardian.co.uk', - description: - 'Test the impact of showing the user a component that highlights the Guardians journalism.', - audience: 40 / 100, - audienceOffset: 0 / 100, - audienceCriteria: 'US-based users that see the US edition.', - successMeasure: 'Users are more likely to engage with the site.', - canRun: () => true, - variants: [ - { - id: 'control', - test: (): void => { - /* no-op */ - }, - }, - { - id: 'variant-free', - test: (): void => { - /* no-op */ - }, - }, - { - id: 'variant-bubble', - test: (): void => { - /* no-op */ - }, - }, - { - id: 'variant-billionaire', - test: (): void => { - /* no-op */ - }, - }, - ], -}; diff --git a/dotcom-rendering/src/layouts/AudioLayout.tsx b/dotcom-rendering/src/layouts/AudioLayout.tsx index e2e64717ca..ba9a7dd2a5 100644 --- a/dotcom-rendering/src/layouts/AudioLayout.tsx +++ b/dotcom-rendering/src/layouts/AudioLayout.tsx @@ -5,7 +5,6 @@ import { space, until, } from '@guardian/source/foundations'; -import { Hide } from '@guardian/source/react-components'; import { StraightLines } from '@guardian/source-development-kitchen/react-components'; import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; import { AffiliateDisclaimer } from '../components/AffiliateDisclaimer'; @@ -18,7 +17,6 @@ import { AudioPlayerWrapper } from '../components/AudioPlayerWrapper.importable' import { Border } from '../components/Border'; import { Carousel } from '../components/Carousel.importable'; import { DiscussionLayout } from '../components/DiscussionLayout'; -import { ExpandableMarketingCardWrapper } from '../components/ExpandableMarketingCardWrapper.importable'; import { Footer } from '../components/Footer'; import { GridItem } from '../components/GridItem'; import { HeaderAdSlot } from '../components/HeaderAdSlot'; @@ -41,7 +39,6 @@ import { getAudioData } from '../lib/audio-data'; import { canRenderAds } from '../lib/canRenderAds'; import { getContributionsServiceUrl } from '../lib/contributions'; import { decideTrail } from '../lib/decideTrail'; -import { getZIndex } from '../lib/getZIndex'; import { parse } from '../lib/slot-machine-flags'; import type { NavType } from '../model/extract-nav'; import { palette as themePalette } from '../palette'; @@ -124,26 +121,6 @@ const maxWidth = css` } `; -const usCardStyles = css` - align-self: start; - position: sticky; - top: 0; - z-index: ${getZIndex('expandableMarketingCardOverlay')}; - - ${from.leftCol} { - margin-top: ${space[6]}px; - margin-bottom: ${space[9]}px; - - /* To align with rich links - if we move this feature to production, we should remove this and make rich link align with everything instead */ - margin-left: 1px; - margin-right: -1px; - } - - ${from.wide} { - margin-left: 0; - } -`; - interface Props { article: ArticleDeprecated; format: ArticleFormat; @@ -274,52 +251,34 @@ export const AudioLayout = (props: WebProps) => {
- <> -
- - {!!article.affiliateLinksDisclaimer && ( - - )} -
-
- - - - - -
- +
+ + {!!article.affiliateLinksDisclaimer && ( + + )} +
{audioData && ( @@ -355,22 +314,6 @@ export const AudioLayout = (props: WebProps) => { /> - - - - - { article.config.shortUrlId } /> - {isWeb && ( -
- - - - - -
- )} )}
- {isWeb && ( - - - - - - )}
{ {!!article.affiliateLinksDisclaimer && ( )} - {isWeb && ( -
- - - - - -
- )} )}
- {isWeb && ( - - - - - - )} ( 'title border headline' '. border standfirst' 'meta border media' - 'uscard border media' - 'uscard border submeta' - 'uscard border .' - 'uscard border .'; + 'meta border submeta'; } ${until.wide} { @@ -96,10 +93,7 @@ const PictureGrid = ({ children }: { children: React.ReactNode }) => ( 'title border headline headline headline' '. border standfirst standfirst standfirst' 'meta border media media media' - 'uscard border media media media' - 'uscard border submeta submeta submeta' - 'uscard border . . .' - 'uscard border . . .'; + 'meta border submeta submeta submeta'; } /* diff --git a/dotcom-rendering/src/layouts/ShowcaseLayout.tsx b/dotcom-rendering/src/layouts/ShowcaseLayout.tsx index 6300d3778b..5d2f67caf8 100644 --- a/dotcom-rendering/src/layouts/ShowcaseLayout.tsx +++ b/dotcom-rendering/src/layouts/ShowcaseLayout.tsx @@ -3,7 +3,6 @@ import { isUndefined } from '@guardian/libs'; import { from, palette as sourcePalette, - space, until, } from '@guardian/source/foundations'; import { Hide } from '@guardian/source/react-components'; @@ -22,7 +21,6 @@ import { Border } from '../components/Border'; import { Carousel } from '../components/Carousel.importable'; import { DecideLines } from '../components/DecideLines'; import { DiscussionLayout } from '../components/DiscussionLayout'; -import { ExpandableMarketingCardWrapper } from '../components/ExpandableMarketingCardWrapper.importable'; import { Footer } from '../components/Footer'; import { GridItem } from '../components/GridItem'; import { HeaderAdSlot } from '../components/HeaderAdSlot'; @@ -49,7 +47,6 @@ import { import { canRenderAds } from '../lib/canRenderAds'; import { getContributionsServiceUrl } from '../lib/contributions'; import { decideTrail } from '../lib/decideTrail'; -import { getZIndex } from '../lib/getZIndex'; import { decideLanguage, decideLanguageDirection } from '../lib/lang'; import { parse } from '../lib/slot-machine-flags'; import type { NavType } from '../model/extract-nav'; @@ -168,26 +165,6 @@ const fullHeight = css` height: 100%; `; -const usCardStyles = css` - align-self: start; - position: sticky; - top: 0; - z-index: ${getZIndex('expandableMarketingCardOverlay')}; - - ${from.leftCol} { - margin-top: ${space[6]}px; - margin-bottom: ${space[9]}px; - - /* To align with rich links - if we move this feature to production, we should remove this and make rich link align with everything instead */ - margin-left: 1px; - margin-right: -1px; - } - - ${from.wide} { - margin-left: 0; - } -`; - const stretchLines = css` ${until.phablet} { margin-left: -20px; @@ -550,48 +527,11 @@ export const ShowcaseLayout = (props: WebProps | AppsProps) => { {!!article.affiliateLinksDisclaimer && ( )} - - {isWeb && ( -
- - - - - -
- )} )}
- {isWeb && ( - - - - - - )} { ) : ( - <> -
- - {!!article.affiliateLinksDisclaimer && ( - - )} -
- {isWeb && ( -
- - - - - -
+
+ + {!!article.affiliateLinksDisclaimer && ( + )} - +
)}
- {isWeb && ( - - - - - - )} { } }; -const expandableMarketingCardBackground: PaletteFunction = () => - sourcePalette.brand[400]; - -const expandableMarketingCardSvgFill: PaletteFunction = () => - sourcePalette.neutral[0]; - -const expandableMarketingCardButtonBackground: PaletteFunction = () => - sourcePalette.neutral[100]; - -const expandableMarketingCardSvgBackground: PaletteFunction = () => - sourcePalette.neutral[100]; - -const expandableMarketingCardFillBackgroundLight: PaletteFunction = ( - format, -) => { - return articleBackgroundLight(format) === 'transparent' - ? sourcePalette.neutral[100] - : articleBackgroundLight(format); -}; -const expandableMarketingCardFillBackgroundDark: PaletteFunction = (format) => - articleBackgroundDark(format); - const youtubeOverlayKicker: PaletteFunction = ({ theme }: ArticleFormat) => { switch (theme) { case Pillar.News: @@ -6430,26 +6408,6 @@ const paletteColours = { light: expandableAtomTextHoverLight, dark: expandableAtomTextHoverDark, }, - '--expandable-marketing-card-background': { - light: expandableMarketingCardBackground, - dark: expandableMarketingCardBackground, - }, - '--expandable-marketing-card-button-background': { - light: expandableMarketingCardButtonBackground, - dark: expandableMarketingCardButtonBackground, - }, - '--expandable-marketing-card-fill-background': { - light: expandableMarketingCardFillBackgroundLight, - dark: expandableMarketingCardFillBackgroundDark, - }, - '--expandable-marketing-card-svg-background': { - light: expandableMarketingCardSvgBackground, - dark: expandableMarketingCardSvgBackground, - }, - '--expandable-marketing-card-svg-fill': { - light: expandableMarketingCardSvgFill, - dark: expandableMarketingCardSvgFill, - }, '--explainer-atom-accent': { light: explainerAtomAccentLight, dark: explainerAtomAccentDark, diff --git a/dotcom-rendering/src/static/logos/red-blue-large-banner-bottom.svg b/dotcom-rendering/src/static/logos/red-blue-large-banner-bottom.svg deleted file mode 100644 index 1c4d7d01ca..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-large-banner-bottom.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dotcom-rendering/src/static/logos/red-blue-large-banner-faded.svg b/dotcom-rendering/src/static/logos/red-blue-large-banner-faded.svg deleted file mode 100644 index da0cd0f7b6..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-large-banner-faded.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dotcom-rendering/src/static/logos/red-blue-large-banner-top.svg b/dotcom-rendering/src/static/logos/red-blue-large-banner-top.svg deleted file mode 100644 index 97f8d6367a..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-large-banner-top.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dotcom-rendering/src/static/logos/red-blue-small-banner-bottom.svg b/dotcom-rendering/src/static/logos/red-blue-small-banner-bottom.svg deleted file mode 100644 index e50ff3da4d..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-small-banner-bottom.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dotcom-rendering/src/static/logos/red-blue-small-banner-faded.svg b/dotcom-rendering/src/static/logos/red-blue-small-banner-faded.svg deleted file mode 100644 index 4ded194ae5..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-small-banner-faded.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dotcom-rendering/src/static/logos/red-blue-small-banner-top.svg b/dotcom-rendering/src/static/logos/red-blue-small-banner-top.svg deleted file mode 100644 index 8e80855e76..0000000000 --- a/dotcom-rendering/src/static/logos/red-blue-small-banner-top.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 07470d4c9960a52911c870097d129a40f5e24be6 Mon Sep 17 00:00:00 2001 From: Charlotte Emms <43961396+cemms1@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:11:14 +0000 Subject: [PATCH 19/19] unnest the containerLevel field in front collection (#13078) --- dotcom-rendering/src/layouts/FrontLayout.tsx | 4 +--- dotcom-rendering/src/model/enhanceCollections.ts | 2 +- dotcom-rendering/src/types/front.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index 5b262e0c17..c941774e85 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -742,9 +742,7 @@ export const FrontLayout = ({ front, NAV }: Props) => { collectionBranding={ collection.collectionBranding } - containerLevel={ - collection.config.containerLevel - } + containerLevel={collection.containerLevel} containerSpacing={collection.containerSpacing} >