+7
-7
components/Common/Banner/index.stories.tsx
+7
-7
components/Common/Banner/index.stories.tsx
···
7
7
8
8
export const Default: Story = {
9
9
args: {
10
-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
10
+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
11
11
type: 'default',
12
-
url: 'https://github.com/openjs-foundation/summit/issues/360',
12
+
link: 'https://github.com/openjs-foundation/summit/issues/360',
13
13
},
14
14
};
15
15
16
16
export const Error: Story = {
17
17
args: {
18
-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
18
+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
19
19
type: 'error',
20
-
url: 'https://github.com/nodejs/nodejs.org/issues/4495',
20
+
link: 'https://github.com/nodejs/nodejs.org/issues/4495',
21
21
},
22
22
};
23
23
24
24
export const Warning: Story = {
25
25
args: {
26
-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
26
+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
27
27
type: 'warning',
28
-
url: 'https://github.com/nodejs/nodejs.org/issues/4495',
28
+
link: 'https://github.com/nodejs/nodejs.org/issues/4495',
29
29
},
30
30
};
31
31
32
32
export const NoLink: Story = {
33
33
args: {
34
-
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
34
+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
35
35
type: 'default',
36
36
},
37
37
};
+10
-7
components/Common/Banner/index.tsx
+10
-7
components/Common/Banner/index.tsx
···
1
1
import { ArrowUpRightIcon } from '@heroicons/react/24/outline';
2
-
import type { FC } from 'react';
2
+
import type { FC, PropsWithChildren } from 'react';
3
3
4
4
import Link from '@/components/Link';
5
5
6
6
import styles from './index.module.css';
7
7
8
8
type BannerProps = {
9
-
type: 'default' | 'error' | 'warning';
10
-
text: string;
11
-
url?: string;
9
+
link?: string;
10
+
type?: 'default' | 'warning' | 'error';
12
11
};
13
12
14
-
const Banner: FC<BannerProps> = ({ type, text, url = '' }) => (
13
+
const Banner: FC<PropsWithChildren<BannerProps>> = ({
14
+
type = 'default',
15
+
link,
16
+
children,
17
+
}) => (
15
18
<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 />}
18
21
</div>
19
22
);
20
23
+21
components/withBadge.tsx
+21
components/withBadge.tsx
···
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;
+21
components/withBanner.tsx
+21
components/withBanner.tsx
···
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;
+2
-8
site.json
+2
-8
site.json
···
25
25
"category": "vulnerability"
26
26
}
27
27
],
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": {},
36
30
"footerLinks": [
37
31
{
38
32
"link": "https://openjsf.org/wp-content/uploads/sites/84/2021/01/OpenJS-Foundation-Trademark-Policy-2021-01-12.docx.pdf",
+2
-1
types/config.ts
+2
-1
types/config.ts
···
1
-
import type { RSSFeed, WebsiteBanner } from './features';
1
+
import type { RSSFeed, WebsiteBadge, WebsiteBanner } from './features';
2
2
3
3
export interface TwitterConfig {
4
4
username: string;
···
34
34
twitter: TwitterConfig;
35
35
rssFeeds: Array<RSSFeed>;
36
36
websiteBanners: Record<string, WebsiteBanner>;
37
+
websiteBadges: Record<string, WebsiteBadge>;
37
38
footerLinks: Array<FooterConfig>;
38
39
socialLinks: Array<SocialConfig>;
39
40
}
+14
-2
types/features.ts
+14
-2
types/features.ts
···
5
5
blogCategory?: string;
6
6
}
7
7
8
-
export interface WebsiteBanner {
8
+
interface WithRange {
9
9
startDate: string;
10
10
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 */
12
17
html?: string;
18
+
type?: 'default' | 'warning' | 'error';
19
+
}
20
+
21
+
export interface WebsiteBadge extends WithRange {
22
+
text: string;
13
23
link: string;
24
+
title?: string;
25
+
kind?: 'default' | 'warning' | 'error';
14
26
}
-11
types/releases.ts
-11
types/releases.ts
···
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
-
12
1
export type NodeReleaseStatus =
13
2
| 'Maintenance LTS'
14
3
| 'Active LTS'
+4
-4
util/__tests__/dateIsBetween.test.mjs
+4
-4
util/__tests__/dateIsBetween.test.mjs
···
19
19
expect(result).toBe(false);
20
20
});
21
21
22
-
it('returns false when either start or end date is invalid', () => {
22
+
it('throws when either start or end date is invalid', () => {
23
23
const invalidStartDate = 'Invalid Start Date';
24
24
const validEndDate = '2024-01-01T00:00:00.000Z';
25
25
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
+
);
29
29
});
30
30
});