This commit is contained in:
Andrew Lyons 2025-07-15 15:55:04 +02:00 committed by GitHub
commit b484c5212c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 56 additions and 2 deletions

View file

@ -169,6 +169,14 @@ export type DocusaurusConfig = {
* @default "warn"
*/
onDuplicateRoutes: ReportingSeverity;
/**
* This option controls whether external links (links are not for the same site)
* open in a new tab or not.
*
* @see https://docusaurus.io/docs/api/docusaurus-config#openExternalLinksInNewTab
* @default true
*/
openExternalLinksInNewTab: boolean;
/**
* The tagline for your website.
*

View file

@ -42,7 +42,7 @@ function Link(
forwardedRef: React.ForwardedRef<HTMLAnchorElement>,
): ReactNode {
const {siteConfig} = useDocusaurusContext();
const {trailingSlash, baseUrl} = siteConfig;
const {trailingSlash, baseUrl, openExternalLinksInNewTab} = siteConfig;
const router = siteConfig.future.experimental_router;
const {withBaseUrl} = useBaseUrlUtils();
const brokenLinks = useBrokenLinks();
@ -188,7 +188,11 @@ function Link(
ref={innerRef}
href={targetLink}
{...(targetLinkUnprefixed &&
!isInternal && {target: '_blank', rel: 'noopener noreferrer'})}
!isInternal &&
openExternalLinksInNewTab && {
target: '_blank',
rel: 'noopener noreferrer',
})}
{...props}
{...testOnlyProps}
/>

View file

@ -19,6 +19,7 @@ type Options = {
baseUrl: string;
router: DocusaurusContext['siteConfig']['future']['experimental_router'];
currentLocation: string;
openExternalLinksInNewTab: boolean;
};
const defaultOptions: Options = {
@ -27,6 +28,7 @@ const defaultOptions: Options = {
router: 'browser',
// currentLocation is nested on purpose, shows relative link resolution
currentLocation: '/sub/category/currentPathname',
openExternalLinksInNewTab: true,
};
function createDocusaurusContext(
@ -40,6 +42,7 @@ function createDocusaurusContext(
future: {
experimental_router: options.router,
},
openExternalLinksInNewTab: options.openExternalLinksInNewTab,
},
});
}
@ -238,6 +241,19 @@ describe('<Link>', () => {
`);
});
it("can render 'https://example.com/xyz' without opening to a new window", () => {
expect(
render(<Link to="https://example.com/xyz" />, {
openExternalLinksInNewTab: false,
}),
).toMatchInlineSnapshot(`
<a
data-test-link-type="regular"
href="https://example.com/xyz"
/>
`);
});
it("can render 'pathname:///docs/intro'", () => {
expect(render(<Link to="pathname:///docs/intro" />))
.toMatchInlineSnapshot(`

View file

@ -61,6 +61,7 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -140,6 +141,7 @@ exports[`loadSiteConfig website with ts + js config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -219,6 +221,7 @@ exports[`loadSiteConfig website with valid JS CJS config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -298,6 +301,7 @@ exports[`loadSiteConfig website with valid JS ESM config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -377,6 +381,7 @@ exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -456,6 +461,7 @@ exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],
@ -535,6 +541,7 @@ exports[`loadSiteConfig website with valid async config 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"organizationName": "endiliey",
"plugins": [],
"presets": [],
@ -616,6 +623,7 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"organizationName": "endiliey",
"plugins": [],
"presets": [],
@ -697,6 +705,7 @@ exports[`loadSiteConfig website with valid config creator function 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"organizationName": "endiliey",
"plugins": [],
"presets": [],
@ -781,6 +790,7 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"organizationName": "endiliey",
"plugins": [
[

View file

@ -147,6 +147,7 @@ exports[`load loads props for site 1`] = `
"onBrokenAnchors": "warn",
"onBrokenLinks": "throw",
"onDuplicateRoutes": "warn",
"openExternalLinksInNewTab": true,
"plugins": [],
"presets": [],
"scripts": [],

View file

@ -114,6 +114,7 @@ export const DEFAULT_CONFIG: Pick<
| 'onBrokenAnchors'
| 'onBrokenMarkdownLinks'
| 'onDuplicateRoutes'
| 'openExternalLinksInNewTab'
| 'plugins'
| 'themes'
| 'presets'
@ -136,6 +137,7 @@ export const DEFAULT_CONFIG: Pick<
onBrokenAnchors: 'warn', // TODO Docusaurus v4: change to throw
onBrokenMarkdownLinks: undefined,
onDuplicateRoutes: 'warn',
openExternalLinksInNewTab: true,
plugins: [],
themes: [],
presets: [],
@ -360,6 +362,9 @@ export const ConfigSchema = Joi.object<DocusaurusConfig>({
onDuplicateRoutes: Joi.string()
.equal('ignore', 'log', 'warn', 'throw')
.default(DEFAULT_CONFIG.onDuplicateRoutes),
openExternalLinksInNewTab: Joi.boolean().default(
DEFAULT_CONFIG.openExternalLinksInNewTab,
),
organizationName: Joi.string().allow(''),
staticDirectories: Joi.array()
.items(Joi.string())

View file

@ -413,6 +413,8 @@ These links are broken (try to single click on them) and should be reported. We
- <TestLink href="/dogfooding/javadoc#badlink2" noCheck />
- <TestLink to="/dogfooding/javadoc#badlink3" target="_self" noCheck />
- <TestLink href="/dogfooding/javadoc#badlink4" target="_self" noCheck />
- <TestLink to="https://example.com/fail" noCheck />
- <TestLink href="https://example.com/fail" noCheck />
### Linking to JSON

View file

@ -292,6 +292,14 @@ The behavior of Docusaurus when it detects any broken Markdown link.
By default, it prints a warning, to let you know about your broken Markdown link.
### `openExternalLinksInNewTab` {#openExternalLinksInNewTab}
- Type: `boolean`
When enabled, Docusaurus will automatically add `target="_blank"` to all external links, so that they open in a new tab.
By default, external links are opened in a new tab.
### `onDuplicateRoutes` {#onDuplicateRoutes}
- Type: `'ignore' | 'log' | 'warn' | 'throw'`