Skip to content

Commit 21ac163

Browse files
authored
Merge pull request #10566 from growilabs/imprv/next-link-target-attribute
imprv: Support target attribute for anchor links
2 parents c00b2fc + d1e2c87 commit 21ac163

File tree

2 files changed

+21
-13
lines changed

2 files changed

+21
-13
lines changed

apps/app/src/components/ReactMarkdownComponents/NextLink.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { JSX } from 'react';
1+
import type { AnchorHTMLAttributes, JSX } from 'react';
22
import type { LinkProps } from 'next/link';
33
import Link from 'next/link';
44
import { pagePathUtils } from '@growi/core/dist/utils';
@@ -8,7 +8,7 @@ import loggerFactory from '~/utils/logger';
88

99
const logger = loggerFactory('growi:components:NextLink');
1010

11-
const isAnchorLink = (href: string): boolean => {
11+
const hasAnchorLink = (href: string): boolean => {
1212
return href.toString().length > 0 && href[0] === '#';
1313
};
1414

@@ -34,15 +34,16 @@ const isCreatablePage = (href: string) => {
3434
}
3535
};
3636

37-
type Props = Omit<LinkProps, 'href'> & {
38-
children: React.ReactNode;
39-
id?: string;
40-
href?: string;
41-
className?: string;
42-
};
37+
type Props = AnchorHTMLAttributes<HTMLAnchorElement> &
38+
Omit<LinkProps, 'href'> & {
39+
children: React.ReactNode;
40+
id?: string;
41+
href?: string;
42+
className?: string;
43+
};
4344

4445
export const NextLink = (props: Props): JSX.Element => {
45-
const { id, href, children, className, onClick, ...rest } = props;
46+
const { id, href, children, className, target, onClick, ...rest } = props;
4647

4748
const siteUrl = useSiteUrl();
4849

@@ -56,7 +57,7 @@ export const NextLink = (props: Props): JSX.Element => {
5657
Object.entries(rest).filter(([key]) => key.startsWith('data-')),
5758
);
5859

59-
if (isExternalLink(href, siteUrl)) {
60+
if (isExternalLink(href, siteUrl) || target === '_blank') {
6061
return (
6162
<a
6263
id={id}
@@ -67,19 +68,25 @@ export const NextLink = (props: Props): JSX.Element => {
6768
rel="noopener noreferrer"
6869
{...dataAttributes}
6970
>
70-
{children}&nbsp;
71-
<span className="growi-custom-icons">external_link</span>
71+
{children}
72+
{target === '_blank' && (
73+
<span style={{ userSelect: 'none' }}>
74+
&nbsp;
75+
<span className="growi-custom-icons">external_link</span>
76+
</span>
77+
)}
7278
</a>
7379
);
7480
}
7581

7682
// when href is an anchor link or not-creatable path
77-
if (isAnchorLink(href) || !isCreatablePage(href)) {
83+
if (hasAnchorLink(href) || !isCreatablePage(href) || target != null) {
7884
return (
7985
<a
8086
id={id}
8187
href={href}
8288
className={className}
89+
target={target}
8390
onClick={onClick}
8491
{...dataAttributes}
8592
>

apps/app/src/services/renderer/recommended-whitelist.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const tagNames: Array<string> = [
5959
];
6060

6161
export const attributes: Attributes = deepmerge(relaxedSchemaAttributes, {
62+
a: ['target'],
6263
iframe: ['allow', 'referrerpolicy', 'sandbox', 'src'],
6364
video: ['controls', 'src', 'muted', 'preload', 'width', 'height', 'autoplay'],
6465
// The special value 'data*' as a property name can be used to allow all data properties.

0 commit comments

Comments
 (0)