Merge pull request #271 from reuters-graphics/jon-site-header

SiteHeader
This commit is contained in:
Jon McClure 2025-04-18 14:39:27 +01:00 committed by GitHub
commit 93ce0bfcdc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 112 additions and 96 deletions

View file

@ -4,13 +4,15 @@
import { normalizeUrl } from '../NavBar/utils/index.js';
interface Props {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?: any;
isMobileMenuOpen?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
releaseMobileMenu?: any;
}
let {
data = [],
data = {},
isMobileMenuOpen = false,
releaseMobileMenu = () => {},
}: Props = $props();

View file

@ -13,12 +13,19 @@
<span>{story.title}</span>
<time datetime={story.display_time}>{getTime(story.display_time)}</time>
</div>
{#if thumbnail}
{#if thumbnail && (thumbnail.resizer_url || thumbnail?.renditions?.square?.['120w'])}
<div class="thumbnail">
<img
src={thumbnail.renditions.square['120w']}
alt={thumbnail.alt_text}
/>
{#if thumbnail.resizer_url}
<img
src="{thumbnail.resizer_url}&width=120&quality=80"
alt={thumbnail.alt_text}
/>
{:else}
<img
src={thumbnail.renditions.square['120w']}
alt={thumbnail.alt_text}
/>
{/if}
</div>
{/if}
</a>
@ -39,9 +46,6 @@
text-decoration: none;
.story-text span {
text-decoration: underline;
&.label {
text-decoration: none;
}
}
}
@ -62,14 +66,6 @@
}
}
span.label {
font-size: 14px;
line-height: 1.1;
margin-bottom: 8px;
display: block;
font-weight: 200;
}
time {
@include font-sans;
margin-top: 8px;

View file

@ -31,7 +31,7 @@ dayjs.updateLocale('en', {
},
});
const getTimeZone = (local) => {
const getTimeZone = (local: boolean) => {
if (local) {
return dayjs.tz.guess();
}
@ -39,15 +39,19 @@ const getTimeZone = (local) => {
return 'UTC';
};
const diff = (dateFrom, dateTo, measurement = 'day') => {
const diff = (
dateFrom: Date,
dateTo: number,
measurement: 'day' | 'hour' = 'day'
) => {
return dayjs(dateFrom).diff(dayjs(dateTo), measurement, true);
};
const olderThanHour = (dateFrom, dateTo, hours = 1) => {
const olderThanHour = (dateFrom: Date, dateTo: number, hours = 1) => {
return diff(dateFrom, dateTo, 'hour') < -hours;
};
const isSameDay = (dateFrom, dateTo) => {
const isSameDay = (dateFrom: Date, dateTo: number) => {
const first = new Date(dateFrom);
const second = new Date(dateTo);
return (
@ -57,10 +61,10 @@ const isSameDay = (dateFrom, dateTo) => {
);
};
export const getTime = (datetime) => {
export const getTime = (datetime: dayjs.ConfigType) => {
const publishTime = dayjs(datetime, { utc: true });
const showRelativeTime = !olderThanHour(publishTime, Date.now());
const showTime = isSameDay(publishTime, Date.now());
const showRelativeTime = !olderThanHour(publishTime.toDate(), Date.now());
const showTime = isSameDay(publishTime.toDate(), Date.now());
const timezone = getTimeZone(false);
if (showRelativeTime) {
return dayjs().to(publishTime);

View file

@ -1,20 +1,34 @@
<!-- @migration-task Error while migrating Svelte code: Can't migrate code with afterUpdate. Please migrate by hand. -->
<script>
import { afterUpdate } from 'svelte';
<script lang="ts">
import StoryCard from './StoryCard/index.svelte';
import Spinner from './Spinner/index.svelte';
import { getContext } from 'svelte';
import { getContext, type Snippet } from 'svelte';
import type { Writable } from 'svelte/store';
const activeSection = getContext('nav-active-section');
interface Props {
headingText?: string;
children: Snippet;
}
export let headingText = 'Trending Stories';
let { headingText = 'Trending Stories', children }: Props = $props();
let stories = [];
let lastFetched = null;
const activeSection =
getContext<Writable<null | string>>('nav-active-section');
afterUpdate(async () => {
if (lastFetched === $activeSection) return;
if ($activeSection === 'more') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let stories = $state<any[]>([]);
let lastFetched = $state<null | string>(null);
$effect(() => {
try {
fetchSection($activeSection);
} catch {
console.log('Error fetching articles');
}
});
const fetchSection = async (activeSection: null | string) => {
if (lastFetched === activeSection) return;
if (activeSection === 'more') {
await fetch(
'https://www.reuters.com/pf/api/v3/content/fetch/articles-by-trends-v1?' +
new URLSearchParams({
@ -27,14 +41,17 @@
.then((response) => response.json())
.then((data) => {
stories = data.result.articles;
lastFetched = $activeSection;
lastFetched = activeSection;
})
.catch(() => {
console.log('Error fetching articles');
});
} else {
await fetch(
'https://www.reuters.com/pf/api/v3/content/fetch/recent-stories-by-sections-v1?' +
new URLSearchParams({
query: JSON.stringify({
section_ids: $activeSection,
section_ids: activeSection,
size: 4,
website: 'reuters',
}),
@ -43,10 +60,13 @@
.then((response) => response.json())
.then((data) => {
stories = data.result.articles;
lastFetched = $activeSection;
lastFetched = activeSection;
})
.catch(() => {
console.log('Error fetching articles');
});
}
});
};
</script>
<div class="dropdown">
@ -54,7 +74,7 @@
<div class="inner">
<div class="submenu">
<div class="inner">
<slot />
{@render children?.()}
</div>
</div>
<div class="stories-container">

View file

@ -4,10 +4,12 @@
import MoreDropdown from './NavDropdown/MoreDropdown.svelte';
import { normalizeUrl } from './utils/index';
import { getContext } from 'svelte';
import type { Writable } from 'svelte/store';
let { sections = [] } = $props();
const activeSection = getContext('nav-active-section');
const activeSection =
getContext<Writable<null | string>>('nav-active-section');
let windowWidth = $state(1200);
@ -16,7 +18,7 @@
return 5;
});
let navTimeout = $state();
let navTimeout = $state<ReturnType<typeof setTimeout>>();
const timeout = 250;
let displayCount = $derived(getDisplayCount());

View file

@ -1,2 +1,2 @@
export const normalizeUrl = (url) =>
export const normalizeUrl = (url: string) =>
/^http/.test(url) ? url : `https://www.reuters.com${url}`;

View file

@ -0,0 +1,33 @@
import { Meta } from '@storybook/blocks';
import * as SiteHeaderStories from './SiteHeader.stories.svelte';
<Meta of={SiteHeaderStories} />
# SiteHeader
Reuters dotcom site header, ported from [Raptor UI components](https://github.com/tr/rcom-arc_raptor-ui/tree/develop/packages/rcom-raptor-ui_common/src/components/site-header).
> **Note:** In the Graphics Kit, you can find this component in `pages/+page.svelte`. Customise it there for the default page.
```svelte
<script>
import { SiteHeader } from '@reuters-graphics/graphics-components';
</script>
<SiteHeader />
```
## Dark theme
Colours are customised by the [`Theme`](?path=/docs/theming-theme--default) component. ([Demo](?path=/story/components-page-furniture-siteheader--customised-theme))
```svelte
<script>
import { SiteHeader, Theme } from '@reuters-graphics/graphics-components';
</script>
<Theme base="dark">
<SiteHeader />
</Theme>
```

View file

@ -1,43 +1,24 @@
<script module lang="ts">
// @ts-ignore raw
import componentDocs from './stories/docs/component.md?raw';
// @ts-ignore raw
import darkThemeDocs from './stories/docs/darkTheme.md?raw';
import { defineMeta } from '@storybook/addon-svelte-csf';
import SiteHeader from './SiteHeader.svelte';
import Theme from '../Theme/Theme.svelte';
import {
withComponentDocs,
withStoryDocs,
} from '$lib/docs/utils/withParams.js';
export const meta = {
const { Story } = defineMeta({
title: 'Components/Page furniture/SiteHeader',
component: SiteHeader,
...withComponentDocs(componentDocs),
argsTypes: {
argTypes: {
themes: { control: { disable: true } },
},
};
});
</script>
<script>
import { Template, Story } from '@storybook/addon-svelte-csf';
<Story name="Demo">
<div>
<SiteHeader />
</div>
</Story>
import Theme from '../Theme/Theme.svelte';
</script>
<Template>
{#snippet children({ args })}
<div>
<SiteHeader {...args} />
</div>
{/snippet}
</Template>
<Story name="Default" />
<Story name="Customised theme" {...withStoryDocs(darkThemeDocs)}>
<Story name="Customised theme">
<div>
<Theme base="dark">
<SiteHeader />

View file

@ -8,7 +8,7 @@
import MenuIcon from './svgs/Menu.svelte';
import MobileMenu from './MobileMenu/index.svelte';
setContext('nav-active-section', writable(null));
setContext('nav-active-section', writable<null | string>(null));
let data = $state(starterData);

View file

@ -1,11 +0,0 @@
Reuters dotcom site header, ported from [Raptor UI components](https://github.com/tr/rcom-arc_raptor-ui/tree/develop/packages/rcom-raptor-ui_common/src/components/site-header).
> **Note:** In the Graphics Kit, you can find this component in `pages/+page.svelte`. Customise it there for the default page.
```svelte
<script>
import { SiteHeader } from '@reuters-graphics/graphics-components';
</script>
<SiteHeader />
```

View file

@ -1,11 +0,0 @@
Colours are customised by the [`Theme`](?path=/docs/theming-theme--default) component.
```svelte
<script>
import { SiteHeader, Theme } from '@reuters-graphics/graphics-components';
</script>
<Theme base="dark">
<SiteHeader />
</Theme>
```