Skip to content

Commit a498531

Browse files
committed
Merge branch 'main' into new-follow-button-opinion-articles
2 parents 68bd711 + 243aad5 commit a498531

21 files changed

Lines changed: 453 additions & 73 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
interface Props {
3+
testName: string;
4+
}
5+
6+
const { testName }: Props = $props();
7+
</script>
8+
9+
<a
10+
href={`https://metrics.gutools.co.uk/d/bfd4abner943ke/page-views?folderUid=efd48ip6ch3i8f&orgId=1&from=now-7d&to=now&var-test_name=${testName}`}
11+
target="_blank"
12+
>
13+
Grafana
14+
</a>

ab-testing/frontend/src/lib/components/OphanLink.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
href={`https://dashboard.ophan.co.uk/graph/breakdown?day=today&ab=${testName}`}
1111
target="_blank"
1212
>
13-
graph
13+
Ophan
1414
</a>

ab-testing/frontend/src/lib/components/TableFixed.svelte

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
2-
import type { ABTest } from '../../../../types.js';
3-
import OphanLink from '$lib/components/OphanLink.svelte';
4-
import TestVariants from '$lib/components/TestVariants.svelte';
2+
import type { ABTest } from "../../../../types.js";
3+
import OphanLink from "$lib/components/OphanLink.svelte";
4+
import TestVariants from "$lib/components/TestVariants.svelte";
5+
import GrafanaLink from "./GrafanaLink.svelte";
56
67
interface Props {
78
tests: ABTest[];
@@ -39,7 +40,7 @@
3940
<th scope="col">Test Groups</th>
4041
<th scope="col">Expires In</th>
4142
<th scope="col">Audience</th>
42-
<th scope="col">Ophan</th>
43+
<th scope="col">Page Views</th>
4344
</tr>
4445
</thead>
4546
<tbody>
@@ -49,7 +50,7 @@
4950
>
5051
<td
5152
class="status"
52-
class:off={test.status === 'OFF'}
53+
class:off={test.status === "OFF"}
5354
class:expired
5455
>
5556
{#if expired}
@@ -69,7 +70,11 @@
6970
>{daysToExpiry(test.expirationDate)} days</td
7071
>
7172
<td>{test.audienceSize * 100}%</td>
72-
<td><OphanLink testName={test.name} /></td>
73+
<td>
74+
<GrafanaLink testName={test.name} /> | <OphanLink
75+
testName={test.name}
76+
/>
77+
</td>
7378
</tr>
7479
<tr>
7580
<th scope="row">Description</th>
@@ -106,7 +111,7 @@
106111
padding: 8px;
107112
}
108113
109-
th[scope='col'] {
114+
th[scope="col"] {
110115
background-color: var(--light-grey);
111116
}
112117

dotcom-rendering/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ declare namespace JSX {
9494
'data-spacefinder-role'?:
9595
| 'nested'
9696
| 'immersive'
97+
| 'fullWidth'
9798
| 'inline'
9899
| 'richLink'
99100
| 'thumbnail';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { CallToActionAtom } from './CallToActionAtom';
2+
3+
export default {
4+
component: CallToActionAtom,
5+
title: 'Components/CallToActionAtom',
6+
};
7+
8+
export const Default = () => {
9+
return (
10+
<CallToActionAtom
11+
linkUrl="https://safety.epicgames.com/en-US?lang=en-US"
12+
backgroundImage="https://media.guim.co.uk/7fe58f11470360bc9f1e4b6bbcbf45d7cf06cfcf/0_0_1300_375/1300.jpg"
13+
text="This is a call to action text"
14+
buttonText="Learn more"
15+
/>
16+
);
17+
};
18+
19+
Default.storyName = 'default';
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { css } from '@emotion/react';
2+
import {
3+
from,
4+
palette as sourcePalette,
5+
textSansBold20,
6+
} from '@guardian/source/foundations';
7+
import { Button, SvgExternal } from '@guardian/source/react-components';
8+
9+
type Props = {
10+
linkUrl: string;
11+
backgroundImage: string;
12+
text: string;
13+
buttonText: string;
14+
};
15+
16+
export const CallToActionAtom = ({
17+
linkUrl,
18+
backgroundImage,
19+
text,
20+
buttonText,
21+
}: Props) => {
22+
return (
23+
<a
24+
href={linkUrl}
25+
css={css`
26+
text-decoration: none;
27+
`}
28+
>
29+
<picture
30+
css={css`
31+
position: relative;
32+
display: flex;
33+
`}
34+
>
35+
<img
36+
src={backgroundImage}
37+
alt={''}
38+
css={css`
39+
height: 200px;
40+
object-fit: cover;
41+
42+
${from.tablet} {
43+
height: 250px;
44+
}
45+
${from.leftCol} {
46+
height: 375px;
47+
}
48+
`}
49+
/>
50+
<div
51+
css={css`
52+
position: absolute;
53+
bottom: 10%;
54+
left: 10%;
55+
transform: translate(-10%, -10%);
56+
`}
57+
>
58+
<h2
59+
css={css`
60+
${textSansBold20}
61+
margin-bottom: 8px;
62+
color: white;
63+
`}
64+
>
65+
{text}
66+
</h2>
67+
<Button
68+
iconSide="right"
69+
size="small"
70+
icon={<SvgExternal />}
71+
theme={{
72+
textPrimary: sourcePalette.neutral[7],
73+
backgroundPrimary: sourcePalette.neutral[97],
74+
backgroundPrimaryHover: sourcePalette.neutral[73],
75+
}}
76+
>
77+
{buttonText}
78+
</Button>
79+
</div>
80+
</picture>
81+
</a>
82+
);
83+
};

dotcom-rendering/src/components/DirectoryPageNav.stories.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ export const OtherCompetition = {
3131
pageId: 'football/premierleague/table',
3232
},
3333
} satisfies Story;
34+
35+
export const WinterOlympics = {
36+
args: {
37+
pageId: 'sport/winter-olympics-2026',
38+
},
39+
} satisfies Story;

dotcom-rendering/src/components/DirectoryPageNav.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,21 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => {
119119

120120
const { textColor, backgroundColor } = config;
121121

122-
const nav = css({
123-
backgroundColor,
124-
'&': css(grid.paddedContainer),
125-
alignContent: 'space-between',
126-
height: 116,
127-
[from.tablet]: {
128-
height: 140,
129-
},
130-
[from.desktop]: {
131-
height: 150,
122+
const nav = css(
123+
{
124+
backgroundColor,
125+
'&': css(grid.paddedContainer),
126+
alignContent: 'space-between',
127+
height: 116,
128+
[from.tablet]: {
129+
height: 140,
130+
},
131+
[from.desktop]: {
132+
height: 150,
133+
},
132134
},
133-
...backgroundImageStyles(config.backgroundImages),
134-
});
135+
backgroundImageStyles(config.backgroundImages),
136+
);
135137

136138
const largeLinkStyles = css({
137139
...headlineBold24Object,

dotcom-rendering/src/components/Figure.tsx

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { css } from '@emotion/react';
2-
import { from, space, until } from '@guardian/source/foundations';
2+
import { breakpoints, from, space, until } from '@guardian/source/foundations';
33
import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat';
44
import type { FEElement, RoleType } from '../types/content';
55

66
type Props = {
77
children: React.ReactNode;
88
format: ArticleFormat;
99
isMainMedia: boolean;
10-
role?: RoleType | 'richLink';
10+
role?: RoleType | 'richLink' | 'fullWidth';
1111
id?: string;
1212
className?: string;
1313
type?: FEElement['_type'];
@@ -70,6 +70,53 @@ const roleCss = {
7070
}
7171
`,
7272

73+
fullWidth: css`
74+
margin-top: ${space[3]}px;
75+
margin-bottom: ${space[3]}px;
76+
77+
${until.tablet} {
78+
margin-left: -20px;
79+
margin-right: -20px;
80+
}
81+
${until.mobileLandscape} {
82+
margin-left: -10px;
83+
margin-right: -10px;
84+
}
85+
${from.tablet} {
86+
--scrollbar-width-fallback: 15px;
87+
--half-scrollbar-width-fallback: 7.5px;
88+
89+
width: calc(
90+
100vw - var(--scrollbar-width, var(--scrollbar-width-fallback))
91+
);
92+
max-width: calc(
93+
100vw - var(--scrollbar-width, var(--scrollbar-width-fallback))
94+
);
95+
96+
--grid-container-max-width: 740px;
97+
--grid-container-left-margin: calc(
98+
((-100vw + (var(--grid-container-max-width) - 42px)) / 2) +
99+
var(
100+
--half-scrollbar-width,
101+
var(--half-scrollbar-width-fallback)
102+
)
103+
);
104+
105+
margin-left: var(--grid-container-left-margin);
106+
}
107+
${from.desktop} {
108+
--grid-container-max-width: ${breakpoints.desktop}px;
109+
}
110+
${from.leftCol} {
111+
--grid-container-max-width: ${breakpoints.leftCol}px;
112+
--grid-left-col-width: 140px;
113+
}
114+
${from.wide} {
115+
--grid-container-max-width: ${breakpoints.wide}px;
116+
--grid-left-col-width: 219px;
117+
}
118+
`,
119+
73120
showcase: css`
74121
margin-top: ${space[3]}px;
75122
margin-bottom: ${space[3]}px;
@@ -150,7 +197,7 @@ const roleCss = {
150197

151198
// Used for vast majority of layouts.
152199
export const defaultRoleStyles = (
153-
role: RoleType | 'richLink',
200+
role: RoleType | 'richLink' | 'fullWidth',
154201
format: ArticleFormat,
155202
isTimeline = false,
156203
) => {
@@ -161,6 +208,8 @@ export const defaultRoleStyles = (
161208
return roleCss.supporting;
162209
case 'immersive':
163210
return roleCss.immersive;
211+
case 'fullWidth':
212+
return roleCss.fullWidth;
164213
case 'showcase':
165214
if (isTimeline) {
166215
return css`
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useEffect } from 'react';
2+
3+
export const InteractivesScrollbarWidth = () => {
4+
useEffect(() => {
5+
const updateScrollbarWidth = () => {
6+
const documentWidth = document.documentElement.clientWidth;
7+
if (documentWidth <= 0) return;
8+
9+
const scrollbarWidth = window.innerWidth - documentWidth;
10+
const root = document.documentElement;
11+
12+
root.style.setProperty('--scrollbar-width', `${scrollbarWidth}px`);
13+
root.style.setProperty(
14+
'--half-scrollbar-width',
15+
`${scrollbarWidth / 2}px`,
16+
);
17+
};
18+
19+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
20+
21+
const debouncedResize = () => {
22+
if (timeoutId) {
23+
clearTimeout(timeoutId);
24+
}
25+
26+
timeoutId = setTimeout(() => {
27+
updateScrollbarWidth();
28+
}, 150);
29+
};
30+
31+
updateScrollbarWidth();
32+
33+
window.addEventListener('resize', debouncedResize);
34+
35+
return () => {
36+
if (timeoutId) {
37+
clearTimeout(timeoutId);
38+
}
39+
window.removeEventListener('resize', debouncedResize);
40+
};
41+
}, []);
42+
43+
return null;
44+
};

0 commit comments

Comments
 (0)