diff --git a/ab-testing/config/abTests.ts b/ab-testing/config/abTests.ts index 9ce332fec59..3a16cf9b6c7 100644 --- a/ab-testing/config/abTests.ts +++ b/ab-testing/config/abTests.ts @@ -109,14 +109,14 @@ const ABTests: ABTest[] = [ { name: "fronts-and-curation-slim-homepage", description: - "Test placing the Most Viewed and Deeply Read components in the right-hand column on the homepage.", + "Test slimming content and placing Most Popular components on the right-hand side on the UK front.", owners: ["fronts.and.curation@guardian.co.uk"], status: "ON", - expirationDate: `2026-04-28`, + expirationDate: "2026-04-28", type: "server", audienceSize: 0 / 100, audienceSpace: "A", - groups: ["control", "variant"], + groups: ["control", "variant-one", "variant-two"], shouldForceMetricsCollection: false, }, { diff --git a/dotcom-rendering/.storybook/main.ts b/dotcom-rendering/.storybook/main.ts index dc727ad7b88..e3a1ab5c94b 100644 --- a/dotcom-rendering/.storybook/main.ts +++ b/dotcom-rendering/.storybook/main.ts @@ -103,24 +103,34 @@ const webpackConfig = (config: Configuration) => { const rules = config.module?.rules ?? []; config.resolve ??= {}; - config.resolve.alias ??= {}; - // Mock JSDOM for storybook - it relies on native node.js packages - // Allows us to use enhancers in stories for better testing of components & full articles - config.resolve.alias['jsdom$'] = path.resolve( - __dirname, - './mocks/jsdom.ts', - ); + config.resolve.alias = { + ...config.resolve?.alias, - // log4js tries to call "fs" in storybook -- we can ignore it - config.resolve.alias[ - `${path.resolve(__dirname, '../src/server/lib/logging')}$` - ] = path.resolve(__dirname, './mocks/log4js.ts'); + Buffer: 'buffer', + react: 'react', + 'react-dom': 'react-dom', - // Mock BridgetApi for storybook - config.resolve.alias[ - `${path.resolve(__dirname, '../src/lib/bridgetApi')}$` - ] = path.resolve(__dirname, './mocks/bridgetApi.ts'); + // Mock JSDOM for storybook - it relies on native node.js packages + // Allows us to use enhancers in stories for better testing of components & full articles + jsdom$: path.resolve(__dirname, './mocks/jsdom.ts'), + + // log4js tries to call "fs" in storybook -- we can ignore it + [`${path.resolve(__dirname, '../src/server/lib/logging')}$`]: + path.resolve(__dirname, './mocks/log4js.ts'), + + // Mock BridgetApi for storybook + [`${path.resolve(__dirname, '../src/lib/bridgetApi')}$`]: path.resolve( + __dirname, + './mocks/bridgetApi.ts', + ), + + // Mock identity auth frontend to prevent Storybook components from hanging in Pending + '@guardian/identity-auth-frontend': path.resolve( + __dirname, + './mocks/identityAuthFrontend.ts', + ), + }; const webpackLoaders = getLoaders('client.web'); @@ -149,12 +159,7 @@ const webpackConfig = (config: Configuration) => { config.resolve.modules = [ ...((config && config.resolve && config.resolve.modules) || []), ]; - config.resolve.alias = { - ...config.resolve.alias, - Buffer: 'buffer', - react: 'react', - 'react-dom': 'react-dom', - }; + return config; }; diff --git a/dotcom-rendering/.storybook/mocks/identityAuthFrontend.ts b/dotcom-rendering/.storybook/mocks/identityAuthFrontend.ts new file mode 100644 index 00000000000..22531ab7bc8 --- /dev/null +++ b/dotcom-rendering/.storybook/mocks/identityAuthFrontend.ts @@ -0,0 +1,8 @@ +export const getIdentityAuth = () => ({ + isSignedInWithAuthState: () => + Promise.resolve({ + isAuthenticated: false, + accessToken: undefined, + idToken: undefined, + }), +}); diff --git a/dotcom-rendering/fixtures/manual/frontCollections.ts b/dotcom-rendering/fixtures/manual/frontCollections.ts index 43c2e4d5b5b..3a3dabb0078 100644 --- a/dotcom-rendering/fixtures/manual/frontCollections.ts +++ b/dotcom-rendering/fixtures/manual/frontCollections.ts @@ -9,7 +9,7 @@ const defaultGrouped = { const defaultValues = { backfill: [], - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', config: { showDateHeader: false, }, @@ -35,13 +35,13 @@ export const testCollectionsUk = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'LongRunningAltPalette', displayName: 'Ukraine invasion', }, { ...defaultValues, - collectionType: 'fixed/small/slow-V-mpu', + collectionType: 'static/medium/4', displayName: 'News extra', }, { @@ -71,12 +71,12 @@ export const testCollectionsUk = [ }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Lifestyle', }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VII', + collectionType: 'flexible/general', displayName: 'Culture', }, { @@ -91,7 +91,7 @@ export const testCollectionsUk = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', displayName: 'The rural network', }, { @@ -111,23 +111,23 @@ export const testCollectionsUk = [ }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Multimedia', }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'Branded', displayName: 'Guardian Labs', }, { ...defaultValues, - collectionType: 'fixed/medium/slow-XII-mpu', + collectionType: 'flexible/general', displayName: 'Explore', }, { ...defaultValues, - collectionType: 'fixed/small/slow-I', + collectionType: 'flexible/general', displayName: 'The big picture', }, { @@ -162,7 +162,7 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', displayName: 'In depth', }, { @@ -172,13 +172,13 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'LongRunningAltPalette', displayName: 'Ukraine invasion', }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Opinion', }, { @@ -193,7 +193,7 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', displayName: 'Climate crisis', }, { @@ -218,7 +218,7 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VII', + collectionType: 'flexible/general', displayName: 'Podcasts', }, { @@ -228,28 +228,28 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Culture', }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'Branded', displayName: 'Business briefs', }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Lifestyle', }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', displayName: 'Take part', }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'Explore', }, { @@ -259,7 +259,7 @@ export const testCollectionsUs = [ }, { ...defaultValues, - collectionType: 'fixed/medium/slow-VI', + collectionType: 'flexible/general', displayName: 'In pictures', }, { @@ -282,7 +282,7 @@ export const brandedTestCollections = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'Branded', displayName: 'Guardian Labs', }, @@ -293,7 +293,7 @@ export const brandedTestCollections = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'Branded', displayName: 'Guardian Labs', }, @@ -304,7 +304,7 @@ export const brandedTestCollections = [ }, { ...defaultValues, - collectionType: 'fixed/small/slow-IV', + collectionType: 'static/medium/4', containerPalette: 'Branded', displayName: 'Guardian Labs', }, diff --git a/dotcom-rendering/fixtures/manual/show-more-trails.ts b/dotcom-rendering/fixtures/manual/show-more-trails.ts deleted file mode 100644 index 25c18a8840a..00000000000 --- a/dotcom-rendering/fixtures/manual/show-more-trails.ts +++ /dev/null @@ -1,1741 +0,0 @@ -/** - * This is the response as it's received from Frontend, and includes fields that - * the DCR types don't know about. - * Exporting this 'as' FEFrontCard because we know it should be of the right shape. - */ - -import type { FEFrontCard } from '../../src/frontend/feFront'; - -export const trails: [ - FEFrontCard, - FEFrontCard, - FEFrontCard, - FEFrontCard, - FEFrontCard, - FEFrontCard, -] = [ - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: false, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 3, - fields: { - displayCredit: 'true', - isMaster: 'true', - altText: - 'Woman stressed about big ladder in her tights', - mediaId: - 'a85912b00046f8ba0314e9064a0851ea17ef76d2', - width: '2124', - source: 'Getty', - photographer: 'artursfoto', - height: '1274', - credit: 'Photograph: artursfoto/Getty', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/a85912b00046f8ba0314e9064a0851ea17ef76d2/0_12_2124_1274/master/2124.jpg', - }, - ], - }, - byline: 'Chloe Mac Donnell', - thumbnailPath: - 'https://i.guim.co.uk/img/media/a85912b00046f8ba0314e9064a0851ea17ef76d2/0_12_2124_1274/500.jpg?quality=85&auto=format&fit=max&s=e6cf84c0603adefcde825d59df704d28', - webPublicationDate: 1666953304000, - }, - metadata: { - id: 'fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - webTitle: - 'Forever tights? Now that’s an idea that could really have legs', - webUrl: 'https://www.theguardian.com/fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - type: 'Article', - - sectionId: { - value: 'fashion', - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
Woman stressed about big ladder in her tights
The average woman spends £3,000 on tights in her lifetime. Photograph: artursfoto/Getty
', - body: '

Would you pay £62 for a pair of tights? What about if they were guaranteed not to rip, ladder or snag for at least three months? For anyone who frequently screams in frustration as they tear a pair while pulling them on, the world’s toughest tights – as they are being described – could prove tempting.

To tight or not to tight used to be a subject as divisive as the Conservatives’ economic policy. With energy bills rising, trying to emulate the year round bare-legged lifestyle of the 1% is no longer viable.

', - standfirst: - '

As the UK gets chillier, rip-resistant tights – embraced even by fashion brands – are coming into their own

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'fashion/tights', - url: '/fashion/tights', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Tights and socks', - webUrl: 'https://www.theguardian.com/fashion/tights', - }, - }, - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'fashion/lingerie', - url: '/fashion/lingerie', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Lingerie', - webUrl: 'https://www.theguardian.com/fashion/lingerie', - }, - }, - { - properties: { - id: 'type/article', - url: '/type/article', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Article', - webUrl: 'https://www.theguardian.com/articles', - }, - }, - { - properties: { - id: 'tone/news', - url: '/tone/news', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'News', - webUrl: 'https://www.theguardian.com/tone/news', - }, - }, - { - properties: { - id: 'tone/features', - url: '/tone/features', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Features', - webUrl: 'https://www.theguardian.com/tone/features', - }, - }, - { - properties: { - id: 'profile/chloe-mac-donnell', - url: '/profile/chloe-mac-donnell', - tagType: 'Contributor', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Chloe Mac Donnell', - webUrl: 'https://www.theguardian.com/profile/chloe-mac-donnell', - twitterHandle: 'tweetchloe', - bio: '

Chloe Mac Donnell is the Guardian\'s deputy fashion and lifestyle editor. Twitter @tweetchloe

', - }, - }, - { - properties: { - id: 'publication/theguardian', - url: '/publication/theguardian', - tagType: 'Publication', - sectionId: 'theguardian', - sectionName: 'From the Guardian', - webTitle: 'The Guardian', - webUrl: 'https://www.theguardian.com/theguardian/all', - }, - }, - { - properties: { - id: 'theguardian/mainsection', - url: '/theguardian/mainsection', - tagType: 'NewspaperBook', - sectionId: 'news', - sectionName: 'News', - webTitle: 'Main section', - webUrl: 'https://www.theguardian.com/theguardian/mainsection', - }, - }, - { - properties: { - id: 'theguardian/mainsection/uknews', - url: '/theguardian/mainsection/uknews', - tagType: 'NewspaperBookSection', - sectionId: 'uk-news', - sectionName: 'UK news', - webTitle: 'UK news', - webUrl: 'https://www.theguardian.com/theguardian/mainsection/uknews', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/uk-fashion', - url: '/tracking/commissioningdesk/uk-fashion', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'UK Fashion', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/uk-fashion', - }, - }, - ], - }, - }, - maybeContentId: - 'fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - isLiveBlog: false, - isCrossword: false, - byline: 'Chloe Mac Donnell', - webTitle: - 'Forever tights? Now that’s an idea that could really have legs', - linkText: - 'Forever tights? Now that’s an idea that could really have legs', - webUrl: 'https://www.theguardian.com/fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: false, - isGallery: false, - isAudio: false, - kicker: { - type: 'FreeHtmlKicker', - item: { - properties: { - kickerText: 'For ever tights? ', - }, - }, - }, - headline: 'Now that’s an idea that could really have legs', - url: '/fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - hasMainVideoElement: false, - }, - card: { - id: 'fashion/2022/oct/28/forever-tights-idea-really-have-legs-rip-resistant-fasion', - cardStyle: { - type: 'Feature', - }, - webPublicationDateOption: 1666953304000, - lastModifiedOption: 1667012469000, - trailText: - 'As the UK gets chillier, rip-resistant tights – embraced even by fashion brands – are coming into their own', - shortUrlPath: '/p/mhet4', - shortUrl: 'https://www.theguardian.com/p/mhet4', - group: '0', - isLive: false, - }, - discussion: { - isCommentable: false, - isClosedForComments: true, - discussionId: '/p/mhet4', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: false, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Feature', - }, - type: 'CuratedContent', - }, - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: true, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 3, - fields: { - displayCredit: 'true', - isMaster: 'true', - altText: - 'USA. Harris Dickinson in a scene from (C)Neon new film : Triangle of Sadness (2022). Plot: A cruise for the super-rich sinks thus leaving survivors, including a fashion model celebrity couple, trapped on an island. Ref: LMK110-J8273-190822 Supplied by LMKMEDIA. Editorial Only. Landmark Media is not the copyright owner of these Film or TV stills but provides a service only for recognised Media outlets. pictures@lmkmedia.com
2JPPN2Y USA. Harris Dickinson in a scene from (C)Neon new film : Triangle of Sadness (2022). Plot: A cruise for the super-rich sinks thus leaving survivors, including a fashion model celebrity couple, trapped on an island. Ref: LMK110-J8273-190822 Supplied by LMKMEDIA. Editorial Only. Landmark Media is not the copyright owner of these Film or TV stills but provides a service only for recognised Media outlets. pictures@lmkmedia.com', - mediaId: - 'c669f6300e637c6eac5b26fd3c3a0eda4b475126', - width: '4454', - source: 'Alamy', - photographer: 'Landmark Media', - height: '2674', - credit: 'Photograph: Landmark Media/Alamy', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/c669f6300e637c6eac5b26fd3c3a0eda4b475126/473_0_4454_2674/master/4454.jpg', - }, - ], - }, - byline: 'Morwenna Ferrier', - thumbnailPath: - 'https://i.guim.co.uk/img/media/c669f6300e637c6eac5b26fd3c3a0eda4b475126/473_0_4454_2674/500.jpg?quality=85&auto=format&fit=max&s=e8979943964b29a8c95f7eef77d67885', - webPublicationDate: 1666958427000, - }, - metadata: { - id: 'commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - webTitle: - 'Rolexes, influencers and H&M grins: Triangle of Sadness nails the fashion industry | Morwenna Ferrier', - webUrl: 'https://www.theguardian.com/commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - type: 'Article', - sectionId: { - value: 'commentisfree', - }, - format: { - design: 'CommentDesign', - theme: 'OpinionPillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
Harris Dickinson (centre) as Carl in Triangle of Sadness.
Harris Dickinson (centre) as Carl in Triangle of Sadness. Photograph: Landmark Media/Alamy
', - body: '

The picture painted of the fashion world in Triangle of Sadness, Ruben Östlund’s Palme d’Or-winning film, is not a kind one. Its title refers to a pair of frown lines that sit between the eyebrows. It’s one of the most Botoxed areas of a person’s face, probably because (as the name suggests) they only appear when you’re unhappy, and no one wants that.

This particular triangle belongs to Carl (Harris Dickinson), an ageing (he’s 24) male model with soft strawberry hair and a gentle pout. We meet Carl at a casting for an unknown fashion brand; as soon as he’s out of earshot, the panel discuss whether his triangle needs Botox. Old, unhappy Carl is unable to smooth his out.

', - standfirst: - '

As a fashion journalist, I recognised the precarity and cruelty on show in Ruben Östlund’s Palm d’Or winner

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'commentisfree/commentisfree', - url: '/commentisfree/commentisfree', - tagType: 'Blog', - sectionId: 'commentisfree', - sectionName: 'Opinion', - webTitle: 'Opinion', - webUrl: 'https://www.theguardian.com/commentisfree/commentisfree', - }, - }, - { - properties: { - id: 'fashion/fashion-industry', - url: '/fashion/fashion-industry', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion industry', - webUrl: 'https://www.theguardian.com/fashion/fashion-industry', - }, - }, - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'film/film', - url: '/film/film', - tagType: 'Keyword', - sectionId: 'film', - sectionName: 'Film', - webTitle: 'Film', - webUrl: 'https://www.theguardian.com/film/film', - }, - }, - { - properties: { - id: 'culture/culture', - url: '/culture/culture', - tagType: 'Keyword', - sectionId: 'culture', - sectionName: 'Culture', - webTitle: 'Culture', - webUrl: 'https://www.theguardian.com/culture/culture', - }, - }, - { - properties: { - id: 'fashion/models', - url: '/fashion/models', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Models', - webUrl: 'https://www.theguardian.com/fashion/models', - }, - }, - { - properties: { - id: 'type/article', - url: '/type/article', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Article', - webUrl: 'https://www.theguardian.com/articles', - }, - }, - { - properties: { - id: 'tone/comment', - url: '/tone/comment', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Comment', - webUrl: 'https://www.theguardian.com/tone/comment', - }, - }, - { - properties: { - id: 'profile/morwennaferrier', - url: '/profile/morwennaferrier', - tagType: 'Contributor', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Morwenna Ferrier', - webUrl: 'https://www.theguardian.com/profile/morwennaferrier', - bio: '

Morwenna Ferrier is the Guardian’s fashion and lifestyle editor

', - contributorLargeImagePath: - 'https://uploads.guim.co.uk/2017/10/09/Morwenna-Ferrier,-L.png', - bylineImageUrl: - 'https://static.guim.co.uk/sys-images/Guardian/Pix/contributor/2014/10/15/1413394164362/Morwenna-Ferrier.jpg', - }, - }, - { - properties: { - id: 'publication/theguardian', - url: '/publication/theguardian', - tagType: 'Publication', - sectionId: 'theguardian', - sectionName: 'From the Guardian', - webTitle: 'The Guardian', - webUrl: 'https://www.theguardian.com/theguardian/all', - }, - }, - { - properties: { - id: 'theguardian/journal', - url: '/theguardian/journal', - tagType: 'NewspaperBook', - sectionId: 'theguardian', - sectionName: 'From the Guardian', - webTitle: 'Journal', - webUrl: 'https://www.theguardian.com/theguardian/journal', - }, - }, - { - properties: { - id: 'theguardian/journal/opinion', - url: '/theguardian/journal/opinion', - tagType: 'NewspaperBookSection', - sectionId: 'theguardian', - sectionName: 'From the Guardian', - webTitle: 'Opinion', - webUrl: 'https://www.theguardian.com/theguardian/journal/opinion', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/uk-opinion', - url: '/tracking/commissioningdesk/uk-opinion', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'UK Opinion', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/uk-opinion', - }, - }, - ], - }, - }, - maybeContentId: - 'commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - isLiveBlog: false, - isCrossword: false, - byline: 'Morwenna Ferrier', - image: { - type: 'Cutout', - item: { - imageSrc: - 'https://uploads.guim.co.uk/2017/10/09/Morwenna-Ferrier,-L.png', - }, - }, - webTitle: - 'Rolexes, influencers and H&M grins: Triangle of Sadness nails the fashion industry | Morwenna Ferrier', - linkText: - 'Rolexes, influencers and H&M grins: Triangle of Sadness nails the fashion industry | Morwenna Ferrier', - webUrl: 'https://www.theguardian.com/commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: true, - isGallery: false, - isAudio: false, - headline: - 'Rolexes, influencers and H&M grins: Triangle of Sadness nails the fashion industry', - url: '/commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - hasMainVideoElement: false, - }, - card: { - id: 'commentisfree/2022/oct/28/triangle-of-sadness-fashion-industry-ruben-ostlund-palm-dor', - cardStyle: { - type: 'Comment', - }, - webPublicationDateOption: 1666958427000, - lastModifiedOption: 1666990950000, - trailText: - 'As a fashion journalist, I recognised the cruelty on show in Ruben Östlund’s film, says Morwenna Ferrier, assistant editor on the Guardian’s Saturday magazine', - shortUrlPath: '/p/mgkpz', - shortUrl: 'https://www.theguardian.com/p/mgkpz', - group: '0', - isLive: false, - }, - discussion: { - isCommentable: true, - isClosedForComments: true, - discussionId: '/p/mgkpz', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: true, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'CommentDesign', - theme: 'OpinionPillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Comment', - }, - type: 'CuratedContent', - }, - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: false, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 12, - fields: { - displayCredit: 'true', - isMaster: 'true', - altText: 'Fire on the Beach - Dana Scruggs', - mediaId: - 'ae693a9bf972f379f725722a1aff5cee881cc574', - width: '2661', - source: 'Dana Scruggs', - photographer: 'Dana Scruggs', - height: '1597', - credit: 'Photograph: Dana Scruggs', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/ae693a9bf972f379f725722a1aff5cee881cc574/80_651_2661_1597/master/2661.jpg', - }, - ], - }, - byline: '', - thumbnailPath: - 'https://i.guim.co.uk/img/media/ae693a9bf972f379f725722a1aff5cee881cc574/80_651_2661_1597/500.jpg?quality=85&auto=format&fit=max&s=fc54e408eafeadf5c0ed3d1b87902325', - webPublicationDate: 1667199650000, - }, - metadata: { - id: 'fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - webTitle: - 'At the vanguard: a new aesthetic in Black portraiture – in pictures', - webUrl: 'https://www.theguardian.com/fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - type: 'Gallery', - sectionId: { - value: 'fashion', - }, - format: { - design: 'GalleryDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
Fire on the Beach.
Fire on the Beach. Photograph: Dana Scruggs
', - body: '', - standfirst: - '

Curated by the US writer and critic Antwaun Sargent, an exhibition aims to explore the new aesthetic in portraiture being created by an emerging generation of black models, photographers and stylists.

The New Black Vanguard: Photography Between Art and Fashion is at the Saatchi Gallery in London until 22 January 2023

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'fashion/fashion-photography', - url: '/fashion/fashion-photography', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion photography', - webUrl: 'https://www.theguardian.com/fashion/fashion-photography', - }, - }, - { - properties: { - id: 'artanddesign/photography', - url: '/artanddesign/photography', - tagType: 'Keyword', - sectionId: 'artanddesign', - sectionName: 'Art and design', - webTitle: 'Photography', - webUrl: 'https://www.theguardian.com/artanddesign/photography', - }, - }, - { - properties: { - id: 'artanddesign/exhibition', - url: '/artanddesign/exhibition', - tagType: 'Keyword', - sectionId: 'artanddesign', - sectionName: 'Art and design', - webTitle: 'Exhibitions', - webUrl: 'https://www.theguardian.com/artanddesign/exhibition', - }, - }, - { - properties: { - id: 'artanddesign/artanddesign', - url: '/artanddesign/artanddesign', - tagType: 'Keyword', - sectionId: 'artanddesign', - sectionName: 'Art and design', - webTitle: 'Art and design', - webUrl: 'https://www.theguardian.com/artanddesign/artanddesign', - }, - }, - { - properties: { - id: 'culture/culture', - url: '/culture/culture', - tagType: 'Keyword', - sectionId: 'culture', - sectionName: 'Culture', - webTitle: 'Culture', - webUrl: 'https://www.theguardian.com/culture/culture', - }, - }, - { - properties: { - id: 'artanddesign/saatchi-gallery', - url: '/artanddesign/saatchi-gallery', - tagType: 'Keyword', - sectionId: 'artanddesign', - sectionName: 'Art and design', - webTitle: 'Saatchi gallery', - webUrl: 'https://www.theguardian.com/artanddesign/saatchi-gallery', - }, - }, - { - properties: { - id: 'artanddesign/art', - url: '/artanddesign/art', - tagType: 'Keyword', - sectionId: 'artanddesign', - sectionName: 'Art and design', - webTitle: 'Art', - webUrl: 'https://www.theguardian.com/artanddesign/art', - }, - }, - { - properties: { - id: 'uk/london', - url: '/uk/london', - tagType: 'Keyword', - sectionId: 'uk-news', - sectionName: 'UK news', - webTitle: 'London', - webUrl: 'https://www.theguardian.com/uk/london', - }, - }, - { - properties: { - id: 'us-news/us-news', - url: '/us-news/us-news', - tagType: 'Keyword', - sectionId: 'us-news', - sectionName: 'US news', - webTitle: 'US news', - webUrl: 'https://www.theguardian.com/us-news/us-news', - }, - }, - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'uk/uk', - url: '/uk/uk', - tagType: 'Keyword', - sectionId: 'uk-news', - sectionName: 'UK news', - webTitle: 'UK news', - webUrl: 'https://www.theguardian.com/uk/uk', - }, - }, - { - properties: { - id: 'world/world', - url: '/world/world', - tagType: 'Keyword', - sectionId: 'world', - sectionName: 'World news', - webTitle: 'World news', - webUrl: 'https://www.theguardian.com/world/world', - }, - }, - { - properties: { - id: 'world/race', - url: '/world/race', - tagType: 'Keyword', - sectionId: 'world', - sectionName: 'World news', - webTitle: 'Race', - webUrl: 'https://www.theguardian.com/world/race', - }, - }, - { - properties: { - id: 'type/gallery', - url: '/type/gallery', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Gallery', - webUrl: 'https://www.theguardian.com/inpictures', - }, - }, - { - properties: { - id: 'tone/features', - url: '/tone/features', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Features', - webUrl: 'https://www.theguardian.com/tone/features', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/uk-pictures-guardian-news', - url: '/tracking/commissioningdesk/uk-pictures-guardian-news', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'UK Pictures Guardian News', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/uk-pictures-guardian-news', - }, - }, - ], - }, - }, - maybeContentId: - 'fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - isLiveBlog: false, - isCrossword: false, - byline: '', - webTitle: - 'At the vanguard: a new aesthetic in Black portraiture – in pictures', - linkText: - 'At the vanguard: a new aesthetic in Black portraiture – in pictures', - webUrl: 'https://www.theguardian.com/fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: false, - isGallery: true, - isAudio: false, - headline: - 'At the vanguard: a new aesthetic in Black portraiture – in pictures', - url: '/fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - hasMainVideoElement: false, - }, - card: { - id: 'fashion/gallery/2022/oct/31/new-black-vanguard-photography-between-art-and-fashion-antwaun-sargent-in-pictures', - cardStyle: { - type: 'Media', - }, - webPublicationDateOption: 1667199650000, - lastModifiedOption: 1667199650000, - trailText: - 'Curated by the US writer and critic Antwaun Sargent, a new exhibition aims to explore the new aesthetic in portraiture being created by an emerging generation of black models, photographers and stylists', - shortUrlPath: '/p/mhe94', - shortUrl: 'https://www.theguardian.com/p/mhe94', - group: '0', - isLive: false, - galleryCount: 11, - }, - discussion: { - isCommentable: false, - isClosedForComments: true, - discussionId: '/p/mhe94', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: false, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'GalleryDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Media', - }, - type: 'CuratedContent', - }, - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: false, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 4, - fields: { - displayCredit: 'true', - isMaster: 'true', - altText: - 'Fan Special Screening of "Morbius" at Cinemark Playa Vista and XD in Playa Vista
Cast member Jared Leto attends a Fan Special Screening of "Morbius" at Cinemark Playa Vista and XD in Playa Vista, California, U.S., March 30, 2022. REUTERS/Aude Guerrucci', - mediaId: - 'db676607592f20362cbe6c6dcb9e2416723d7af1', - width: '3500', - source: 'Reuters', - photographer: 'Aude Guerrucci', - height: '2100', - credit: 'Photograph: Aude Guerrucci/Reuters', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/db676607592f20362cbe6c6dcb9e2416723d7af1/0_25_3500_2100/master/3500.jpg', - }, - ], - }, - byline: 'Alaina Demopoulos in New York', - thumbnailPath: - 'https://i.guim.co.uk/img/media/db676607592f20362cbe6c6dcb9e2416723d7af1/0_25_3500_2100/500.jpg?quality=85&auto=format&fit=max&s=97c0c4a69484caad6f9da36484795910', - webPublicationDate: 1666933219000, - }, - metadata: { - id: 'fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - webTitle: - 'Jared Leto says he’s not interested in skincare – while selling $97 eye cream', - webUrl: 'https://www.theguardian.com/fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - type: 'Article', - sectionId: { - value: 'fashion', - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
leto flashes peace sign
Jared Leto is launching a line of skin, body and hair care products. Photograph: Aude Guerrucci/Reuters
', - body: '

In 2022, it seems you’re more likely to see a celebrity announce a new beauty line than promote their next project. This year alone, Hailey Bieber, Gwen Stefani, Halsey, Ciara and Winnie Harlow all dropped new brands. But it’s not just a woman’s game – now men want a piece of the $5bn market, too.

Last week, Jared Leto announced Twentynine Palms, “an 11-piece range of gender-neutral skin care, body care and hair care products”, per Vogue. Though the 50-year-old told the glossy he’s “never been really interested in beauty products”, he unveiled items like $47 hand wash and $97 eye cream. Each product is made from “desert botanicals” like prickly pear extract.

', - standfirst: - '

The actor has joined Brad Pitt – another self-described newbie – in hawking pricey products. Will anyone shell out?

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'film/jared-leto', - url: '/film/jared-leto', - tagType: 'Keyword', - sectionId: 'film', - sectionName: 'Film', - webTitle: 'Jared Leto', - webUrl: 'https://www.theguardian.com/film/jared-leto', - }, - }, - { - properties: { - id: 'film/film', - url: '/film/film', - tagType: 'Keyword', - sectionId: 'film', - sectionName: 'Film', - webTitle: 'Film', - webUrl: 'https://www.theguardian.com/film/film', - }, - }, - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'culture/culture', - url: '/culture/culture', - tagType: 'Keyword', - sectionId: 'culture', - sectionName: 'Culture', - webTitle: 'Culture', - webUrl: 'https://www.theguardian.com/culture/culture', - }, - }, - { - properties: { - id: 'fashion/skincare', - url: '/fashion/skincare', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Skincare', - webUrl: 'https://www.theguardian.com/fashion/skincare', - }, - }, - { - properties: { - id: 'film/bradpitt', - url: '/film/bradpitt', - tagType: 'Keyword', - sectionId: 'film', - sectionName: 'Film', - webTitle: 'Brad Pitt', - webUrl: 'https://www.theguardian.com/film/bradpitt', - }, - }, - { - properties: { - id: 'fashion/beauty', - url: '/fashion/beauty', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Beauty', - webUrl: 'https://www.theguardian.com/fashion/beauty', - }, - }, - { - properties: { - id: 'type/article', - url: '/type/article', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Article', - webUrl: 'https://www.theguardian.com/articles', - }, - }, - { - properties: { - id: 'tone/features', - url: '/tone/features', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Features', - webUrl: 'https://www.theguardian.com/tone/features', - }, - }, - { - properties: { - id: 'profile/alaina-demopoulos', - url: '/profile/alaina-demopoulos', - tagType: 'Contributor', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Alaina Demopoulos', - webUrl: 'https://www.theguardian.com/profile/alaina-demopoulos', - bio: '

Alaina Demopoulos is a daily features writer for the Guardian

', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/east-coast-features', - url: '/tracking/commissioningdesk/east-coast-features', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'East coast features', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/east-coast-features', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/us-features', - url: '/tracking/commissioningdesk/us-features', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'US Features', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/us-features', - }, - }, - ], - }, - }, - maybeContentId: - 'fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - isLiveBlog: false, - isCrossword: false, - byline: 'Alaina Demopoulos in New York', - webTitle: - 'Jared Leto says he’s not interested in skincare – while selling $97 eye cream', - linkText: - 'Jared Leto says he’s not interested in skincare – while selling $97 eye cream', - webUrl: 'https://www.theguardian.com/fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: false, - isGallery: false, - isAudio: false, - kicker: { - type: 'FreeHtmlKicker', - item: { - properties: { - kickerText: 'Jared Leto', - }, - }, - }, - headline: - 'Actor says he’s not interested in skincare – while selling $97 eye cream', - url: '/fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - hasMainVideoElement: false, - }, - card: { - id: 'fashion/2022/oct/27/jared-leto-skincare-celebrity-men-brad-pitt', - cardStyle: { - type: 'Feature', - }, - webPublicationDateOption: 1666933219000, - lastModifiedOption: 1666966902000, - trailText: - 'The actor has joined Brad Pitt – another self-described newbie – in hawking pricey products. Will anyone shell out?', - shortUrlPath: '/p/mh28e', - shortUrl: 'https://www.theguardian.com/p/mh28e', - group: '0', - isLive: false, - }, - discussion: { - isCommentable: false, - isClosedForComments: true, - discussionId: '/p/mh28e', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: false, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Feature', - }, - type: 'CuratedContent', - }, - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: false, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 20, - fields: { - displayCredit: 'true', - source: 'REX/Shutterstock', - isMaster: 'true', - altText: 'Winter coats', - mediaId: - '11f672598753a0604016f1d79e9ea57d22773e44', - width: '2560', - height: '1536', - credit: 'Composite: REX/Shutterstock', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/11f672598753a0604016f1d79e9ea57d22773e44/0_0_2560_1536/master/2560.jpg', - }, - ], - }, - byline: 'by Jo Jones', - thumbnailPath: - 'https://i.guim.co.uk/img/media/11f672598753a0604016f1d79e9ea57d22773e44/0_0_2560_1536/500.jpg?quality=85&auto=format&fit=max&s=18a365bbe36ec46533d68addc35b39e4', - webPublicationDate: 1667083509000, - }, - metadata: { - id: 'fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - webTitle: - ' 20 of the best winter coats to wear now - in pictures ', - webUrl: 'https://www.theguardian.com/fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - type: 'Gallery', - sectionId: { - value: 'fashion', - }, - format: { - design: 'GalleryDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
L-R: Ghospell, Cos, Street Style PFW, La Redoute, Monki
L-R: Ghospell, Cos, Street Style PFW, La Redoute, Monki Composite: REX/Shutterstock
', - body: '', - standfirst: - '

A pop of colour, a classic camel or a cuddly teddy? Choose the coat that best suits your personality

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'fashion/womens-coats', - url: '/fashion/womens-coats', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: "Women's coats and jackets", - webUrl: 'https://www.theguardian.com/fashion/womens-coats', - }, - }, - { - properties: { - id: 'fashion/winter-coats', - url: '/fashion/winter-coats', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Winter coats', - webUrl: 'https://www.theguardian.com/fashion/winter-coats', - }, - }, - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'type/gallery', - url: '/type/gallery', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Gallery', - webUrl: 'https://www.theguardian.com/inpictures', - }, - }, - { - properties: { - id: 'profile/jojones', - url: '/profile/jojones', - tagType: 'Contributor', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Jo Jones', - webUrl: 'https://www.theguardian.com/profile/jojones', - bio: '

Jo Jones is fashion editor for the Observer

', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/observer-magazine', - url: '/tracking/commissioningdesk/observer-magazine', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Observer Magazine', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/observer-magazine', - }, - }, - ], - }, - }, - maybeContentId: - 'fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - isLiveBlog: false, - isCrossword: false, - byline: 'by Jo Jones', - webTitle: ' 20 of the best winter coats to wear now - in pictures ', - linkText: ' 20 of the best winter coats to wear now - in pictures ', - webUrl: 'https://www.theguardian.com/fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: false, - isGallery: true, - isAudio: false, - kicker: { - type: 'FreeHtmlKicker', - item: { - properties: { - kickerText: 'Winter is coming', - }, - }, - }, - headline: ' 20 of the best winter coats to wear now', - url: '/fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - hasMainVideoElement: false, - }, - card: { - id: 'fashion/gallery/2022/oct/29/20-of-the-best-winter-coats-to-wear-now-in-pictures', - cardStyle: { - type: 'Media', - }, - webPublicationDateOption: 1667083509000, - lastModifiedOption: 1667083509000, - trailText: - 'A pop of colour, a classic camel or a cuddly teddy? Choose the coat that best suits your personality', - shortUrlPath: '/p/mgmvx', - shortUrl: 'https://www.theguardian.com/p/mgmvx', - group: '0', - isLive: false, - galleryCount: 19, - }, - discussion: { - isCommentable: false, - isClosedForComments: true, - discussionId: '/p/mgmvx', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: false, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'GalleryDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Media', - }, - type: 'CuratedContent', - }, - { - properties: { - isBreaking: false, - mediaSelect: { - showMainVideo: false, - imageSlideshowReplace: false, - videoReplace: false, - }, - showKickerTag: false, - showByline: false, - maybeContent: { - trail: { - trailPicture: { - allImages: [ - { - index: 4, - fields: { - displayCredit: 'true', - source: 'TikTok', - isMaster: 'true', - altText: 'IMG 0740', - mediaId: - 'dd9d8bd4f8805a0ddd3dabbb1ddb5add71fb8cc9', - width: '1170', - height: '702', - credit: 'Photograph: TikTok', - }, - mediaType: 'Image', - url: 'https://media.guim.co.uk/dd9d8bd4f8805a0ddd3dabbb1ddb5add71fb8cc9/0_335_1170_702/master/1170.jpg', - }, - ], - }, - byline: 'Alaina Demopoulos', - thumbnailPath: - 'https://i.guim.co.uk/img/media/dd9d8bd4f8805a0ddd3dabbb1ddb5add71fb8cc9/0_335_1170_702/500.jpg?quality=85&auto=format&fit=max&s=d58b9663df81f862f6756209122cb7ef', - webPublicationDate: 1667192447000, - }, - metadata: { - id: 'lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - webTitle: - '‘Sadness is a trend’: why TikTok loves ‘crying makeup’', - webUrl: 'https://www.theguardian.com/lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - type: 'Article', - sectionId: { - value: 'lifeandstyle', - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - }, - fields: { - main: '
close up of Kenealy - she looks like she\'s been crying
Zoe Kim Kenealy on TikTok. Photograph: Screengrab/TikTok
', - body: '

