Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions dotcom-rendering/src/components/AffiliateDisclaimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { css } from '@emotion/react';
import {
palette,
space,
textSans12,
textSans14,
textSans15,
} from '@guardian/source/foundations';
Expand All @@ -20,6 +21,23 @@ const disclaimerLeftColStyles = css`
padding-bottom: ${space[1]}px;
`;

const galleryDisclaimerStyles = css`
${textSans12};
line-height: 1.5;
color: ${themePalette('--affiliate-disclaimer-text')};
a {
color: ${themePalette('--affiliate-disclaimer-text')};
transition: border-color 0.15s ease-out;
border-bottom: 1px solid ${palette.neutral[46]};
text-decoration: none;
}
a:hover {
border-bottom: 1px solid
${themePalette('--affiliate-disclaimer-text-hover')};
text-decoration: none;
}
`;

const disclaimerInlineStyles = css`
${textSans14};
/**
Expand Down Expand Up @@ -64,8 +82,9 @@ const DisclaimerText = () => (
The Guardian’s journalism is independent. We will earn a commission if
you buy something through an affiliate link. 
<a href="https://www.theguardian.com/info/2017/nov/01/reader-information-on-affiliate-links">
Learn more.
Learn more
</a>
.
</p>
);

Expand Down Expand Up @@ -99,4 +118,17 @@ const AffiliateDisclaimerInline = ({ isAmp = false }) =>
</Hide>
);

export { AffiliateDisclaimer, AffiliateDisclaimerInline };
const GalleryAffiliateDisclaimer = () => (
<aside
css={[disclaimerLeftColStyles, galleryDisclaimerStyles]}
data-testid="affiliate-disclaimer"
>
<DisclaimerText />
</aside>
);

export {
AffiliateDisclaimer,
AffiliateDisclaimerInline,
GalleryAffiliateDisclaimer,
};
24 changes: 3 additions & 21 deletions dotcom-rendering/src/components/ArticleMeta.web.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { css } from '@emotion/react';
import { between, from, space, until } from '@guardian/source/foundations';
import { StraightLines } from '@guardian/source-development-kitchen/react-components';
import { grid } from '../../src/grid';
import type { FEArticle } from '../frontend/feArticle';
import { interactiveLegacyClasses } from '../layouts/lib/interactiveLegacyStyling';
import {
Expand Down Expand Up @@ -184,28 +183,11 @@ export const metaContainer = (format: ArticleFormat) => {
}
`;
case ArticleDesign.LiveBlog:
case ArticleDesign.DeadBlog: {
case ArticleDesign.DeadBlog:
case ArticleDesign.Gallery: {
return '';
}
case ArticleDesign.Gallery:
return css`
${grid.column.centre}
padding-bottom: ${space[3]}px;
${from.tablet} {
position: relative;
&::before {
content: '';
position: absolute;
left: -10px;
top: 0;
bottom: 0;
width: 1px;
background-color: ${themePalette(
'--article-border',
)};
}
}
`;

default:
return defaultMargins;
}
Expand Down
15 changes: 15 additions & 0 deletions dotcom-rendering/src/components/Caption.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { css } from '@emotion/react';
import {
between,
from,
space,
textSans12,
Expand Down Expand Up @@ -211,10 +212,24 @@ const captionLink = css`

const galleryStyles = css`
${grid.column.centre}
margin-bottom: 0;
padding-bottom: 6px;
${from.leftCol} {
${grid.column.left}
grid-row-start: 8;
}
${between.tablet.and.leftCol} {
position: relative;
&::before {
content: '';
position: absolute;
left: -10px;
top: 0;
bottom: 0;
width: 1px;
background-color: ${palette('--article-border')};
}
}
`;

const CameraIcon = ({ format }: IconProps) => {
Expand Down
13 changes: 12 additions & 1 deletion dotcom-rendering/src/components/CaptionText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { css } from '@emotion/react';
import { headlineMedium17, space } from '@guardian/source/foundations';
import { type ReactNode } from 'react';
import sanitise, { type IOptions } from 'sanitize-html';
import { isSkimlink } from '../lib/affiliateLinksUtils';
import { getAttrs, parseHtml } from '../lib/domUtils';
import { palette } from '../palette';

Expand Down Expand Up @@ -38,6 +39,7 @@ const renderTextElement = (node: Node, key: number): ReactNode => {
return text === '' ? null : <em key={key}>{children}</em>;
case 'A': {
const attrs = getAttrs(node);
const href = attrs?.getNamedItem('href')?.value;

return (
<a
Expand All @@ -51,7 +53,7 @@ const renderTextElement = (node: Node, key: number): ReactNode => {
${palette('--article-link-border-hover')};
}
`}
href={attrs?.getNamedItem('href')?.value}
href={href}
target={attrs?.getNamedItem('target')?.value}
data-link-name={
attrs?.getNamedItem('data-link-name')?.value
Expand All @@ -60,6 +62,15 @@ const renderTextElement = (node: Node, key: number): ReactNode => {
attrs?.getNamedItem('data-component')?.value
}
key={key}
/**
* Affiliate links must have the rel attribute set to "sponsored"
* @see https://developers.google.com/search/docs/crawling-indexing/qualify-outbound-links
*/
rel={
isSkimlink(href)
? 'sponsored'
: getAttrs(node)?.getNamedItem('rel')?.value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to set this to undefined if not a skimlink?

Or we could check with CP how they expect rel attributes to be propagated on caption links in DCR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, we had made the change together in the call, but I'd forgotten to push to repo :)
For now lets leave it as undefined (not to impose any change to the existing functionality in prod), but I'll email CP and ask about this

}
>
{children}
</a>
Expand Down
7 changes: 4 additions & 3 deletions dotcom-rendering/src/layouts/GalleryLayout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export default meta;

type Story = StoryObj<typeof meta>;

const addBranding = (gallery: Gallery): Gallery => ({
const addBrandingAndAffiliateDisclaimer = (gallery: Gallery): Gallery => ({
...gallery,
frontendData: {
...gallery.frontendData,
affiliateLinksDisclaimer: 'true',
webPublicationDateDeprecated: '2020-03-28T07:27:19.000Z',
commercialProperties: {
...gallery.frontendData.commercialProperties,
Expand All @@ -40,7 +41,7 @@ if (appsArticle.design !== ArticleDesign.Gallery) {
export const Apps = {
args: {
renderingTarget: 'Apps',
gallery: addBranding(appsArticle),
gallery: addBrandingAndAffiliateDisclaimer(appsArticle),
},
parameters: {
formats: [
Expand Down Expand Up @@ -69,7 +70,7 @@ export const Web = {
...extractNAV(webArticle.frontendData.nav),
selectedPillar: getCurrentPillar(webArticle.frontendData),
},
gallery: addBranding(webArticle),
gallery: addBrandingAndAffiliateDisclaimer(webArticle),
},
parameters: {
formats: [
Expand Down
76 changes: 52 additions & 24 deletions dotcom-rendering/src/layouts/GalleryLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { css } from '@emotion/react';
import { from, palette as sourcePalette } from '@guardian/source/foundations';
import {
from,
palette as sourcePalette,
space,
} from '@guardian/source/foundations';
import { GalleryAffiliateDisclaimer } from '../components/AffiliateDisclaimer';
import { AppsFooter } from '../components/AppsFooter.importable';
import { ArticleHeadline } from '../components/ArticleHeadline';
import { ArticleMetaApps } from '../components/ArticleMeta.apps';
Expand Down Expand Up @@ -51,6 +56,23 @@ const headerStyles = css`
}
`;

const webMetaAndDisclaimerContainer = css`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "web" specific? It looks like it wraps both apps and web?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have amazing eyes, just fixed it 👍

${grid.column.centre}
padding-bottom: ${space[6]}px;
${from.tablet} {
position: relative;
&::before {
content: '';
position: absolute;
left: -10px;
top: 0;
bottom: 0;
width: 1px;
background-color: ${palette('--article-border')};
}
}
`;

export const GalleryLayout = (props: WebProps | AppProps) => {
const gallery = props.gallery;
const frontendData = gallery.frontendData;
Expand Down Expand Up @@ -123,30 +145,36 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
isMainMedia={true}
/>
{isWeb ? (
<ArticleMeta
branding={
frontendData.commercialProperties[
frontendData.editionId
].branding
}
format={format}
pageId={frontendData.pageId}
webTitle={frontendData.webTitle}
byline={frontendData.byline}
tags={frontendData.tags}
primaryDateline={
frontendData.webPublicationDateDisplay
}
secondaryDateline={
frontendData.webPublicationSecondaryDateDisplay
}
isCommentable={frontendData.isCommentable}
discussionApiUrl={
frontendData.config.discussionApiUrl
}
shortUrlId={frontendData.config.shortUrlId}
/>
<div css={webMetaAndDisclaimerContainer}>
<ArticleMeta
branding={
frontendData.commercialProperties[
frontendData.editionId
].branding
}
format={format}
pageId={frontendData.pageId}
webTitle={frontendData.webTitle}
byline={frontendData.byline}
tags={frontendData.tags}
primaryDateline={
frontendData.webPublicationDateDisplay
}
secondaryDateline={
frontendData.webPublicationSecondaryDateDisplay
}
isCommentable={frontendData.isCommentable}
discussionApiUrl={
frontendData.config.discussionApiUrl
}
shortUrlId={frontendData.config.shortUrlId}
/>
{!!frontendData.affiliateLinksDisclaimer && (
<GalleryAffiliateDisclaimer />
)}
</div>
) : null}

{isApps ? (
<ArticleMetaApps
branding={
Expand Down
24 changes: 24 additions & 0 deletions dotcom-rendering/src/paletteDeclarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4659,6 +4659,22 @@ const affiliateDisclaimerBackgroundHoverLight: PaletteFunction = ({
const affiliateDisclaimerBackgroundHoverDark: PaletteFunction = () =>
sourcePalette.neutral[10];

const affiliateDisclaimerTextLight: PaletteFunction = () => {
return sourcePalette.neutral[86];
};

const affiliateDisclaimerTextDark: PaletteFunction = () => {
return sourcePalette.neutral[73];
};

const affiliateDisclaimerTextHoverLight: PaletteFunction = () => {
return sourcePalette.neutral[73];
};

const affiliateDisclaimerTextHoverDark: PaletteFunction = () => {
return sourcePalette.neutral[86];
};

const seriesTitleBackgroundLight: PaletteFunction = ({
theme,
display,
Expand Down Expand Up @@ -6104,6 +6120,14 @@ const paletteColours = {
light: affiliateDisclaimerBackgroundHoverLight,
dark: affiliateDisclaimerBackgroundHoverDark,
},
'--affiliate-disclaimer-text': {
light: affiliateDisclaimerTextLight,
dark: affiliateDisclaimerTextDark,
},
'--affiliate-disclaimer-text-hover': {
light: affiliateDisclaimerTextHoverLight,
dark: affiliateDisclaimerTextHoverDark,
},
'--age-warning-background': {
light: ageWarningBackgroundLight,
dark: ageWarningBackgroundDark,
Expand Down