Skip to content

Commit c2bb12f

Browse files
authored
feat: added renderer for badges (#6213)
* feat: added renderer for badges * refactor: refactored banner components and some optimizations * fix: unit test * fix: types
1 parent 132032b commit c2bb12f

File tree

11 files changed

+99
-53
lines changed

11 files changed

+99
-53
lines changed

components/Common/Banner/index.stories.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,31 @@ type Meta = MetaObj<typeof Banner>;
77

88
export const Default: Story = {
99
args: {
10-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
10+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
1111
type: 'default',
12-
url: 'https://github.com/openjs-foundation/summit/issues/360',
12+
link: 'https://github.com/openjs-foundation/summit/issues/360',
1313
},
1414
};
1515

1616
export const Error: Story = {
1717
args: {
18-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
18+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
1919
type: 'error',
20-
url: 'https://github.com/nodejs/nodejs.org/issues/4495',
20+
link: 'https://github.com/nodejs/nodejs.org/issues/4495',
2121
},
2222
};
2323

2424
export const Warning: Story = {
2525
args: {
26-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
26+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
2727
type: 'warning',
28-
url: 'https://github.com/nodejs/nodejs.org/issues/4495',
28+
link: 'https://github.com/nodejs/nodejs.org/issues/4495',
2929
},
3030
};
3131

3232
export const NoLink: Story = {
3333
args: {
34-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
34+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
3535
type: 'default',
3636
},
3737
};

components/Common/Banner/index.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import { ArrowUpRightIcon } from '@heroicons/react/24/outline';
2-
import type { FC } from 'react';
2+
import type { FC, PropsWithChildren } from 'react';
33

44
import Link from '@/components/Link';
55

66
import styles from './index.module.css';
77

88
type BannerProps = {
9-
type: 'default' | 'error' | 'warning';
10-
text: string;
11-
url?: string;
9+
link?: string;
10+
type?: 'default' | 'warning' | 'error';
1211
};
1312

14-
const Banner: FC<BannerProps> = ({ type, text, url = '' }) => (
13+
const Banner: FC<PropsWithChildren<BannerProps>> = ({
14+
type = 'default',
15+
link,
16+
children,
17+
}) => (
1518
<div className={`${styles.banner} ${styles[type] || styles.default}`}>
16-
{(url.length > 0 && <Link href={url}>{text}</Link>) || text}
17-
{url.length > 0 && <ArrowUpRightIcon />}
19+
{link ? <Link href={link}>{children}</Link> : children}
20+
{link && <ArrowUpRightIcon />}
1821
</div>
1922
);
2023

components/withBadge.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { FC } from 'react';
2+
3+
import Badge from '@/components/Common/Badge';
4+
import { siteConfig } from '@/next.json.mjs';
5+
import { dateIsBetween } from '@/util/dateIsBetween';
6+
7+
const WithBadge: FC<{ section: string }> = ({ section }) => {
8+
const badge = siteConfig.websiteBadges[section];
9+
10+
if (badge && dateIsBetween(badge.startDate, badge.endDate)) {
11+
return (
12+
<Badge badgeText={badge.title} kind={badge.kind} href={badge.link}>
13+
{badge.text}
14+
</Badge>
15+
);
16+
}
17+
18+
return null;
19+
};
20+
21+
export default WithBadge;

components/withBanner.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { FC } from 'react';
2+
3+
import Banner from '@/components/Common/Banner';
4+
import { siteConfig } from '@/next.json.mjs';
5+
import { dateIsBetween } from '@/util/dateIsBetween';
6+
7+
const WithBanner: FC<{ section: string }> = ({ section }) => {
8+
const banner = siteConfig.websiteBanners[section];
9+
10+
if (banner && dateIsBetween(banner.startDate, banner.endDate)) {
11+
return (
12+
<Banner type={banner.type} link={banner.link}>
13+
{banner.text}
14+
</Banner>
15+
);
16+
}
17+
18+
return null;
19+
};
20+
21+
export default WithBanner;

components/withNavBar.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useTheme } from 'next-themes';
55
import type { FC } from 'react';
66

77
import NavBar from '@/components/Containers/NavBar';
8+
import WithBanner from '@/components/withBanner';
89
import { useClientContext, useSiteNavigation } from '@/hooks';
910
import { useRouter } from '@/navigation.mjs';
1011
import { availableLocales } from '@/next.locales.mjs';
@@ -21,18 +22,22 @@ const WithNavBar: FC = () => {
2122
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
2223

2324
return (
24-
<NavBar
25-
onThemeTogglerClick={toggleCurrnetTheme}
26-
languages={{
27-
currentLanguage: locale,
28-
availableLanguages: availableLocales,
29-
onChange: locale => replace(pathname!, { locale: locale.code }),
30-
}}
31-
navItems={navigationItems.map(([, { label, link }]) => ({
32-
link,
33-
text: label,
34-
}))}
35-
/>
25+
<>
26+
<WithBanner section="index" />
27+
28+
<NavBar
29+
onThemeTogglerClick={toggleCurrnetTheme}
30+
languages={{
31+
currentLanguage: locale,
32+
availableLanguages: availableLocales,
33+
onChange: locale => replace(pathname!, { locale: locale.code }),
34+
}}
35+
navItems={navigationItems.map(([, { label, link }]) => ({
36+
link,
37+
text: label,
38+
}))}
39+
/>
40+
</>
3641
);
3742
};
3843

site.json

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,8 @@
2525
"category": "vulnerability"
2626
}
2727
],
28-
"websiteBanners": {
29-
"index": {
30-
"startDate": "2023-11-26T00:00:00.000Z",
31-
"endDate": "2023-12-05T00:00:00.000Z",
32-
"link": "https://training.linuxfoundation.org/cyber-monday-js-2023/?utm_source=openjsf&utm_medium=homepage-ticker&utm_campaign=cybermonday23",
33-
"html": "<img src='https://i.imgur.com/8FgdVOy.png' alt='Node.js Training Banner' style='margin:0 auto;border-radius:4px;' />"
34-
}
35-
},
28+
"websiteBanners": {},
29+
"websiteBadges": {},
3630
"footerLinks": [
3731
{
3832
"link": "https://openjsf.org/wp-content/uploads/sites/84/2021/01/OpenJS-Foundation-Trademark-Policy-2021-01-12.docx.pdf",

types/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RSSFeed, WebsiteBanner } from './features';
1+
import type { RSSFeed, WebsiteBadge, WebsiteBanner } from './features';
22

33
export interface TwitterConfig {
44
username: string;
@@ -34,6 +34,7 @@ export interface SiteConfig {
3434
twitter: TwitterConfig;
3535
rssFeeds: Array<RSSFeed>;
3636
websiteBanners: Record<string, WebsiteBanner>;
37+
websiteBadges: Record<string, WebsiteBadge>;
3738
footerLinks: Array<FooterConfig>;
3839
socialLinks: Array<SocialConfig>;
3940
}

types/features.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@ export interface RSSFeed {
55
blogCategory?: string;
66
}
77

8-
export interface WebsiteBanner {
8+
interface WithRange {
99
startDate: string;
1010
endDate: string;
11-
text?: string;
11+
}
12+
13+
export interface WebsiteBanner extends WithRange {
14+
text: string;
15+
link?: string;
16+
/** @deprecated the html field is unsupported on the website redesign */
1217
html?: string;
18+
type?: 'default' | 'warning' | 'error';
19+
}
20+
21+
export interface WebsiteBadge extends WithRange {
22+
text: string;
1323
link: string;
24+
title?: string;
25+
kind?: 'default' | 'warning' | 'error';
1426
}

types/releases.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
export interface UpcomingReleaseData {
2-
releaseDate: string;
3-
releaseType: 'Current' | 'LTS' | 'Maintenance' | 'End-of-life';
4-
alreadyReleased: boolean;
5-
}
6-
7-
export interface UpcomingRelease {
8-
name: string;
9-
releases: UpcomingReleaseData[];
10-
}
11-
121
export type NodeReleaseStatus =
132
| 'Maintenance LTS'
143
| 'Active LTS'

util/__tests__/dateIsBetween.test.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ describe('dateIsBetween', () => {
1919
expect(result).toBe(false);
2020
});
2121

22-
it('returns false when either start or end date is invalid', () => {
22+
it('throws when either start or end date is invalid', () => {
2323
const invalidStartDate = 'Invalid Start Date';
2424
const validEndDate = '2024-01-01T00:00:00.000Z';
2525

26-
const result = dateIsBetween(invalidStartDate, validEndDate);
27-
28-
expect(result).toBe(false);
26+
expect(() => dateIsBetween(invalidStartDate, validEndDate)).toThrow(
27+
'dateIsBetween got called with invalid dates'
28+
);
2929
});
3030
});

util/dateIsBetween.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const dateIsBetween = (startDate: string, endDate: string): boolean => {
44
const end = new Date(endDate);
55

66
if ([start.toString(), end.toString()].includes(invalidDateStr)) {
7-
return false;
7+
throw new Error('dateIsBetween got called with invalid dates');
88
}
99

1010
const now = new Date();

0 commit comments

Comments
 (0)