Beauty magazines once taught readers how to use makeup to conceal a recent sobbing sesh. But now, one TikTok trend encourages us to embrace those misty eyes and rosy noses. “Crying makeup,” it seems, is in.

In a clip that has gained over 507,000 likes, the Boston-based content creator Zoe Kim Kenealy offers a tutorial “for the unstable girlies” to achieve the look of a fresh sob even “if you’re not in the mood to cry”.

', - standfirst: - '

From #sadgirlwalk to dazed stares, misery – or the performance of it – is all over the platform in what some see as a bid for belonging

', - }, - elements: { - mediaAtoms: [], - }, - tags: { - tags: [ - { - properties: { - id: 'lifeandstyle/lifeandstyle', - url: '/lifeandstyle/lifeandstyle', - tagType: 'Keyword', - sectionId: 'lifeandstyle', - sectionName: 'Life and style', - webTitle: 'Life and style', - webUrl: 'https://www.theguardian.com/lifeandstyle/lifeandstyle', - }, - }, - { - properties: { - id: 'us-news/us-news', - url: '/us-news/us-news', - tagType: 'Keyword', - sectionId: 'us-news', - sectionName: 'US news', - webTitle: 'US news', - webUrl: 'https://www.theguardian.com/us-news/us-news', - }, - }, - { - properties: { - id: 'technology/tiktok', - url: '/technology/tiktok', - tagType: 'Keyword', - sectionId: 'technology', - sectionName: 'Technology', - webTitle: 'TikTok', - webUrl: 'https://www.theguardian.com/technology/tiktok', - }, - }, - { - properties: { - id: 'fashion/fashion', - url: '/fashion/fashion', - tagType: 'Keyword', - sectionId: 'fashion', - sectionName: 'Fashion', - webTitle: 'Fashion', - webUrl: 'https://www.theguardian.com/fashion/fashion', - }, - }, - { - properties: { - id: 'type/article', - url: '/type/article', - tagType: 'Type', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Article', - webUrl: 'https://www.theguardian.com/articles', - }, - }, - { - properties: { - id: 'tone/features', - url: '/tone/features', - tagType: 'Tone', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Features', - webUrl: 'https://www.theguardian.com/tone/features', - }, - }, - { - properties: { - id: 'profile/alaina-demopoulos', - url: '/profile/alaina-demopoulos', - tagType: 'Contributor', - sectionId: 'global', - sectionName: 'global', - webTitle: 'Alaina Demopoulos', - webUrl: 'https://www.theguardian.com/profile/alaina-demopoulos', - bio: '

