Skip to content

Commit 14acab3

Browse files
authored
fix: open external link in new tab for navbar items (#6487)
fix: set link in new tab for navbar items
1 parent dd983af commit 14acab3

File tree

6 files changed

+29
-10
lines changed

6 files changed

+29
-10
lines changed

components/Containers/NavBar/NavItem/index.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ArrowUpRightIcon } from '@heroicons/react/24/solid';
22
import classNames from 'classnames';
3-
import type { FC, PropsWithChildren } from 'react';
3+
import type { FC, HTMLAttributeAnchorTarget, PropsWithChildren } from 'react';
44

55
import ActiveLink from '@/components/Common/ActiveLink';
66

@@ -12,23 +12,26 @@ type NavItemProps = {
1212
href: string;
1313
type?: NavItemType;
1414
className?: string;
15+
target?: HTMLAttributeAnchorTarget | undefined;
1516
};
1617

1718
const NavItem: FC<PropsWithChildren<NavItemProps>> = ({
1819
href = '',
1920
type = 'nav',
2021
children,
2122
className,
23+
target,
2224
}) => (
2325
<ActiveLink
2426
href={href}
2527
className={classNames(styles.navItem, styles[type], className)}
2628
activeClassName={styles.active}
2729
allowSubPath={href.startsWith('/')}
30+
target={target}
2831
>
2932
<span className={styles.label}>{children}</span>
3033

31-
{type === 'nav' && href.startsWith('http') && (
34+
{((type === 'nav' && href.startsWith('http')) || target === '_blank') && (
3235
<ArrowUpRightIcon className={styles.icon} />
3336
)}
3437
</ActiveLink>

components/Containers/NavBar/index.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Hamburger from '@heroicons/react/24/solid/Bars3Icon';
44
import XMark from '@heroicons/react/24/solid/XMarkIcon';
55
import * as Label from '@radix-ui/react-label';
66
import { useState } from 'react';
7-
import type { FC, ComponentProps } from 'react';
7+
import type { FC, ComponentProps, HTMLAttributeAnchorTarget } from 'react';
88

99
import LanguageDropdown from '@/components/Common/LanguageDropDown';
1010
import { SearchButton } from '@/components/Common/Search';
@@ -24,7 +24,11 @@ const navInteractionIcons = {
2424
};
2525

2626
type NavbarProps = {
27-
navItems: Array<{ text: FormattedMessage; link: string }>;
27+
navItems: Array<{
28+
text: FormattedMessage;
29+
link: string;
30+
target?: HTMLAttributeAnchorTarget | undefined;
31+
}>;
2832
languages: ComponentProps<typeof LanguageDropdown>;
2933
onThemeTogglerClick: () => void;
3034
};
@@ -57,8 +61,8 @@ const NavBar: FC<NavbarProps> = ({
5761

5862
<div className={`${style.main} peer-checked:flex`}>
5963
<div className={style.navItems}>
60-
{navItems.map(({ text, link }) => (
61-
<NavItem key={link} href={link}>
64+
{navItems.map(({ text, link, target }) => (
65+
<NavItem key={link} href={link} target={target}>
6266
{text}
6367
</NavItem>
6468
))}

components/withNavBar.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ const WithNavBar: FC = () => {
3232
availableLanguages: availableLocales,
3333
onChange: locale => replace(pathname!, { locale: locale.code }),
3434
}}
35-
navItems={navigationItems.map(([, { label, link }]) => ({
35+
navItems={navigationItems.map(([, { label, link, target }]) => ({
3636
link,
3737
text: label,
38+
target,
3839
}))}
3940
/>
4041
</div>

hooks/react-generic/useSiteNavigation.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useTranslations } from 'next-intl';
22
import type { RichTranslationValues } from 'next-intl';
3+
import type { HTMLAttributeAnchorTarget } from 'react';
34

45
import { siteNavigation } from '@/next.json.mjs';
56
import type {
@@ -15,6 +16,7 @@ interface MappedNavigationEntry {
1516
items: Array<[string, MappedNavigationEntry]>;
1617
label: FormattedMessage;
1718
link: string;
19+
target?: HTMLAttributeAnchorTarget | undefined;
1820
}
1921

2022
// Provides Context replacement for variables within the Link. This is also something that is not going
@@ -40,9 +42,13 @@ const useSiteNavigation = () => {
4042
t.rich(label, context[key] || {});
4143

4244
return Object.entries(entries).map(
43-
([key, { label, link, items }]): [string, MappedNavigationEntry] => [
45+
([key, { label, link, items, target }]): [
46+
string,
47+
MappedNavigationEntry,
48+
] => [
4449
key,
4550
{
51+
target,
4652
label: label ? getFormattedMessage(label, key) : '',
4753
link: link ? replaceLinkWithContext(link, context[key]) : '',
4854
items: items ? mapNavigationEntries(items, context) : [],

navigation.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
},
1919
"docs": {
2020
"link": "https://nodejs.org/docs/latest/api/",
21-
"label": "components.containers.navBar.links.docs"
21+
"label": "components.containers.navBar.links.docs",
22+
"target": "_blank"
2223
},
2324
"certification": {
2425
"link": "https://openjsf.org/certification",
25-
"label": "components.containers.navBar.links.certification"
26+
"label": "components.containers.navBar.links.certification",
27+
"target": "_blank"
2628
}
2729
},
2830
"footerLinks": [

types/navigation.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { HTMLAttributeAnchorTarget } from 'react';
2+
13
export interface FooterConfig {
24
text: string;
35
link: string;
@@ -22,6 +24,7 @@ export interface NavigationEntry {
2224
label?: string;
2325
link?: string;
2426
items?: Record<string, NavigationEntry>;
27+
target?: HTMLAttributeAnchorTarget | undefined;
2528
}
2629

2730
export interface SiteNavigation {

0 commit comments

Comments
 (0)