From 2b3b50b3de56925e063254659cdc330f284f9846 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 9 Dec 2024 16:50:14 +0000 Subject: [PATCH 01/22] initial addition of crossword player this is a first pass, it needs work --- dotcom-rendering/index.d.ts | 3 + dotcom-rendering/package.json | 1 + dotcom-rendering/scripts/env/check-deps.js | 22 +- .../src/components/ArticleContainer.tsx | 3 + .../src/components/BylineLink.tsx | 5 + .../src/components/Crossword.importable.tsx | 6 + .../src/components/CrosswordInstructions.tsx | 28 + .../src/components/CrosswordLinks.tsx | 34 + .../src/layouts/CrosswordLayout.tsx | 790 ++++++++++++++++++ dotcom-rendering/src/layouts/DecideLayout.tsx | 10 + dotcom-rendering/src/lib/articleFormat.ts | 5 + dotcom-rendering/src/lib/renderElement.tsx | 9 + .../src/server/handler.article.web.ts | 14 +- dotcom-rendering/src/server/server.dev.ts | 3 + dotcom-rendering/src/server/server.prod.ts | 2 + dotcom-rendering/src/types/article.ts | 41 +- dotcom-rendering/src/types/content.ts | 9 +- dotcom-rendering/src/types/frontend.ts | 4 +- pnpm-lock.yaml | 41 + 19 files changed, 1022 insertions(+), 8 deletions(-) create mode 100644 dotcom-rendering/src/components/Crossword.importable.tsx create mode 100644 dotcom-rendering/src/components/CrosswordInstructions.tsx create mode 100644 dotcom-rendering/src/components/CrosswordLinks.tsx create mode 100644 dotcom-rendering/src/layouts/CrosswordLayout.tsx diff --git a/dotcom-rendering/index.d.ts b/dotcom-rendering/index.d.ts index 43cc9db9544..eabc3930621 100644 --- a/dotcom-rendering/index.d.ts +++ b/dotcom-rendering/index.d.ts @@ -2,6 +2,9 @@ // 3rd party type declarations // // ------------------------------ +type GuardianCrossword = + import('@guardian/react-crossword-next').CrosswordProps['data']; + declare module 'chromatic/isChromatic'; declare module 'dynamic-import-polyfill' { diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 6ab042c52b6..6c9a5f52384 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -50,6 +50,7 @@ "@guardian/libs": "19.2.1", "@guardian/ophan-tracker-js": "2.2.5", "@guardian/react-crossword": "2.0.2", + "@guardian/react-crossword-next": "npm:@guardian/react-crossword@0.0.0-canary-20241209150926", "@guardian/shimport": "1.0.2", "@guardian/source": "8.0.0", "@guardian/source-development-kitchen": "12.0.0", diff --git a/dotcom-rendering/scripts/env/check-deps.js b/dotcom-rendering/scripts/env/check-deps.js index c34c6ed0fec..2a9110c4195 100644 --- a/dotcom-rendering/scripts/env/check-deps.js +++ b/dotcom-rendering/scripts/env/check-deps.js @@ -8,10 +8,24 @@ if (pkg.devDependencies) { process.exit(1); } -const mismatches = Object.entries(pkg.dependencies).filter( - ([, version]) => - !semver.valid(version) && !version.startsWith('workspace:'), -); +/** + * We don't check packages that are not semver-compatible + * @type {RegExp[]} + */ +const exceptions = /** @type {const} */ ([ + /npm:@guardian\/react-crossword@0.0.0-canary/, +]); + +const mismatches = Object.entries(pkg.dependencies) + .filter( + ([, version]) => + !exceptions.some((exception) => exception.test(version)), + ) + + .filter( + ([, version]) => + !semver.valid(version) && !version.startsWith('workspace:'), + ); if (mismatches.length !== 0) { warn('dotcom-rendering dependencies should be pinned.'); diff --git a/dotcom-rendering/src/components/ArticleContainer.tsx b/dotcom-rendering/src/components/ArticleContainer.tsx index 94e02c55792..5a1e46222e4 100644 --- a/dotcom-rendering/src/components/ArticleContainer.tsx +++ b/dotcom-rendering/src/components/ArticleContainer.tsx @@ -24,6 +24,9 @@ const articleWidth = (format: ArticleFormat) => { } `; } + case ArticleDesign.Crossword: + /* The crossword player manages its own width; */ + return null; case ArticleDesign.Video: case ArticleDesign.Audio: return css` diff --git a/dotcom-rendering/src/components/BylineLink.tsx b/dotcom-rendering/src/components/BylineLink.tsx index 3c9c4beca07..e7c1eb745d0 100644 --- a/dotcom-rendering/src/components/BylineLink.tsx +++ b/dotcom-rendering/src/components/BylineLink.tsx @@ -1,6 +1,7 @@ import { isString } from '@guardian/libs'; import { Hide } from '@guardian/source/react-components'; import { DottedLines } from '@guardian/source-development-kitchen/react-components'; +import { Fragment } from 'react'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; import { getBylineComponentsFromTokens, @@ -160,6 +161,10 @@ const getRenderedTokens = ( ); } + if (design === ArticleDesign.Crossword && renderedTokens.length > 0) { + return [Set by: , ...renderedTokens]; + } + return renderedTokens; }; diff --git a/dotcom-rendering/src/components/Crossword.importable.tsx b/dotcom-rendering/src/components/Crossword.importable.tsx new file mode 100644 index 00000000000..87ad0d53796 --- /dev/null +++ b/dotcom-rendering/src/components/Crossword.importable.tsx @@ -0,0 +1,6 @@ +import { Crossword as ReactCrossword } from '@guardian/react-crossword-next'; +import type { CrosswordProps } from '@guardian/react-crossword-next'; + +export const Crossword = ({ data }: CrosswordProps) => ( + +); diff --git a/dotcom-rendering/src/components/CrosswordInstructions.tsx b/dotcom-rendering/src/components/CrosswordInstructions.tsx new file mode 100644 index 00000000000..1d59bdb966b --- /dev/null +++ b/dotcom-rendering/src/components/CrosswordInstructions.tsx @@ -0,0 +1,28 @@ +import { css } from '@emotion/react'; +import { + textEgyptian17, + textEgyptianBold17, +} from '@guardian/source/foundations'; + +const instructionsStyles = css` + ${textEgyptian17}; + white-space: pre-line; +`; + +const headerStyles = css` + ${textEgyptianBold17}; + white-space: pre-line; +`; + +export const CrosswordInstructions = ({ + instructions, + className, +}: { + instructions: string; + className?: string; +}) => ( +
+ Special instructions: + {instructions} +
+); diff --git a/dotcom-rendering/src/components/CrosswordLinks.tsx b/dotcom-rendering/src/components/CrosswordLinks.tsx new file mode 100644 index 00000000000..363a70a4113 --- /dev/null +++ b/dotcom-rendering/src/components/CrosswordLinks.tsx @@ -0,0 +1,34 @@ +import { css } from '@emotion/react'; +import { isUndefined } from '@guardian/libs'; +import { textSans15 } from '@guardian/source/foundations'; +import { palette } from '../palette'; + +const crosswordLinkStyles = css` + ${textSans15}; + + a { + color: ${palette('--standfirst-link-text')}; + text-decoration: none; + :hover { + border-bottom: 1px solid ${palette('--standfirst-link-border')}; + } + } +`; + +export const CrosswordLinks = ({ + crossword, + className, +}: { + crossword: GuardianCrossword; + className?: string; +}) => { + return ( + isUndefined(crossword.pdf) || ( + + + PDF version + + + ) + ); +}; diff --git a/dotcom-rendering/src/layouts/CrosswordLayout.tsx b/dotcom-rendering/src/layouts/CrosswordLayout.tsx new file mode 100644 index 00000000000..072420a8eb3 --- /dev/null +++ b/dotcom-rendering/src/layouts/CrosswordLayout.tsx @@ -0,0 +1,790 @@ +import { css } from '@emotion/react'; +import { + from, + palette as sourcePalette, + until, +} from '@guardian/source/foundations'; +import { Hide } from '@guardian/source/react-components'; +import { StraightLines } from '@guardian/source-development-kitchen/react-components'; +import React from 'react'; +import { AdPortals } from '../components/AdPortals.importable'; +import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; +import { AppsFooter } from '../components/AppsFooter.importable'; +import { ArticleBody } from '../components/ArticleBody'; +import { ArticleContainer } from '../components/ArticleContainer'; +import { ArticleHeadline } from '../components/ArticleHeadline'; +import { ArticleMetaApps } from '../components/ArticleMeta.apps'; +import { ArticleMeta } from '../components/ArticleMeta.web'; +import { ArticleTitle } from '../components/ArticleTitle'; +import { Carousel } from '../components/Carousel.importable'; +import { CrosswordInstructions } from '../components/CrosswordInstructions'; +import { CrosswordLinks } from '../components/CrosswordLinks'; +import { DecideLines } from '../components/DecideLines'; +import { DiscussionLayout } from '../components/DiscussionLayout'; +import { Footer } from '../components/Footer'; +import { GridItem } from '../components/GridItem'; +import { HeaderAdSlot } from '../components/HeaderAdSlot'; +import { Island } from '../components/Island'; +import { Masthead } from '../components/Masthead/Masthead'; +import { MostViewedFooterData } from '../components/MostViewedFooterData.importable'; +import { MostViewedFooterLayout } from '../components/MostViewedFooterLayout'; +import { OnwardsUpper } from '../components/OnwardsUpper.importable'; +import { RightColumn } from '../components/RightColumn'; +import { Section } from '../components/Section'; +import { SlotBodyEnd } from '../components/SlotBodyEnd.importable'; +import { Standfirst } from '../components/Standfirst'; +import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; +import { SubMeta } from '../components/SubMeta'; +import { SubNav } from '../components/SubNav.importable'; +import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat'; +import { canRenderAds } from '../lib/canRenderAds'; +import { getContributionsServiceUrl } from '../lib/contributions'; +import { decideTrail } from '../lib/decideTrail'; +import type { NavType } from '../model/extract-nav'; +import { palette as themePalette } from '../palette'; +import type { ArticleDeprecated } from '../types/article'; +import type { RenderingTarget } from '../types/renderingTarget'; +import { BannerWrapper, Stuck } from './lib/stickiness'; + +const CrosswordGrid = ({ children }: { children: React.ReactNode }) => ( +
+ {children} +
+); + +const maxWidth = css` + ${from.desktop} { + max-width: 620px; + } +`; + +const stretchLines = css` + ${until.phablet} { + margin-left: -20px; + margin-right: -20px; + } + ${until.mobileLandscape} { + margin-left: -10px; + margin-right: -10px; + } +`; + +interface CommonProps { + article: ArticleDeprecated; + format: ArticleFormat; + renderingTarget: RenderingTarget; +} + +interface WebProps extends CommonProps { + NAV: NavType; + renderingTarget: 'Web'; +} + +interface AppsProps extends CommonProps { + renderingTarget: 'Apps'; +} + +export const CrosswordLayout = (props: WebProps | AppsProps) => { + const { article, format, renderingTarget } = props; + const { + config: { isPaidContent, host, hasSurveyAd }, + } = article; + + const isApps = renderingTarget === 'Apps'; + const isWeb = renderingTarget === 'Web'; + + const showComments = article.isCommentable && !isPaidContent; + + const { branding } = article.commercialProperties[article.editionId]; + + const contributionsServiceUrl = getContributionsServiceUrl(article); + + const { absoluteServerTimes = false } = article.config.switches; + + /** + * This property currently only applies to the header and merchandising slots + */ + const renderAds = isWeb && canRenderAds(article); + return ( + <> + {isWeb && ( + <> +
+ {renderAds && ( + +
+
+ +
+
+
+ )} + + +
+ + {renderAds && hasSurveyAd && ( + + )} + + )} +
+ {isApps && ( + <> + + + + + )} +
+
+ + +
+ +
+
+ +
+ +
+
+ {article.crossword && ( + + + + )} + + + + + +
+
+ +
+
+
+ +
+ {isApps ? ( + <> + + + + + + + + ) : ( + + )} +
+
+ {!!article.crossword?.instructions && ( + + + + )} + + + + + + + + {renderAds ? ( + + ) : null} + + +
+
+
+ +
+
+ + + +
+
+ +
+ +
+ +
+ +
+ {renderAds && ( +
+ +
+ )} + + {article.storyPackage && ( +
+ + + +
+ )} + + + + + + {showComments && ( +
+ +
+ )} + + {!isPaidContent && ( +
+ + + + + +
+ )} + + {renderAds && ( +
+ +
+ )} +
+ + {isWeb && props.NAV.subNavSections && ( +
+ + + +
+ )} + + {isWeb && ( + <> +
+
+
+ + + + + + + + + )} + {isApps && ( +
+ + + +
+ )} + + ); +}; diff --git a/dotcom-rendering/src/layouts/DecideLayout.tsx b/dotcom-rendering/src/layouts/DecideLayout.tsx index b55f4973791..9920cb9d19c 100644 --- a/dotcom-rendering/src/layouts/DecideLayout.tsx +++ b/dotcom-rendering/src/layouts/DecideLayout.tsx @@ -8,6 +8,7 @@ import type { ArticleDeprecated } from '../types/article'; import type { RenderingTarget } from '../types/renderingTarget'; import { AudioLayout } from './AudioLayout'; import { CommentLayout } from './CommentLayout'; +import { CrosswordLayout } from './CrosswordLayout'; import { FullPageInteractiveLayout } from './FullPageInteractiveLayout'; import { ImmersiveLayout } from './ImmersiveLayout'; import { InteractiveLayout } from './InteractiveLayout'; @@ -273,6 +274,15 @@ const DecideLayoutWeb = ({ NAV={NAV} /> ); + case ArticleDesign.Crossword: + return ( + + ); default: return ( ): ArticleDesign => { return ArticleDesign.Timeline; case 'ProfileDesign': return ArticleDesign.Profile; + case 'CrosswordDesign': + return ArticleDesign.Crossword; default: return ArticleDesign.Standard; } @@ -270,6 +273,8 @@ const designToFEDesign = (design: ArticleDesign): FEDesign => { return 'TimelineDesign'; case ArticleDesign.Profile: return 'ProfileDesign'; + case ArticleDesign.Crossword: + return 'CrosswordDesign'; } }; diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index cd0241d8f26..e54f031b380 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -9,6 +9,7 @@ import { CartoonComponent } from '../components/CartoonComponent'; import { ChartAtom } from '../components/ChartAtom.importable'; import { CodeBlockComponent } from '../components/CodeBlockComponent'; import { CommentBlockComponent } from '../components/CommentBlockComponent'; +import { Crossword } from '../components/Crossword.importable'; import { DividerBlockComponent } from '../components/DividerBlockComponent'; import { DocumentBlockComponent } from '../components/DocumentBlockComponent.importable'; import { EmailSignUpWrapper } from '../components/EmailSignUpWrapper'; @@ -841,6 +842,14 @@ export const renderElement = ({ case 'model.dotcomrendering.pageElements.DisclaimerBlockElement': { return ; } + case 'model.dotcomrendering.pageElements.CrosswordElement': + return ( +
+ + + +
+ ); case 'model.dotcomrendering.pageElements.AudioBlockElement': case 'model.dotcomrendering.pageElements.ContentAtomBlockElement': case 'model.dotcomrendering.pageElements.GenericAtomBlockElement': diff --git a/dotcom-rendering/src/server/handler.article.web.ts b/dotcom-rendering/src/server/handler.article.web.ts index f98084f570a..25d071303f1 100644 --- a/dotcom-rendering/src/server/handler.article.web.ts +++ b/dotcom-rendering/src/server/handler.article.web.ts @@ -3,7 +3,7 @@ import { Standard as ExampleArticle } from '../../fixtures/generated/fe-articles import { decideFormat } from '../lib/articleFormat'; import { enhanceBlocks } from '../model/enhanceBlocks'; import { validateAsArticleType, validateAsBlock } from '../model/validate'; -import { enhanceArticleType } from '../types/article'; +import { enhanceArticleType, enhanceCrossword } from '../types/article'; import type { FEBlocksRequest } from '../types/frontend'; import { makePrefetchHeader } from './lib/header'; import { recordTypeAndPlatform } from './lib/logging-store'; @@ -54,6 +54,18 @@ export const handleInteractive: RequestHandler = ({ body }, res) => { res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); }; +export const handleCrossword: RequestHandler = ({ body }, res) => { + recordTypeAndPlatform('crossword', 'web'); + + const article = enhanceArticleType(body, 'Web'); + const crosswordArticle = enhanceCrossword(article); + const { html, prefetchScripts } = renderHtml({ + article: crosswordArticle, + }); + + res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); +}; + export const handleBlocks: RequestHandler = ({ body }, res) => { recordTypeAndPlatform('blocks'); const { diff --git a/dotcom-rendering/src/server/server.dev.ts b/dotcom-rendering/src/server/server.dev.ts index 0e9c0fb3762..1df15031170 100644 --- a/dotcom-rendering/src/server/server.dev.ts +++ b/dotcom-rendering/src/server/server.dev.ts @@ -10,6 +10,7 @@ import { handleArticle, handleArticleJson, handleBlocks, + handleCrossword, handleInteractive, } from './handler.article.web'; import { handleEditionsCrossword } from './handler.editionsCrossword'; @@ -71,6 +72,8 @@ export const devServer = (): Handler => { return handleInteractive(req, res, next); case 'AMPInteractive': return handleAMPArticle(req, res, next); + case 'Crossword': + return handleCrossword(req, res, next); case 'Blocks': return handleBlocks(req, res, next); case 'Front': diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index 7704baaeed3..27cc8e39fa6 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -18,6 +18,7 @@ import { handleArticleJson, handleArticlePerfTest, handleBlocks, + handleCrossword, handleInteractive, } from './handler.article.web'; import { handleEditionsCrossword } from './handler.editionsCrossword'; @@ -67,6 +68,7 @@ export const prodServer = (): void => { app.post('/AMPArticle', logRenderTime, handleAMPArticle); app.post('/Interactive', logRenderTime, handleInteractive); app.post('/AMPInteractive', logRenderTime, handleAMPArticle); + app.post('/Crossword', logRenderTime, handleCrossword); app.post('/Blocks', logRenderTime, handleBlocks); app.post('/Front', logRenderTime, handleFront); app.post('/FrontJSON', logRenderTime, handleFrontJson); diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index e1875c1f123..a15e90c8398 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -1,4 +1,9 @@ -import { type ArticleFormat, decideFormat } from '../lib/articleFormat'; +import { randomUUID } from 'node:crypto'; +import { + ArticleDesign, + type ArticleFormat, + decideFormat, +} from '../lib/articleFormat'; import type { ImageForAppsLightbox } from '../model/appsLightboxImages'; import { appsLightboxImages } from '../model/appsLightboxImages'; import { buildLightboxImages } from '../model/buildLightboxImages'; @@ -30,6 +35,40 @@ export type Article = { frontendData: ArticleDeprecated; }; +export const enhanceCrossword = (article: Article): Article => { + if (article.frontendData.crossword) { + const element = { + _type: 'model.dotcomrendering.pageElements.CrosswordElement' as const, + crossword: article.frontendData.crossword, + }; + return { + ...article, + format: { ...article.format, design: ArticleDesign.Crossword }, + frontendData: { + ...article.frontendData, + blocks: [ + { + id: randomUUID(), + elements: [element], + attributes: { + pinned: false, + keyEvent: false, + summary: false, + }, + primaryDateLine: + article.frontendData.webPublicationDateDisplay, + secondaryDateLine: + article.frontendData + .webPublicationSecondaryDateDisplay, + }, + ], + }, + }; + } + + throw new TypeError('article did not contain a crossword'); +}; + export const enhanceArticleType = ( data: FEArticleType, renderingTarget: RenderingTarget, diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index d481eacf9c8..c809556a4bf 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -732,6 +732,12 @@ interface WitnessTypeBlockElement extends ThirdPartyEmbeddedContent { | WitnessTypeDataVideo | WitnessTypeDataText; } + +export interface CrosswordElement { + _type: 'model.dotcomrendering.pageElements.CrosswordElement'; + crossword: GuardianCrossword & { instructions?: string }; +} + export type FEElement = | AdPlaceholderBlockElement | AudioAtomBlockElement @@ -791,7 +797,8 @@ export type FEElement = | VideoYoutubeBlockElement | VineBlockElement | YoutubeBlockElement - | WitnessTypeBlockElement; + | WitnessTypeBlockElement + | CrosswordElement; // ------------------------------------- // Misc diff --git a/dotcom-rendering/src/types/frontend.ts b/dotcom-rendering/src/types/frontend.ts index 2f91fe527d7..42b66f9b773 100644 --- a/dotcom-rendering/src/types/frontend.ts +++ b/dotcom-rendering/src/types/frontend.ts @@ -126,6 +126,7 @@ export interface FEArticleType { showTableOfContents: boolean; lang?: string; isRightToLeftLang?: boolean; + crossword?: GuardianCrossword & { instructions: string }; } type PageTypeType = { @@ -202,7 +203,8 @@ export type FEDesign = | 'FullPageInteractiveDesign' | 'NewsletterSignupDesign' | 'TimelineDesign' - | 'ProfileDesign'; // FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the displaystyle of the content e.g. Immersive + | 'ProfileDesign' + | 'CrosswordDesign'; // FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the displaystyle of the content e.g. Immersive // https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala export type FEDisplay = diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a72b4251eb..d7510f18932 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -364,6 +364,9 @@ importers: '@guardian/react-crossword': specifier: 2.0.2 version: 2.0.2 + '@guardian/react-crossword-next': + specifier: npm:@guardian/react-crossword@0.0.0-canary-20241209150926 + version: /@guardian/react-crossword@0.0.0-canary-20241209150926(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) '@guardian/shimport': specifier: 1.0.2 version: 1.0.2 @@ -4256,6 +4259,33 @@ packages: tslib: 2.6.2 dev: false + /@guardian/react-crossword@0.0.0-canary-20241209150926(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): + resolution: {integrity: sha512-nJ9vi454SqMynQ0UDz+jBmO/l7YxgVIq6Gvfyy4p/b5cnBNsxh8n4OZtIexhJx/dOhKYsMSPw/5KU8YOnVhF9A==} + peerDependencies: + '@emotion/react': ^11.11.3 + '@guardian/libs': ^19.1.0 + '@guardian/source': ^8.0.0 + '@types/react': ^18.2.79 + react: ^18.2.0 + typescript: ~5.5.2 + peerDependenciesMeta: + '@types/react': + optional: true + typescript: + optional: true + dependencies: + '@emotion/react': 11.11.3(@types/react@18.3.1)(react@18.3.1) + '@guardian/libs': 19.2.1(tslib@2.6.2)(typescript@5.5.3) + '@guardian/source': 8.0.0(@emotion/react@11.11.3)(@types/react@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@5.5.3) + '@types/react': 18.3.1 + react: 18.3.1 + tslib: 2.6.2 + typescript: 5.5.3 + use-local-storage-state: 19.5.0(react-dom@18.3.1)(react@18.3.1) + transitivePeerDependencies: + - react-dom + dev: false + /@guardian/react-crossword@2.0.2: resolution: {integrity: sha512-pFvCpuUH+GKz12uUzW4+Lck/ZhDWvqLodr1UwXIE7qjJCz8V4NEfuiGZkkIpVoPh+dEHTkiDQ6Ks4653KdH01g==} dependencies: @@ -17691,6 +17721,17 @@ packages: qs: 6.13.0 dev: false + /use-local-storage-state@19.5.0(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-sUJAyFvsmqMpBhdwaRr7GTKkkoxb6PWeNVvpBDrLuwQF1PpbJRKIbOYeLLeqJI7B3wdfFlLLCBbmOdopiSTBOw==} + engines: {node: '>=14'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false From 05ed396d6f44c2bff1aa3ade4f112832196c29ae Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:25:18 +0000 Subject: [PATCH 02/22] dont use ambient types --- dotcom-rendering/index.d.ts | 3 --- dotcom-rendering/src/components/Crossword.importable.tsx | 2 +- dotcom-rendering/src/components/CrosswordLinks.tsx | 3 ++- dotcom-rendering/src/types/content.ts | 3 ++- dotcom-rendering/src/types/frontend.ts | 3 ++- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dotcom-rendering/index.d.ts b/dotcom-rendering/index.d.ts index eabc3930621..43cc9db9544 100644 --- a/dotcom-rendering/index.d.ts +++ b/dotcom-rendering/index.d.ts @@ -2,9 +2,6 @@ // 3rd party type declarations // // ------------------------------ -type GuardianCrossword = - import('@guardian/react-crossword-next').CrosswordProps['data']; - declare module 'chromatic/isChromatic'; declare module 'dynamic-import-polyfill' { diff --git a/dotcom-rendering/src/components/Crossword.importable.tsx b/dotcom-rendering/src/components/Crossword.importable.tsx index 87ad0d53796..78cda1d207d 100644 --- a/dotcom-rendering/src/components/Crossword.importable.tsx +++ b/dotcom-rendering/src/components/Crossword.importable.tsx @@ -1,6 +1,6 @@ import { Crossword as ReactCrossword } from '@guardian/react-crossword-next'; import type { CrosswordProps } from '@guardian/react-crossword-next'; -export const Crossword = ({ data }: CrosswordProps) => ( +export const Crossword = (data: CrosswordProps['data']) => ( ); diff --git a/dotcom-rendering/src/components/CrosswordLinks.tsx b/dotcom-rendering/src/components/CrosswordLinks.tsx index 363a70a4113..b5b2be9e669 100644 --- a/dotcom-rendering/src/components/CrosswordLinks.tsx +++ b/dotcom-rendering/src/components/CrosswordLinks.tsx @@ -1,5 +1,6 @@ import { css } from '@emotion/react'; import { isUndefined } from '@guardian/libs'; +import { type CrosswordProps } from '@guardian/react-crossword-next'; import { textSans15 } from '@guardian/source/foundations'; import { palette } from '../palette'; @@ -19,7 +20,7 @@ export const CrosswordLinks = ({ crossword, className, }: { - crossword: GuardianCrossword; + crossword: CrosswordProps['data']; className?: string; }) => { return ( diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index c809556a4bf..a1db80b0f9a 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -1,3 +1,4 @@ +import { type CrosswordProps } from '@guardian/react-crossword-next'; import type { ArticleTheme } from '../lib/articleFormat'; export type StarRating = 0 | 1 | 2 | 3 | 4 | 5; @@ -735,7 +736,7 @@ interface WitnessTypeBlockElement extends ThirdPartyEmbeddedContent { export interface CrosswordElement { _type: 'model.dotcomrendering.pageElements.CrosswordElement'; - crossword: GuardianCrossword & { instructions?: string }; + crossword: CrosswordProps['data'] & { instructions?: string }; } export type FEElement = diff --git a/dotcom-rendering/src/types/frontend.ts b/dotcom-rendering/src/types/frontend.ts index 42b66f9b773..b737b7bebff 100644 --- a/dotcom-rendering/src/types/frontend.ts +++ b/dotcom-rendering/src/types/frontend.ts @@ -1,3 +1,4 @@ +import { type CrosswordProps } from '@guardian/react-crossword-next'; import type { SharedAdTargeting } from '../lib/ad-targeting'; import type { EditionId } from '../lib/edition'; import type { FEArticleBadgeType } from './badge'; @@ -126,7 +127,7 @@ export interface FEArticleType { showTableOfContents: boolean; lang?: string; isRightToLeftLang?: boolean; - crossword?: GuardianCrossword & { instructions: string }; + crossword?: CrosswordProps['data'] & { instructions: string }; } type PageTypeType = { From eaaaaf860c93991b3412ab00f196bcc9541bdd7c Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:26:04 +0000 Subject: [PATCH 03/22] put comments in the right place and use JSdoc for editor assistance --- dotcom-rendering/src/types/frontend.ts | 47 +++++++++++++++----------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/dotcom-rendering/src/types/frontend.ts b/dotcom-rendering/src/types/frontend.ts index b737b7bebff..f0f8ee43c77 100644 --- a/dotcom-rendering/src/types/frontend.ts +++ b/dotcom-rendering/src/types/frontend.ts @@ -162,10 +162,15 @@ export interface FEBlocksRequest { keywordIds: string; } -// Themes are used for styling -// RealPillars have pillar palette colours and have a `Pillar` type in Scala -// FakePillars allow us to make modifications to style based on rules outside of the pillar of an article and have a `Special` type in Scala -// https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Theme.scala +/** + * Themes are used for styling. + * + * RealPillars have pillar palette colours and have a `Pillar` type in Scala. + * + * FakePillars allow us to make modifications to style based on rules outside of the pillar of an article and have a `Special` type in Scala. + * + * https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Theme.scala + */ type ThemePillar = | 'NewsPillar' | 'OpinionPillar' @@ -176,9 +181,10 @@ type ThemePillar = type ThemeSpecial = 'SpecialReportTheme' | 'Labs' | 'SpecialReportAltTheme'; export type FETheme = ThemePillar | ThemeSpecial; -// FEDesign is what frontend gives (originating in the capi scala client) us on the Format field -// https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Design.scala - +/** + * FEDesign is what frontend gives (originating in the capi scala client) us on the Format field + * https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Design.scala + */ export type FEDesign = | 'ArticleDesign' | 'PictureDesign' @@ -205,29 +211,31 @@ export type FEDesign = | 'NewsletterSignupDesign' | 'TimelineDesign' | 'ProfileDesign' - | 'CrosswordDesign'; // FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the displaystyle of the content e.g. Immersive -// https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala + | 'CrosswordDesign'; +/** FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the display style of the content e.g. Immersive +https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala */ export type FEDisplay = | 'StandardDisplay' | 'ImmersiveDisplay' | 'ShowcaseDisplay' | 'NumberedListDisplay'; -// FEFormat is the stringified version of Format passed through from Frontend. -// It gets converted to the @guardian/libs format on platform +/** + * FEFormat is the stringified version of Format passed through from Frontend. + * It gets converted to the `@guardian/libs` format on platform + */ export type FEFormat = { design: FEDesign; theme: FETheme; display: FEDisplay; }; -// Data types for the API request bodies from clients that require -// transformation before internal use. -// Where data types are coming from Frontend we try to use the 'FE' prefix. -// -// Prior to this we used 'CAPI' as a prefix which wasn't entirely accurate, -// and some data structures never received the prefix, meaning some are still missing it. +/** + * Data types for the API request bodies from clients that require transformation before internal use. + * Where data types are coming from Frontend we try to use the 'FE' prefix. + * Prior to this we used 'CAPI' as a prefix which wasn't entirely accurate, and some data structures never received the prefix, meaning some are still missing it. + */ export interface FELinkType { url: string; title: string; @@ -254,8 +262,9 @@ export interface FENavType { } // Pillars are used for styling -// RealPillars have pillar palette colours -// FakePillars allow us to make modifications to style based on rules outside of the pillar of an article + +/** `RealPillars` have pillar palette colours */ type RealPillars = 'news' | 'opinion' | 'sport' | 'culture' | 'lifestyle'; +/** `FakePillars` allow us to make modifications to style based on rules outside of the pillar of an article */ type FakePillars = 'labs'; export type LegacyPillar = RealPillars | FakePillars; From 841fc8893a4959be6fc3570ad04242f82ed2bbff Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:26:46 +0000 Subject: [PATCH 04/22] remove custom styling of crossword instructions --- dotcom-rendering/src/components/CrosswordInstructions.tsx | 4 +--- dotcom-rendering/src/layouts/CrosswordLayout.tsx | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dotcom-rendering/src/components/CrosswordInstructions.tsx b/dotcom-rendering/src/components/CrosswordInstructions.tsx index 1d59bdb966b..93d17ffdb0c 100644 --- a/dotcom-rendering/src/components/CrosswordInstructions.tsx +++ b/dotcom-rendering/src/components/CrosswordInstructions.tsx @@ -16,12 +16,10 @@ const headerStyles = css` export const CrosswordInstructions = ({ instructions, - className, }: { instructions: string; - className?: string; }) => ( -
+
Special instructions: {instructions}
diff --git a/dotcom-rendering/src/layouts/CrosswordLayout.tsx b/dotcom-rendering/src/layouts/CrosswordLayout.tsx index 072420a8eb3..fc97fdf6483 100644 --- a/dotcom-rendering/src/layouts/CrosswordLayout.tsx +++ b/dotcom-rendering/src/layouts/CrosswordLayout.tsx @@ -418,7 +418,6 @@ export const CrosswordLayout = (props: WebProps | AppsProps) => { instructions={ article.crossword.instructions } - css={maxWidth} /> )} From 25813d07e3b91787f05bb66263b952062e32b366 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:27:25 +0000 Subject: [PATCH 05/22] render crosswords as a normal article --- .../src/server/handler.article.web.ts | 18 +++++------------- dotcom-rendering/src/server/server.dev.ts | 3 --- dotcom-rendering/src/server/server.prod.ts | 2 -- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/dotcom-rendering/src/server/handler.article.web.ts b/dotcom-rendering/src/server/handler.article.web.ts index 25d071303f1..ba38b4a2425 100644 --- a/dotcom-rendering/src/server/handler.article.web.ts +++ b/dotcom-rendering/src/server/handler.article.web.ts @@ -14,8 +14,12 @@ export const handleArticle: RequestHandler = ({ body }, res) => { const frontendData = validateAsArticleType(body); const article = enhanceArticleType(frontendData, 'Web'); + + const crosswordArticle = + article.frontendData.crossword && enhanceCrossword(article); + const { html, prefetchScripts } = renderHtml({ - article, + article: crosswordArticle ?? article, }); res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); @@ -54,18 +58,6 @@ export const handleInteractive: RequestHandler = ({ body }, res) => { res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); }; -export const handleCrossword: RequestHandler = ({ body }, res) => { - recordTypeAndPlatform('crossword', 'web'); - - const article = enhanceArticleType(body, 'Web'); - const crosswordArticle = enhanceCrossword(article); - const { html, prefetchScripts } = renderHtml({ - article: crosswordArticle, - }); - - res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); -}; - export const handleBlocks: RequestHandler = ({ body }, res) => { recordTypeAndPlatform('blocks'); const { diff --git a/dotcom-rendering/src/server/server.dev.ts b/dotcom-rendering/src/server/server.dev.ts index 1df15031170..0e9c0fb3762 100644 --- a/dotcom-rendering/src/server/server.dev.ts +++ b/dotcom-rendering/src/server/server.dev.ts @@ -10,7 +10,6 @@ import { handleArticle, handleArticleJson, handleBlocks, - handleCrossword, handleInteractive, } from './handler.article.web'; import { handleEditionsCrossword } from './handler.editionsCrossword'; @@ -72,8 +71,6 @@ export const devServer = (): Handler => { return handleInteractive(req, res, next); case 'AMPInteractive': return handleAMPArticle(req, res, next); - case 'Crossword': - return handleCrossword(req, res, next); case 'Blocks': return handleBlocks(req, res, next); case 'Front': diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index 27cc8e39fa6..7704baaeed3 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -18,7 +18,6 @@ import { handleArticleJson, handleArticlePerfTest, handleBlocks, - handleCrossword, handleInteractive, } from './handler.article.web'; import { handleEditionsCrossword } from './handler.editionsCrossword'; @@ -68,7 +67,6 @@ export const prodServer = (): void => { app.post('/AMPArticle', logRenderTime, handleAMPArticle); app.post('/Interactive', logRenderTime, handleInteractive); app.post('/AMPInteractive', logRenderTime, handleAMPArticle); - app.post('/Crossword', logRenderTime, handleCrossword); app.post('/Blocks', logRenderTime, handleBlocks); app.post('/Front', logRenderTime, handleFront); app.post('/FrontJSON', logRenderTime, handleFrontJson); From 841001dae091c4002dcfbfd20b1eb6e0c4b6f7bb Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:27:42 +0000 Subject: [PATCH 06/22] remove unnecessary div --- dotcom-rendering/src/lib/renderElement.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index e54f031b380..f2923d63e87 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -844,11 +844,9 @@ export const renderElement = ({ } case 'model.dotcomrendering.pageElements.CrosswordElement': return ( -
- - - -
+ + + ); case 'model.dotcomrendering.pageElements.AudioBlockElement': case 'model.dotcomrendering.pageElements.ContentAtomBlockElement': From 75069d7379c15f765a3f711415ec56281258fdcb Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 10:28:23 +0000 Subject: [PATCH 07/22] =?UTF-8?q?don=E2=80=99t=20override=20crossword=20`f?= =?UTF-8?q?ormat.design`=20in=20enhancer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotcom-rendering/src/types/article.ts | 66 +++++++++++++-------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index a15e90c8398..b1b56980715 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -1,9 +1,5 @@ -import { randomUUID } from 'node:crypto'; -import { - ArticleDesign, - type ArticleFormat, - decideFormat, -} from '../lib/articleFormat'; +import { isUndefined } from '@guardian/libs'; +import { type ArticleFormat, decideFormat } from '../lib/articleFormat'; import type { ImageForAppsLightbox } from '../model/appsLightboxImages'; import { appsLightboxImages } from '../model/appsLightboxImages'; import { buildLightboxImages } from '../model/buildLightboxImages'; @@ -36,37 +32,37 @@ export type Article = { }; export const enhanceCrossword = (article: Article): Article => { - if (article.frontendData.crossword) { - const element = { - _type: 'model.dotcomrendering.pageElements.CrosswordElement' as const, - crossword: article.frontendData.crossword, - }; - return { - ...article, - format: { ...article.format, design: ArticleDesign.Crossword }, - frontendData: { - ...article.frontendData, - blocks: [ - { - id: randomUUID(), - elements: [element], - attributes: { - pinned: false, - keyEvent: false, - summary: false, - }, - primaryDateLine: - article.frontendData.webPublicationDateDisplay, - secondaryDateLine: - article.frontendData - .webPublicationSecondaryDateDisplay, - }, - ], - }, - }; + if (isUndefined(article.frontendData.crossword)) { + throw new TypeError('article does not contain a crossword'); } - throw new TypeError('article did not contain a crossword'); + const element = { + _type: 'model.dotcomrendering.pageElements.CrosswordElement' as const, + crossword: article.frontendData.crossword, + }; + + return { + ...article, + format: { ...article.format }, + frontendData: { + ...article.frontendData, + blocks: [ + { + id: article.frontendData.crossword.id, + elements: [element], + attributes: { + pinned: false, + keyEvent: false, + summary: false, + }, + primaryDateLine: + article.frontendData.webPublicationDateDisplay, + secondaryDateLine: + article.frontendData.webPublicationSecondaryDateDisplay, + }, + ], + }, + }; }; export const enhanceArticleType = ( From 4efb62b5a4d7264d136f48781532e5bb80832e5d Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 11:06:19 +0000 Subject: [PATCH 08/22] no `CrosswordLinks` custom styles --- dotcom-rendering/src/components/CrosswordLinks.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dotcom-rendering/src/components/CrosswordLinks.tsx b/dotcom-rendering/src/components/CrosswordLinks.tsx index b5b2be9e669..9f4ee8844a6 100644 --- a/dotcom-rendering/src/components/CrosswordLinks.tsx +++ b/dotcom-rendering/src/components/CrosswordLinks.tsx @@ -18,14 +18,12 @@ const crosswordLinkStyles = css` export const CrosswordLinks = ({ crossword, - className, }: { crossword: CrosswordProps['data']; - className?: string; }) => { return ( isUndefined(crossword.pdf) || ( - + PDF version From bb9f687669d17e296f0147d878b61f0523857c3c Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 13:04:44 +0000 Subject: [PATCH 09/22] mutate lcoal scope for legiblity --- dotcom-rendering/src/server/handler.article.web.ts | 11 ++++++----- dotcom-rendering/src/types/article.ts | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dotcom-rendering/src/server/handler.article.web.ts b/dotcom-rendering/src/server/handler.article.web.ts index ba38b4a2425..a29e03a54ae 100644 --- a/dotcom-rendering/src/server/handler.article.web.ts +++ b/dotcom-rendering/src/server/handler.article.web.ts @@ -3,7 +3,7 @@ import { Standard as ExampleArticle } from '../../fixtures/generated/fe-articles import { decideFormat } from '../lib/articleFormat'; import { enhanceBlocks } from '../model/enhanceBlocks'; import { validateAsArticleType, validateAsBlock } from '../model/validate'; -import { enhanceArticleType, enhanceCrossword } from '../types/article'; +import { enhanceArticleType, enhanceCrosswordArticle } from '../types/article'; import type { FEBlocksRequest } from '../types/frontend'; import { makePrefetchHeader } from './lib/header'; import { recordTypeAndPlatform } from './lib/logging-store'; @@ -13,13 +13,14 @@ export const handleArticle: RequestHandler = ({ body }, res) => { recordTypeAndPlatform('article', 'web'); const frontendData = validateAsArticleType(body); - const article = enhanceArticleType(frontendData, 'Web'); + let article = enhanceArticleType(frontendData, 'Web'); - const crosswordArticle = - article.frontendData.crossword && enhanceCrossword(article); + if (article.frontendData.crossword) { + article = enhanceCrosswordArticle(article); + } const { html, prefetchScripts } = renderHtml({ - article: crosswordArticle ?? article, + article, }); res.status(200).set('Link', makePrefetchHeader(prefetchScripts)).send(html); diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index b1b56980715..dd44c820a0a 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -11,7 +11,7 @@ import { type TableOfContentsItem, } from '../model/enhanceTableOfContents'; import { enhancePinnedPost } from '../model/pinnedPost'; -import type { ImageForLightbox } from './content'; +import type { CrosswordElement, ImageForLightbox } from './content'; import type { FEArticleType } from './frontend'; import { type RenderingTarget } from './renderingTarget'; @@ -31,12 +31,12 @@ export type Article = { frontendData: ArticleDeprecated; }; -export const enhanceCrossword = (article: Article): Article => { +export const enhanceCrosswordArticle = (article: Article): Article => { if (isUndefined(article.frontendData.crossword)) { throw new TypeError('article does not contain a crossword'); } - const element = { + const element: CrosswordElement = { _type: 'model.dotcomrendering.pageElements.CrosswordElement' as const, crossword: article.frontendData.crossword, }; From f3b9feb8b5bedab83d63cf17c06f7f76451e7007 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 13:07:46 +0000 Subject: [PATCH 10/22] removing crossowrd formatting --- .../src/components/ArticleContainer.tsx | 3 - .../src/components/BylineLink.tsx | 5 - .../src/components/CrosswordInstructions.tsx | 26 - .../src/components/CrosswordLinks.tsx | 33 - .../src/layouts/CrosswordLayout.tsx | 789 ------------------ dotcom-rendering/src/layouts/DecideLayout.tsx | 10 - dotcom-rendering/src/lib/articleFormat.ts | 5 - dotcom-rendering/src/types/frontend.ts | 3 +- 8 files changed, 1 insertion(+), 873 deletions(-) delete mode 100644 dotcom-rendering/src/components/CrosswordInstructions.tsx delete mode 100644 dotcom-rendering/src/components/CrosswordLinks.tsx delete mode 100644 dotcom-rendering/src/layouts/CrosswordLayout.tsx diff --git a/dotcom-rendering/src/components/ArticleContainer.tsx b/dotcom-rendering/src/components/ArticleContainer.tsx index 5a1e46222e4..94e02c55792 100644 --- a/dotcom-rendering/src/components/ArticleContainer.tsx +++ b/dotcom-rendering/src/components/ArticleContainer.tsx @@ -24,9 +24,6 @@ const articleWidth = (format: ArticleFormat) => { } `; } - case ArticleDesign.Crossword: - /* The crossword player manages its own width; */ - return null; case ArticleDesign.Video: case ArticleDesign.Audio: return css` diff --git a/dotcom-rendering/src/components/BylineLink.tsx b/dotcom-rendering/src/components/BylineLink.tsx index e7c1eb745d0..3c9c4beca07 100644 --- a/dotcom-rendering/src/components/BylineLink.tsx +++ b/dotcom-rendering/src/components/BylineLink.tsx @@ -1,7 +1,6 @@ import { isString } from '@guardian/libs'; import { Hide } from '@guardian/source/react-components'; import { DottedLines } from '@guardian/source-development-kitchen/react-components'; -import { Fragment } from 'react'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; import { getBylineComponentsFromTokens, @@ -161,10 +160,6 @@ const getRenderedTokens = ( ); } - if (design === ArticleDesign.Crossword && renderedTokens.length > 0) { - return [Set by: , ...renderedTokens]; - } - return renderedTokens; }; diff --git a/dotcom-rendering/src/components/CrosswordInstructions.tsx b/dotcom-rendering/src/components/CrosswordInstructions.tsx deleted file mode 100644 index 93d17ffdb0c..00000000000 --- a/dotcom-rendering/src/components/CrosswordInstructions.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { css } from '@emotion/react'; -import { - textEgyptian17, - textEgyptianBold17, -} from '@guardian/source/foundations'; - -const instructionsStyles = css` - ${textEgyptian17}; - white-space: pre-line; -`; - -const headerStyles = css` - ${textEgyptianBold17}; - white-space: pre-line; -`; - -export const CrosswordInstructions = ({ - instructions, -}: { - instructions: string; -}) => ( -
- Special instructions: - {instructions} -
-); diff --git a/dotcom-rendering/src/components/CrosswordLinks.tsx b/dotcom-rendering/src/components/CrosswordLinks.tsx deleted file mode 100644 index 9f4ee8844a6..00000000000 --- a/dotcom-rendering/src/components/CrosswordLinks.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { css } from '@emotion/react'; -import { isUndefined } from '@guardian/libs'; -import { type CrosswordProps } from '@guardian/react-crossword-next'; -import { textSans15 } from '@guardian/source/foundations'; -import { palette } from '../palette'; - -const crosswordLinkStyles = css` - ${textSans15}; - - a { - color: ${palette('--standfirst-link-text')}; - text-decoration: none; - :hover { - border-bottom: 1px solid ${palette('--standfirst-link-border')}; - } - } -`; - -export const CrosswordLinks = ({ - crossword, -}: { - crossword: CrosswordProps['data']; -}) => { - return ( - isUndefined(crossword.pdf) || ( - - - PDF version - - - ) - ); -}; diff --git a/dotcom-rendering/src/layouts/CrosswordLayout.tsx b/dotcom-rendering/src/layouts/CrosswordLayout.tsx deleted file mode 100644 index fc97fdf6483..00000000000 --- a/dotcom-rendering/src/layouts/CrosswordLayout.tsx +++ /dev/null @@ -1,789 +0,0 @@ -import { css } from '@emotion/react'; -import { - from, - palette as sourcePalette, - until, -} from '@guardian/source/foundations'; -import { Hide } from '@guardian/source/react-components'; -import { StraightLines } from '@guardian/source-development-kitchen/react-components'; -import React from 'react'; -import { AdPortals } from '../components/AdPortals.importable'; -import { AdSlot, MobileStickyContainer } from '../components/AdSlot.web'; -import { AppsFooter } from '../components/AppsFooter.importable'; -import { ArticleBody } from '../components/ArticleBody'; -import { ArticleContainer } from '../components/ArticleContainer'; -import { ArticleHeadline } from '../components/ArticleHeadline'; -import { ArticleMetaApps } from '../components/ArticleMeta.apps'; -import { ArticleMeta } from '../components/ArticleMeta.web'; -import { ArticleTitle } from '../components/ArticleTitle'; -import { Carousel } from '../components/Carousel.importable'; -import { CrosswordInstructions } from '../components/CrosswordInstructions'; -import { CrosswordLinks } from '../components/CrosswordLinks'; -import { DecideLines } from '../components/DecideLines'; -import { DiscussionLayout } from '../components/DiscussionLayout'; -import { Footer } from '../components/Footer'; -import { GridItem } from '../components/GridItem'; -import { HeaderAdSlot } from '../components/HeaderAdSlot'; -import { Island } from '../components/Island'; -import { Masthead } from '../components/Masthead/Masthead'; -import { MostViewedFooterData } from '../components/MostViewedFooterData.importable'; -import { MostViewedFooterLayout } from '../components/MostViewedFooterLayout'; -import { OnwardsUpper } from '../components/OnwardsUpper.importable'; -import { RightColumn } from '../components/RightColumn'; -import { Section } from '../components/Section'; -import { SlotBodyEnd } from '../components/SlotBodyEnd.importable'; -import { Standfirst } from '../components/Standfirst'; -import { StickyBottomBanner } from '../components/StickyBottomBanner.importable'; -import { SubMeta } from '../components/SubMeta'; -import { SubNav } from '../components/SubNav.importable'; -import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat'; -import { canRenderAds } from '../lib/canRenderAds'; -import { getContributionsServiceUrl } from '../lib/contributions'; -import { decideTrail } from '../lib/decideTrail'; -import type { NavType } from '../model/extract-nav'; -import { palette as themePalette } from '../palette'; -import type { ArticleDeprecated } from '../types/article'; -import type { RenderingTarget } from '../types/renderingTarget'; -import { BannerWrapper, Stuck } from './lib/stickiness'; - -const CrosswordGrid = ({ children }: { children: React.ReactNode }) => ( -
- {children} -
-); - -const maxWidth = css` - ${from.desktop} { - max-width: 620px; - } -`; - -const stretchLines = css` - ${until.phablet} { - margin-left: -20px; - margin-right: -20px; - } - ${until.mobileLandscape} { - margin-left: -10px; - margin-right: -10px; - } -`; - -interface CommonProps { - article: ArticleDeprecated; - format: ArticleFormat; - renderingTarget: RenderingTarget; -} - -interface WebProps extends CommonProps { - NAV: NavType; - renderingTarget: 'Web'; -} - -interface AppsProps extends CommonProps { - renderingTarget: 'Apps'; -} - -export const CrosswordLayout = (props: WebProps | AppsProps) => { - const { article, format, renderingTarget } = props; - const { - config: { isPaidContent, host, hasSurveyAd }, - } = article; - - const isApps = renderingTarget === 'Apps'; - const isWeb = renderingTarget === 'Web'; - - const showComments = article.isCommentable && !isPaidContent; - - const { branding } = article.commercialProperties[article.editionId]; - - const contributionsServiceUrl = getContributionsServiceUrl(article); - - const { absoluteServerTimes = false } = article.config.switches; - - /** - * This property currently only applies to the header and merchandising slots - */ - const renderAds = isWeb && canRenderAds(article); - return ( - <> - {isWeb && ( - <> -
- {renderAds && ( - -
-
- -
-
-
- )} - - -
- - {renderAds && hasSurveyAd && ( - - )} - - )} -
- {isApps && ( - <> - - - - - )} -
-
- - -
- -
-
- -
- -
-
- {article.crossword && ( - - - - )} - - - - - -
-
- -
-
-
- -
- {isApps ? ( - <> - - - - - - - - ) : ( - - )} -
-
- {!!article.crossword?.instructions && ( - - - - )} - - - - - - - - {renderAds ? ( - - ) : null} - - -
-
-
- -
-
- - - -
-
- -
- -
- -
- -
- {renderAds && ( -
- -
- )} - - {article.storyPackage && ( -
- - - -
- )} - - - - - - {showComments && ( -
- -
- )} - - {!isPaidContent && ( -
- - - - - -
- )} - - {renderAds && ( -
- -
- )} -
- - {isWeb && props.NAV.subNavSections && ( -
- - - -
- )} - - {isWeb && ( - <> -
-
-
- - - - - - - - - )} - {isApps && ( -
- - - -
- )} - - ); -}; diff --git a/dotcom-rendering/src/layouts/DecideLayout.tsx b/dotcom-rendering/src/layouts/DecideLayout.tsx index 9920cb9d19c..b55f4973791 100644 --- a/dotcom-rendering/src/layouts/DecideLayout.tsx +++ b/dotcom-rendering/src/layouts/DecideLayout.tsx @@ -8,7 +8,6 @@ import type { ArticleDeprecated } from '../types/article'; import type { RenderingTarget } from '../types/renderingTarget'; import { AudioLayout } from './AudioLayout'; import { CommentLayout } from './CommentLayout'; -import { CrosswordLayout } from './CrosswordLayout'; import { FullPageInteractiveLayout } from './FullPageInteractiveLayout'; import { ImmersiveLayout } from './ImmersiveLayout'; import { InteractiveLayout } from './InteractiveLayout'; @@ -274,15 +273,6 @@ const DecideLayoutWeb = ({ NAV={NAV} /> ); - case ArticleDesign.Crossword: - return ( - - ); default: return ( ): ArticleDesign => { return ArticleDesign.Timeline; case 'ProfileDesign': return ArticleDesign.Profile; - case 'CrosswordDesign': - return ArticleDesign.Crossword; default: return ArticleDesign.Standard; } @@ -273,8 +270,6 @@ const designToFEDesign = (design: ArticleDesign): FEDesign => { return 'TimelineDesign'; case ArticleDesign.Profile: return 'ProfileDesign'; - case ArticleDesign.Crossword: - return 'CrosswordDesign'; } }; diff --git a/dotcom-rendering/src/types/frontend.ts b/dotcom-rendering/src/types/frontend.ts index f0f8ee43c77..1c3bee83cd1 100644 --- a/dotcom-rendering/src/types/frontend.ts +++ b/dotcom-rendering/src/types/frontend.ts @@ -210,8 +210,7 @@ export type FEDesign = | 'FullPageInteractiveDesign' | 'NewsletterSignupDesign' | 'TimelineDesign' - | 'ProfileDesign' - | 'CrosswordDesign'; + | 'ProfileDesign'; /** FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the display style of the content e.g. Immersive https://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala */ From 8c339765f1fd5563048c2efeeeaf98ef619a3306 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 13:11:51 +0000 Subject: [PATCH 11/22] update schemas --- .../src/model/article-schema.json | 369 ++++++++++++++++++ dotcom-rendering/src/model/block-schema.json | 250 ++++++++++++ dotcom-rendering/src/model/front-schema.json | 13 + .../src/model/newsletter-page-schema.json | 1 + .../src/model/tag-page-schema.json | 6 + 5 files changed, 639 insertions(+) diff --git a/dotcom-rendering/src/model/article-schema.json b/dotcom-rendering/src/model/article-schema.json index f7bb836e913..1572360541b 100644 --- a/dotcom-rendering/src/model/article-schema.json +++ b/dotcom-rendering/src/model/article-schema.json @@ -119,6 +119,7 @@ } }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -262,6 +263,7 @@ "$ref": "#/definitions/OnwardsSource" }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -434,6 +436,119 @@ }, "isRightToLeftLang": { "type": "boolean" + }, + "crossword": { + "allOf": [ + { + "type": "object", + "properties": { + "creator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "webUrl": { + "type": "string" + } + }, + "required": [ + "name", + "webUrl" + ] + }, + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { + "type": "object", + "properties": { + "rows": { + "type": "number" + }, + "cols": { + "type": "number" + } + }, + "required": [ + "cols", + "rows" + ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" + } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" + ] + }, + { + "type": "object", + "properties": { + "instructions": { + "type": "string" + } + }, + "required": [ + "instructions" + ] + } + ] } }, "required": [ @@ -762,6 +877,9 @@ }, { "$ref": "#/definitions/WitnessTypeBlockElement" + }, + { + "$ref": "#/definitions/CrosswordElement" } ] }, @@ -3976,6 +4094,253 @@ "witnessEmbedType" ] }, + "CrosswordElement": { + "type": "object", + "properties": { + "_type": { + "type": "string", + "const": "model.dotcomrendering.pageElements.CrosswordElement" + }, + "crossword": { + "allOf": [ + { + "type": "object", + "properties": { + "creator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "webUrl": { + "type": "string" + } + }, + "required": [ + "name", + "webUrl" + ] + }, + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { + "type": "object", + "properties": { + "rows": { + "type": "number" + }, + "cols": { + "type": "number" + } + }, + "required": [ + "cols", + "rows" + ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" + } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" + ] + }, + { + "type": "object", + "properties": { + "instructions": { + "type": "string" + } + } + } + ] + } + }, + "required": [ + "_type", + "crossword" + ] + }, + "CAPIEntry": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + }, + "group": { + "type": "array", + "items": [ + { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + } + ], + "minItems": 1, + "additionalItems": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + } + }, + "number": { + "type": "number" + } + }, + "required": [ + "group", + "id", + "number" + ] + }, + { + "type": "object", + "properties": { + "direction": { + "$ref": "#/definitions/Direction" + }, + "position": { + "$ref": "#/definitions/Coords", + "description": "Coords of first cell" + }, + "clue": { + "description": "The clue for the current entry", + "type": "string" + }, + "humanNumber": { + "description": "The number for the clue", + "type": "string" + }, + "solution": { + "description": "The solution to the entry's clue", + "type": "string" + }, + "length": { + "description": "The length of the solution (we don't always have a solution)", + "type": "number" + }, + "separatorLocations": { + "$ref": "#/definitions/Record", + "description": "Separators for multi-part solutions e.g.\n- ready,steady,go\n- tofu-eating" + } + }, + "required": [ + "clue", + "direction", + "humanNumber", + "length", + "position", + "separatorLocations" + ] + } + ] + }, + "Direction": { + "enum": [ + "across", + "down" + ], + "type": "string" + }, + "Coords": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y" + ] + }, + "Record": { + "type": "object" + }, "Block": { "type": "object", "properties": { @@ -4139,6 +4504,7 @@ "type": "string" }, "FEDesign": { + "description": "FEDesign is what frontend gives (originating in the capi scala client) us on the Format field\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Design.scala", "enum": [ "AnalysisDesign", "ArticleDesign", @@ -4182,6 +4548,7 @@ "type": "string" }, "FEDisplay": { + "description": "FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the display style of the content e.g. Immersive\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala", "enum": [ "ImmersiveDisplay", "NumberedListDisplay", @@ -4202,6 +4569,7 @@ "type": "string" }, "FELinkType": { + "description": "Data types for the API request bodies from clients that require transformation before internal use.\nWhere data types are coming from Frontend we try to use the 'FE' prefix.\nPrior to this we used 'CAPI' as a prefix which wasn't entirely accurate, and some data structures never received the prefix, meaning some are still missing it.", "type": "object", "properties": { "url": { @@ -4459,6 +4827,7 @@ "type": "object", "properties": { "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index ab6241ca5b4..53e7a6246b8 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -349,6 +349,9 @@ }, { "$ref": "#/definitions/WitnessTypeBlockElement" + }, + { + "$ref": "#/definitions/CrosswordElement" } ] }, @@ -3563,6 +3566,253 @@ "witnessEmbedType" ] }, + "CrosswordElement": { + "type": "object", + "properties": { + "_type": { + "type": "string", + "const": "model.dotcomrendering.pageElements.CrosswordElement" + }, + "crossword": { + "allOf": [ + { + "type": "object", + "properties": { + "creator": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "webUrl": { + "type": "string" + } + }, + "required": [ + "name", + "webUrl" + ] + }, + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { + "type": "object", + "properties": { + "rows": { + "type": "number" + }, + "cols": { + "type": "number" + } + }, + "required": [ + "cols", + "rows" + ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" + } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" + ] + }, + { + "type": "object", + "properties": { + "instructions": { + "type": "string" + } + } + } + ] + } + }, + "required": [ + "_type", + "crossword" + ] + }, + "CAPIEntry": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + }, + "group": { + "type": "array", + "items": [ + { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + } + ], + "minItems": 1, + "additionalItems": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9]*-across$" + }, + { + "type": "string", + "pattern": "^[0-9]*-down$" + } + ] + } + }, + "number": { + "type": "number" + } + }, + "required": [ + "group", + "id", + "number" + ] + }, + { + "type": "object", + "properties": { + "direction": { + "$ref": "#/definitions/Direction" + }, + "position": { + "$ref": "#/definitions/Coords", + "description": "Coords of first cell" + }, + "clue": { + "description": "The clue for the current entry", + "type": "string" + }, + "humanNumber": { + "description": "The number for the clue", + "type": "string" + }, + "solution": { + "description": "The solution to the entry's clue", + "type": "string" + }, + "length": { + "description": "The length of the solution (we don't always have a solution)", + "type": "number" + }, + "separatorLocations": { + "$ref": "#/definitions/Record", + "description": "Separators for multi-part solutions e.g.\n- ready,steady,go\n- tofu-eating" + } + }, + "required": [ + "clue", + "direction", + "humanNumber", + "length", + "position", + "separatorLocations" + ] + } + ] + }, + "Direction": { + "enum": [ + "across", + "down" + ], + "type": "string" + }, + "Coords": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y" + ] + }, + "Record": { + "type": "object" + }, "Attributes": { "type": "object", "properties": { diff --git a/dotcom-rendering/src/model/front-schema.json b/dotcom-rendering/src/model/front-schema.json index 1b08fa3f4b6..4abceb81996 100644 --- a/dotcom-rendering/src/model/front-schema.json +++ b/dotcom-rendering/src/model/front-schema.json @@ -617,6 +617,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1090,6 +1091,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1178,6 +1180,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1365,6 +1368,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1838,6 +1842,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1926,6 +1931,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -2113,6 +2119,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -2586,6 +2593,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -2674,6 +2682,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -3036,6 +3045,7 @@ ] }, "FEDesign": { + "description": "FEDesign is what frontend gives (originating in the capi scala client) us on the Format field\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Design.scala", "enum": [ "AnalysisDesign", "ArticleDesign", @@ -3079,6 +3089,7 @@ "type": "string" }, "FEDisplay": { + "description": "FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the display style of the content e.g. Immersive\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala", "enum": [ "ImmersiveDisplay", "NumberedListDisplay", @@ -3393,6 +3404,7 @@ ] }, "FELinkType": { + "description": "Data types for the API request bodies from clients that require transformation before internal use.\nWhere data types are coming from Frontend we try to use the 'FE' prefix.\nPrior to this we used 'CAPI' as a prefix which wasn't entirely accurate, and some data structures never received the prefix, meaning some are still missing it.", "type": "object", "properties": { "url": { @@ -3558,6 +3570,7 @@ "type": "object", "properties": { "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { diff --git a/dotcom-rendering/src/model/newsletter-page-schema.json b/dotcom-rendering/src/model/newsletter-page-schema.json index 9ed29434550..bc4cc4df9e1 100644 --- a/dotcom-rendering/src/model/newsletter-page-schema.json +++ b/dotcom-rendering/src/model/newsletter-page-schema.json @@ -291,6 +291,7 @@ ] }, "FELinkType": { + "description": "Data types for the API request bodies from clients that require transformation before internal use.\nWhere data types are coming from Frontend we try to use the 'FE' prefix.\nPrior to this we used 'CAPI' as a prefix which wasn't entirely accurate, and some data structures never received the prefix, meaning some are still missing it.", "type": "object", "properties": { "url": { diff --git a/dotcom-rendering/src/model/tag-page-schema.json b/dotcom-rendering/src/model/tag-page-schema.json index 610b9414df7..ea1a7dbdc9d 100644 --- a/dotcom-rendering/src/model/tag-page-schema.json +++ b/dotcom-rendering/src/model/tag-page-schema.json @@ -138,6 +138,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -611,6 +612,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -699,6 +701,7 @@ ] }, "format": { + "description": "FEFormat is the stringified version of Format passed through from Frontend.\nIt gets converted to the `@guardian/libs` format on platform", "type": "object", "properties": { "design": { @@ -1275,6 +1278,7 @@ ], "definitions": { "FEDesign": { + "description": "FEDesign is what frontend gives (originating in the capi scala client) us on the Format field\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Design.scala", "enum": [ "AnalysisDesign", "ArticleDesign", @@ -1318,6 +1322,7 @@ "type": "string" }, "FEDisplay": { + "description": "FEDisplay is the display information passed through from frontend (originating in the capi scala client) and dictates the display style of the content e.g. Immersive\nhttps://github.com/guardian/content-api-scala-client/blob/master/client/src/main/scala/com.gu.contentapi.client/utils/format/Display.scala", "enum": [ "ImmersiveDisplay", "NumberedListDisplay", @@ -1705,6 +1710,7 @@ ] }, "FELinkType": { + "description": "Data types for the API request bodies from clients that require transformation before internal use.\nWhere data types are coming from Frontend we try to use the 'FE' prefix.\nPrior to this we used 'CAPI' as a prefix which wasn't entirely accurate, and some data structures never received the prefix, meaning some are still missing it.", "type": "object", "properties": { "url": { From 73a5071f790ac36d65c4cda94dccdfd30a0ad8fb Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Thu, 12 Dec 2024 13:46:04 +0000 Subject: [PATCH 12/22] crossword instructions are in the main type --- .../src/model/article-schema.json | 335 ++++++++---------- dotcom-rendering/src/model/block-schema.json | 168 ++++----- dotcom-rendering/src/types/content.ts | 2 +- dotcom-rendering/src/types/frontend.ts | 2 +- 4 files changed, 234 insertions(+), 273 deletions(-) diff --git a/dotcom-rendering/src/model/article-schema.json b/dotcom-rendering/src/model/article-schema.json index 1572360541b..e3f3d0027ea 100644 --- a/dotcom-rendering/src/model/article-schema.json +++ b/dotcom-rendering/src/model/article-schema.json @@ -438,116 +438,101 @@ "type": "boolean" }, "crossword": { - "allOf": [ - { + "type": "object", + "properties": { + "creator": { "type": "object", "properties": { - "creator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "webUrl": { - "type": "string" - } - }, - "required": [ - "name", - "webUrl" - ] - }, - "crosswordType": { - "enum": [ - "cryptic", - "everyman", - "prize", - "quick", - "quick-cryptic", - "quiptic", - "special", - "speedy", - "weekend" - ], - "type": "string" - }, - "date": { - "type": "number" - }, - "dateSolutionAvailable": { - "type": "number" - }, - "dimensions": { - "type": "object", - "properties": { - "rows": { - "type": "number" - }, - "cols": { - "type": "number" - } - }, - "required": [ - "cols", - "rows" - ] - }, - "entries": { - "type": "array", - "items": [ - { - "$ref": "#/definitions/CAPIEntry" - } - ], - "minItems": 1, - "additionalItems": { - "$ref": "#/definitions/CAPIEntry" - } - }, - "id": { - "type": "string" - }, "name": { "type": "string" }, - "number": { - "type": "number" - }, - "pdf": { - "type": "string" - }, - "solutionAvailable": { - "type": "boolean" - }, - "webPublicationDate": { - "type": "number" - }, - "instructions": { + "webUrl": { "type": "string" } }, "required": [ - "crosswordType", - "date", - "dimensions", - "entries", - "id", "name", - "number", - "solutionAvailable" + "webUrl" ] }, - { + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { "type": "object", "properties": { - "instructions": { - "type": "string" + "rows": { + "type": "number" + }, + "cols": { + "type": "number" } }, "required": [ - "instructions" + "cols", + "rows" ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" + } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" ] } }, @@ -4102,113 +4087,101 @@ "const": "model.dotcomrendering.pageElements.CrosswordElement" }, "crossword": { - "allOf": [ - { + "type": "object", + "properties": { + "creator": { "type": "object", "properties": { - "creator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "webUrl": { - "type": "string" - } - }, - "required": [ - "name", - "webUrl" - ] - }, - "crosswordType": { - "enum": [ - "cryptic", - "everyman", - "prize", - "quick", - "quick-cryptic", - "quiptic", - "special", - "speedy", - "weekend" - ], - "type": "string" - }, - "date": { - "type": "number" - }, - "dateSolutionAvailable": { - "type": "number" - }, - "dimensions": { - "type": "object", - "properties": { - "rows": { - "type": "number" - }, - "cols": { - "type": "number" - } - }, - "required": [ - "cols", - "rows" - ] - }, - "entries": { - "type": "array", - "items": [ - { - "$ref": "#/definitions/CAPIEntry" - } - ], - "minItems": 1, - "additionalItems": { - "$ref": "#/definitions/CAPIEntry" - } - }, - "id": { - "type": "string" - }, "name": { "type": "string" }, - "number": { - "type": "number" - }, - "pdf": { - "type": "string" - }, - "solutionAvailable": { - "type": "boolean" - }, - "webPublicationDate": { - "type": "number" - }, - "instructions": { + "webUrl": { "type": "string" } }, "required": [ - "crosswordType", - "date", - "dimensions", - "entries", - "id", "name", - "number", - "solutionAvailable" + "webUrl" ] }, - { + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { "type": "object", "properties": { - "instructions": { - "type": "string" + "rows": { + "type": "number" + }, + "cols": { + "type": "number" } + }, + "required": [ + "cols", + "rows" + ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" ] } }, diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index 53e7a6246b8..6ef5a7178ab 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -3574,113 +3574,101 @@ "const": "model.dotcomrendering.pageElements.CrosswordElement" }, "crossword": { - "allOf": [ - { + "type": "object", + "properties": { + "creator": { "type": "object", "properties": { - "creator": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "webUrl": { - "type": "string" - } - }, - "required": [ - "name", - "webUrl" - ] - }, - "crosswordType": { - "enum": [ - "cryptic", - "everyman", - "prize", - "quick", - "quick-cryptic", - "quiptic", - "special", - "speedy", - "weekend" - ], - "type": "string" - }, - "date": { - "type": "number" - }, - "dateSolutionAvailable": { - "type": "number" - }, - "dimensions": { - "type": "object", - "properties": { - "rows": { - "type": "number" - }, - "cols": { - "type": "number" - } - }, - "required": [ - "cols", - "rows" - ] - }, - "entries": { - "type": "array", - "items": [ - { - "$ref": "#/definitions/CAPIEntry" - } - ], - "minItems": 1, - "additionalItems": { - "$ref": "#/definitions/CAPIEntry" - } - }, - "id": { - "type": "string" - }, "name": { "type": "string" }, - "number": { - "type": "number" - }, - "pdf": { - "type": "string" - }, - "solutionAvailable": { - "type": "boolean" - }, - "webPublicationDate": { - "type": "number" - }, - "instructions": { + "webUrl": { "type": "string" } }, "required": [ - "crosswordType", - "date", - "dimensions", - "entries", - "id", "name", - "number", - "solutionAvailable" + "webUrl" ] }, - { + "crosswordType": { + "enum": [ + "cryptic", + "everyman", + "prize", + "quick", + "quick-cryptic", + "quiptic", + "special", + "speedy", + "weekend" + ], + "type": "string" + }, + "date": { + "type": "number" + }, + "dateSolutionAvailable": { + "type": "number" + }, + "dimensions": { "type": "object", "properties": { - "instructions": { - "type": "string" + "rows": { + "type": "number" + }, + "cols": { + "type": "number" } + }, + "required": [ + "cols", + "rows" + ] + }, + "entries": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/CAPIEntry" + } + ], + "minItems": 1, + "additionalItems": { + "$ref": "#/definitions/CAPIEntry" } + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "number" + }, + "pdf": { + "type": "string" + }, + "solutionAvailable": { + "type": "boolean" + }, + "webPublicationDate": { + "type": "number" + }, + "instructions": { + "type": "string" } + }, + "required": [ + "crosswordType", + "date", + "dimensions", + "entries", + "id", + "name", + "number", + "solutionAvailable" ] } }, diff --git a/dotcom-rendering/src/types/content.ts b/dotcom-rendering/src/types/content.ts index a1db80b0f9a..b8c92263795 100644 --- a/dotcom-rendering/src/types/content.ts +++ b/dotcom-rendering/src/types/content.ts @@ -736,7 +736,7 @@ interface WitnessTypeBlockElement extends ThirdPartyEmbeddedContent { export interface CrosswordElement { _type: 'model.dotcomrendering.pageElements.CrosswordElement'; - crossword: CrosswordProps['data'] & { instructions?: string }; + crossword: CrosswordProps['data']; } export type FEElement = diff --git a/dotcom-rendering/src/types/frontend.ts b/dotcom-rendering/src/types/frontend.ts index 1c3bee83cd1..ce15ae5681f 100644 --- a/dotcom-rendering/src/types/frontend.ts +++ b/dotcom-rendering/src/types/frontend.ts @@ -127,7 +127,7 @@ export interface FEArticleType { showTableOfContents: boolean; lang?: string; isRightToLeftLang?: boolean; - crossword?: CrosswordProps['data'] & { instructions: string }; + crossword?: CrosswordProps['data']; } type PageTypeType = { From 516c59ffbe31459235e48181616169e905dc5d97 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 16 Dec 2024 10:53:58 +0000 Subject: [PATCH 13/22] pass the data in a proper prop, don't spread it --- dotcom-rendering/src/components/Crossword.importable.tsx | 2 +- dotcom-rendering/src/lib/renderElement.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/Crossword.importable.tsx b/dotcom-rendering/src/components/Crossword.importable.tsx index 78cda1d207d..7561ca5158c 100644 --- a/dotcom-rendering/src/components/Crossword.importable.tsx +++ b/dotcom-rendering/src/components/Crossword.importable.tsx @@ -1,6 +1,6 @@ import { Crossword as ReactCrossword } from '@guardian/react-crossword-next'; import type { CrosswordProps } from '@guardian/react-crossword-next'; -export const Crossword = (data: CrosswordProps['data']) => ( +export const Crossword = ({ data }: { data: CrosswordProps['data'] }) => ( ); diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index f2923d63e87..d89ca2813b4 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -845,7 +845,7 @@ export const renderElement = ({ case 'model.dotcomrendering.pageElements.CrosswordElement': return ( - + ); case 'model.dotcomrendering.pageElements.AudioBlockElement': From 74c40a1a57bd811064598873bbf7dd05cdfbf2e0 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 16 Dec 2024 13:29:27 +0000 Subject: [PATCH 14/22] remove unnecessary `const` --- dotcom-rendering/src/types/article.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index dd44c820a0a..5d4891af81e 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -37,7 +37,7 @@ export const enhanceCrosswordArticle = (article: Article): Article => { } const element: CrosswordElement = { - _type: 'model.dotcomrendering.pageElements.CrosswordElement' as const, + _type: 'model.dotcomrendering.pageElements.CrosswordElement', crossword: article.frontendData.crossword, }; From 633aabd0b24ae6b3458f0b67247e1f93eef3cc5d Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 16 Dec 2024 13:30:06 +0000 Subject: [PATCH 15/22] this is not needed now --- dotcom-rendering/src/types/article.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index 5d4891af81e..34fce6ebafe 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -43,7 +43,6 @@ export const enhanceCrosswordArticle = (article: Article): Article => { return { ...article, - format: { ...article.format }, frontendData: { ...article.frontendData, blocks: [ From 6b21e8e33744dc857946f624984891c7fbea7a02 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 16 Dec 2024 13:45:41 +0000 Subject: [PATCH 16/22] bump package --- dotcom-rendering/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 6c9a5f52384..cd7cf50374f 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -50,7 +50,7 @@ "@guardian/libs": "19.2.1", "@guardian/ophan-tracker-js": "2.2.5", "@guardian/react-crossword": "2.0.2", - "@guardian/react-crossword-next": "npm:@guardian/react-crossword@0.0.0-canary-20241209150926", + "@guardian/react-crossword-next": "npm:@guardian/react-crossword@0.0.0-canary-20241216134231", "@guardian/shimport": "1.0.2", "@guardian/source": "8.0.0", "@guardian/source-development-kitchen": "12.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7510f18932..8984e7e5053 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -365,8 +365,8 @@ importers: specifier: 2.0.2 version: 2.0.2 '@guardian/react-crossword-next': - specifier: npm:@guardian/react-crossword@0.0.0-canary-20241209150926 - version: /@guardian/react-crossword@0.0.0-canary-20241209150926(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) + specifier: npm:@guardian/react-crossword@0.0.0-canary-20241216134231 + version: /@guardian/react-crossword@0.0.0-canary-20241216134231(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) '@guardian/shimport': specifier: 1.0.2 version: 1.0.2 @@ -4259,8 +4259,8 @@ packages: tslib: 2.6.2 dev: false - /@guardian/react-crossword@0.0.0-canary-20241209150926(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): - resolution: {integrity: sha512-nJ9vi454SqMynQ0UDz+jBmO/l7YxgVIq6Gvfyy4p/b5cnBNsxh8n4OZtIexhJx/dOhKYsMSPw/5KU8YOnVhF9A==} + /@guardian/react-crossword@0.0.0-canary-20241216134231(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): + resolution: {integrity: sha512-ys7FdWujxL3oOY+1JGQveIt+jjyllqVLrQwEEySOqKYlXPmoPmk4ZIZAP8TA4SoPkv99ygHcCDhqVhfijzex3g==} peerDependencies: '@emotion/react': ^11.11.3 '@guardian/libs': ^19.1.0 From 0fcbe3ab5ffa21514043ad2f94c3fcb5ed0de45c Mon Sep 17 00:00:00 2001 From: oliverabrahams Date: Tue, 14 Jan 2025 14:33:56 +0000 Subject: [PATCH 17/22] move adding the crossword into the enhanceBlocks.ts / article.ts files and out of handle.article.web --- .../src/model/buildCrosswordBlock.ts | 23 +++++++++++ dotcom-rendering/src/model/enhanceBlocks.ts | 10 +++-- .../src/server/handler.article.web.ts | 9 +--- dotcom-rendering/src/types/article.ts | 41 +++---------------- 4 files changed, 38 insertions(+), 45 deletions(-) create mode 100644 dotcom-rendering/src/model/buildCrosswordBlock.ts diff --git a/dotcom-rendering/src/model/buildCrosswordBlock.ts b/dotcom-rendering/src/model/buildCrosswordBlock.ts new file mode 100644 index 00000000000..b85a5058ea5 --- /dev/null +++ b/dotcom-rendering/src/model/buildCrosswordBlock.ts @@ -0,0 +1,23 @@ +import type { Block } from '../types/blocks'; +import type { FEArticleType } from '../types/frontend'; + +export const buildCrosswordBlock = (data: FEArticleType): Block | undefined => { + return data.crossword + ? { + id: data.crossword.id, + elements: [ + { + _type: 'model.dotcomrendering.pageElements.CrosswordElement', + crossword: data.crossword, + }, + ], + attributes: { + pinned: false, + keyEvent: false, + summary: false, + }, + primaryDateLine: data.webPublicationDateDisplay, + secondaryDateLine: data.webPublicationSecondaryDateDisplay, + } + : undefined; +}; diff --git a/dotcom-rendering/src/model/enhanceBlocks.ts b/dotcom-rendering/src/model/enhanceBlocks.ts index c8b76a7c5d0..c5851176bc9 100644 --- a/dotcom-rendering/src/model/enhanceBlocks.ts +++ b/dotcom-rendering/src/model/enhanceBlocks.ts @@ -31,6 +31,7 @@ type Options = { imagesForLightbox: ImageForLightbox[]; hasAffiliateLinksDisclaimer: boolean; audioArticleImage?: ImageBlockElement; + crossword?: Block; tags?: TagType[]; }; @@ -105,9 +106,12 @@ export const enhanceBlocks = ( format: ArticleFormat, options: Options, ): Block[] => { - const additionalElement: FEElement[] = []; + const additionalElements: FEElement[] = []; if (options.audioArticleImage) { - additionalElement.push(options.audioArticleImage); + additionalElements.push(options.audioArticleImage); + } + if (options.crossword) { + blocks.push(options.crossword); } return blocks.map((block) => ({ ...block, @@ -115,6 +119,6 @@ export const enhanceBlocks = ( format, block.id, options, - )([...block.elements, ...additionalElement]), + )([...block.elements, ...additionalElements]), })); }; diff --git a/dotcom-rendering/src/server/handler.article.web.ts b/dotcom-rendering/src/server/handler.article.web.ts index a29e03a54ae..f98084f570a 100644 --- a/dotcom-rendering/src/server/handler.article.web.ts +++ b/dotcom-rendering/src/server/handler.article.web.ts @@ -3,7 +3,7 @@ import { Standard as ExampleArticle } from '../../fixtures/generated/fe-articles import { decideFormat } from '../lib/articleFormat'; import { enhanceBlocks } from '../model/enhanceBlocks'; import { validateAsArticleType, validateAsBlock } from '../model/validate'; -import { enhanceArticleType, enhanceCrosswordArticle } from '../types/article'; +import { enhanceArticleType } from '../types/article'; import type { FEBlocksRequest } from '../types/frontend'; import { makePrefetchHeader } from './lib/header'; import { recordTypeAndPlatform } from './lib/logging-store'; @@ -13,12 +13,7 @@ export const handleArticle: RequestHandler = ({ body }, res) => { recordTypeAndPlatform('article', 'web'); const frontendData = validateAsArticleType(body); - let article = enhanceArticleType(frontendData, 'Web'); - - if (article.frontendData.crossword) { - article = enhanceCrosswordArticle(article); - } - + const article = enhanceArticleType(frontendData, 'Web'); const { html, prefetchScripts } = renderHtml({ article, }); diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index 956b3cef4e7..1dd1362db1e 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -1,7 +1,7 @@ -import { isUndefined } from '@guardian/libs'; import { type ArticleFormat, decideFormat } from '../lib/articleFormat'; import type { ImageForAppsLightbox } from '../model/appsLightboxImages'; import { appsLightboxImages } from '../model/appsLightboxImages'; +import { buildCrosswordBlock } from '../model/buildCrosswordBlock'; import { buildLightboxImages } from '../model/buildLightboxImages'; import { enhanceBlocks, enhanceMainMedia } from '../model/enhanceBlocks'; import { enhanceCommercialProperties } from '../model/enhanceCommercialProperties'; @@ -11,7 +11,8 @@ import { type TableOfContentsItem, } from '../model/enhanceTableOfContents'; import { enhancePinnedPost } from '../model/pinnedPost'; -import type { CrosswordElement, ImageForLightbox } from './content'; +import type { Block } from './blocks'; +import type { ImageForLightbox } from './content'; import type { FEArticleType } from './frontend'; import { type RenderingTarget } from './renderingTarget'; @@ -31,45 +32,14 @@ export type Article = { frontendData: ArticleDeprecated; }; -export const enhanceCrosswordArticle = (article: Article): Article => { - if (isUndefined(article.frontendData.crossword)) { - throw new TypeError('article does not contain a crossword'); - } - - const element: CrosswordElement = { - _type: 'model.dotcomrendering.pageElements.CrosswordElement', - crossword: article.frontendData.crossword, - }; - - return { - ...article, - frontendData: { - ...article.frontendData, - blocks: [ - { - id: article.frontendData.crossword.id, - elements: [element], - attributes: { - pinned: false, - keyEvent: false, - summary: false, - }, - primaryDateLine: - article.frontendData.webPublicationDateDisplay, - secondaryDateLine: - article.frontendData.webPublicationSecondaryDateDisplay, - }, - ], - }, - }; -}; - export const enhanceArticleType = ( data: FEArticleType, renderingTarget: RenderingTarget, ): Article => { const format = decideFormat(data.format); + const crossword: Block | undefined = buildCrosswordBlock(data); + const imagesForLightbox = data.config.switches.lightbox ? buildLightboxImages(data.format, data.blocks, data.mainMediaElements) : []; @@ -80,6 +50,7 @@ export const enhanceArticleType = ( imagesForLightbox, hasAffiliateLinksDisclaimer: !!data.affiliateLinksDisclaimer, audioArticleImage: data.audioArticleImage, + crossword, tags: data.tags, }); From 0c590a6123c5f29d0c8bfeb46358537d67fe975e Mon Sep 17 00:00:00 2001 From: oliverabrahams Date: Tue, 14 Jan 2025 15:40:00 +0000 Subject: [PATCH 18/22] update canary version --- dotcom-rendering/package.json | 2 +- pnpm-lock.yaml | 46 +++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 92928bff5de..7fd488ba3c7 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -50,7 +50,7 @@ "@guardian/libs": "19.2.1", "@guardian/ophan-tracker-js": "2.2.5", "@guardian/react-crossword": "2.0.2", - "@guardian/react-crossword-next": "npm:@guardian/react-crossword@0.0.0-canary-20241216134231", + "@guardian/react-crossword-next": "npm:@guardian/react-crossword@0.0.0-canary-20250114144251", "@guardian/shimport": "1.0.2", "@guardian/source": "8.0.0", "@guardian/source-development-kitchen": "12.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71e11dcf256..2579fa569f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -365,8 +365,8 @@ importers: specifier: 2.0.2 version: 2.0.2 '@guardian/react-crossword-next': - specifier: npm:@guardian/react-crossword@0.0.0-canary-20241216134231 - version: /@guardian/react-crossword@0.0.0-canary-20241216134231(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) + specifier: npm:@guardian/react-crossword@0.0.0-canary-20250114144251 + version: /@guardian/react-crossword@0.0.0-canary-20250114144251(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3) '@guardian/shimport': specifier: 1.0.2 version: 1.0.2 @@ -4027,7 +4027,7 @@ packages: '@typescript-eslint/parser': 6.18.0(eslint@8.56.0)(typescript@5.5.3) eslint: 8.56.0 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.18.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint@8.56.0) tslib: 2.6.2 typescript: 5.5.3 transitivePeerDependencies: @@ -4259,11 +4259,11 @@ packages: tslib: 2.6.2 dev: false - /@guardian/react-crossword@0.0.0-canary-20241216134231(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): - resolution: {integrity: sha512-ys7FdWujxL3oOY+1JGQveIt+jjyllqVLrQwEEySOqKYlXPmoPmk4ZIZAP8TA4SoPkv99ygHcCDhqVhfijzex3g==} + /@guardian/react-crossword@0.0.0-canary-20250114144251(@emotion/react@11.11.3)(@guardian/libs@19.2.1)(@guardian/source@8.0.0)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1)(typescript@5.5.3): + resolution: {integrity: sha512-7PkXvsC+e7K/VvJTzW/dhEfl/aQdIq6BTHYpnXO+vI9MLdsWnV4rfeOTA2Vqm2Bsd9hyNelRmGE5cuj6f5OunQ==} peerDependencies: '@emotion/react': ^11.11.3 - '@guardian/libs': ^19.1.0 + '@guardian/libs': 0.0.0-canary-20250114144251 '@guardian/source': ^8.0.0 '@types/react': ^18.2.79 react: ^18.2.0 @@ -6221,7 +6221,7 @@ packages: react-docgen-typescript: 2.2.2(typescript@5.5.3) tslib: 2.6.2 typescript: 5.5.3 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color dev: false @@ -7779,8 +7779,8 @@ packages: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.97.1): @@ -7790,8 +7790,8 @@ packages: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) dev: false /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.97.1): @@ -7805,8 +7805,8 @@ packages: webpack-dev-server: optional: true dependencies: - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) dev: false @@ -8414,7 +8414,7 @@ packages: '@babel/core': 7.26.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /babel-plugin-istanbul@6.1.1: @@ -9582,7 +9582,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /css-loader@7.1.2(webpack@5.97.1): @@ -10560,7 +10560,7 @@ packages: enhanced-resolve: 5.17.0 eslint: 8.56.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 is-core-module: 2.15.1 @@ -11566,7 +11566,7 @@ packages: semver: 7.5.4 tapable: 2.2.1 typescript: 5.5.3 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /form-data-encoder@2.1.4: @@ -16750,7 +16750,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /stylelint-config-recommended@14.0.0(stylelint@16.5.0): @@ -17236,7 +17236,7 @@ packages: semver: 7.5.4 source-map: 0.7.4 typescript: 5.5.3 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /ts-node@10.9.2(@swc/core@1.9.2)(@types/node@16.18.68)(typescript@5.1.6): @@ -18029,7 +18029,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) dev: false /webpack-dev-middleware@7.4.2(webpack@5.97.1): @@ -18089,8 +18089,8 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.1.0)(webpack@5.97.1) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.97.1) webpack-dev-middleware: 7.4.2(webpack@5.97.1) ws: 8.18.0 transitivePeerDependencies: @@ -18135,7 +18135,7 @@ packages: webpack: ^5.47.0 dependencies: tapable: 2.2.1 - webpack: 5.97.1(@swc/core@1.9.2)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.97.1(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 2.3.1 dev: false patched: true From b3b073eca38cc43024cfe36f0dadd277e99f394e Mon Sep 17 00:00:00 2001 From: oliverabrahams Date: Tue, 14 Jan 2025 16:14:34 +0000 Subject: [PATCH 19/22] dont push to array blocks --- dotcom-rendering/src/model/enhanceBlocks.ts | 26 ++++++++++++--------- dotcom-rendering/src/types/article.ts | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/dotcom-rendering/src/model/enhanceBlocks.ts b/dotcom-rendering/src/model/enhanceBlocks.ts index c5851176bc9..36429702368 100644 --- a/dotcom-rendering/src/model/enhanceBlocks.ts +++ b/dotcom-rendering/src/model/enhanceBlocks.ts @@ -31,7 +31,7 @@ type Options = { imagesForLightbox: ImageForLightbox[]; hasAffiliateLinksDisclaimer: boolean; audioArticleImage?: ImageBlockElement; - crossword?: Block; + crosswordBlock?: Block; tags?: TagType[]; }; @@ -107,18 +107,22 @@ export const enhanceBlocks = ( options: Options, ): Block[] => { const additionalElements: FEElement[] = []; + const additionalBlocks: Block[] = []; if (options.audioArticleImage) { additionalElements.push(options.audioArticleImage); } - if (options.crossword) { - blocks.push(options.crossword); + if (options.crosswordBlock) { + additionalBlocks.push(options.crosswordBlock); } - return blocks.map((block) => ({ - ...block, - elements: enhanceElements( - format, - block.id, - options, - )([...block.elements, ...additionalElements]), - })); + return [ + ...blocks.map((block) => ({ + ...block, + elements: enhanceElements( + format, + block.id, + options, + )([...block.elements, ...additionalElements]), + })), + ...additionalBlocks, + ]; }; diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index 1dd1362db1e..81f475069b7 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -38,7 +38,7 @@ export const enhanceArticleType = ( ): Article => { const format = decideFormat(data.format); - const crossword: Block | undefined = buildCrosswordBlock(data); + const crosswordBlock: Block | undefined = buildCrosswordBlock(data); const imagesForLightbox = data.config.switches.lightbox ? buildLightboxImages(data.format, data.blocks, data.mainMediaElements) @@ -50,7 +50,7 @@ export const enhanceArticleType = ( imagesForLightbox, hasAffiliateLinksDisclaimer: !!data.affiliateLinksDisclaimer, audioArticleImage: data.audioArticleImage, - crossword, + crosswordBlock, tags: data.tags, }); From c2da6466c9d8a2eac8d2e7f0bb5c118c1d74e50a Mon Sep 17 00:00:00 2001 From: oliverabrahams Date: Tue, 14 Jan 2025 16:24:24 +0000 Subject: [PATCH 20/22] change option to allow for more blocks to be added --- dotcom-rendering/src/model/enhanceBlocks.ts | 7 ++----- dotcom-rendering/src/types/article.ts | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dotcom-rendering/src/model/enhanceBlocks.ts b/dotcom-rendering/src/model/enhanceBlocks.ts index 36429702368..c37ec8e7279 100644 --- a/dotcom-rendering/src/model/enhanceBlocks.ts +++ b/dotcom-rendering/src/model/enhanceBlocks.ts @@ -31,7 +31,7 @@ type Options = { imagesForLightbox: ImageForLightbox[]; hasAffiliateLinksDisclaimer: boolean; audioArticleImage?: ImageBlockElement; - crosswordBlock?: Block; + additionalBlocks?: Block[]; tags?: TagType[]; }; @@ -107,13 +107,10 @@ export const enhanceBlocks = ( options: Options, ): Block[] => { const additionalElements: FEElement[] = []; - const additionalBlocks: Block[] = []; + const additionalBlocks: Block[] = options.additionalBlocks ?? []; if (options.audioArticleImage) { additionalElements.push(options.audioArticleImage); } - if (options.crosswordBlock) { - additionalBlocks.push(options.crosswordBlock); - } return [ ...blocks.map((block) => ({ ...block, diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index 81f475069b7..a107d7e30b1 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -40,6 +40,8 @@ export const enhanceArticleType = ( const crosswordBlock: Block | undefined = buildCrosswordBlock(data); + const additionalBlocks: Block[] = crosswordBlock ? [crosswordBlock] : []; + const imagesForLightbox = data.config.switches.lightbox ? buildLightboxImages(data.format, data.blocks, data.mainMediaElements) : []; @@ -50,7 +52,7 @@ export const enhanceArticleType = ( imagesForLightbox, hasAffiliateLinksDisclaimer: !!data.affiliateLinksDisclaimer, audioArticleImage: data.audioArticleImage, - crosswordBlock, + additionalBlocks, tags: data.tags, }); From 57b0a176d05dffa4b5b8e922c37a91cca073ac57 Mon Sep 17 00:00:00 2001 From: oliverabrahams Date: Tue, 14 Jan 2025 16:26:10 +0000 Subject: [PATCH 21/22] remove inferred types --- dotcom-rendering/src/types/article.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index a107d7e30b1..f115065f196 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -11,7 +11,6 @@ import { type TableOfContentsItem, } from '../model/enhanceTableOfContents'; import { enhancePinnedPost } from '../model/pinnedPost'; -import type { Block } from './blocks'; import type { ImageForLightbox } from './content'; import type { FEArticleType } from './frontend'; import { type RenderingTarget } from './renderingTarget'; @@ -38,9 +37,9 @@ export const enhanceArticleType = ( ): Article => { const format = decideFormat(data.format); - const crosswordBlock: Block | undefined = buildCrosswordBlock(data); + const crosswordBlock = buildCrosswordBlock(data); - const additionalBlocks: Block[] = crosswordBlock ? [crosswordBlock] : []; + const additionalBlocks = crosswordBlock ? [crosswordBlock] : []; const imagesForLightbox = data.config.switches.lightbox ? buildLightboxImages(data.format, data.blocks, data.mainMediaElements) From 5d0ce6babd7fbd460f0999b638a0f2712fef7dbc Mon Sep 17 00:00:00 2001 From: Ravi <7014230+arelra@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:55:29 +0000 Subject: [PATCH 22/22] Add additional blocks in enhanceArticleType Co-authored-by: Oliver Abrahams --- dotcom-rendering/src/model/enhanceBlocks.ts | 3 --- dotcom-rendering/src/types/article.ts | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dotcom-rendering/src/model/enhanceBlocks.ts b/dotcom-rendering/src/model/enhanceBlocks.ts index c37ec8e7279..f2a4449307b 100644 --- a/dotcom-rendering/src/model/enhanceBlocks.ts +++ b/dotcom-rendering/src/model/enhanceBlocks.ts @@ -31,7 +31,6 @@ type Options = { imagesForLightbox: ImageForLightbox[]; hasAffiliateLinksDisclaimer: boolean; audioArticleImage?: ImageBlockElement; - additionalBlocks?: Block[]; tags?: TagType[]; }; @@ -107,7 +106,6 @@ export const enhanceBlocks = ( options: Options, ): Block[] => { const additionalElements: FEElement[] = []; - const additionalBlocks: Block[] = options.additionalBlocks ?? []; if (options.audioArticleImage) { additionalElements.push(options.audioArticleImage); } @@ -120,6 +118,5 @@ export const enhanceBlocks = ( options, )([...block.elements, ...additionalElements]), })), - ...additionalBlocks, ]; }; diff --git a/dotcom-rendering/src/types/article.ts b/dotcom-rendering/src/types/article.ts index f115065f196..cb97c698f06 100644 --- a/dotcom-rendering/src/types/article.ts +++ b/dotcom-rendering/src/types/article.ts @@ -37,10 +37,6 @@ export const enhanceArticleType = ( ): Article => { const format = decideFormat(data.format); - const crosswordBlock = buildCrosswordBlock(data); - - const additionalBlocks = crosswordBlock ? [crosswordBlock] : []; - const imagesForLightbox = data.config.switches.lightbox ? buildLightboxImages(data.format, data.blocks, data.mainMediaElements) : []; @@ -51,10 +47,14 @@ export const enhanceArticleType = ( imagesForLightbox, hasAffiliateLinksDisclaimer: !!data.affiliateLinksDisclaimer, audioArticleImage: data.audioArticleImage, - additionalBlocks, tags: data.tags, }); + const crosswordBlock = buildCrosswordBlock(data); + const additionalBlocks = crosswordBlock ? [crosswordBlock] : []; + + const blocks = [...enhancedBlocks, ...additionalBlocks]; + const mainMediaElements = enhanceMainMedia( format, imagesForLightbox, @@ -67,7 +67,7 @@ export const enhanceArticleType = ( frontendData: { ...data, mainMediaElements, - blocks: enhancedBlocks, + blocks, pinnedPost: enhancePinnedPost( format, renderingTarget,