Alaina Demopoulos is a daily features writer for the Guardian

', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/east-coast-features', - url: '/tracking/commissioningdesk/east-coast-features', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'East coast features', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/east-coast-features', - }, - }, - { - properties: { - id: 'tracking/commissioningdesk/us-features', - url: '/tracking/commissioningdesk/us-features', - tagType: 'Tracking', - sectionId: 'global', - sectionName: 'global', - webTitle: 'US Features', - webUrl: 'https://www.theguardian.com/tracking/commissioningdesk/us-features', - }, - }, - ], - }, - }, - maybeContentId: - 'lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - isLiveBlog: false, - isCrossword: false, - byline: 'Alaina Demopoulos', - webTitle: '‘Sadness is a trend’: why TikTok loves ‘crying makeup’', - linkText: '‘Sadness is a trend’: why TikTok loves ‘crying makeup’', - webUrl: 'https://www.theguardian.com/lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - editionBrandings: [ - { - edition: { - id: 'UK', - }, - }, - { - edition: { - id: 'US', - }, - }, - { - edition: { - id: 'AU', - }, - }, - { - edition: { - id: 'INT', - }, - }, - ], - }, - header: { - isVideo: false, - isComment: false, - isGallery: false, - isAudio: false, - headline: '‘Sadness is a trend’: why TikTok loves ‘crying makeup’', - url: '/lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - hasMainVideoElement: false, - }, - card: { - id: 'lifeandstyle/2022/oct/30/sadness-is-a-trend-why-tiktok-loves-crying-makeup', - cardStyle: { - type: 'Feature', - }, - webPublicationDateOption: 1667192447000, - lastModifiedOption: 1667228583000, - trailText: - 'From #sadgirlwalk to dazed stares, misery – or the performance of it – is all over the platform in what some see as a bid for belonging', - shortUrlPath: '/p/mhgkn', - shortUrl: 'https://www.theguardian.com/p/mhgkn', - group: '0', - isLive: false, - }, - discussion: { - isCommentable: false, - isClosedForComments: true, - discussionId: '/p/mhgkn', - }, - display: { - isBoosted: false, - boostLevel: 'default', - showBoostedHeadline: false, - showQuotedHeadline: false, - imageHide: false, - showLivePlayable: false, - }, - format: { - design: 'FeatureDesign', - theme: 'LifestylePillar', - display: 'StandardDisplay', - }, - enriched: {}, - supportingContent: [], - cardStyle: { - type: 'Feature', - }, - type: 'CuratedContent', - }, -]; diff --git a/dotcom-rendering/src/components/Accessibility.importable.tsx b/dotcom-rendering/src/components/Accessibility.importable.tsx index d83af9dc576..9cfd681f704 100644 --- a/dotcom-rendering/src/components/Accessibility.importable.tsx +++ b/dotcom-rendering/src/components/Accessibility.importable.tsx @@ -93,7 +93,7 @@ export const Accessibility = () => { }; return ( - +

