Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
65 changes: 65 additions & 0 deletions dotcom-rendering/src/components/ArticleMeta.web.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,69 @@ describe('ArticleMeta', () => {
container.querySelector(`.${interactiveLegacyClasses.shareIcons}`),
).toBeNull();
});

describe('dateline', () => {
const webPublicationDate = '2026-01-02T03:04:05.000Z';

const renderWithTags = (
tags: Array<{ id: string; type: string; title: string }>,
) =>
render(
<ConfigProvider
value={{
renderingTarget: 'Web',
darkModeAvailable: false,
assetOrigin: '/',
editionId: 'UK',
}}
>
<ArticleMeta
format={{
theme: Pillar.Lifestyle,
design: ArticleDesign.Standard,
display: ArticleDisplay.Standard,
}}
pageId="1234"
webTitle="A title"
byline="Observer writers"
tags={tags}
primaryDateline="primary date line"
secondaryDateline="secondary date line"
webPublicationDate={webPublicationDate}
isCommentable={false}
discussionApiUrl=""
shortUrlId=""
/>
</ConfigProvider>,
).container;

it('renders a time element with the raw publication date and no secondary dateline for a Filter series tag', () => {
const container = renderWithTags([
{
id: 'thefilter/series/the-filter',
type: 'Series',
title: 'The Filter',
},
]);

const time = container.querySelector('time');
expect(time?.getAttribute('datetime')).toBe(webPublicationDate);
expect(time?.textContent).toBe('primary date line');
expect(container.textContent).not.toContain('secondary date line');
});

it('renders the standard Dateline for a non-Filter tag', () => {
const container = renderWithTags([
{
id: 'lifeandstyle/series/observer-design',
type: 'Series',
title: 'Observer Design',
},
]);

expect(container.querySelector('time')).toBeNull();
expect(container.textContent).toContain('primary date line');
expect(container.textContent).toContain('secondary date line');
});
});
});
7 changes: 2 additions & 5 deletions dotcom-rendering/src/components/ArticleMeta.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import { getAudioData } from '../lib/audio-data';
import { getSoleContributor } from '../lib/byline';
import { decideMainMediaCaption } from '../lib/decide-caption';
import { hasFilterTag } from '../lib/theFilter';
import { palette as themePalette } from '../palette';
import { hasPreferredSourceButton } from '../preferredSource';
import type { Branding as BrandingType } from '../types/branding';
Expand Down Expand Up @@ -339,11 +340,7 @@ export const ArticleMeta = ({
const podcast = getPodcast(tags);
const rssFeedUrl = getRssFeedUrl(tags);

const isFilterArticle = tags.some(
(tag) =>
tag.id === 'tracking/commissioningdesk/the-filter' ||
tag.id === 'tracking/commissioningdesk/filter-us',
);
const isFilterArticle = hasFilterTag(tags);
Comment thread
peranosborn marked this conversation as resolved.

const mediaType:
| 'YouTubeVideo'
Expand Down
6 changes: 6 additions & 0 deletions dotcom-rendering/src/layouts/ImmersiveLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,9 @@ export const ImmersiveLayout = (props: WebProps | AppProps) => {
secondaryDateline={
article.webPublicationSecondaryDateDisplay
}
webPublicationDate={
article.webPublicationDate
}
isCommentable={
article.isCommentable
}
Expand Down Expand Up @@ -650,6 +653,9 @@ export const ImmersiveLayout = (props: WebProps | AppProps) => {
secondaryDateline={
article.webPublicationSecondaryDateDisplay
}
webPublicationDate={
article.webPublicationDate
}
isCommentable={
article.isCommentable
}
Expand Down
6 changes: 5 additions & 1 deletion dotcom-rendering/src/layouts/StandardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import { canRenderAds } from '../lib/canRenderAds';
import { getContributionsServiceUrl } from '../lib/contributions';
import { decideStoryPackageTrails } from '../lib/decideTrail';
import { hasFilterTag } from '../lib/theFilter';
import { worldCupTagId } from '../lib/worldCup2026';
import type { NavType } from '../model/extract-nav';
import { palette as themePalette } from '../palette';
Expand Down Expand Up @@ -109,6 +110,8 @@ export const StandardLayout = (props: WebProps | AppProps) => {

const isWorldCup2026 = article.tags.some((tag) => tag.id === worldCupTagId);

const isFilterArticle = hasFilterTag(article.tags);

const renderAds = canRenderAds(article);

return (
Expand Down Expand Up @@ -149,7 +152,8 @@ export const StandardLayout = (props: WebProps | AppProps) => {
}
}
showSlimNav={
format.display === ArticleDisplay.Immersive ||
(format.display === ArticleDisplay.Immersive &&
!isFilterArticle) ||
isInteractive
}
hasPageSkinContentSelfConstrain={true}
Expand Down
44 changes: 43 additions & 1 deletion dotcom-rendering/src/lib/theFilter.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isFilterPageId } from './theFilter';
import { hasFilterTag, isFilterPageId } from './theFilter';

describe('isFilterPageId', () => {
it('returns true for a UK Filter article pageId', () => {
Expand Down Expand Up @@ -29,3 +29,45 @@ describe('isFilterPageId', () => {
).toBe(false);
});
});

describe('hasFilterTag', () => {
it('returns true for the UK Filter series tag', () => {
expect(
hasFilterTag([
{ id: 'lifeandstyle/lifeandstyle' },
{ id: 'thefilter/series/the-filter' },
]),
).toBe(true);
});

it('returns true for the US Filter series tag', () => {
expect(hasFilterTag([{ id: 'thefilter-us/series/thefilter-us' }])).toBe(
true,
);
});

it('returns true for the UK Filter commissioning desk', () => {
expect(
hasFilterTag([{ id: 'tracking/commissioningdesk/the-filter' }]),
).toBe(true);
});

it('returns true for the US Filter commissioning desk', () => {
expect(
hasFilterTag([{ id: 'tracking/commissioningdesk/filter-us' }]),
).toBe(true);
});

it('returns false when there is no Filter series or desk tag', () => {
expect(
hasFilterTag([
{ id: 'technology/technology' },
{ id: 'tracking/commissioningdesk/newsletters' },
]),
).toBe(false);
});

it('returns false for no tags', () => {
expect(hasFilterTag([])).toBe(false);
});
});
18 changes: 18 additions & 0 deletions dotcom-rendering/src/lib/theFilter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { TagType } from '../types/tag';

/**
* The Filter is the Guardian's shopping/product-review vertical. Its
* article pageIds always start with one of these section prefixes,
Expand All @@ -7,3 +9,19 @@ const FILTER_PAGE_ID_PREFIXES = ['thefilter/', 'thefilter-us/'];

export const isFilterPageId = (pageId: string): boolean =>
FILTER_PAGE_ID_PREFIXES.some((prefix) => pageId.startsWith(prefix));

/**
* Tags that identify Filter content: the Filter series tags, which Composer
* uses to present an article as The Filter, and the Filter commissioning desk
* tracking tags. Matching either one counts as Filter content, so prefer this
* over the pageId when deciding whether something is The Filter.
*/
const FILTER_TAG_IDS = [
'thefilter/series/the-filter',
'thefilter-us/series/thefilter-us',
'tracking/commissioningdesk/the-filter',
'tracking/commissioningdesk/filter-us',
];

export const hasFilterTag = (tags: Array<Pick<TagType, 'id'>>): boolean =>
tags.some((tag) => FILTER_TAG_IDS.includes(tag.id));
Loading