Skip to content

Commit 3d0a4b8

Browse files
Add story packages to galleries (#14426)
The primary reason for this change is to add support for story packages to the new gallery layout. At the moment every layout duplicates the same transformation on the frontend story package data, mapping over the trails with `decideTrail`. This change extracts the story package type definition and parsing into its own module, and calls the parsing code up front when the `Article` type is constructed (i.e. in one place rather than several). For now, we can only use this in galleries, as other layouts are still using the `ArticleDeprecated` type. Also adds a type annotation to the story package fixtures, so that we can use them in stories without type errors. Also changed the gallery story snapshot width to leftCol, to make it fit the maximum snapshot size in Chromatic. Co-authored-by: Marjan Kalanaki <[email protected]>
1 parent 688e13f commit 3d0a4b8

File tree

10 files changed

+138
-35
lines changed

10 files changed

+138
-35
lines changed

dotcom-rendering/fixtures/generated/story-package.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
* gen-fixtures.js directly.
1212
*/
1313

14-
export const storyPackage = {
14+
import type { FEStoryPackage } from '../../src/frontend/feArticle';
15+
16+
export const storyPackage: FEStoryPackage = {
1517
heading: 'More on this story',
1618
trails: [
1719
{

dotcom-rendering/scripts/test-data/gen-fixtures.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,10 @@ requests.push(
302302
.then((res) => res.json())
303303
.then((json) => {
304304
// Write the new fixture data
305-
const contents = `${HEADER}export const storyPackage = ${JSON.stringify(
306-
json,
307-
null,
308-
4,
309-
)}`;
305+
const contents = `${HEADER}
306+
import type { FEStoryPackage } from '../../src/frontend/feArticle';
307+
308+
export const storyPackage: FEStoryPackage = ${JSON.stringify(json, null, 4)}`;
310309
return fs.writeFile(
311310
`${root}/fixtures/generated/story-package.ts`,
312311
contents,

dotcom-rendering/src/frontend/feArticle.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,7 @@ export interface FEArticle {
8484
hasRelated: boolean;
8585
publication: string; // TODO: check who uses?
8686
hasStoryPackage: boolean;
87-
storyPackage?: {
88-
trails: FETrailType[];
89-
heading: string;
90-
};
87+
storyPackage?: FEStoryPackage;
9188
onwards?: FEOnwards[];
9289
beaconURL: string;
9390
isCommentable: boolean;
@@ -197,3 +194,8 @@ export type FEFormat = {
197194
theme: FETheme;
198195
display: FEDisplay;
199196
};
197+
198+
export type FEStoryPackage = {
199+
heading: string;
200+
trails: FETrailType[];
201+
};

dotcom-rendering/src/frontend/schemas/feArticle.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,14 @@
223223
"storyPackage": {
224224
"type": "object",
225225
"properties": {
226+
"heading": {
227+
"type": "string"
228+
},
226229
"trails": {
227230
"type": "array",
228231
"items": {
229232
"$ref": "#/definitions/FETrailType"
230233
}
231-
},
232-
"heading": {
233-
"type": "string"
234234
}
235235
},
236236
"required": [

dotcom-rendering/src/layouts/DecideLayout.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export const WebStandardLifestyleReviewLight: Story = {
187187
display: WebStandardStandardNewsLight.args.article.display,
188188
theme: Pillar.Lifestyle,
189189
design: ArticleDesign.Review,
190+
storyPackage: undefined,
190191
},
191192
},
192193
parameters: webParameters,

dotcom-rendering/src/layouts/GalleryLayout.stories.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Meta, StoryObj } from '@storybook/react';
22
import { allModes } from '../../.storybook/modes';
33
import { Gallery as GalleryFixture } from '../../fixtures/generated/fe-articles/Gallery';
4+
import { storyPackage } from '../../fixtures/generated/story-package';
45
import { WithBranding } from '../components/ArticleMeta.web.stories';
56
import { ArticleDesign } from '../lib/articleFormat';
67
import { getCurrentPillar } from '../lib/layoutHelpers';
@@ -14,7 +15,7 @@ const meta = {
1415
parameters: {
1516
chromatic: {
1617
modes: {
17-
'light wide': allModes['light wide'],
18+
'light leftCol': allModes['light leftCol'],
1819
},
1920
},
2021
},
@@ -40,7 +41,13 @@ const addBrandingAndAffiliateDisclaimer = (gallery: Gallery): Gallery => ({
4041
},
4142
});
4243

43-
const appsArticle = enhanceArticleType(GalleryFixture, 'Apps');
44+
const appsArticle = enhanceArticleType(
45+
{
46+
...GalleryFixture,
47+
storyPackage,
48+
},
49+
'Apps',
50+
);
4451

4552
if (appsArticle.design !== ArticleDesign.Gallery) {
4653
throw new Error('Expected gallery');
@@ -65,7 +72,13 @@ export const Apps = {
6572
},
6673
} satisfies Story;
6774

68-
const webArticle = enhanceArticleType(GalleryFixture, 'Web');
75+
const webArticle = enhanceArticleType(
76+
{
77+
...GalleryFixture,
78+
storyPackage,
79+
},
80+
'Web',
81+
);
6982

7083
if (webArticle.design !== ArticleDesign.Gallery) {
7184
throw new Error('Expected gallery');

dotcom-rendering/src/layouts/GalleryLayout.tsx

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ArticleMetaApps } from '../components/ArticleMeta.apps';
1616
import { ArticleMeta } from '../components/ArticleMeta.web';
1717
import { ArticleTitle } from '../components/ArticleTitle';
1818
import { Caption } from '../components/Caption';
19+
import { Carousel } from '../components/Carousel.importable';
1920
import { DiscussionLayout } from '../components/DiscussionLayout';
2021
import { Footer } from '../components/Footer';
2122
import { DesktopAdSlot, MobileAdSlot } from '../components/GalleryAdSlots';
@@ -35,7 +36,7 @@ import { canRenderAds } from '../lib/canRenderAds';
3536
import { getContributionsServiceUrl } from '../lib/contributions';
3637
import { decideMainMediaCaption } from '../lib/decide-caption';
3738
import type { NavType } from '../model/extract-nav';
38-
import { palette as themePalette } from '../palette';
39+
import { palette } from '../palette';
3940
import type { Gallery } from '../types/article';
4041
import type { RenderingTarget } from '../types/renderingTarget';
4142
import { BannerWrapper, Stuck } from './lib/stickiness';
@@ -56,10 +57,10 @@ interface AppProps extends Props {
5657

5758
const headerStyles = css`
5859
${grid.container}
59-
background-color: ${themePalette('--article-inner-background')};
60+
background-color: ${palette('--article-inner-background')};
6061
6162
${from.tablet} {
62-
border-bottom: 1px solid ${themePalette('--article-border')};
63+
border-bottom: 1px solid ${palette('--article-border')};
6364
}
6465
`;
6566

@@ -75,19 +76,19 @@ const metaAndDisclaimerContainer = css`
7576
top: 0;
7677
bottom: 0;
7778
width: 1px;
78-
background-color: ${themePalette('--article-border')};
79+
background-color: ${palette('--article-border')};
7980
}
8081
}
8182
`;
8283

8384
const galleryItemAdvertStyles = css`
8485
${grid.paddedContainer}
8586
grid-auto-flow: row dense;
86-
background-color: ${themePalette('--article-inner-background')};
87+
background-color: ${palette('--article-inner-background')};
8788
8889
${from.tablet} {
89-
border-left: 1px solid ${themePalette('--article-border')};
90-
border-right: 1px solid ${themePalette('--article-border')};
90+
border-left: 1px solid ${palette('--article-border')};
91+
border-right: 1px solid ${palette('--article-border')};
9192
}
9293
`;
9394

@@ -116,7 +117,7 @@ const galleryBorder = css`
116117
top: 0;
117118
bottom: 0;
118119
width: 1px;
119-
background-color: ${themePalette('--article-border')};
120+
background-color: ${palette('--article-border')};
120121
}
121122
}
122123
@@ -130,7 +131,7 @@ const galleryBorder = css`
130131
top: 0;
131132
bottom: 0;
132133
width: 1px;
133-
background-color: ${themePalette('--article-border')};
134+
background-color: ${palette('--article-border')};
134135
}
135136
}
136137
`;
@@ -170,6 +171,7 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
170171
const isLabs = format.theme === ArticleSpecial.Labs;
171172

172173
const renderAds = canRenderAds(frontendData);
174+
const showMerchandisingHigh = isWeb && renderAds && !isLabs;
173175

174176
const contributionsServiceUrl = getContributionsServiceUrl(frontendData);
175177

@@ -229,7 +231,7 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
229231

230232
<main
231233
css={{
232-
backgroundColor: themePalette('--article-background'),
234+
backgroundColor: palette('--article-background'),
233235
}}
234236
>
235237
{isApps && renderAds && (
@@ -378,14 +380,15 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
378380
}
379381
/>
380382
</main>
381-
{isWeb && renderAds && !isLabs && (
383+
{/* More galleries container */}
384+
{showMerchandisingHigh && (
382385
<Section
383386
fullWidth={true}
384387
data-print-layout="hide"
385388
padSides={false}
386389
showTopBorder={false}
387390
showSideBorders={false}
388-
backgroundColour={themePalette('--ad-background')}
391+
backgroundColour={palette('--ad-background')}
389392
element="aside"
390393
>
391394
<AdSlot
@@ -395,17 +398,25 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
395398
/>
396399
</Section>
397400
)}
401+
<StoryPackage
402+
absoluteServerTimes={switches['absoluteServerTimes'] ?? false}
403+
discussionApiUrl={discussionApiUrl}
404+
format={format}
405+
renderingTarget={renderingTarget}
406+
storyPackage={gallery.storyPackage}
407+
topBorder={showMerchandisingHigh}
408+
/>
398409
{/** More Galleries container goes here */}
399410
{showComments && (
400411
<Section
401412
fullWidth={true}
402413
sectionId="comments"
403414
element="section"
404-
backgroundColour={themePalette(
415+
backgroundColour={palette(
405416
'--discussion-section-background',
406417
)}
407-
borderColour={themePalette('--article-border')}
408-
fontColour={themePalette('--discussion-text')}
418+
borderColour={palette('--article-border')}
419+
fontColour={palette('--discussion-text')}
409420
>
410421
<DiscussionLayout
411422
discussionApiUrl={frontendData.config.discussionApiUrl}
@@ -432,7 +443,7 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
432443
padSides={false}
433444
showTopBorder={false}
434445
showSideBorders={false}
435-
backgroundColour={themePalette('--ad-background')}
446+
backgroundColour={palette('--ad-background')}
436447
element="aside"
437448
>
438449
<AdSlot position="merchandising" display={format.display} />
@@ -487,9 +498,7 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
487498
{isApps && (
488499
<div
489500
css={{
490-
backgroundColor: themePalette(
491-
'--apps-footer-background',
492-
),
501+
backgroundColor: palette('--apps-footer-background'),
493502
}}
494503
>
495504
<Island priority="critical">
@@ -500,3 +509,40 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
500509
</>
501510
);
502511
};
512+
513+
const StoryPackage = ({
514+
storyPackage,
515+
format,
516+
discussionApiUrl,
517+
absoluteServerTimes,
518+
renderingTarget,
519+
topBorder,
520+
}: {
521+
storyPackage: Gallery['storyPackage'];
522+
format: ArticleFormat;
523+
discussionApiUrl: string;
524+
absoluteServerTimes: boolean;
525+
renderingTarget: RenderingTarget;
526+
topBorder: boolean;
527+
}) =>
528+
storyPackage === undefined ? null : (
529+
<Section
530+
fullWidth={true}
531+
backgroundColour={palette('--article-section-background')}
532+
borderColour={palette('--onward-content-border')}
533+
showTopBorder={topBorder}
534+
>
535+
<Island priority="feature" defer={{ until: 'visible' }}>
536+
<Carousel
537+
heading={storyPackage.heading}
538+
trails={storyPackage.trails}
539+
onwardsSource="more-on-this-story"
540+
format={format}
541+
leftColSize="compact"
542+
discussionApiUrl={discussionApiUrl}
543+
absoluteServerTimes={absoluteServerTimes}
544+
renderingTarget={renderingTarget}
545+
/>
546+
</Island>
547+
</Section>
548+
);

dotcom-rendering/src/paletteDeclarations.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6162,6 +6162,15 @@ const crosswordCluesHeaderBorderBottom: PaletteFunction = () =>
61626162
const crosswordTextLight: PaletteFunction = () => sourcePalette.neutral[7];
61636163
const crosswordTextDark: PaletteFunction = () => sourcePalette.neutral[86];
61646164

6165+
const onwardContentBorderLight: PaletteFunction = (format) => {
6166+
switch (format.design) {
6167+
case ArticleDesign.Gallery:
6168+
return sourcePalette.neutral[86];
6169+
default:
6170+
return articleBorderLight(format);
6171+
}
6172+
};
6173+
61656174
// ----- Palette ----- //
61666175

61676176
/**
@@ -7352,6 +7361,10 @@ const paletteColours = {
73527361
light: numberedListTitleLight,
73537362
dark: numberedListTitleDark,
73547363
},
7364+
'--onward-content-border': {
7365+
light: onwardContentBorderLight,
7366+
dark: () => sourcePalette.neutral[20],
7367+
},
73557368
'--pagination-text': {
73567369
light: paginationTextLight,
73577370
dark: paginationTextDark,

dotcom-rendering/src/storyPackage.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { FEStoryPackage } from './frontend/feArticle';
2+
import { decideTrail } from './lib/decideTrail';
3+
import type { TrailType } from './types/trails';
4+
5+
export type StoryPackage = {
6+
heading: string;
7+
trails: TrailType[];
8+
};
9+
10+
export const parse = (
11+
feStoryPackage: FEStoryPackage | undefined,
12+
): StoryPackage | undefined => {
13+
if (feStoryPackage === undefined) {
14+
return undefined;
15+
}
16+
17+
return {
18+
heading: feStoryPackage.heading,
19+
trails: feStoryPackage.trails.map(decideTrail),
20+
};
21+
};

0 commit comments

Comments
 (0)