diff --git a/dotcom-rendering/src/components/Card/Card.stories.tsx b/dotcom-rendering/src/components/Card/Card.stories.tsx index b837ddb33bd..92097d86ef5 100644 --- a/dotcom-rendering/src/components/Card/Card.stories.tsx +++ b/dotcom-rendering/src/components/Card/Card.stories.tsx @@ -1555,8 +1555,7 @@ export const WithSpecialPaletteVariations = () => { {containerPalettes.map((containerPalette) => ( { const hasSublinks = supportingContent && supportingContent.length > 0; const sublinkPosition = decideSublinkPosition( @@ -696,6 +698,14 @@ export const Card = ({ return ; } + if (isInSlimHomepageAbTestVariant) { + return ( + + + + ); + } + return ( @@ -707,7 +717,7 @@ export const Card = ({ if (!hasSublinks) return null; if (sublinkPosition !== 'inner') return null; - return ( + const Sublinks = () => ( {isStorylines ? ( ); + + if (isInSlimHomepageAbTestVariant) { + return ( + + + + ); + } + + return ; }; const determinePadContent = ( diff --git a/dotcom-rendering/src/components/CardHeadline.tsx b/dotcom-rendering/src/components/CardHeadline.tsx index 43786c45804..00c8d482cc8 100644 --- a/dotcom-rendering/src/components/CardHeadline.tsx +++ b/dotcom-rendering/src/components/CardHeadline.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import { between, + from, headlineMedium14, headlineMedium15, headlineMedium17, @@ -142,6 +143,7 @@ export enum FontFamily { export type HeadlineSize = keyof typeof fontFamilies.headlineMedium; export type ResponsiveFontSize = { + wide?: HeadlineSize; desktop: HeadlineSize; tablet?: HeadlineSize; mobile?: HeadlineSize; @@ -151,7 +153,7 @@ export type ResponsiveFontSize = { const getFontSize = (sizes: ResponsiveFontSize, family: FontFamily) => { const font = fontFamilies[family]; - const { desktop, tablet, mobileMedium, mobile } = sizes; + const { wide, desktop, tablet, mobileMedium, mobile } = sizes; return css` ${font[desktop]}; @@ -176,6 +178,13 @@ const getFontSize = (sizes: ResponsiveFontSize, family: FontFamily) => { ${font[mobileMedium]}; } `} + + ${wide && + css` + ${from.wide} { + ${font[wide]}; + } + `} `; }; @@ -204,6 +213,15 @@ export const WithLink = ({ return <>{children}; }; +/** + * headline medium 20 on desktop and headline medium 17 on tablet and mobile + */ +export const defaultFontSizes: ResponsiveFontSize = { + desktop: 'xsmall', + tablet: 'xxsmall', + mobile: 'xxsmall', +}; + export const CardHeadline = ({ headlineText, format, @@ -211,8 +229,7 @@ export const CardHeadline = ({ kickerText, showPulsingDot, hasInlineKicker, - /** headline medium 20 on desktop and headline medium 17 on tablet and mobile */ - fontSizes = { desktop: 'xsmall', tablet: 'xxsmall', mobile: 'xxsmall' }, + fontSizes = defaultFontSizes, byline, showByline, linkTo, diff --git a/dotcom-rendering/src/components/ContainerOverrides.tsx b/dotcom-rendering/src/components/ContainerOverrides.tsx index d8d18e05601..aac3b18be2d 100644 --- a/dotcom-rendering/src/components/ContainerOverrides.tsx +++ b/dotcom-rendering/src/components/ContainerOverrides.tsx @@ -28,9 +28,8 @@ const cardHeadlineLight: ContainerFunction = ( return sourcePalette.neutral[93]; case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; - // TODO: @commercial-dev to update to neutral[7] when launching Redesigned Labs Containers case 'Branded': - return sourcePalette.neutral[20]; + return 'inherit'; } }; const cardHeadlineDark: ContainerFunction = ( @@ -56,7 +55,7 @@ const cardHeadlineDark: ContainerFunction = ( case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[700]; case 'Branded': - return sourcePalette.neutral[86]; + return 'inherit'; } }; @@ -83,8 +82,7 @@ const cardTrailTextLight: ContainerFunction = ( case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - // TODO: @commercial-dev to update to palette('--card-trail-text') when launching Redesigned Labs Containers - return sourcePalette.neutral[20]; + return 'inherit'; } }; const cardTrailTextDark: ContainerFunction = ( @@ -110,8 +108,7 @@ const cardTrailTextDark: ContainerFunction = ( case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[700]; case 'Branded': - // TODO: @commercial-dev to update to palette('--card-trail-text') when launching Redesigned Labs Containers - return sourcePalette.neutral[86]; + return 'inherit'; } }; @@ -136,8 +133,7 @@ const cardKickerTextLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - // TODO: @commercial-dev to update to labs[100] when launching Redesigned Labs Containers - return sourcePalette.labs[200]; + return sourcePalette.labs[100]; } }; const cardKickerTextDark: ContainerFunction = (containerPalette) => { @@ -186,8 +182,7 @@ const cardQuoteIconLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - // TODO: @commercial-dev to update to labs[100] when launching Redesigned Labs Containers - return sourcePalette.labs[200]; + return sourcePalette.labs[100]; } }; const cardQuoteIconDark: ContainerFunction = (containerPalette) => { @@ -335,7 +330,7 @@ const sectionToggleButtonLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.neutral[60]; case 'Branded': - return palette('--section-toggle-button'); + return 'inherit'; } }; const sectionToggleButtonDark: ContainerFunction = (containerPalette) => { @@ -359,7 +354,7 @@ const sectionToggleButtonDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.neutral[60]; case 'Branded': - return palette('--section-toggle-button'); + return 'inherit'; } }; const sectionToggleButtonHoverLight: ContainerFunction = (containerPalette) => { @@ -383,7 +378,7 @@ const sectionToggleButtonHoverLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.neutral[93]; case 'Branded': - return palette('--section-toggle-button-hover'); + return 'inherit'; } }; const sectionToggleButtonHoverDark: ContainerFunction = (containerPalette) => { @@ -431,7 +426,7 @@ const cardBorderTopLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - return sourcePalette.neutral[73]; + return 'inherit'; } }; @@ -454,7 +449,7 @@ const cardBorderTopDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - return sourcePalette.neutral[60]; + return 'inherit'; } }; @@ -486,7 +481,7 @@ const articleBorderLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - return sourcePalette.neutral[73]; + return 'inherit'; } }; @@ -508,7 +503,7 @@ const articleBorderDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.neutral[38]; case 'Branded': - return sourcePalette.neutral[38]; + return 'inherit'; } }; @@ -533,7 +528,7 @@ const cardBackgroundLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[800]; case 'Branded': - return palette('--section-background'); + return 'inherit'; } }; @@ -550,7 +545,7 @@ const cardBackgroundDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - return palette('--section-background'); + return 'inherit'; case 'BreakingPalette': return palette('--section-background'); case 'SombreAltPalette': @@ -560,11 +555,23 @@ const cardBackgroundDark: ContainerFunction = (containerPalette) => { } }; -const cardMediaBackgroundLight: ContainerFunction = (containerPalette) => - transparentColour(cardHeadlineLight(containerPalette), 0.1); -const cardMediaBackgroundDark: ContainerFunction = (containerPalette) => - transparentColour(cardHeadlineDark(containerPalette), 0.1); +const cardMediaBackgroundLight: ContainerFunction = (containerPalette) => { + switch (containerPalette) { + case 'Branded': + return 'inherit'; + default: + return transparentColour(cardHeadlineLight(containerPalette), 0.1); + } +}; +const cardMediaBackgroundDark: ContainerFunction = (containerPalette) => { + switch (containerPalette) { + case 'Branded': + return 'inherit'; + default: + return transparentColour(cardHeadlineDark(containerPalette), 0.1); + } +}; const cardMediaWaveformLight: ContainerFunction = (containerPalette) => { switch (containerPalette) { case 'InvestigationPalette': @@ -583,8 +590,7 @@ const cardMediaWaveformLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[800]; case 'Branded': - // TODO: @commercial-dev to update to palette('--card-media-waveform') when launching Redesigned Labs Containers - return sourcePalette.neutral[86]; + return 'inherit'; } }; @@ -604,8 +610,7 @@ const cardMediaWaveformDark: ContainerFunction = (containerPalette) => { case 'EventAltPalette': return sourcePalette.culture[300]; case 'Branded': - // TODO: @commercial-dev to update to palette('--card-media-waveform') when launching Redesigned Labs Containers - return sourcePalette.neutral[38]; + return 'inherit'; } }; @@ -630,7 +635,7 @@ const sectionBackgroundLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[800]; case 'Branded': - return palette('--section-background'); + return 'inherit'; } }; @@ -654,7 +659,7 @@ const sectionBackgroundDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - return palette('--section-background'); + return 'inherit'; } }; @@ -712,8 +717,7 @@ const cardBorderSupportingLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - // TODO: @commercial-dev to update to palette('--card-border-supporting') when launching Redesigned Labs Containers - return sourcePalette.neutral[73]; + return 'inherit'; } }; const cardBorderSupportingDark: ContainerFunction = (containerPalette) => { @@ -735,8 +739,7 @@ const cardBorderSupportingDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - // TODO: @commercial-dev to update to palette('--card-border-supporting') when launching Redesigned Labs Containers - return sourcePalette.neutral[46]; + return 'inherit'; } }; @@ -746,7 +749,6 @@ const sectionBorderLight: ContainerFunction = (containerPalette) => { case 'LongRunningAltPalette': case 'EventPalette': case 'EventAltPalette': - case 'Branded': return sourcePalette.neutral[86]; case 'SombrePalette': case 'SombreAltPalette': @@ -757,6 +759,8 @@ const sectionBorderLight: ContainerFunction = (containerPalette) => { return sourcePalette.news[600]; case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); + case 'Branded': + return 'inherit'; } }; const sectionBorderDark: ContainerFunction = (containerPalette) => { @@ -778,7 +782,7 @@ const sectionBorderDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return transparentColour(sourcePalette.neutral[46], 0.3); case 'Branded': - return sourcePalette.neutral[46]; + return 'inherit'; } }; @@ -803,7 +807,7 @@ const sectionDateLight: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; case 'Branded': - return palette('--section-date'); + return 'inherit'; } }; @@ -825,7 +829,7 @@ const sectionDateDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[700]; case 'Branded': - return palette('--section-date'); + return 'inherit'; } }; @@ -849,9 +853,8 @@ const sectionTitleLight: ContainerFunction = (containerPalette) => { return sourcePalette.neutral[93]; case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[100]; - // Branded is expected to be used with LabsSection case 'Branded': - return palette('--article-section-title'); + return 'inherit'; } }; @@ -876,7 +879,7 @@ const sectionTitleDark: ContainerFunction = (containerPalette) => { case 'SpecialReportAltPalette': return sourcePalette.specialReportAlt[700]; case 'Branded': - return palette('--article-section-title'); + return 'inherit'; } }; diff --git a/dotcom-rendering/src/components/DecideContainer.tsx b/dotcom-rendering/src/components/DecideContainer.tsx index fc514f1c2d3..fea97f0f8c8 100644 --- a/dotcom-rendering/src/components/DecideContainer.tsx +++ b/dotcom-rendering/src/components/DecideContainer.tsx @@ -7,19 +7,6 @@ import type { DCRFrontCard, DCRGroupedTrails, } from '../types/front'; -import { FixedLargeSlowXIV } from './FixedLargeSlowXIV'; -import { FixedMediumFastXI } from './FixedMediumFastXI'; -import { FixedMediumFastXII } from './FixedMediumFastXII'; -import { FixedMediumSlowVI } from './FixedMediumSlowVI'; -import { FixedMediumSlowVII } from './FixedMediumSlowVII'; -import { FixedMediumSlowXIIMPU } from './FixedMediumSlowXIIMPU'; -import { FixedSmallFastVIII } from './FixedSmallFastVIII'; -import { FixedSmallSlowI } from './FixedSmallSlowI'; -import { FixedSmallSlowIII } from './FixedSmallSlowIII'; -import { FixedSmallSlowIV } from './FixedSmallSlowIV'; -import { FixedSmallSlowVHalf } from './FixedSmallSlowVHalf'; -import { FixedSmallSlowVMPU } from './FixedSmallSlowVMPU'; -import { FixedSmallSlowVThird } from './FixedSmallSlowVThird'; import { FlexibleGeneral } from './FlexibleGeneral'; import { FlexibleSpecial } from './FlexibleSpecial'; import { Island } from './Island'; @@ -44,6 +31,7 @@ type Props = { frontId?: string; collectionId: number; containerLevel?: DCRContainerLevel; + isInSlimHomepageAbTestVariant?: boolean; }; export const DecideContainer = ({ @@ -59,138 +47,9 @@ export const DecideContainer = ({ frontId, collectionId, containerLevel, + isInSlimHomepageAbTestVariant = false, }: Props) => { switch (containerType) { - case 'fixed/large/slow-XIV': - return ( - - ); - case 'fixed/small/slow-IV': - return ( - - ); - case 'fixed/small/slow-V-mpu': - return ( - - ); - case 'fixed/small/slow-III': - return ( - - ); - case 'fixed/small/slow-I': - return ( - - ); - case 'fixed/small/slow-V-third': - return ( - - ); - case 'fixed/small/slow-V-half': - return ( - - ); - case 'fixed/medium/slow-VI': - return ( - - ); - case 'fixed/medium/slow-VII': - return ( - - ); - case 'fixed/medium/slow-XII-mpu': - return ( - - ); - case 'fixed/medium/fast-XII': - return ( - - ); - case 'fixed/medium/fast-XI': - return ( - - ); - case 'fixed/small/fast-VIII': - return ( - - ); case 'nav/list': return ; case 'nav/media-list': @@ -211,6 +70,9 @@ export const DecideContainer = ({ imageLoading={imageLoading} aspectRatio={aspectRatio} collectionId={collectionId} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); case 'flexible/general': @@ -224,6 +86,9 @@ export const DecideContainer = ({ aspectRatio={aspectRatio} containerLevel={containerLevel} collectionId={collectionId} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); case 'scrollable/small': @@ -251,6 +116,9 @@ export const DecideContainer = ({ serverTime={serverTime} aspectRatio={aspectRatio} sectionId={sectionId} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); @@ -263,6 +131,9 @@ export const DecideContainer = ({ serverTime={serverTime} imageLoading={imageLoading} aspectRatio={aspectRatio} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); case 'scrollable/feature': @@ -275,6 +146,9 @@ export const DecideContainer = ({ serverTime={serverTime} aspectRatio={aspectRatio} collectionId={collectionId} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); diff --git a/dotcom-rendering/src/components/DecideContainerByTrails.stories.tsx b/dotcom-rendering/src/components/DecideContainerByTrails.stories.tsx index c1900636a37..41785959de7 100644 --- a/dotcom-rendering/src/components/DecideContainerByTrails.stories.tsx +++ b/dotcom-rendering/src/components/DecideContainerByTrails.stories.tsx @@ -1,5 +1,4 @@ import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; import { multipleBrandedTrails, singleBrandedTrails, @@ -26,11 +25,7 @@ const ASPECT_RATIO = '5:4'; export const OneCardFast = () => { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( - + { return ( { return ( - + ( - - - -); -Default.storyName = 'FixedLargeSlowXIV'; diff --git a/dotcom-rendering/src/components/FixedLargeSlowXIV.tsx b/dotcom-rendering/src/components/FixedLargeSlowXIV.tsx deleted file mode 100644 index 4c5a87245a0..00000000000 --- a/dotcom-rendering/src/components/FixedLargeSlowXIV.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { - Card25Media25, - Card25Media25SmallHeadline, - Card75Media50Right, - CardDefault, -} from '../lib/cardWrappers'; -import { shouldPadWrappableRows } from '../lib/dynamicSlices'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedLargeSlowXIV = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice75 = trails.slice(0, 1); - const firstSlice25 = trails.slice(1, 2); - const secondSlice25 = trails.slice(2, 6); - const thirdSlice25 = trails.slice(6, 14); - - return ( - <> -

    - {firstSlice75.map((card) => { - return ( -
  • - -
  • - ); - })} - {firstSlice25.map((card) => { - return ( -
  • - -
  • - ); - })} -
-
    - {secondSlice25.map((card, cardIndex) => { - return ( -
  • 0} - key={card.url} - > - -
  • - ); - })} -
-
    - {thirdSlice25.map((card, cardIndex, { length }) => { - const columns = 4; - return ( -
  • - -
  • - ); - })} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedMediumFastXI.stories.tsx b/dotcom-rendering/src/components/FixedMediumFastXI.stories.tsx deleted file mode 100644 index 348f25b4782..00000000000 --- a/dotcom-rendering/src/components/FixedMediumFastXI.stories.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedMediumFastXI } from './FixedMediumFastXI'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedMediumFastXI, - title: 'Front Containers/Deprecated Containers/FixedMediumFastXI', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const OneTrail = () => ( - - - -); -OneTrail.storyName = 'with one trail'; - -export const TwoTrails = () => ( - - - -); -TwoTrails.storyName = 'with two trails'; - -export const ThreeTrails = () => ( - - - -); -ThreeTrails.storyName = 'with three trails'; - -export const FourTrails = () => ( - - - -); -FourTrails.storyName = 'with four trails'; - -export const FiveTrails = () => ( - - - -); -FiveTrails.storyName = 'with five trails'; - -export const SixTrails = () => ( - - - -); -SixTrails.storyName = 'with six trails'; - -export const SevenTrails = () => ( - - - -); -SevenTrails.storyName = 'with seven trails'; - -export const EightTrails = () => ( - - - -); -EightTrails.storyName = 'with eight trails'; - -export const NineTrails = () => ( - - - -); -NineTrails.storyName = 'with nine trails'; - -export const TenTrails = () => ( - - - -); -TenTrails.storyName = 'with ten trails'; - -export const ElevenTrails = () => ( - - - -); -ElevenTrails.storyName = 'with eleven trails'; diff --git a/dotcom-rendering/src/components/FixedMediumFastXI.tsx b/dotcom-rendering/src/components/FixedMediumFastXI.tsx deleted file mode 100644 index 70a75ac4ec9..00000000000 --- a/dotcom-rendering/src/components/FixedMediumFastXI.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { CardDefault } from '../lib/cardWrappers'; -import { Card50_Card25_Card25 } from '../lib/dynamicSlices'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -const decideOffset = ({ - length, - position, -}: { - length: number; - position: number; -}): boolean => { - if (length <= 5) return false; - switch (length) { - case 6: { - switch (position) { - case 2: - return true; - default: - return false; - } - } - case 7: - return false; - case 8: { - if (position <= 4) return true; - else return false; - } - } - return false; -}; - -export const FixedMediumFastXI = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice = trails.slice(0, 3); - const remaining = trails.slice(3, 11); - return ( - <> - - {/* - * This pattern of using wrapCards on the UL + percentage=25 and stretch=true - * on the LI creates a dynanic list of cards over two rows where the second row - * only appears when there are more than 4 cards - * - * E.g: - * ._____._____._____._____. - * |_____|_____|_____|_____| - * |___________|___________| - */} -
    - {remaining.map((trail, trailIndex) => ( -
  • - -
  • - ))} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedMediumFastXII.stories.tsx b/dotcom-rendering/src/components/FixedMediumFastXII.stories.tsx deleted file mode 100644 index edffdc1fffd..00000000000 --- a/dotcom-rendering/src/components/FixedMediumFastXII.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedMediumFastXII } from './FixedMediumFastXII'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedMediumFastXII, - title: 'Front Containers/Deprecated Containers/FixedMediumFastXII', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedMediumFastXII'; diff --git a/dotcom-rendering/src/components/FixedMediumFastXII.tsx b/dotcom-rendering/src/components/FixedMediumFastXII.tsx deleted file mode 100644 index f5f06ec66f7..00000000000 --- a/dotcom-rendering/src/components/FixedMediumFastXII.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Card25Media25, CardDefault } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedMediumFastXII = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice25 = trails.slice(0, 4); - const remaining = trails.slice(4, 12); - - return ( - <> -
    - {firstSlice25.map((trail, index) => { - return ( -
  • 0} - > - -
  • - ); - })} -
-
    - {remaining.map((trail, index) => { - return ( -
  • - -
  • - ); - })} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedMediumSlowVI.stories.tsx b/dotcom-rendering/src/components/FixedMediumSlowVI.stories.tsx deleted file mode 100644 index fa7244462ce..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowVI.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedMediumSlowVI } from './FixedMediumSlowVI'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedMediumSlowVI, - title: 'Front Containers/Deprecated Containers/FixedMediumSlowVI', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedMediumSlowVI'; diff --git a/dotcom-rendering/src/components/FixedMediumSlowVI.tsx b/dotcom-rendering/src/components/FixedMediumSlowVI.tsx deleted file mode 100644 index fc956efa2d9..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowVI.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { - Card25Media25TallNoTrail, - Card25Media25TallSmallHeadline, - Card75Media50Right, -} from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedMediumSlowVI = ({ - trails, - containerPalette, - showAge, - imageLoading, - serverTime, -}: Props) => { - const firstSlice75 = trails.slice(0, 1); - const firstSlice25 = trails.slice(1, 2); - const secondSlice25 = trails.slice(2, 6); - - return ( - <> -
    - {firstSlice75.map((trail) => ( -
  • - -
  • - ))} - {firstSlice25.map((trail) => ( -
  • - -
  • - ))} -
-
    - {secondSlice25.map((trail, index) => ( -
  • 0}> - -
  • - ))} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedMediumSlowVII.stories.tsx b/dotcom-rendering/src/components/FixedMediumSlowVII.stories.tsx deleted file mode 100644 index ac23ff1102b..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowVII.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedMediumSlowVII } from './FixedMediumSlowVII'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedMediumSlowVII, - title: 'Front Containers/Deprecated Containers/FixedMediumSlowVII', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedMediumSlowVII'; diff --git a/dotcom-rendering/src/components/FixedMediumSlowVII.tsx b/dotcom-rendering/src/components/FixedMediumSlowVII.tsx deleted file mode 100644 index 2a55104f77b..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowVII.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { - Card25Media25Tall, - Card25Media25TallSmallHeadline, - Card50Media50, -} from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedMediumSlowVII = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice50 = trails.slice(0, 1); - const firstSlice25 = trails.slice(1, 3); - const secondSlice25 = trails.slice(3, 7); - - return ( - <> -
    - {firstSlice50.map((trail) => ( -
  • - -
  • - ))} - - {firstSlice25.map((trail) => ( -
  • - -
  • - ))} -
-
    - {secondSlice25.map((trail, index) => ( -
  • 0}> - -
  • - ))} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.stories.tsx b/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.stories.tsx deleted file mode 100644 index cf4c1f7914d..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.stories.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedMediumSlowXIIMPU } from './FixedMediumSlowXIIMPU'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedMediumSlowXIIMPU, - title: 'Front Containers/Deprecated Containers/FixedMediumSlowXIIMPU', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const OneTrail = () => ( - - - -); -OneTrail.storyName = 'with one trail'; - -export const TwoTrails = () => ( - - - -); -TwoTrails.storyName = 'with two trails'; - -export const ThreeTrails = () => ( - - - -); -ThreeTrails.storyName = 'with three trails'; - -export const FourTrails = () => ( - - - -); -FourTrails.storyName = 'with four trails'; - -export const FiveTrails = () => ( - - - -); -FiveTrails.storyName = 'with five trails'; - -export const SixTrails = () => ( - - - -); -SixTrails.storyName = 'with six trails'; - -export const SevenTrails = () => ( - - - -); -SevenTrails.storyName = 'with seven trails'; - -export const EightTrails = () => ( - - - -); -EightTrails.storyName = 'with eight trails'; - -export const NineTrails = () => ( - - - -); -NineTrails.storyName = 'with nine trails'; diff --git a/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.tsx b/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.tsx deleted file mode 100644 index 03d706ec422..00000000000 --- a/dotcom-rendering/src/components/FixedMediumSlowXIIMPU.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import { - Card100Media100Tall, - Card33Media33MobileTopTall, - Card33Media33Tall, - CardDefault, -} from '../lib/cardWrappers'; -import { shouldPadWrappableRows } from '../lib/dynamicSlices'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - containerPalette?: DCRContainerPalette; - imageLoading: Loading; - serverTime?: number; - showAge?: boolean; -}; - -/* .___________.___________.___________. - * |###########|###########|###########| - * | | | | - * |___________|___________|___________| - */ -const Card33_Card33_Card33 = ({ - trails, - containerPalette, - showAge, - padBottom, - imageLoading, - serverTime, -}: { - trails: DCRFrontCard[]; - imageLoading: Loading; - serverTime?: number; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - padBottom?: boolean; -}) => { - const card33 = trails.slice(0, 1); - const cards33 = trails.slice(1, 3); - - return ( -
    - {card33.map((trail) => ( -
  • - -
  • - ))} - {cards33.map((trail) => ( -
  • - -
  • - ))} -
- ); -}; - -/* .___________.___________. - * |###########|###########| - * | | | - * |___________|___________| - */ -const Card50_Card50 = ({ - trails, - containerPalette, - showAge, - padBottom, - imageLoading, - serverTime, -}: { - trails: DCRFrontCard[]; - imageLoading: Loading; - serverTime?: number; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - padBottom?: boolean; -}) => { - const card50 = trails.slice(0, 1); - const cards50 = trails.slice(1); - return ( -
    - {card50.map((trail) => ( -
  • - -
  • - ))} - {cards50.map((trail) => ( -
  • - -
  • - ))} -
- ); -}; - -/** - * @deprecated Adverts are no longer shown within containers. We no - * longer use containers that reserve a space for an MPU advert. - */ -export const FixedMediumSlowXIIMPU = ({ - trails, - containerPalette, - showAge, - imageLoading, - serverTime, -}: Props) => { - const firstSlice = trails.slice(0, 3); - const remaining = trails.slice(3, 9); - return ( - <> - {trails.length === 1 ? ( -
    -
  • - {trails.map((trail) => ( - - ))} -
  • -
- ) : trails.length === 2 ? ( - - ) : ( - - )} - -
    - {remaining.map((trail, trailIndex) => ( -
  • - -
  • - ))} -
- - ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallFastVIII.stories.tsx b/dotcom-rendering/src/components/FixedSmallFastVIII.stories.tsx deleted file mode 100644 index ab825c68e9f..00000000000 --- a/dotcom-rendering/src/components/FixedSmallFastVIII.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallFastVIII } from './FixedSmallFastVIII'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallFastVIII, - title: 'Front Containers/Deprecated Containers/FixedSmallFastVIII', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallFastVIII'; diff --git a/dotcom-rendering/src/components/FixedSmallFastVIII.tsx b/dotcom-rendering/src/components/FixedSmallFastVIII.tsx deleted file mode 100644 index e74d16ce15e..00000000000 --- a/dotcom-rendering/src/components/FixedSmallFastVIII.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Card25Media25, CardDefault } from '../lib/cardWrappers'; -import { shouldPadWrappableRows } from '../lib/dynamicSlices'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallFastVIII = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - if (!trails[0]) return null; - const firstSlice25 = trails.slice(0, 2); - const remaining = trails.slice(2, 8); - - return ( -
    - {firstSlice25.map((card, cardIndex) => { - return ( -
  • - -
  • - ); - })} -
  • -
      - {remaining.map((card, cardIndex) => { - const columns = 2; - return ( -
    • - -
    • - ); - })} -
    -
  • -
- ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallSlowI.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowI.stories.tsx deleted file mode 100644 index a309b053d6a..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowI.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowI } from './FixedSmallSlowI'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowI, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowI', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallSlowI'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowI.tsx b/dotcom-rendering/src/components/FixedSmallSlowI.tsx deleted file mode 100644 index 69c8d18182e..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowI.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Card100Media75 } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallSlowI = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice100 = trails.slice(0, 1); - - return ( -
    - {firstSlice100.map((card) => ( -
  • - -
  • - ))} -
- ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallSlowIII.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowIII.stories.tsx deleted file mode 100644 index df9f8a3da56..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowIII.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowIII } from './FixedSmallSlowIII'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowIII, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowIII', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallSlowIII'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowIII.tsx b/dotcom-rendering/src/components/FixedSmallSlowIII.tsx deleted file mode 100644 index 16be9d1b7c9..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowIII.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Card25Media25Tall, Card50Media50 } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallSlowIII = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice50 = trails.slice(0, 1); - const firstSlice25 = trails.slice(1, 3); - - return ( -
    - {firstSlice50.map((trail) => ( -
  • - -
  • - ))} - {firstSlice25.map((trail) => ( -
  • - -
  • - ))} -
- ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallSlowIV.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowIV.stories.tsx deleted file mode 100644 index 88599e5b485..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowIV.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowIV } from './FixedSmallSlowIV'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowIV, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowIV', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallSlowIV'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowIV.tsx b/dotcom-rendering/src/components/FixedSmallSlowIV.tsx deleted file mode 100644 index 61abae4aac6..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowIV.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Card25Media25 } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallSlowIV = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice25 = trails.slice(0, 4); - - return ( -
    - {firstSlice25.map((trail, index) => { - return ( -
  • 0}> - -
  • - ); - })} -
- ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallSlowVHalf.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowVHalf.stories.tsx deleted file mode 100644 index 35da2505f36..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVHalf.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowVHalf } from './FixedSmallSlowVHalf'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowVHalf, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowVHalf', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallSlowVHalf'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowVHalf.tsx b/dotcom-rendering/src/components/FixedSmallSlowVHalf.tsx deleted file mode 100644 index 049dc5995e2..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVHalf.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Card50Media50, CardDefaultMediaMobile } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallSlowVHalf = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice50 = trails.slice(0, 1); - const remaining = trails.slice(1, 5); - - return ( -
    - {firstSlice50.map((trail) => { - return ( -
  • - -
  • - ); - })} -
  • -
      - {remaining.map((trail) => { - return ( -
    • - -
    • - ); - })} -
    -
  • -
- ); -}; diff --git a/dotcom-rendering/src/components/FixedSmallSlowVMPU.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowVMPU.stories.tsx deleted file mode 100644 index 8d5c28ba012..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVMPU.stories.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowVMPU } from './FixedSmallSlowVMPU'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowVMPU, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowVMPU', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const FourCards = () => ( - - - -); - -FourCards.storyName = 'With 4 cards'; - -export const ThreeCards = () => ( - - - -); - -ThreeCards.storyName = 'With 3 cards'; - -export const TwoCards = () => ( - - - -); - -TwoCards.storyName = 'With 2 cards'; - -export const OneCard = () => ( - - - -); - -OneCard.storyName = 'With 1 card'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowVMPU.tsx b/dotcom-rendering/src/components/FixedSmallSlowVMPU.tsx deleted file mode 100644 index 8af6450cdde..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVMPU.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Card25Media25SmallHeadline } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -/** - * @deprecated Adverts are no longer shown within containers. We no - * longer use containers that reserve a space for an MPU advert. - */ -export const FixedSmallSlowVMPU = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => ( -
    - {trails.slice(0, 4).map((card, cardIndex) => { - return ( -
  • 0} - key={card.url} - > - -
  • - ); - })} -
-); diff --git a/dotcom-rendering/src/components/FixedSmallSlowVThird.stories.tsx b/dotcom-rendering/src/components/FixedSmallSlowVThird.stories.tsx deleted file mode 100644 index 81e5c656877..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVThird.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/trails'; -import { FixedSmallSlowVThird } from './FixedSmallSlowVThird'; -import { FrontSection } from './FrontSection'; - -export default { - component: FixedSmallSlowVThird, - title: 'Front Containers/Deprecated Containers/FixedSmallSlowVThird', - parameters: { - chromatic: { - viewports: [ - breakpoints.mobile, - breakpoints.tablet, - breakpoints.wide, - ], - }, - }, -}; - -export const Default = () => ( - - - -); -Default.storyName = 'FixedSmallSlowVThird'; diff --git a/dotcom-rendering/src/components/FixedSmallSlowVThird.tsx b/dotcom-rendering/src/components/FixedSmallSlowVThird.tsx deleted file mode 100644 index 61b36ce02ff..00000000000 --- a/dotcom-rendering/src/components/FixedSmallSlowVThird.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Card25Media25 } from '../lib/cardWrappers'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { LI } from './Card/components/LI'; -import { UL } from './Card/components/UL'; -import type { Loading } from './CardPicture'; -import { FrontCard } from './FrontCard'; - -type Props = { - trails: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}; - -export const FixedSmallSlowVThird = ({ - trails, - containerPalette, - showAge, - serverTime, - imageLoading, -}: Props) => { - const firstSlice25 = trails.slice(0, 2); - const remaining = trails.slice(2, 5); - - return ( -
    - {firstSlice25.map((trail, index) => { - return ( -
  • 0} - percentage="25%" - > - -
  • - ); - })} -
  • -
      - {remaining.map((trail) => { - return ( -
    • - -
    • - ); - })} -
    -
  • -
- ); -}; diff --git a/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx b/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx index 603fff642d6..8073f6f5207 100644 --- a/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx +++ b/dotcom-rendering/src/components/FlexibleGeneral.stories.tsx @@ -157,13 +157,16 @@ const meta = { imageLoading: 'eager', aspectRatio: '5:4', collectionId: 1, + isInSlimHomepageAbTestVariant: false, }, render: ({ frontSectionTitle, ...args }) => ( @@ -190,6 +193,14 @@ export const SplashWithStandards: Story = { }, }; +export const SplashWithStandardsInSlimHomepageAbTest: Story = { + name: 'Splash with big and standard cards in the Slim Homepage AB Test', + args: { + ...SplashWithStandards.args, + isInSlimHomepageAbTestVariant: true, + }, +}; + export const SplashWithSublinks: Story = { name: 'Standard splash with sublinks', args: { @@ -206,12 +217,7 @@ export const SplashWithSublinks: Story = { title: string; supportingContent?: DCRSupportingContent[]; }) => ( - + ( - + ( - + ( - + ( - + {containerPalettes.map((containerPalette) => ( ( - + ( -
    -
  • - -
  • -
-); + isInSlimHomepageAbTestVariant, +}: ImmersiveCardLayoutProps) => { + const headlineSizes = { desktop: 'medium', tablet: 'small' } as const; + + return ( +
    +
  • + +
  • +
+ ); +}; type BoostedSplashProperties = { headlineSizes: ResponsiveFontSize; @@ -170,13 +182,18 @@ const decideSplashCardProperties = ( useLargerHeadlineSizeDesktop: boolean, avatarUrl: boolean, isStorylines?: boolean, + isInSlimHomepageAbTestVariant?: boolean, ): BoostedSplashProperties => { switch (boostLevel) { // The default boost level is equal to no boost. It is the same as the default card layout. case 'default': return { headlineSizes: { - desktop: useLargerHeadlineSizeDesktop ? 'large' : 'medium', + desktop: + useLargerHeadlineSizeDesktop && + !isInSlimHomepageAbTestVariant + ? 'large' + : 'medium', tablet: 'medium', mobile: 'medium', }, @@ -190,13 +207,13 @@ const decideSplashCardProperties = ( subtitleSize: 'medium', }; case 'boost': - const boostSupportingContentAlignment: Alignment = - isStorylines || supportingContentLength < 4 - ? 'vertical' - : 'horizontal'; - return { headlineSizes: { + wide: + !isInSlimHomepageAbTestVariant && + useLargerHeadlineSizeDesktop + ? 'xlarge' + : 'large', desktop: useLargerHeadlineSizeDesktop ? 'xlarge' : 'large', tablet: 'large', mobile: 'large', @@ -204,7 +221,10 @@ const decideSplashCardProperties = ( mediaPositionOnDesktop: 'right', mediaPositionOnMobile: mediaCard ? 'top' : 'bottom', mediaSize: avatarUrl ? 'large' : 'xlarge', - supportingContentAlignment: boostSupportingContentAlignment, + supportingContentAlignment: + isStorylines || supportingContentLength < 4 + ? 'vertical' + : 'horizontal', liveUpdatesAlignment: 'vertical', trailTextSize: 'regular', subtitleSize: 'medium', @@ -212,6 +232,11 @@ const decideSplashCardProperties = ( case 'megaboost': return { headlineSizes: { + wide: + !isInSlimHomepageAbTestVariant && + useLargerHeadlineSizeDesktop + ? 'xxlarge' + : 'xlarge', desktop: useLargerHeadlineSizeDesktop ? 'xxlarge' : 'xlarge', @@ -229,6 +254,11 @@ const decideSplashCardProperties = ( case 'gigaboost': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant + ? 'xlarge' + : useLargerHeadlineSizeDesktop + ? 'xxxlarge' + : 'xxlarge', desktop: useLargerHeadlineSizeDesktop ? 'xxxlarge' : 'xxlarge', @@ -257,6 +287,7 @@ type SplashCardLayoutProps = { containerLevel: DCRContainerLevel; collectionId: number; isStorylines?: boolean; + isInSlimHomepageAbTestVariant?: boolean; }; const SplashCardLayout = ({ @@ -270,6 +301,7 @@ const SplashCardLayout = ({ containerLevel, collectionId, isStorylines, + isInSlimHomepageAbTestVariant, }: SplashCardLayoutProps) => { const card = cards[0]; if (!card) return null; @@ -283,6 +315,7 @@ const SplashCardLayout = ({ serverTime={serverTime} imageLoading={imageLoading} collectionId={collectionId} + isInSlimHomepageAbTestVariant={isInSlimHomepageAbTestVariant} /> ); } @@ -309,6 +342,7 @@ const SplashCardLayout = ({ useLargerHeadlineSizeDesktop, !!card.avatarUrl, isStorylines, + isInSlimHomepageAbTestVariant, ); return ( @@ -355,7 +389,10 @@ const SplashCardLayout = ({ subtitleSize={subtitleSize} headlinePosition={card.showLivePlayable ? 'outer' : 'inner'} isStorylines={isStorylines} - starRatingSize={'medium'} + starRatingSize="medium" + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> @@ -368,6 +405,7 @@ type BoostedCardProperties = { liveUpdatesPosition: Position; supportingContentAlignment: Alignment; subtitleSize: SubtitleSize; + isInSlimHomepageAbTestVariant?: boolean; }; /** @@ -378,11 +416,13 @@ const decideCardProperties = ( supportingContentLength: number, boostLevel: Omit = 'boost', avatarUrl?: string, + isInSlimHomepageAbTestVariant = false, ): BoostedCardProperties => { switch (boostLevel) { case 'megaboost': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant ? 'small' : 'medium', desktop: 'medium', tablet: 'small', mobile: 'medium', @@ -422,6 +462,7 @@ type FullWidthCardLayoutProps = { containerLevel: DCRContainerLevel; collectionId: number; isStorylines?: boolean; + isInSlimHomepageAbTestVariant?: boolean; }; const FullWidthCardLayout = ({ @@ -436,6 +477,7 @@ const FullWidthCardLayout = ({ containerLevel, collectionId, isStorylines, + isInSlimHomepageAbTestVariant, }: FullWidthCardLayoutProps) => { const card = cards[0]; if (!card) return null; @@ -450,6 +492,7 @@ const FullWidthCardLayout = ({ card.supportingContent?.length ?? 0, card.boostLevel, card.avatarUrl, + isInSlimHomepageAbTestVariant, ); const shouldShowImmersive = card.isImmersive; @@ -512,7 +555,10 @@ const FullWidthCardLayout = ({ showKickerImage={card.format.design === ArticleDesign.Audio} subtitleSize={subtitleSize} isStorylines={isStorylines} - starRatingSize={'medium'} + starRatingSize="medium" + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> @@ -531,6 +577,7 @@ type HalfWidthCardLayoutProps = { isLastRow: boolean; containerLevel: DCRContainerLevel; isStorylines?: boolean; + isInSlimHomepageAbTestVariant?: boolean; }; const HalfWidthCardLayout = ({ @@ -545,6 +592,7 @@ const HalfWidthCardLayout = ({ isLastRow, containerLevel, isStorylines, + isInSlimHomepageAbTestVariant, }: HalfWidthCardLayoutProps) => { if (cards.length === 0) return null; @@ -597,9 +645,19 @@ const HalfWidthCardLayout = ({ (containerLevel !== 'Primary' && cardIndex > 0) } trailText={undefined} - headlineSizes={undefined} + headlineSizes={ + isInSlimHomepageAbTestVariant + ? { + ...defaultFontSizes, + wide: defaultFontSizes.tablet, + } + : defaultFontSizes + } canPlayInline={false} isStorylines={isStorylines} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); @@ -618,6 +676,7 @@ export const FlexibleGeneral = ({ containerLevel = 'Primary', collectionId, isStorylines = false, + isInSlimHomepageAbTestVariant = false, }: Props) => { const splash = [...groupedTrails.splash].slice(0, 1).map((snap) => ({ ...snap, @@ -647,6 +706,9 @@ export const FlexibleGeneral = ({ containerLevel={containerLevel} collectionId={collectionId} isStorylines={isStorylines} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> )} {groupedCards.map((row, i) => { @@ -666,6 +728,9 @@ export const FlexibleGeneral = ({ containerLevel={containerLevel} collectionId={collectionId} isStorylines={isStorylines} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); @@ -686,6 +751,9 @@ export const FlexibleGeneral = ({ isLastRow={i === groupedCards.length - 1} containerLevel={containerLevel} isStorylines={isStorylines} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); } diff --git a/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx b/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx index 95c9c333222..69831123a5c 100644 --- a/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx +++ b/dotcom-rendering/src/components/FlexibleSpecial.stories.tsx @@ -85,13 +85,16 @@ const meta = { imageLoading: 'eager', aspectRatio: '5:4', frontSectionTitle: 'Flexible special', + isInSlimHomepageAbTestVariant: false, }, render: ({ frontSectionTitle, ...args }) => ( @@ -157,6 +160,13 @@ export const Five: Story = { collectionId: 1, }, }; +export const FiveSlimHomepageAbTest: Story = { + name: 'With one splash card and four standard cards in the Slim Homepage AB Test', + args: { + ...Five.args, + isInSlimHomepageAbTestVariant: true, + }, +}; export const OpinionStandardCards: Story = { name: 'With one splash card and two standard opinion cards', @@ -359,8 +369,7 @@ export const WithSpecialPaletteVariations = { <> {containerPalettes.map((containerPalette) => ( { const shouldDisplaySublinksHorizontally = supportingContentLength >= 3 || hasLiveUpdates; @@ -63,6 +65,11 @@ const determineCardProperties = ( case 'default': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant + ? 'large' + : imageSuppressed + ? 'xxlarge' + : 'xlarge', desktop: imageSuppressed ? 'xxlarge' : 'xlarge', tablet: 'large', mobile: 'medium', @@ -80,6 +87,11 @@ const determineCardProperties = ( case 'boost': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant + ? 'xlarge' + : imageSuppressed + ? 'xxxlarge' + : 'xxlarge', desktop: imageSuppressed ? 'xxxlarge' : 'xxlarge', tablet: 'xlarge', mobile: 'large', @@ -97,6 +109,11 @@ const determineCardProperties = ( case 'megaboost': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant + ? 'xlarge' + : imageSuppressed + ? 'xxxlarge' + : 'xxlarge', desktop: imageSuppressed ? 'xxxlarge' : 'xxlarge', tablet: 'xlarge', mobile: 'xlarge', @@ -112,6 +129,9 @@ const determineCardProperties = ( case 'gigaboost': return { headlineSizes: { + wide: isInSlimHomepageAbTestVariant + ? 'xxlarge' + : 'xxxlarge', desktop: 'xxxlarge', tablet: 'xxlarge', mobile: 'xxlarge', @@ -138,6 +158,7 @@ type OneCardLayoutProps = { isFirstRow: boolean; containerLevel: DCRContainerLevel; isSplashCard?: boolean; + isInSlimHomepageAbTestVariant?: boolean; }; export const OneCardLayout = ({ @@ -151,6 +172,7 @@ export const OneCardLayout = ({ isFirstRow, containerLevel, isSplashCard, + isInSlimHomepageAbTestVariant = false, }: OneCardLayoutProps) => { const card = cards[0]; if (!card) return null; @@ -170,6 +192,7 @@ export const OneCardLayout = ({ isMediaCard(card.format), !card.image, card.showLivePlayable, + isInSlimHomepageAbTestVariant, ); return ( @@ -206,6 +229,9 @@ export const OneCardLayout = ({ headlinePosition={isSplashCard ? 'outer' : 'inner'} subtitleSize={subtitleSize} starRatingSize="medium" + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> @@ -233,6 +259,7 @@ type TwoOrFourCardLayoutProps = { aspectRatio: AspectRatio; isFirstRow: boolean; containerLevel: DCRContainerLevel; + isInSlimHomepageAbTestVariant?: boolean; }; const TwoOrFourCardLayout = ({ @@ -245,6 +272,7 @@ const TwoOrFourCardLayout = ({ aspectRatio, isFirstRow, containerLevel, + isInSlimHomepageAbTestVariant, }: TwoOrFourCardLayoutProps) => { if (cards.length === 0) return null; const hasTwoOrFewerCards = cards.length <= 2; @@ -273,7 +301,14 @@ const TwoOrFourCardLayout = ({ isMediaCard(card.format) || !!card.isNewsletter, )} mediaPositionOnMobile="left" - headlineSizes={undefined} + headlineSizes={ + isInSlimHomepageAbTestVariant + ? { + ...defaultFontSizes, + wide: defaultFontSizes.tablet, + } + : defaultFontSizes + } /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} mediaSize="small" @@ -287,6 +322,9 @@ const TwoOrFourCardLayout = ({ !isMediaCard(card.format)) } canPlayInline={false} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> ); @@ -304,6 +342,7 @@ export const FlexibleSpecial = ({ aspectRatio, containerLevel = 'Primary', collectionId, + isInSlimHomepageAbTestVariant = false, }: Props) => { const snaps = [...groupedTrails.snap].slice(0, 1).map((snap) => ({ ...snap, @@ -332,6 +371,9 @@ export const FlexibleSpecial = ({ isLastRow={splash.length === 0 && cards.length === 0} containerLevel={containerLevel} isSplashCard={false} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> )} {isNonEmptyArray(splash) && ( @@ -346,6 +388,9 @@ export const FlexibleSpecial = ({ isFirstRow={!isNonEmptyArray(snaps)} containerLevel={containerLevel} isSplashCard={true} + isInSlimHomepageAbTestVariant={ + isInSlimHomepageAbTestVariant + } /> )} @@ -358,6 +403,7 @@ export const FlexibleSpecial = ({ aspectRatio={aspectRatio} isFirstRow={!isNonEmptyArray(snaps) && !isNonEmptyArray(splash)} containerLevel={containerLevel} + isInSlimHomepageAbTestVariant={isInSlimHomepageAbTestVariant} /> ); diff --git a/dotcom-rendering/src/components/FrontSection.stories.tsx b/dotcom-rendering/src/components/FrontSection.stories.tsx index 07b28960fc3..3735d8a623c 100644 --- a/dotcom-rendering/src/components/FrontSection.stories.tsx +++ b/dotcom-rendering/src/components/FrontSection.stories.tsx @@ -2,7 +2,6 @@ import { css } from '@emotion/react'; import { breakpoints } from '@guardian/source/foundations'; import type { Meta, StoryObj } from '@storybook/react-webpack5'; import type { ReactNode } from 'react'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; import { LI } from './Card/components/LI'; import { FrontSection } from './FrontSection'; @@ -82,7 +81,6 @@ export default { }, args: { - discussionApiUrl, editionId: 'UK', children: , url: '/', @@ -171,49 +169,29 @@ export const MultipleStory = { - + - + - +

Insert call to action here

- + diff --git a/dotcom-rendering/src/components/FrontSection.tsx b/dotcom-rendering/src/components/FrontSection.tsx index 4581b7aea1c..0fc1347e3d8 100644 --- a/dotcom-rendering/src/components/FrontSection.tsx +++ b/dotcom-rendering/src/components/FrontSection.tsx @@ -3,7 +3,6 @@ import { isString } from '@guardian/libs'; import { between, from, space, until } from '@guardian/source/foundations'; import { pageSkinContainer } from '../layouts/lib/pageSkin'; import { type EditionId, isNetworkFront } from '../lib/edition'; -import { hideAge } from '../lib/hideAge'; import { getOphanComponents } from '../lib/labs'; import { palette as schemePalette } from '../palette'; import type { CollectionBranding } from '../types/branding'; @@ -26,7 +25,6 @@ import { Island } from './Island'; import { LabsSectionHeader } from './LabsSectionHeader'; import { MostPopularFrontRight } from './MostPopularFrontRight'; import { ShowHideButton } from './ShowHideButton'; -import { ShowMore } from './ShowMore.importable'; import { Treats } from './Treats'; type Props = { @@ -38,7 +36,6 @@ type Props = { url?: string; /** The html `id` property of the element */ sectionId?: string; - collectionId?: string; pageId?: string; /** Defaults to `true`. If we should render the top border */ showTopBorder?: boolean; @@ -75,9 +72,6 @@ type Props = { editionId: EditionId; /** A list of related links that appear in the bottom of the left column on fronts */ treats?: TreatType[]; - /** Enable the "Show More" button on this container to allow readers to load more cards */ - canShowMore?: boolean; - ajaxUrl?: string; /** Puts pagination at the bottom of the container allowing the user to navigate to other pages, * usually used on the last container on a page */ pagination?: TagPagePagination; @@ -97,8 +91,8 @@ type Props = { * The Slim Homepage AB test requires some sections to have reduced width so that * the Most Popular Front Right component can be placed on the right-hand side. */ - slimifySectionForAbTest?: boolean; - discussionApiUrl: string; + slimifySectionForSlimHomepageAbTest?: boolean; + showRightContentForSlimHomepageAbTest?: boolean; collectionBranding?: CollectionBranding; isTagPage?: boolean; hasNavigationButtons?: boolean; @@ -552,8 +546,6 @@ const sponsoredContentLabelWrapper = css` * in the centre. Extra elements can be passed to `leftContent`, which will * automatically fall into the left column on larger breakpoints. * - * Defaults to an HTML `section`, but the specific tag can be set. - * * @example * * from `mobile` (320) to `phablet` (660) @@ -563,9 +555,6 @@ const sponsoredContentLabelWrapper = css` * ├───────┤ * │▒▒▒▒▒▒▒│ * │▒▒▒▒▒▒▒│ - * ├───────┤ - * │Show │ - * |More │ * └───────┘ * * from `tablet` (740) to `desktop` (980) @@ -577,8 +566,6 @@ const sponsoredContentLabelWrapper = css` * ├───────────────────────┤ * │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ * │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ - * ├───────────────────────┤ - * │Show More │ * └───────────────────────┘ * * on `leftCol` (1140) if component is toggleable @@ -591,8 +578,6 @@ const sponsoredContentLabelWrapper = css` * │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ * │Tre│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ * │ats│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ - * ├───┼─────────────────────┤ - * │ │Show More │ * └───┴─────────────────────┘ * * on `leftCol` (1140) if component is not toggleable @@ -605,8 +590,6 @@ const sponsoredContentLabelWrapper = css` * │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ * │Tre│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ * │ats│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ - * ├───┼──────────────────────┤ - * │ │Show More │ * └───┴──────────────────────┘ * * on `wide` (1300) @@ -619,8 +602,6 @@ const sponsoredContentLabelWrapper = css` * │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ │ * │ │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ │ * │Treat│▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ │ - * ├─────┼─────────────────────────┤ - * │ │Show More │ * └─────┴─────────────────────────┘ * */ @@ -637,21 +618,18 @@ export const FrontSection = ({ ophanComponentLink, ophanComponentName, sectionId = '', - collectionId, pageId, showDateHeader = false, showTopBorder = true, toggleable = false, treats, url, - canShowMore, - ajaxUrl, pagination, isOnPaidContentFront, targetedTerritory, hasPageSkin = false, - slimifySectionForAbTest = false, - discussionApiUrl, + slimifySectionForSlimHomepageAbTest = false, + showRightContentForSlimHomepageAbTest = false, collectionBranding, isTagPage = false, hasNavigationButtons = false, @@ -664,14 +642,6 @@ export const FrontSection = ({ const isToggleable = toggleable && !!sectionId; const showVerticalRule = !hasPageSkin; const isBetaContainer = !!containerLevel; - const showMore = - canShowMore && - !!title && - !!sectionId && - !!collectionId && - !!pageId && - !!ajaxUrl && - !isBetaContainer; // These are for beta containers only const useLargeSpacingMobile = !!isNextCollectionPrimary || isAboveMobileAd; @@ -687,10 +657,6 @@ export const FrontSection = ({ }) : undefined; - /** - * id is being used to set the containerId in @see {ShowMore.importable.tsx} - * this id pre-existed showMore so is probably also being used for something else. - */ return (
- {slimifySectionForAbTest && sectionId === 'news' && ( -
- - - -
- )} + {showRightContentForSlimHomepageAbTest && + sectionId === 'news' && ( +
+ + + +
+ )} - {slimifySectionForAbTest && sectionId === 'features' && ( -
- - - -
- )} + {showRightContentForSlimHomepageAbTest && + sectionId === 'features' && ( +
+ + + +
+ )}
- ) : showMore ? ( - - - ) : null} {isLabs && diff --git a/dotcom-rendering/src/components/NavList.stories.tsx b/dotcom-rendering/src/components/NavList.stories.tsx index f02b7c440be..b96b88ec682 100644 --- a/dotcom-rendering/src/components/NavList.stories.tsx +++ b/dotcom-rendering/src/components/NavList.stories.tsx @@ -1,5 +1,4 @@ import { breakpoints } from '@guardian/source/foundations'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; import { trails } from '../../fixtures/manual/trails-nav'; import { FrontSection } from './FrontSection'; import { NavList } from './NavList'; @@ -19,22 +18,14 @@ export default { }; export const Default = () => ( - + ); Default.storyName = 'NavList'; export const DefaultWithImages = () => ( - + ); diff --git a/dotcom-rendering/src/components/Palettes.stories.tsx b/dotcom-rendering/src/components/Palettes.stories.tsx index 854058d6e1c..7a0f023565a 100644 --- a/dotcom-rendering/src/components/Palettes.stories.tsx +++ b/dotcom-rendering/src/components/Palettes.stories.tsx @@ -1,7 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react-webpack5'; import type { ComponentProps } from 'react'; import { allModes } from '../../.storybook/modes'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; import { trails } from '../../fixtures/manual/trails'; import type { DCRGroupedTrails } from '../types/front'; import { FlexibleGeneral } from './FlexibleGeneral'; @@ -54,7 +53,6 @@ export const EventPalette: Story = { containerPalette: 'EventPalette', showDateHeader: true, editionId: 'UK', - discussionApiUrl, }, flexibleGeneral: { groupedTrails, diff --git a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx index de0d2c62baf..02ef737ee19 100644 --- a/dotcom-rendering/src/components/ScrollableFeature.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableFeature.importable.tsx @@ -16,6 +16,7 @@ type Props = { imageLoading: 'lazy' | 'eager'; aspectRatio: AspectRatio; collectionId: number; + isInSlimHomepageAbTestVariant?: boolean; }; /** @@ -32,11 +33,18 @@ export const ScrollableFeature = ({ imageLoading, aspectRatio, collectionId, + isInSlimHomepageAbTestVariant, }: Props) => { const isBelowTabletBreakpoint = useMatchMedia( removeMediaRulePrefix(until.tablet), ); + const headlineSizes = { + desktop: 'xsmall', + tablet: 'xxsmall', + mobile: 'xsmall', + } as const; + return ( ( @@ -100,6 +102,12 @@ type Story = StoryObj; export const Default = {}; +export const WithSlimHomepageAbTest = { + args: { + isInSlimHomepageAbTestVariant: true, + }, +} satisfies Story; + export const Media = { args: { trails: [galleryTrails[0], galleryTrails[1], audioTrails[0]], @@ -121,12 +129,7 @@ export const SelfHostedVideo = { title: string; videos: DCRFrontCard[]; }) => ( - + ); @@ -193,7 +196,6 @@ export const WithPrimaryContainer = { render: (args) => ( @@ -225,7 +227,6 @@ export const WithSpecialPaletteVariations = { {containerPalettes.map((containerPalette) => ( { const isBelowTabletBreakpoint = useMatchMedia( removeMediaRulePrefix(until.tablet), @@ -52,6 +54,11 @@ export const ScrollableMedium = ({ ? 'top' : 'bottom'; + const headlineSizes = { + desktop: 'xsmall', + tablet: 'xxsmall', + } as const; + return ( ( @@ -55,6 +57,13 @@ export const WithFourCards = { }, } satisfies Story; +export const WithFourCardsInSlimHomepageAbTest = { + args: { + ...WithFourCards.args, + isInSlimHomepageAbTestVariant: true, + }, +} satisfies Story; + export const WithThreeCards = { args: { trails: trails.slice(0, 3), @@ -89,7 +98,6 @@ export const WithPrimaryContainer = { render: (args) => ( @@ -121,7 +129,6 @@ export const WithSpecialPaletteVariations = { {containerPalettes.map((containerPalette) => ( ( @@ -87,8 +85,7 @@ export const WithPrimaryContainer = { render: (args) => ( @@ -119,7 +116,6 @@ export const WithSpecialPaletteVariations = { {containerPalettes.map((containerPalette) => ( { - if (isOpen && loading) return <>Loading; - if (isOpen) { - return ( - <> - Less{' '} - - {/* The context of what we're hiding is likely more useful for screen-reader users */} - {title} - - - ); - } - return <>More {title}; -}; - -type Props = { - title: string; - pageId: string; - /** - * `collectionId` is the id of the collection as it figures in the fronts - * config. It is used to generate the URL for the show-more API endpoint. - */ - collectionId: string; - /** - * The value of the `id` attribute on the container element that this 'show more' - * button sits beneath. The main container is server-side rendered, so this show more - * button needs to access its contents on the client side so that we can check whether - * any of the stories received from the `show-more` endpoint are already being displayed - * in the main container. (This can happen due to a lag between when the page is SSRd - * and when the user clicks the 'show more' button.) - */ - sectionId: string; - showAge: boolean; - ajaxUrl: string; - editionId: EditionId; - containerPalette?: DCRContainerPalette; - discussionApiUrl: string; -}; - -export const ShowMore = ({ - title, - pageId, - sectionId, - collectionId, - showAge, - ajaxUrl, - editionId, - containerPalette, - discussionApiUrl, -}: Props) => { - const [existingCardLinks, setExistingCardLinks] = useState([]); - const [isOpen, setIsOpen] = useState(false); - - /** - * Store the URLs of the cards in the main container for this button, to - * allow us to filter out duplicated stories when we load them from the - * 'show-more' endpoint. - */ - useEffect(() => { - const container = document.getElementById(`container-${sectionId}`); - const containerLinks = Array.from( - container?.querySelectorAll('a') ?? [], - ) - .map((element) => element.attributes.getNamedItem('href')?.value) - .filter(isNonNullable); - - setExistingCardLinks(containerLinks); - }, [sectionId]); - - /** We only pass an actual URL to SWR when 'showMore' is true. - * Toggling 'isOpen' will trigger a re-render - * @see https://swr.vercel.app/docs/conditional-fetching#conditional - */ - const url = isOpen - ? `${ajaxUrl}/${pageId}/show-more/${collectionId}.json?dcr=true` - : undefined; - const { data, error, loading } = useApi(url); - - const cards = - data && - enhanceCards(data, { - cardInTagPage: false, - editionId, - discussionApiUrl, - }).filter((card) => !existingCardLinks.includes(card.url)); - - const showMoreContainerId = `show-more-${collectionId}`; - - useEffect(() => { - /** - * Focus the first of the new cards when they're loaded in. - * There's no need to check `isOpen` here because if `isOpen` is - * `false` then `filteredData` will be `undefined`. - * */ - - const [card] = cards ?? []; - if (card) { - const maybeFirstCard = document.querySelector( - `#${showMoreContainerId} [data-link-name="${card.dataLinkName}"]`, - ); - if (maybeFirstCard instanceof HTMLElement) { - maybeFirstCard.focus(); - } - } - }, [cards, showMoreContainerId]); - - return ( - <> -
- {cards && ( -
-
    - {cards.map((card, cardIndex) => { - const columns = 3; - return ( -
  • - -
  • - ); - })} -
-
- )} -
-
- - - Loads more stories and moves focus to first new story. - - {error && ( - - Sorry, failed to load more stories. Retrying in a few - seconds. - - )} -
- - ); -}; diff --git a/dotcom-rendering/src/components/ShowMore.stories.tsx b/dotcom-rendering/src/components/ShowMore.stories.tsx deleted file mode 100644 index c82fed71609..00000000000 --- a/dotcom-rendering/src/components/ShowMore.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { userEvent, within } from 'storybook/test'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; -import { trails } from '../../fixtures/manual/show-more-trails'; -import { customMockFetch } from '../lib/mockRESTCalls'; -import { ShowMore } from './ShowMore.importable'; - -/** - * Clicks the 'show more' button so that Chromatic can capture it the component - * in its 'open' state. - */ -const play = async ({ canvasElement }: { canvasElement: HTMLElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole('button')); -}; - -const title = 'Opinion'; -const pageId = 'uk/lifestyle'; -const collectionId = '5011-3940-8793-33a9'; -const ajaxUrl = 'https://api.nextgen.guardianapps.co.uk'; -const sectionId = 'container-id'; -const editionId = 'UK'; - -const defaultProps = { - title, - ajaxUrl, - pageId, - collectionId, - sectionId, - showAge: false, - discussionApiUrl, - editionId, -} satisfies Parameters[0]; - -export default { - component: ShowMore, - title: 'Components/ShowMore', -}; - -const mockShowMoreSuccessRequestFetch = customMockFetch([ - { - mockedMethod: 'GET', - mockedUrl: `${ajaxUrl}/${pageId}/show-more/${collectionId}.json?dcr=true`, - mockedStatus: 200, - mockedBody: trails.slice(0, 6), - }, -]); - -const mockShowMoreErrorRequestFetch = customMockFetch([ - { - mockedMethod: 'GET', - mockedUrl: `${ajaxUrl}/${pageId}/show-more/${collectionId}.json?dcr=true`, - mockedStatus: 400, - mockedBody: undefined, - }, -]); - -export const ShowMoreSuccess = () => { - global.fetch = mockShowMoreSuccessRequestFetch; - - return ShowMore(defaultProps); -}; - -ShowMoreSuccess.play = play; -ShowMoreSuccess.storyName = 'ShowMore button, success'; - -export const ShowMoreError = () => { - global.fetch = mockShowMoreErrorRequestFetch; - - return ShowMore(defaultProps); -}; - -ShowMoreError.play = play; -ShowMoreError.storyName = 'ShowMore button, error'; diff --git a/dotcom-rendering/src/components/StaticFeatureTwo.stories.tsx b/dotcom-rendering/src/components/StaticFeatureTwo.stories.tsx index af1c2e6cf3f..899c4e06139 100644 --- a/dotcom-rendering/src/components/StaticFeatureTwo.stories.tsx +++ b/dotcom-rendering/src/components/StaticFeatureTwo.stories.tsx @@ -1,6 +1,5 @@ import { breakpoints } from '@guardian/source/foundations'; import type { Meta, StoryObj } from '@storybook/react-webpack5'; -import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl'; import { audioTrails, galleryTrails, @@ -36,7 +35,6 @@ const meta = { render: (args) => ( @@ -97,12 +95,7 @@ export const SelfHostedVideo = { title: string; videos: DCRFrontCard[]; }) => ( - + ); @@ -175,7 +168,6 @@ export const WithSpecialPaletteVariations = { {containerPalettes.map((containerPalette) => ( ( @@ -53,6 +55,14 @@ export const Four = { }, }; +export const FourSlimHomepageAbTest = { + name: 'With Four Cards in Slim Homepage AB Test', + args: { + trails: trails.slice(0, 4), + isInSlimHomepageAbTestVariant: true, + }, +}; + export const Three: Story = { name: 'With Three Cards', args: { @@ -109,7 +119,6 @@ export const WithSpecialPaletteVariations = { {containerPalettes.map((containerPalette) => ( { const cards = trails.slice(0, 4); @@ -70,7 +73,14 @@ export const StaticMediumFour = ({ !!card.isNewsletter, )} mediaPositionOnMobile="left" - headlineSizes={undefined} + headlineSizes={ + isInSlimHomepageAbTestVariant + ? { + ...defaultFontSizes, + wide: defaultFontSizes.tablet, + } + : defaultFontSizes + } /* we don't want to support sublinks on standard cards here so we hard code to undefined */ supportingContent={undefined} mediaSize="medium" diff --git a/dotcom-rendering/src/components/TopBarSupport.importable.tsx b/dotcom-rendering/src/components/TopBarSupport.importable.tsx index c5ff084cc00..f1d76407bd2 100644 --- a/dotcom-rendering/src/components/TopBarSupport.importable.tsx +++ b/dotcom-rendering/src/components/TopBarSupport.importable.tsx @@ -5,7 +5,6 @@ import { css } from '@emotion/react'; import { getCookie, isUndefined } from '@guardian/libs'; import type { ComponentEvent } from '@guardian/ophan-tracker-js'; -import { getHeader } from '@guardian/support-dotcom-components'; import type { HeaderPayload, ModuleData, @@ -18,9 +17,11 @@ import type { import { useEffect, useState } from 'react'; import { submitComponentEvent } from '../client/ophan/ophan'; import { + getAuthHeaders, getPurchaseInfo, shouldHideSupportMessaging, } from '../lib/contributions'; +import { getHeader } from '../lib/sdcRequests'; import { useBetaAB } from '../lib/useAB'; import { useIsSignedIn } from '../lib/useAuthStatus'; import { useCountryCode } from '../lib/useCountryCode'; @@ -68,6 +69,7 @@ const ReaderRevenueLinksRemote = ({ const hideSupportMessagingForUser = shouldHideSupportMessaging(isSignedIn); + if (hideSupportMessagingForUser === 'Pending') { // We don't yet know the user's supporter status return; @@ -90,7 +92,10 @@ const ReaderRevenueLinksRemote = ({ }, }; - getHeader(contributionsServiceUrl, requestData) + getAuthHeaders() + .then((headers) => + getHeader(contributionsServiceUrl, requestData, headers), + ) .then((response: ModuleDataResponse) => { if (!response.data) { return null; @@ -102,9 +107,9 @@ const ReaderRevenueLinksRemote = ({ return ( module.name === 'SignInPromptHeader' ? /* webpackChunkName: "sign-in-prompt-header" */ - import(`./marketing/header/SignInPromptHeader`) + import('./marketing/header/SignInPromptHeader') : /* webpackChunkName: "header" */ - import(`./marketing/header/Header`) + import('./marketing/header/Header') ).then( (headerModule: { [key: string]: React.ElementType; @@ -117,7 +122,6 @@ const ReaderRevenueLinksRemote = ({ }) .catch((error) => { const msg = `Error importing RR header links: ${String(error)}`; - console.log(msg); window.guardian.modules.sentry.reportError( new Error(msg), diff --git a/dotcom-rendering/src/frontend/feFront.ts b/dotcom-rendering/src/frontend/feFront.ts index a9fcb040380..383611a67d3 100644 --- a/dotcom-rendering/src/frontend/feFront.ts +++ b/dotcom-rendering/src/frontend/feFront.ts @@ -40,19 +40,6 @@ interface FEPressedPage { /* This list of containers supported in DCR must be kept up to date with frontend **manually**. * @see https://github.com/guardian/frontend/blob/167dce23a8453ed13a97fbd23c7fc45ecb06e3fe/facia/app/services/dotcomrendering/FaciaPicker.scala#L21-L45 */ export type FEContainer = - | 'fixed/large/slow-XIV' - | 'fixed/medium/fast-XI' - | 'fixed/medium/fast-XII' - | 'fixed/medium/slow-VI' - | 'fixed/medium/slow-VII' - | 'fixed/medium/slow-XII-mpu' - | 'fixed/small/fast-VIII' - | 'fixed/small/slow-I' - | 'fixed/small/slow-III' - | 'fixed/small/slow-IV' - | 'fixed/small/slow-V-half' - | 'fixed/small/slow-V-mpu' - | 'fixed/small/slow-V-third' | 'fixed/thrasher' | 'nav/list' | 'nav/media-list' @@ -299,7 +286,7 @@ type FECollectionConfig = { showLatestUpdate: boolean; excludeFromRss: boolean; showTimestamps: boolean; - hideShowMore: boolean; + hideShowMore?: boolean; // deprecated. Collections no longer show more content. platform: string; aspectRatio?: FEAspectRatio; }; @@ -322,7 +309,7 @@ export type FECollection = { showDateHeader: boolean; showLatestUpdate: boolean; config: FECollectionConfig; - hasMore: boolean; + hasMore?: boolean; // deprecated. Collections no longer show more content. targetedTerritory?: Territory; }; diff --git a/dotcom-rendering/src/frontend/schemas/feFront.json b/dotcom-rendering/src/frontend/schemas/feFront.json index e55d29ae127..039df052ce4 100644 --- a/dotcom-rendering/src/frontend/schemas/feFront.json +++ b/dotcom-rendering/src/frontend/schemas/feFront.json @@ -2926,7 +2926,6 @@ "displayName", "excludeFromRss", "hideKickers", - "hideShowMore", "platform", "showDateHeader", "showLatestUpdate", @@ -2949,7 +2948,6 @@ "config", "curated", "displayName", - "hasMore", "hideKickers", "id", "showDateHeader", @@ -3393,19 +3391,6 @@ }, "FEContainer": { "enum": [ - "fixed/large/slow-XIV", - "fixed/medium/fast-XI", - "fixed/medium/fast-XII", - "fixed/medium/slow-VI", - "fixed/medium/slow-VII", - "fixed/medium/slow-XII-mpu", - "fixed/small/fast-VIII", - "fixed/small/slow-I", - "fixed/small/slow-III", - "fixed/small/slow-IV", - "fixed/small/slow-V-half", - "fixed/small/slow-V-mpu", - "fixed/small/slow-V-third", "fixed/thrasher", "flexible/general", "flexible/special", diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index 7325e7e5bf1..e9630739c85 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -40,6 +40,7 @@ import { } from '../lib/getFrontsAdPositions'; import { hideAge } from '../lib/hideAge'; import { ophanComponentId } from '../lib/ophan-helpers'; +import { doesPageQualifyForSlimHomepageAbTest } from '../lib/SlimHomepageAbTestHelpers'; import { useBetaAB } from '../lib/useAB'; import type { NavType } from '../model/extract-nav'; import { palette as schemePalette } from '../palette'; @@ -82,15 +83,16 @@ const isToggleable = ( * The show/hide button would be covered by the MostPopularFrontRight component * in the variant of the Slim Homepage AB test. */ - isTargetedContainerInSlimHomepageAbTest: boolean, + isShowingRightContentForSlimHomepageAbTest: boolean, ) => { + if (isShowingRightContentForSlimHomepageAbTest) return; + if (isNetworkFront) { return ( collection.displayName.toLowerCase() !== 'headlines' && !isNavList(collection) && !isHighlights(collection) && - !isLabs(collection) && - !isTargetedContainerInSlimHomepageAbTest + !isLabs(collection) ); } @@ -152,18 +154,33 @@ export const FrontLayout = ({ front, NAV }: Props) => { /** * The Slim Homepage AB test only runs on /uk and on screen widths >=1300px. - * In the variant of this test a Most Popular component is added to the right-hand side of the page. - * Page skins require slim content and is incompatible with this test. We do not run this test - * on pages where there is a page skin (a page skin takes precedence). + * In variant one and two of this test, the content is slimmed down. + * In variant two of this test, a Most Popular component is inserted into the right-hand side of the page. + * Page skins require slim content and is incompatible with this test, so we do not run + * this test on pages where there is a page skin (a page skin takes precedence). */ - const isInSlimHomepageAbTestVariant = - (pageId === 'uk' && - !hasPageSkin && + const pageQualifiesForSlimHomepageAbTest = + doesPageQualifyForSlimHomepageAbTest( + front.pressedPage.collections, + pageId, + hasPageSkin, + ); + const isInSlimHomepageAbTestVariantOne = + (pageQualifiesForSlimHomepageAbTest && + abTests?.isUserInTestGroup( + 'fronts-and-curation-slim-homepage', + 'variant-one', + )) ?? + false; + const isInSlimHomepageAbTestVariantTwo = + (pageQualifiesForSlimHomepageAbTest && abTests?.isUserInTestGroup( 'fronts-and-curation-slim-homepage', - 'variant', + 'variant-two', )) ?? false; + const isInEitherSlimHomepageAbTestVariant = + isInSlimHomepageAbTestVariantOne || isInSlimHomepageAbTestVariantTwo; const fallbackAspectRatio = (collectionType: DCRContainerType) => { switch (collectionType) { @@ -309,11 +326,6 @@ export const FrontLayout = ({ front, NAV }: Props) => { * We only shrink the content of certain sections to place the Most Popular * content on the right-hand side. Other sections remain full-width. */ - const isTargetedContainerInSlimHomepageAbTest = - isInSlimHomepageAbTestVariant && - (collection.displayName === 'News' || - collection.displayName === 'Features' || - collection.displayName === 'More features'); if (collection.collectionType === 'scrollable/highlights') { // Highlights are rendered in the Masthead component @@ -436,9 +448,6 @@ export const FrontLayout = ({ front, NAV }: Props) => { treats={collection.treats} data-print-layout="hide" hasPageSkin={hasPageSkin} - discussionApiUrl={ - front.config.discussionApiUrl - } > { index, collection, front.isNetworkFront, - isTargetedContainerInSlimHomepageAbTest, + isInSlimHomepageAbTestVariantTwo, )} leftContent={decideLeftContent( front, collection, )} sectionId={ophanName} - collectionId={collection.id} pageId={front.pressedPage.id} showDateHeader={ collection.config.showDateHeader } editionId={front.editionId} treats={collection.treats} - canShowMore={collection.canShowMore} - ajaxUrl={front.config.ajaxUrl} isOnPaidContentFront={isPaidContent} targetedTerritory={collection.targetedTerritory} hasPageSkin={hasPageSkin} - discussionApiUrl={front.config.discussionApiUrl} collectionBranding={ collection.collectionBranding } @@ -534,8 +539,11 @@ export const FrontLayout = ({ front, NAV }: Props) => { index, )} isLabs={isLabs(collection)} - slimifySectionForAbTest={ - isTargetedContainerInSlimHomepageAbTest + slimifySectionForSlimHomepageAbTest={ + isInEitherSlimHomepageAbTestVariant + } + showRightContentForSlimHomepageAbTest={ + isInSlimHomepageAbTestVariantTwo } mostViewed={front.mostViewed} deeplyRead={front.deeplyRead} @@ -563,6 +571,9 @@ export const FrontLayout = ({ front, NAV }: Props) => { sectionId={ophanName} collectionId={index + 1} containerLevel={collection.containerLevel} + isInSlimHomepageAbTestVariant={ + isInEitherSlimHomepageAbTestVariant + } /> diff --git a/dotcom-rendering/src/layouts/TagPageLayout.tsx b/dotcom-rendering/src/layouts/TagPageLayout.tsx index d04caa4d218..54134266100 100644 --- a/dotcom-rendering/src/layouts/TagPageLayout.tsx +++ b/dotcom-rendering/src/layouts/TagPageLayout.tsx @@ -193,19 +193,14 @@ export const TagPageLayout = ({ tagPage, NAV }: Props) => { toggleable={false} pageId={tagPage.pageId} editionId={tagPage.editionId} - canShowMore={false} - ajaxUrl={tagPage.config.ajaxUrl} pagination={tagPagePagination} - discussionApiUrl={ - tagPage.config.discussionApiUrl - } > {insertStorylinesSection && diff --git a/dotcom-rendering/src/lib/SlimHomepageAbTestHelpers.ts b/dotcom-rendering/src/lib/SlimHomepageAbTestHelpers.ts new file mode 100644 index 00000000000..020f416dd05 --- /dev/null +++ b/dotcom-rendering/src/lib/SlimHomepageAbTestHelpers.ts @@ -0,0 +1,37 @@ +import type { DCRCollectionType } from '../types/front'; + +const requiredCollectionsForTest = ['news', 'features', 'more features']; + +const hasRequiredSlimHomepageAbTestCollections = ( + collections: DCRCollectionType[], +): boolean => { + return requiredCollectionsForTest.every((displayName) => + collections + .map((collection) => collection.displayName.toLowerCase()) + .includes(displayName), + ); +}; + +export const doesPageQualifyForSlimHomepageAbTest = ( + collections: DCRCollectionType[], + pageId: string, + hasPageSkin: boolean, +): boolean => { + /** + * This is temporarily commmented out so that we can review this in a 0% test. + * There is currently a container above News on the UK front, which means the test won't run. + * We will only start this test once News is again the top container, but in the meantime, we + * would like to be able to review this test. + */ + // const isFirstCollectionNews = + // collections[0]?.displayName.toLowerCase() === 'news'; + const hasRequiredCollections = + hasRequiredSlimHomepageAbTestCollections(collections); + + return ( + pageId === 'uk' && + !hasPageSkin && + // isFirstCollectionNews && + hasRequiredCollections + ); +}; diff --git a/dotcom-rendering/src/lib/braze/BrazeBannersSystem.tsx b/dotcom-rendering/src/lib/braze/BrazeBannersSystem.tsx index 7df0bf1d9dc..109752d4a4a 100644 --- a/dotcom-rendering/src/lib/braze/BrazeBannersSystem.tsx +++ b/dotcom-rendering/src/lib/braze/BrazeBannersSystem.tsx @@ -955,7 +955,7 @@ export const BrazeBannersSystemDisplay = ({ position: relative; display: grid; margin: 0px auto; - padding: 12px 4px 0px 12px; + padding: 0px 4px 0px 12px; bottom: 0px; column-gap: 10px; align-self: stretch; @@ -981,7 +981,7 @@ export const BrazeBannersSystemDisplay = ({ '. copy-container close-button close-button' / minmax(0px, 0.5fr) 492px max-content minmax(0px, 0.5fr); - padding: 12px 12px 0px; + padding: 0px 12px 0px; } ${until.phablet} { max-width: 660px; @@ -1024,7 +1024,7 @@ export const BrazeBannersSystemDisplay = ({ background-color: ${wrapperModeForegroundColor}; width: 1px; opacity: 0.2; - margin: 24px 8px 0px; + margin: 18px 8px 0px; ${until.leftCol} { display: none; @@ -1042,18 +1042,14 @@ export const BrazeBannersSystemDisplay = ({ grid-area: copy-container; padding-left: 12px; padding-right: 12px; - padding-top: 24px; - padding-bottom: 12px; + padding-top: 18px; + padding-bottom: 24px; ${until.leftCol} { padding-left: 0px; padding-right: 0px; } - ${until.phablet} { - padding-top: 0px; - padding-bottom: 0px; - } - ${until.phablet} { - padding-top: 24px; + ${until.desktop} { + padding-top: 12px; } ` : undefined diff --git a/dotcom-rendering/src/lib/cardWrappers.tsx b/dotcom-rendering/src/lib/cardWrappers.tsx index 7a70efebecf..6d7d0c893e2 100644 --- a/dotcom-rendering/src/lib/cardWrappers.tsx +++ b/dotcom-rendering/src/lib/cardWrappers.tsx @@ -143,381 +143,21 @@ export const Card100Media75 = ({ ); }; -/** - * ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃ ┃ - * ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - * Card designed to take up 100% of the container, with media that takes up 75% - * - * Options: - * - Huge headline (large on mobile) - * - Jumbo image on the top (top on mobile) - * - No trail text - * - Up to 4 supporting content items, always aligned horizontal - */ -export const Card100Media100 = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ - * ┃ ┃ - * ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - * Card designed to take up 100% of the container, with media that takes up 75% - * - * Options: - * - Medium headline (large on mobile) - * - Jumbo image on the top (top on mobile) - * - Trail text - * - Up to 2 supporting content items, always aligned vertically - */ - -export const Card100Media100Tall = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┐ - * ┃ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ 25% ┊ - * ┃ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃Remaining┊ - * ┃ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ ┊ - * ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 75% of the container, with media that takes up 50% - * - * Options: - * - Large headline (large on mobile) - * - Large image on the right (top on mobile) - * - Trail text - * - Up to 3 supporting content items, 1-2 aligned vertical, 3 aligned horizontal - */ -export const Card75Media50Right = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - 2 - ? 'horizontal' - : 'vertical' - } - mediaPositionOnDesktop="right" - mediaSize="large" - mediaPositionOnMobile="top" - imageLoading={imageLoading} - isTagPage={isTagPage} - headlineSizes={{ desktop: 'small', tablet: 'xsmall' }} - aspectRatio={aspectRatio} - /> - ); -}; - -/** - * ┌┈┈┈┈┈┈┈┈┈┲━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - * ┊ 25% ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ┃ - * ┊Remaining┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ┃ - * ┊ ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ┃ - * └┈┈┈┈┈┈┈┈┈┺━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - * Card designed to take up 75% of the container, with media that takes up 50% - * - * Options: - * - Large headline (large on mobile) - * - Large image on the left (top on mobile) - * - Trail text - * - Up to 3 supporting content items, 1-2 aligned vertical, 3 aligned horizontal - */ -export const Card75Media50Left = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - 2 - ? 'horizontal' - : 'vertical' - } - mediaPositionOnDesktop="left" - mediaPositionOnMobile="top" - mediaSize="large" - imageLoading={imageLoading} - isTagPage={isTagPage} - headlineSizes={{ desktop: 'small', tablet: 'xsmall' }} - aspectRatio={aspectRatio} - /> - ); -}; - -/** - * ┏━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒┃ 75% ┊ - * ┃▒▒▒▒▒▒▒▒▒┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 25% of the container, with media that takes up 25% - * - * Options: - * - Medium headline (medium on mobile) - * - Small image on the top (left on mobile) - * - No trail text - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card25Media25 = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒┃ 75% ┊ - * ┃▒▒▒▒▒▒▒▒▒┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 25% of the container, with media that takes up 25% - * - * Options: - * - Medium headline (medium on mobile) - * - Small image on the top (left on mobile) - * - No trail text - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card25Media25SmallHeadline = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒┃ ┊ - * ┃▒▒▒▒▒▒▒▒▒┃ 75% ┊ - * ┃ ┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 25% of the container, with media that takes up 25% - * - * Options: - * - Medium headline (medium on mobile) - * - Small image on the top (left on mobile) - * - Trail text when there is no supporting content - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card25Media25Tall = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒┃ ┊ - * ┃▒▒▒▒▒▒▒▒▒┃ 75% ┊ - * ┃ ┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 25% of the container, with media that takes up 25% - * - * Options: - * - Medium headline (medium on mobile) - * - Small image on the top (left on mobile) - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card25Media25TallNoTrail = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - /** * ┏━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒┃ ┊ * ┃▒▒▒▒▒▒▒▒▒┃ 75% ┊ - * ┃ ┃ Remaining ┊ + * ┃▒▒▒▒▒▒▒▒▒┃ Remaining ┊ * ┃ ┃ ┊ * ┗━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ * Card designed to take up 25% of the container, with media that takes up 25% * * Options: - * - Small headline (medium on mobile) + * - Medium headline (medium on mobile) * - Small image on the top (left on mobile) + * - No trail text * - Up to 2 supporting content items, always aligned vertical */ -export const Card25Media25TallSmallHeadline = ({ +export const Card25Media25 = ({ trail, showAge, containerPalette, @@ -529,6 +169,8 @@ export const Card25Media25TallSmallHeadline = ({ return ( @@ -587,91 +228,6 @@ export const Card50Media50 = ({ ); }; -/** - * ┏━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ ┊ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ 50% ┊ - * ┃ ┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 50% of the container, with media that takes up 50% - * - * Options: - * - Large headline (large on mobile) - * - Medium image on the top (top on mobile) - * - Trail text - * - Up to 3 supporting content items, always aligned horizontal - */ -export const Card50Media50Tall = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ 33% ┊ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━━━━━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 66% of the container, with media that takes up 66% - * - * Options: - * - Medium headline (medium on mobile) - * - Large image on the top (top on mobile) - * - Trail text - * - No supporting content - */ -export const Card66Media66 = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - /** * ┏━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ * ┃▒▒▒▒▒▒▒▒▒▒▒▒┃ 66% ┊ @@ -712,90 +268,6 @@ export const Card33Media33 = ({ /> ); }; -/** - * ┏━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒┃ 66% ┊ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 33% of the container, with media that takes up 33% - * - * Options: - * - Medium headline (medium on mobile) - * - Medium image on the top (left on mobile) - * - No trail text - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card33Media33Tall = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒┃ 66% ┊ - * ┃▒▒▒▒▒▒▒▒▒▒▒▒┃ Remaining ┊ - * ┃ ┃ ┊ - * ┗━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up 33% of the container, with media that takes up 33% - * - * Options: - * - Medium headline (large on mobile) - * - Medium image at the top, including on mobile - * - Trail text - * - Up to 2 supporting content items, always aligned vertical - */ -export const Card33Media33MobileTopTall = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; /** * ┏━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ @@ -833,81 +305,3 @@ export const CardDefault = ({ /> ); }; - -/** - * ┏━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃ ▒▒▒▒ ┃ Any% Remaining ┊ - * ┗━━━━━━━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up any width of container, with small left media - * - * Options: - * - Small headline (small on mobile) - * - Small image on the left (none on mobile) - * - No trail text - * - No supporting content - */ -export const CardDefaultMedia = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; - -/** - * ┏━━━━━━━━━━━━━━━━━━┱┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐ - * ┃ ▒▒▒▒ ┃ Any% Remaining ┊ - * ┗━━━━━━━━━━━━━━━━━━┹┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘ - * Card designed to take up any width of container, with small left media - * - * Options: - * - Small headline (small on mobile) - * - Small image on the left (left on mobile) - * - No trail text - * - No supporting content - */ -export const CardDefaultMediaMobile = ({ - trail, - showAge, - containerPalette, - imageLoading, - isTagPage, - serverTime, - aspectRatio, -}: TrailProps) => { - return ( - - ); -}; diff --git a/dotcom-rendering/src/lib/contributions.ts b/dotcom-rendering/src/lib/contributions.ts index f5a31632c69..1bb2d8b671b 100644 --- a/dotcom-rendering/src/lib/contributions.ts +++ b/dotcom-rendering/src/lib/contributions.ts @@ -227,10 +227,14 @@ export const getPurchaseInfo = (): PurchaseInfo => { return purchaseInfo; }; -const hasCanTargetConsent = (): Promise => - onConsent() +const hasCanTargetConsent = (): Promise => { + if (getCookie({ name: 'gu-cmp-disabled', shouldMemoize: true })) { + return Promise.resolve(true); + } + return onConsent() .then(({ canTarget }: ConsentState) => canTarget) .catch(() => false); +}; // Returns Auth headers only if user has targeting consent and is signed in export const getAuthHeaders = async (): Promise => { diff --git a/dotcom-rendering/src/lib/dynamicSlices.test.ts b/dotcom-rendering/src/lib/dynamicSlices.test.ts deleted file mode 100644 index 8b6fa51e064..00000000000 --- a/dotcom-rendering/src/lib/dynamicSlices.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { shouldPadWrappableRows } from './dynamicSlices'; - -describe('shouldPadWrappableRows', () => { - describe('Three columns', () => { - const columns = 3; - describe('Five cards', () => { - const cards = 5; - // true: 0 1 2 - // false: 3 4 - it.each([ - [0, true], - [1, true], - [2, true], - [3, false], - [4, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - - describe('Six cards', () => { - const cards = 6; - // true: 0 1 2 - // false: 3 4 5 - it.each([ - [0, true], - [1, true], - [2, true], - [3, false], - [4, false], - [5, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - - describe('Seven cards', () => { - const cards = 7; - // true: 0 1 2 - // true: 3 4 5 - // false: 6 - it.each([ - [0, true], - [1, true], - [2, true], - [3, true], - [4, true], - [5, true], - [6, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - }); - - describe('Two columns', () => { - const columns = 2; - describe('Three cards', () => { - const cards = 3; - // true: 0 1 - // false: 2 - it.each([ - [0, true], - [1, true], - [2, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - - describe('Four cards', () => { - const cards = 4; - // true: 0 1 - // false: 2 3 - it.each([ - [0, true], - [1, true], - [2, false], - [3, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - - describe('Five cards', () => { - const cards = 5; - // true: 0 1 - // true: 2 3 - // false: 4 - it.each([ - [0, true], - [1, true], - [2, true], - [3, true], - [4, false], - ])('card number %s should return %s', (index, expected) => { - expect(shouldPadWrappableRows(index, cards, columns)).toBe( - expected, - ); - }); - }); - }); -}); diff --git a/dotcom-rendering/src/lib/dynamicSlices.tsx b/dotcom-rendering/src/lib/dynamicSlices.tsx deleted file mode 100644 index c64f1b4ade5..00000000000 --- a/dotcom-rendering/src/lib/dynamicSlices.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { LI } from '../components/Card/components/LI'; -import { UL } from '../components/Card/components/UL'; -import type { Loading } from '../components/CardPicture'; -import type { DCRContainerPalette, DCRFrontCard } from '../types/front'; -import { Card25Media25Tall, Card50Media50 } from './cardWrappers'; - -/* ._________________.________.________. - * |#################|########|########| - * | | | | - * |_________________|________|________| - */ -export const Card50_Card25_Card25 = ({ - cards, - containerPalette, - showAge, - serverTime, - imageLoading, -}: { - cards: DCRFrontCard[]; - imageLoading: Loading; - containerPalette?: DCRContainerPalette; - showAge?: boolean; - serverTime?: number; -}) => { - const card50 = cards.slice(0, 1); - const cards25 = cards.slice(1, 3); - - return ( -
    - {card50.map((trail) => ( -
  • - -
  • - ))} - - {cards25.map((trail) => ( -
  • - -
  • - ))} -
- ); -}; - -/** - * Abstraction to decide whether to show padding on wrapped rows of cards. - * - * For three columns, We have different results with 5 or 9 cards - * - * @example - All but last 2 - * ``` - * ┌───┐ ┌───┐ ┌───┐ - * │Pad│ │Pad│ │Pad│ - * └───┘ └───┘ └───┘ - * ┌───┐ ┌───┐ - * │No!│ │No!│ - * └───┘ └───┘ - * ``` - * - All but last 3 - * ``` - * ┌───┐ ┌───┐ ┌───┐ - * │Pad│ │Pad│ │Pad│ - * └───┘ └───┘ └───┘ - * ┌───┐ ┌───┐ ┌───┐ - * │Pad│ │Pad│ │Pad│ - * └───┘ └───┘ └───┘ - * ┌───┐ ┌───┐ ┌───┐ - * │No!│ │No!│ │No!│ - * └───┘ └───┘ └───┘ - * ``` - * - * @param index - Index of the current card - * @param totalCards - Total number of cards being shown - * @param cardsPerRow - No. of cards in each row (if full) - */ -export const shouldPadWrappableRows = ( - index: number, - totalCards: number, - cardsPerRow: number, -): boolean => index < totalCards - (totalCards % cardsPerRow || cardsPerRow); diff --git a/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts b/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts index 0eb6f82b616..fb07bbb378e 100644 --- a/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts +++ b/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts @@ -17,7 +17,7 @@ import { } from './getFrontsAdPositions'; const testCollection: AdCandidate = { - collectionType: 'fixed/large/slow-XIV', + collectionType: 'flexible/general', displayName: 'Test Collection', containerLevel: 'Primary', containerPalette: 'EventPalette', @@ -73,18 +73,18 @@ describe('Mobile Ads', () => { // We used https://www.theguardian.com/uk/commentisfree as a blueprint it('Non-network front, with more than 4 collections, without thrashers', () => { const testCollections: AdCandidate[] = [ - { ...testCollection, collectionType: 'fixed/large/slow-XIV' }, // Ad position (0) - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (2) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (4) + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (2) + { ...testCollection, collectionType: 'static/medium/4' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (4) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (6) { ...testCollection, collectionType: 'flexible/general' }, - { ...testCollection, collectionType: 'fixed/small/slow-I' }, // Ad position (6) - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (8) - { ...testCollection, collectionType: 'fixed/small/slow-III' }, - { ...testCollection, collectionType: 'fixed/medium/fast-XII' }, // Ignored - is before merch high position - { ...testCollection, collectionType: 'fixed/small/fast-VIII' }, // Ignored - is merch high position + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (8) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is before merch high position + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -97,28 +97,28 @@ describe('Mobile Ads', () => { it('UK Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, + { ...testCollection, collectionType: 'static/medium/4' }, { ...testCollection, collectionType: 'flexible/special' }, // Ad position (2) { ...testCollection, collectionType: 'flexible/special' }, - { ...testCollection, collectionType: 'fixed/small/slow-V-mpu' }, // Ad position (4) + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (4) { ...testCollection, collectionType: 'flexible/special' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (8) { ...testCollection, collectionType: 'flexible/general' }, { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (11) { ...testCollection, collectionType: 'flexible/general' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (14) { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, { ...testCollection, collectionType: 'scrollable/feature' }, // Ad position (17) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (19) + { ...testCollection, collectionType: 'static/medium/4' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (19) { ...testCollection, collectionType: 'flexible/general' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - is before merch high position - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ignored - is merch high position + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - is before merch high position + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -131,24 +131,24 @@ describe('Mobile Ads', () => { it('International Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, + { ...testCollection, collectionType: 'static/medium/4' }, { ...testCollection, collectionType: 'flexible/special' }, // Ad position (2) { ...testCollection, collectionType: 'flexible/general' }, { ...testCollection, collectionType: 'flexible/special' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (5) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, + { ...testCollection, collectionType: 'static/medium/4' }, { ...testCollection, collectionType: 'flexible/general' }, // Ad position (7) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (11) { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, { ...testCollection, collectionType: 'flexible/general' }, // Ad position (14) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (16) + { ...testCollection, collectionType: 'static/medium/4' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (16) { ...testCollection, collectionType: 'scrollable/feature' }, - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ignored - is merch high position + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -161,24 +161,24 @@ describe('Mobile Ads', () => { it('US Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (2) + { ...testCollection, collectionType: 'static/medium/4' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (2) { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, { ...testCollection, collectionType: 'flexible/special' }, // Ad position (5) { ...testCollection, collectionType: 'flexible/special' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, - { ...testCollection, collectionType: 'fixed/small/slow-III' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (9) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, + { ...testCollection, collectionType: 'static/medium/4' }, { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (12) - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, + { ...testCollection, collectionType: 'flexible/general' }, { ...testCollection, collectionType: 'flexible/general' }, // Ad position (14) { ...testCollection, collectionType: 'flexible/general' }, - { ...testCollection, collectionType: 'fixed/small/slow-V-mpu' }, // Ad position (16) + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (16) { ...testCollection, collectionType: 'scrollable/feature' }, - { ...testCollection, collectionType: 'fixed/small/slow-III' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -192,20 +192,20 @@ describe('Mobile Ads', () => { it('Lifeandstyle front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/special' }, // Ad position (0) - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ad position (3) - { ...testCollection, collectionType: 'fixed/small/slow-V-third' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (3) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (6) - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'static/medium/4' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ad position (9) - { ...testCollection, collectionType: 'fixed/medium/slow-XII-mpu' }, - { ...testCollection, collectionType: 'fixed/small/fast-VIII' }, // Ignored - before thrasher + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (9) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher { ...testCollection, collectionType: 'fixed/thrasher' }, // Ad position (12) - { ...testCollection, collectionType: 'fixed/small/slow-III' }, - { ...testCollection, collectionType: 'fixed/small/slow-I' }, // Ignored - is merch high position + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -218,18 +218,18 @@ describe('Mobile Ads', () => { it('Recipes front, with more than 4 collections, with thrasher at the first position', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'fixed/thrasher' }, // Ignored - is first container and thrasher - { ...testCollection, collectionType: 'fixed/medium/slow-VI' }, // Ad position (1) - { ...testCollection, collectionType: 'fixed/small/slow-V-third' }, - { ...testCollection, collectionType: 'fixed/medium/slow-XII-mpu' }, // Ad position (3) - { ...testCollection, collectionType: 'fixed/medium/fast-XII' }, - { ...testCollection, collectionType: 'fixed/small/slow-V-third' }, // Ad position (5) - { ...testCollection, collectionType: 'fixed/small/fast-VIII' }, - { ...testCollection, collectionType: 'fixed/medium/fast-XI' }, // Ad position (7) - { ...testCollection, collectionType: 'fixed/small/slow-III' }, - { ...testCollection, collectionType: 'fixed/small/slow-IV' }, // Ad position (9) - { ...testCollection, collectionType: 'fixed/small/slow-V-half' }, - { ...testCollection, collectionType: 'fixed/small/slow-V-third' }, // Ignored - is before merch high position - { ...testCollection, collectionType: 'fixed/small/fast-VIII' }, // Ignored - is merch high position + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (1) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (3) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (5) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ad position (7) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'static/medium/4' }, // Ad position (9) + { ...testCollection, collectionType: 'flexible/general' }, + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is before merch high position + { ...testCollection, collectionType: 'flexible/general' }, // Ignored - is merch high position { ...testCollection, collectionType: 'news/most-popular' }, // Ignored - is most viewed container ]; @@ -350,13 +350,13 @@ describe('Desktop Ads', () => { it('calculates ad positions correctly for an example of the UK network front', () => { const adPositions = getDesktopAdPositions(testCollectionsUk, 'uk'); - expect(adPositions).toEqual([3, 6, 8, 11, 14, 17, 20]); + expect(adPositions).toEqual([3, 6, 8, 14, 17]); }); it('calculates ad positions correctly for an example of the US network front', () => { const adPositions = getDesktopAdPositions(testCollectionsUs, 'us'); - expect(adPositions).toEqual([3, 6, 8, 11, 14, 18, 20, 22]); + expect(adPositions).toEqual([3, 6, 10, 12, 19]); }); it('does NOT insert ads above or below branded content', () => { diff --git a/dotcom-rendering/src/lib/getFrontsAdPositions.ts b/dotcom-rendering/src/lib/getFrontsAdPositions.ts index 40c123de1e1..e6fca6bc194 100644 --- a/dotcom-rendering/src/lib/getFrontsAdPositions.ts +++ b/dotcom-rendering/src/lib/getFrontsAdPositions.ts @@ -260,8 +260,6 @@ const getCollectionHeight = (collection: AdCandidate): number => { case 'fixed/thrasher': return 0.5; - case 'fixed/small/slow-IV': - case 'fixed/small/slow-V-mpu': case 'nav/list': case 'nav/media-list': case 'scrollable/small': @@ -269,25 +267,12 @@ const getCollectionHeight = (collection: AdCandidate): number => { case 'static/medium/4': return 1; - case 'fixed/small/slow-I': - case 'fixed/small/slow-III': - case 'fixed/small/slow-V-third': - case 'fixed/small/slow-V-half': - case 'fixed/small/fast-VIII': case 'scrollable/feature': return 1.5; - case 'fixed/medium/slow-VI': - case 'fixed/medium/slow-VII': - case 'fixed/medium/slow-XII-mpu': - case 'fixed/medium/fast-XI': - case 'fixed/medium/fast-XII': case 'static/feature/2': return 2; - case 'fixed/large/slow-XIV': - return 3; - case 'flexible/special': return getFlexibleSpecialHeight(grouped); diff --git a/dotcom-rendering/src/lib/mockRESTCalls.ts b/dotcom-rendering/src/lib/mockRESTCalls.ts index 1fad65c2f9a..70d757754e4 100644 --- a/dotcom-rendering/src/lib/mockRESTCalls.ts +++ b/dotcom-rendering/src/lib/mockRESTCalls.ts @@ -202,15 +202,14 @@ export const mockFetch: typeof global.fetch = ( url, ): return createMockResponse(200, discussion); - case /.*contributions\.(code\.dev-)?guardianapis\.com\/header/.test( - url, - ) && requestInit?.method === 'POST': - return createMockResponse(200, contributionsHeaderResponse); - // Get contributions header - case /.*contributions\.(code\.dev-)?guardianapis\.com\/header/.test( - url, - ): + // Get contributions header or local Storybook header fetches + case /.*\/header/.test(url): return createMockResponse(200, contributionsHeaderResponse); + // Catch local Storybook epic fetches + case /.*\/epic/.test(url): + return createMockResponse(200, { + data: { module: { url: '', name: 'Epic', props: {} } }, + }); // Get Ophan case /.*ophan\.theguardian\.com\/img\/.*/.test(url): return createMockResponse(200); diff --git a/dotcom-rendering/src/lib/sdcRequests.ts b/dotcom-rendering/src/lib/sdcRequests.ts index 36656c2b8d2..e2d9ff9d4d6 100644 --- a/dotcom-rendering/src/lib/sdcRequests.ts +++ b/dotcom-rendering/src/lib/sdcRequests.ts @@ -112,5 +112,6 @@ export const getGutterLiveblog = ( export const getHeader = ( baseUrl: string, payload: HeaderPayload, + headers?: HeadersInit, ): Promise> => - getModuleData('header', baseUrl, payload); + getModuleData('header', baseUrl, payload, headers); diff --git a/dotcom-rendering/src/model/enhanceCollections.ts b/dotcom-rendering/src/model/enhanceCollections.ts index 7e6c62464ba..53d8c2ac088 100644 --- a/dotcom-rendering/src/model/enhanceCollections.ts +++ b/dotcom-rendering/src/model/enhanceCollections.ts @@ -78,7 +78,7 @@ export const enhanceCollections = ({ findCollectionSuitableForFrontBranding(collections); return collections.filter(isSupported).map((collection, index) => { - const { id, displayName, collectionType, hasMore, href, description } = + const { id, displayName, collectionType, href, description } = collection; const allCards = [...collection.curated, ...collection.backfill]; @@ -118,9 +118,6 @@ export const enhanceCollections = ({ const isNextCollectionPrimary = collections[index + 1]?.config.collectionLevel === 'Primary'; - const isBetaContainer = BETA_CONTAINERS.includes( - collection.collectionType, - ); return { id, @@ -164,8 +161,6 @@ export const enhanceCollections = ({ config: { showDateHeader: collection.config.showDateHeader, }, - canShowMore: - hasMore && !collection.config.hideShowMore && !isBetaContainer, targetedTerritory: collection.targetedTerritory, aspectRatio: collection.config.aspectRatio, }; diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts index 8cb053ae763..36e9a5873b7 100644 --- a/dotcom-rendering/src/paletteDeclarations.ts +++ b/dotcom-rendering/src/paletteDeclarations.ts @@ -7421,22 +7421,6 @@ const paletteColours = { light: () => sourcePalette.neutral[0], dark: () => sourcePalette.neutral[100], }, - '--labs-legacy-article-section-title': { - light: () => sourcePalette.neutral[100], - dark: () => sourcePalette.neutral[97], - }, - '--labs-legacy-section-background': { - light: () => sourcePalette.neutral[93], - dark: () => sourcePalette.neutral[20], - }, - '--labs-legacy-section-background-left': { - light: () => sourcePalette.labs[400], - dark: () => sourcePalette.labs[200], - }, - '--labs-legacy-treat-text': { - light: () => sourcePalette.neutral[46], - dark: () => sourcePalette.neutral[38], - }, '--labs-logo-background': { light: () => sourcePalette.labs[100], dark: () => sourcePalette.labs[200], diff --git a/dotcom-rendering/src/types/front.ts b/dotcom-rendering/src/types/front.ts index f3d8f87a6fd..a6e404741e5 100644 --- a/dotcom-rendering/src/types/front.ts +++ b/dotcom-rendering/src/types/front.ts @@ -135,13 +135,6 @@ export type DCRCollectionType = { config: { showDateHeader: boolean; }; - /** - * @property {?boolean} canShowMore - Whether the 'show more' button should be shown. - * nb. the value of this will typically reflect the `FECollectionType.hasMore` value we get from Frontend, - * except when `FECollectionType.config.hideShowMore` is set to `true`, in which case `DCRCollectionType.canShowMore` - * will always be `false`. - **/ - canShowMore?: boolean; collectionBranding?: CollectionBranding; targetedTerritory?: Territory; aspectRatio?: AspectRatio; diff --git a/dotcom-rendering/webpack/webpack.config.server.js b/dotcom-rendering/webpack/webpack.config.server.js index f7cb667c4db..e6d6c4fc719 100644 --- a/dotcom-rendering/webpack/webpack.config.server.js +++ b/dotcom-rendering/webpack/webpack.config.server.js @@ -2,6 +2,7 @@ const nodeExternals = require('webpack-node-externals'); const swcConfig = require('./.swcrc.json'); const { svgr } = require('./svg.cjs'); +const { transpileExclude } = require('./webpack.config.client.js'); const DEV = process.env.NODE_ENV === 'development'; const nodeVersion = process.versions.node; @@ -77,6 +78,7 @@ module.exports = { rules: [ { test: /(\.tsx|\.js|\.ts)$/, + exclude: transpileExclude, use: swcLoader, }, svgr, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c97d2239ac..d37cfa89f5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12502,13 +12502,13 @@ snapshots: '@guardian/eslint-config-typescript@12.0.0(eslint@8.57.1)(tslib@2.6.2)(typescript@5.5.3)': dependencies: - '@guardian/eslint-config': 9.0.0(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1)(tslib@2.6.2) + '@guardian/eslint-config': 9.0.0(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)(tslib@2.6.2) '@stylistic/eslint-plugin': 2.6.2(eslint@8.57.1)(typescript@5.5.3) '@typescript-eslint/eslint-plugin': 8.1.0(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1)(typescript@5.5.3) '@typescript-eslint/parser': 8.1.0(eslint@8.57.1)(typescript@5.5.3) eslint: 8.57.1 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) tslib: 2.6.2 typescript: 5.5.3 transitivePeerDependencies: @@ -12537,12 +12537,12 @@ snapshots: - supports-color - typescript - '@guardian/eslint-config@9.0.0(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1)(tslib@2.6.2)': + '@guardian/eslint-config@9.0.0(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)(tslib@2.6.2)': dependencies: eslint: 8.57.1 eslint-config-prettier: 9.1.0(eslint@8.57.1) eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) tslib: 2.6.2 transitivePeerDependencies: - '@typescript-eslint/parser' @@ -16546,13 +16546,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.1): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 4.4.3(supports-color@8.1.1) enhanced-resolve: 5.19.0 eslint: 8.57.1 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.3 get-tsconfig: 4.7.2 is-core-module: 2.16.1 @@ -16580,14 +16580,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.1.0(eslint@8.57.1)(typescript@5.5.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -16632,7 +16632,7 @@ snapshots: - supports-color - typescript - eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: array-includes: 3.1.9 array.prototype.findlastindex: 1.2.6 @@ -16642,7 +16642,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.1.0(eslint@8.57.1)(typescript@5.5.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3