Merge pull request #245 from reuters-graphics/mf-headline

Updates Headline
This commit is contained in:
MinamiFunakoshiTR 2025-04-07 10:14:16 -05:00 committed by GitHub
commit e7658fc3ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 357 additions and 290 deletions

View file

@ -58,7 +58,7 @@ updateTime: 2021-09-12T12:57:00.000Z
<Canvas of={BylineStories.Demo} />
## Cutomisation
## Custom byline, published and updated datelines
Use [snippets](https://svelte.dev/docs/svelte/snippet) to customise the byline, published and updated datelines.
@ -82,3 +82,25 @@ Use [snippets](https://svelte.dev/docs/svelte/snippet) to customise the byline,
```
<Canvas of={BylineStories.Customised} />
## Custom author page
By default, the `Byline` component will hyperlink each author's byline to their Reuters.com page, formatted `https://www.reuters.com/authors/{author-slug}/`.
To hyperlink to different pages or email addresses, pass a custom function to the `getAuthorPage` prop.
```svelte
<!-- Pass a custom function as `getAuthorPage` -->
<Byline
authors={['Dea Bankova', 'Prasanta Kumar Dutta', 'Anurag Rao', 'Mariano Zafra']}
publishTime="2021-09-12T00:00:00Z"
updateTime="2021-09-12T13:57:00Z"
getAuthorPage={(author: string) => {
return `mailto:${author.replace(' ', '')}@example.com`;
}}
/>
```
<Canvas of={BylineStories.CustomAuthorPage} />
````

View file

@ -18,7 +18,6 @@
<Story
name="Demo"
args={{
align: 'left',
authors: [
'Dea Bankova',
'Prasanta Kumar Dutta',
@ -43,3 +42,22 @@
{/snippet}
</Byline>
</Story>
<Story
name="Custom author page"
exportName="CustomAuthorPage"
tags={['!autodocs', '!dev']}
args={{
authors: [
'Dea Bankova',
'Prasanta Kumar Dutta',
'Anurag Rao',
'Mariano Zafra',
],
publishTime: '2021-09-12T00:00:00Z',
updateTime: '2021-09-12T13:57:00Z',
getAuthorPage: (author: string) => {
return `mailto:${author.replace(' ', '')}@example.com`;
},
}}
/>

View file

@ -1,8 +1,8 @@
<!-- @migration-task Error while migrating Svelte code: Cannot set properties of undefined (setting 'next') -->
<!-- @component `Byline` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-text-elements-byline--docs) -->
<script lang="ts">
import { getAuthorPageUrl } from '../../utils';
import Block from '../Block/Block.svelte';
import slugify from 'slugify';
import { apdate } from 'journalize';
import type { Snippet } from 'svelte';
@ -46,7 +46,6 @@
/**
* Optional snippet for a custom published dateline.
*/
// Specify that this prop should have the type of a Svelte snippet, i.e. basic html
published?: Snippet;
/**
* Optional snippet for a custom updated dateline.
@ -61,10 +60,7 @@
align = 'left',
id = '',
cls = '',
getAuthorPage = (author: string): string => {
const authorSlug = slugify(author.trim(), { lower: true });
return `https://www.reuters.com/authors/${authorSlug}/`;
},
getAuthorPage = getAuthorPageUrl,
byline,
published,
updated,

View file

@ -0,0 +1,152 @@
import { Meta, Canvas } from '@storybook/blocks';
import * as HeadlineStories from './Headline.stories.svelte';
<Meta of={HeadlineStories} />
# Headline
The `Headline` component creates headlines in the legacy Reuters Graphics style, with the text centred on the page.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline
hed="Reuters Graphics Interactive"
dek="The beginning of a beautiful page"
section="World News"
/>
```
<Canvas of={HeadlineStories.Demo} />
## With bylines and dateline
Optionally, you can add authors and a publish time to the headline, which the `Headline` component internally renders with the [Byline](./?path=/docs/components-text-elements-byline--docs) component.
> **Note**: Since `Headline` uses `Byline`, you can customise the author page hyperlink and bylines with the `getAuthorPage`, `byline`, `published` and `updated` props.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline
hed={'Reuters Graphics Interactive'}
dek={'The beginning of a beautiful page'}
section={'Global news'}
authors={['Jane Doe']}
publishTime={new Date('2020-01-01').toISOString()}
getAuthorPage={(author: string) => {
return `mailto:${author.replace(' ', '')}@example.com`;
}}
/>
```
<Canvas of={HeadlineStories.Byline} />
## Custom hed and dek
Use the `hed` and/or `dek` [snippets](https://svelte.dev/docs/svelte/snippet) to override those elements with custom elements.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline width="wide">
<!-- Custom hed snippet -->
{#snippet hed()}
<h1 class="custom-hed">
<span class="small block text-base">The secret to</span>
“The Nutcracker's”
<span class="small block text-base fpt-1">success</span>
</h1>
{/snippet}
<!-- Custom dek snippet -->
{#snippet dek()}
<p class="custom-dek !fmt-3">
How “The Nutcracker” ballet became an<span
class="font-medium mx-1 px-1.5 py-1">American holday staple</span
>and a financial pillar of ballet companies across the country
</p>
{/snippet}
</Headline>
<!-- Custom styles -->
<style lang="scss">
.custom-hed {
line-height: 0.9;
}
.custom-dek {
span {
background-color: #fde68a;
}
}
</style>
```
<Canvas of={HeadlineStories.CustomHedDek} />
## With crown image
To add a crown image, use the `crown` [snippet](https://svelte.dev/docs/svelte/snippet).
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
import { assets } from '$app/paths';
</script>
<Headline
class="!fmt-3"
hed="Europa"
publishTime={new Date('2020-01-01').toISOString()}
>
<!-- Add a crown -->
{#snippet crown()}
<img
src={crownImgSrc}
width="100"
class="mx-auto mb-0"
alt="Illustration of Europe"
/>
{/snippet}
</Headline>
```
<Canvas of={HeadlineStories.CrownImage} />
## With crown graphic
Add a full graphic or any other component in the crown.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
import { assets } from '$app/paths'; // If in Graphis Kit
import Map from './ai2svelte/graphic.svelte'; // Import the crown graphic component
</script>
<Headline
width="wider"
class="!fmt-1"
hed={'Unfriendly skies'}
dek={'How Russias invasion of Ukraine is redrawing air routes'}
section={'Ukraine Crisis'}
authors={['Simon Scarr', 'Vijdan Mohammad Kawoosa']}
publishTime={new Date('2022-03-04').toISOString()}
>
<!-- Add a crown graphic -->
{#snippet crown()}
<!-- Pass `assetsPath` if in Graphics Kit -->
<Map assetsPath={assets || '/'} />
{/snippet}
</Headline>
```
<Canvas of={HeadlineStories.CrownGraphic} />

View file

@ -1,28 +1,10 @@
<script module lang="ts">
// @ts-ignore raw
import componentDocs from './stories/docs/component.md?raw';
// @ts-ignore raw
import withBylineDocs from './stories/docs/withByline.md?raw';
// @ts-ignore raw
import withDekDocs from './stories/docs/withDek.md?raw';
// @ts-ignore raw
import customHedDocs from './stories/docs/customHed.md?raw';
// @ts-ignore raw
import withCrownImgDocs from './stories/docs/withCrownImage.md?raw';
// @ts-ignore raw
import withCrownGraphicDocs from './stories/docs/withCrownGraphic.md?raw';
import { defineMeta } from '@storybook/addon-svelte-csf';
import Headline from './Headline.svelte';
import {
withComponentDocs,
withStoryDocs,
} from '$lib/docs/utils/withParams.js';
export const meta = {
const { Story } = defineMeta({
title: 'Components/Text elements/Headline',
component: Headline,
...withComponentDocs(componentDocs),
argTypes: {
hedSize: {
control: 'select',
@ -33,115 +15,97 @@
options: ['normal', 'wide', 'wider', 'widest'],
},
},
};
});
</script>
<script>
import { Template, Story } from '@storybook/addon-svelte-csf';
// @ts-ignore img
import crownImgSrc from './stories/crown.png';
import Map from './stories/graphic.svelte';
import crownImgSrc from './demo/crown.png';
import Map from './demo/graphic.svelte';
</script>
<Template >
{#snippet children({ args })}
<Headline {...args} />
{/snippet}
</Template>
<Story
name="Default"
args="{{
section: 'World News',
name="Demo"
args={{
hed: 'Reuters Graphics interactive',
hedSize: 'normal',
dek: '',
authors: [],
}}"
dek: 'The beginning of a beautiful page',
section: 'World News',
}}
/>
<Story name="With dek" {...withStoryDocs(withDekDocs)}>
<Story name="With byline and dateline" exportName="Byline">
<Headline
hed="{'Reuters Graphics Interactive'}"
dek="{'The beginning of a beautiful page'}"
section="{'Global news'}"
hed={'Reuters Graphics Interactive'}
dek={'The beginning of a beautiful page'}
section={'Global news'}
authors={['Jane Doe']}
publishTime={new Date('2020-01-01').toISOString()}
getAuthorPage={(author: string) => {
return `mailto:${author.replace(' ', '')}@example.com`;
}}
/>
</Story>
<Story name="With byline" {...withStoryDocs(withBylineDocs)}>
<Headline
hed="{'Reuters Graphics Interactive'}"
dek="{'The beginning of a beautiful page'}"
section="{'Global news'}"
authors="{['Dea Bankova', 'Aditi Bhandari']}"
publishTime="{new Date('2020-01-01').toISOString()}"
/>
</Story>
<Story name="With custom hed" {...withStoryDocs(customHedDocs)}>
<Story name="Custom hed and dek" exportName="CustomHedDek">
<Headline width="wide">
{#snippet hed()}
<h1 class="custom-hed" >
<h1 class="custom-hed">
<span class="small block text-base">The secret to</span>
“The Nutcracker's”
<span class="small block text-base fpt-1">success</span>
</h1>
{/snippet}
{/snippet}
{#snippet dek()}
<p class="custom-dek !fmt-3" >
<p class="custom-dek !fmt-3">
How “The Nutcracker” ballet became an<span
class="font-medium mx-1 px-1.5 py-1">American holday staple</span
>and a financial pillar of ballet companies across the country
</p>
{/snippet}
{/snippet}
</Headline>
<style lang="scss">
.custom-hed {
line-height: 0.9;
}
.custom-dek {
span {
background-color: #fde68a;
}
}
</style>
</Story>
<Story name="With crown image" {...withStoryDocs(withCrownImgDocs)}>
<Headline class="!fmt-3" publishTime="{new Date('2020-01-01').toISOString()}">
<Story name="Crown image" exportName="CrownImage">
<Headline
class="!fmt-3"
hed="Europa"
publishTime={new Date('2020-01-01').toISOString()}
>
<!-- Add a crown -->
{#snippet crown()}
<img
src="{crownImgSrc}"
<img
src={crownImgSrc}
width="100"
class="mx-auto mb-0"
alt="Illustration of Europe"
/>
{/snippet}
<!-- Override the hed with a named slot -->
{#snippet hed()}
<h1 class="!font-serif !tracking-wide">Europa</h1>
{/snippet}
{/snippet}
</Headline>
</Story>
<Story name="With crown graphic" {...withStoryDocs(withCrownGraphicDocs)}>
<Story name="Crown graphic" exportName="CrownGraphic">
<Headline
width="wider"
class="!fmt-1"
hed="{'Unfriendly skies'}"
dek="{'How Russias invasion of Ukraine is redrawing air routes'}"
section="{'Ukraine Crisis'}"
authors="{['Simon Scarr', 'Vijdan Mohammad Kawoosa']}"
publishTime="{new Date('2022-03-04').toISOString()}"
hed={'Unfriendly skies'}
dek={'How Russias invasion of Ukraine is redrawing air routes'}
section={'Ukraine Crisis'}
authors={['Simon Scarr', 'Vijdan Mohammad Kawoosa']}
publishTime={new Date('2022-03-04').toISOString()}
>
<!-- Add a crown graphic -->
{#snippet crown()}
<div >
<Map />
</div>
{/snippet}
<Map />
{/snippet}
</Headline>
</Story>
<style lang="scss">
.custom-hed {
line-height: 0.9;
}
.custom-dek {
span {
background-color: #fde68a;
}
}
</style>

View file

@ -1,60 +1,76 @@
<!-- @migration-task Error while migrating Svelte code: Cannot set properties of undefined (setting 'next') -->
<!-- @component `Headline` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-text-elements-headline--docs) -->
<script lang="ts">
// Types
import type { HeadlineSize } from './../@types/global';
import type { Snippet } from 'svelte';
/**
* Headline, parsed as an _inline_ markdown string in an `h1` element.
* @type {string}
*/
export let hed: string = 'Reuters Graphics Interactive';
/** Add extra classes to the block tag to target it with custom CSS. */
let cls: string = '';
export { cls as class };
/**
* Headline size
* @type {string}
*/
export let hedSize: HeadlineSize = 'normal';
/**
* Dek, parsed as a markdown string.
* @type {string}
*/
export let dek: string | null = null;
/**
* Section title
* @type {string}
*/
export let section: string | null = null;
/**
* Array of author names, which will be slugified to create links to Reuters author pages
*/
export let authors: string[] = [];
/**
* Publish time as a datetime string.
* @type {string}
*/
export let publishTime: string = '';
/**
* Update time as a datetime string.
* @type {string}
*/
export let updateTime: string = '';
/**
* Width of the headline.
*/
export let width: 'normal' | 'wide' | 'wider' | 'widest' = 'normal';
// Components
import Block from '../Block/Block.svelte';
import Byline from '../Byline/Byline.svelte';
import Markdown from '../Markdown/Markdown.svelte';
let hedClass: string;
$: {
interface Props {
/** Headline, parsed as an _inline_ markdown string in an `h1` element OR as a custom snippet. */
hed: string | Snippet;
/** Add extra classes to the block tag to target it with custom CSS. */
class?: string;
/** Headline size: small, normal, big, bigger, biggest */
hedSize?: HeadlineSize;
/** Dek, parsed as a markdown string OR as a custom snippet. */
dek?: string | Snippet;
/** Section title */
section?: string;
/** Array of author names, which will be slugified to create links to Reuters author pages */
authors?: string[];
/** Publish time as a datetime string. */
publishTime?: string;
/** Update time as a datetime string. */
updateTime?: string;
/** Width of the headline: normal, wide, wider, widest */
width?: 'normal' | 'wide' | 'wider' | 'widest';
/**
* Custom function that returns an author page URL.
*/
getAuthorPage?: (author: string) => string;
/** Custom crown snippet */
crown?: Snippet;
/**
* Optional snippet for a custom byline.
*/
byline?: Snippet;
/**
* Optional snippet for a custom published dateline.
*/
published?: Snippet;
/**
* Optional snippet for a custom updated dateline.
*/
updated?: Snippet;
}
let {
hed = 'Reuters Graphics Interactive',
class: cls = '',
hedSize = 'normal',
dek,
section,
authors = [],
publishTime = '',
updateTime = '',
width = 'normal',
getAuthorPage,
crown,
byline,
published,
updated,
}: Props = $props();
// Set the headline text size class based on the `hedSize` prop
let hedClass = $state('text-3xl');
$effect(() => {
switch (hedSize) {
case 'biggest':
hedClass = 'text-6xl';
@ -71,16 +87,16 @@
default:
hedClass = 'text-3xl';
}
}
});
</script>
<div class="headline-wrapper" style="display:contents;">
<Block {width} class="headline text-center fmt-7 fmb-6 {cls}">
<header class="relative">
{#if $$slots.crown}
{#if crown}
<div class="crown-container">
<!-- Crown named slot -->
<slot name="crown" />
<!-- Crown snippet -->
{@render crown()}
</div>
{/if}
<div class="title">
@ -91,53 +107,56 @@
{section}
</p>
{/if}
{#if $$slots.hed}
<!-- Headline named slot -->
<slot name="hed" />
{:else}
<h1 class="{hedClass}">
<Markdown source="{hed}" parseInline />
{#if typeof hed === 'string'}
<h1 class={hedClass}>
<Markdown source={hed} parseInline />
</h1>
{:else if hed}
<!-- Headline snippet -->
{@render hed()}
{/if}
{#if $$slots.dek}
<!-- Dek named slot-->
{#if typeof dek === 'string'}
<div class="dek fmx-auto fmb-6">
<slot name="dek" />
<Markdown source={dek} />
</div>
{:else if dek}
<!-- Dek snippet-->
<div class="dek fmx-auto fmb-6">
<Markdown source="{dek}" />
{@render dek()}
</div>
{/if}
</div>
{#if $$slots.byline}
<!-- Custom byline/dateline -->
<slot name="byline" />
{:else if authors.length > 0 || publishTime}
{#if authors.length > 0 || publishTime}
<Byline
class="fmy-4"
cls="fmy-4"
{authors}
{publishTime}
{updateTime}
{getAuthorPage}
{published}
{updated}
align="center"
/>
{:else if byline}
<!-- Custom byline/dateline -->
{@render byline()}
{/if}
</header>
</Block>
</div>
<style lang="scss">
@use '../../scss/mixins' as *;
@use '../../scss/mixins' as mixins;
.headline-wrapper {
:global(.dek) {
max-width: $column-width-normal;
max-width: mixins.$column-width-normal;
}
:global(.dek p) {
@include fmt-0;
@include font-note;
@include leading-tight;
@include font-light;
@include fmb-3;
@include mixins.fmt-0;
@include mixins.font-note;
@include mixins.leading-tight;
@include mixins.font-light;
@include mixins.fmb-3;
}
}
</style>

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 363 KiB

After

Width:  |  Height:  |  Size: 363 KiB

View file

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 226 KiB

View file

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View file

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 519 KiB

View file

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

View file

@ -14,7 +14,7 @@
let width = $state(null);
</script>
<div id="g-graphic-box" bind:clientWidth="{width}">
<div id="g-graphic-box" bind:clientWidth={width}>
<!-- Artboard: xs -->
{#if width && width >= 0 && width < 510}
<div id="g-graphic-xs" class="g-artboard" style="">
@ -22,7 +22,7 @@
<div
id="g-graphic-xs-img"
class="g-aiImg"
style="{`background-image: url(${chartXs});`}"
style={`background-image: url(${chartXs});`}
></div>
<div
id="g-ai0-3"
@ -89,7 +89,7 @@
<div
id="g-graphic-sm-img"
class="g-aiImg"
style="{`background-image: url(${chartSm});`}"
style={`background-image: url(${chartSm});`}
></div>
<div
id="g-ai1-1"
@ -213,7 +213,7 @@
<div
id="g-graphic-md-img"
class="g-aiImg"
style="{`background-image: url(${chartMd});`}"
style={`background-image: url(${chartMd});`}
></div>
<div
@ -346,7 +346,7 @@
<div
id="g-graphic-lg-img"
class="g-aiImg"
style="{`background-image: url(${chartLg});`}"
style={`background-image: url(${chartLg});`}
></div>
<div
id="g-ai3-1"
@ -485,7 +485,7 @@
<div
id="g-graphic-xl-img"
class="g-aiImg"
style="{`background-image: url(${chartXl});`}"
style={`background-image: url(${chartXl});`}
></div>
<div
id="g-ai4-1"

View file

@ -1,13 +0,0 @@
Reuters Graphics headline
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline
hed="{'Reuters Graphics Interactive'}"
dek="{'The beginning of a beautiful page'}"
section="{'World News'}"
/>
```

View file

@ -1,31 +0,0 @@
Use the `hed` and/or `dek` named slots to override those elements with completely custom markup.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline width="wide">
<h1 class="custom-hed" slot="hed">
<span class="small block text-base">The secret to</span>
“The Nutcracker's”
<span class="small block text-base fpt-1">success</span>
</h1>
<p class="custom-dek !fmt-3" slot="dek">
How “The Nutcracker” ballet became an<span
class="font-medium mx-1 px-1.5 py-1">American holday staple</span
>and a financial pillar of ballet companies across the country
</p>
</Headline>
<style lang="scss">
.custom-hed {
line-height: 0.9;
}
.custom-dek {
span {
background-color: #fde68a;
}
}
</style>
```

View file

@ -1,13 +0,0 @@
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline
hed="{'Reuters Graphics Interactive'}"
dek="{'The beginning of a beautiful page'}"
section="{'Global news'}"
authors="{['Dea Bankova', 'Aditi Bhandari']}"
publishTime="{new Date('2020-01-01').toISOString()}"
/>
```

View file

@ -1,24 +0,0 @@
Add a full graphic or any other component in the crown.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
import Map from './ai2svelte/graphic.svelte';
import { assets } from '$app/paths';
</script>
<Headline
width="wider"
class="!fmt-1"
hed="{'Unfriendly skies'}"
dek="{'How Russias invasion of Ukraine is redrawing air routes'}"
section="{'Ukraine Crisis'}"
authors="{['Simon Scarr', 'Vijdan Mohammad Kawoosa']}"
publishTime="{new Date('2022-03-04').toISOString()}"
>
<!-- Add a crown graphic -->
<div slot="crown">
<Map assetsPath="{assets}" />
</div>
</Headline>
```

View file

@ -1,21 +0,0 @@
Add a crown image in the `crown` named slot and override the headline in the `hed` named slot.
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
import { assets } from '$app/paths';
</script>
<Headline class="!fmt-3" publishTime="{new Date('2020-01-01').toISOString()}">
<!-- Add a crown -->
<img
slot="crown"
src="{crownImgSrc}"
width="100"
class="mx-auto mb-0"
alt="Illustration of Europe"
/>
<!-- Override the hed with a named slot -->
<h1 slot="hed" class="!font-serif !tracking-wide">Europa</h1>
</Headline>
```

View file

@ -1,11 +0,0 @@
```svelte
<script>
import { Headline } from '@reuters-graphics/graphics-components';
</script>
<Headline
hed="{'Reuters Graphics Interactive'}"
dek="{'The beginning of a beautiful page'}"
section="{'Global news'}"
/>
```

9
src/utils/index.ts Normal file
View file

@ -0,0 +1,9 @@
import slugify from 'slugify';
/**
* Custom function that returns an author page URL.
*/
export const getAuthorPageUrl = (author: string): string => {
const authorSlug = slugify(author.trim(), { lower: true });
return `https://www.reuters.com/authors/${authorSlug}/`;
};