diff --git a/src/frame/components/Link.tsx b/src/frame/components/Link.tsx index 166904b0d16e..cec0441dbda8 100644 --- a/src/frame/components/Link.tsx +++ b/src/frame/components/Link.tsx @@ -1,36 +1,52 @@ +import { useRouter } from 'next/router' import NextLink from 'next/link' import { ComponentProps } from 'react' +import { DEFAULT_VERSION, useVersion } from 'src/versions/components/useVersion' + const { NODE_ENV } = process.env -type Props = { locale?: string; disableClientTransition?: boolean } & ComponentProps<'a'> +type Props = { + locale?: string + disableClientTransition?: boolean + makeAbsolute?: boolean +} & ComponentProps<'a'> export function Link(props: Props) { - const { href, locale, disableClientTransition = false, ...restProps } = props + const { + href, + locale, + disableClientTransition = false, + makeAbsolute = false, + ...restProps + } = props + const router = useRouter() + const { currentVersion } = useVersion() if (!href && NODE_ENV !== 'production') { console.warn('Missing href on Link') } - const isExternal = href?.startsWith('http') || href?.startsWith('//') + let url = href || '' + const isExternal = url.startsWith('http') || url.startsWith('//') + if (makeAbsolute && !isExternal) { + url = `/${locale || router.locale}` + if (currentVersion !== DEFAULT_VERSION) { + url += `/${currentVersion}` + } + url += href + } else if (locale && !isExternal) { + url = `/${locale}${href}` + } if (disableClientTransition) { return ( /* eslint-disable-next-line jsx-a11y/anchor-has-content */ - + ) } return ( - + {/* eslint-disable-next-line jsx-a11y/anchor-has-content */} diff --git a/src/graphql/components/Interface.tsx b/src/graphql/components/Interface.tsx index 980b605978c5..97dd13fc69d3 100644 --- a/src/graphql/components/Interface.tsx +++ b/src/graphql/components/Interface.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { GraphqlItem } from './GraphqlItem' import { Table } from './Table' @@ -12,7 +10,6 @@ type Props = { } export function Interface({ item, objects }: Props) { - const { locale } = useRouter() const { t } = useTranslation('graphql') const heading = t('reference.implemented_by').replace('{{ GraphQLItemTitle }}', item.name) const heading2 = t('reference.fields').replace('{{ GraphQLItemTitle }}', item.name) @@ -29,7 +26,7 @@ export function Interface({ item, objects }: Props) { {implementedBy.map((object) => (
  • - + {object.name} diff --git a/src/graphql/components/Mutation.tsx b/src/graphql/components/Mutation.tsx index 7f763e29fc62..64af7735e776 100644 --- a/src/graphql/components/Mutation.tsx +++ b/src/graphql/components/Mutation.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { GraphqlItem } from './GraphqlItem' import { Notice } from './Notice' @@ -13,7 +11,6 @@ type Props = { } export function Mutation({ item }: Props) { - const { locale } = useRouter() const { t } = useTranslation('graphql') const heading = t('reference.input_fields').replace('{{ GraphQLItemTitle }}', item.name) const heading2 = t('reference.return_fields').replace('{{ GraphQLItemTitle }}', item.name) @@ -26,7 +23,7 @@ export function Mutation({ item }: Props) {
  • {input.name} ( - + {input.type} diff --git a/src/graphql/components/Notice.tsx b/src/graphql/components/Notice.tsx index 745ef8eb407d..e9ed802c5e65 100644 --- a/src/graphql/components/Notice.tsx +++ b/src/graphql/components/Notice.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { useTranslation } from 'src/languages/components/useTranslation' import type { GraphqlT } from './types' @@ -10,8 +8,6 @@ type Props = { } export function Notice({ item, variant = 'preview' }: Props) { - const { locale } = useRouter() - const { t } = useTranslation('graphql') const previewTitle = variant === 'preview' ? t('reference.preview_notice') : t('reference.deprecation_notice') @@ -24,7 +20,7 @@ export function Notice({ item, variant = 'preview' }: Props) { {variant === 'preview' && item.preview ? (

    {item.name} is available under the{' '} - + {item.preview.title} . {t('reference.preview_period')} diff --git a/src/graphql/components/Object.tsx b/src/graphql/components/Object.tsx index ae44053c3a62..d89d6e42c1b5 100644 --- a/src/graphql/components/Object.tsx +++ b/src/graphql/components/Object.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { GraphqlItem } from './GraphqlItem' import { Table } from './Table' @@ -11,7 +9,6 @@ type Props = { } export function Object({ item }: Props) { - const { locale } = useRouter() const { t } = useTranslation('graphql') const heading1 = t('reference.implements').replace('{{ GraphQLItemTitle }}', item.name) const heading2 = t('reference.fields').replace('{{ GraphQLItemTitle }}', item.name) @@ -25,7 +22,7 @@ export function Object({ item }: Props) { {item.implements.map((implement: ImplementsT) => (

  • - + {implement.name} diff --git a/src/graphql/components/Query.tsx b/src/graphql/components/Query.tsx index def2142f563d..e65abcfc943c 100644 --- a/src/graphql/components/Query.tsx +++ b/src/graphql/components/Query.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { GraphqlItem } from './GraphqlItem' import { Table } from './Table' @@ -11,7 +9,6 @@ type Props = { } export function Query({ item }: Props) { - const { locale } = useRouter() const { t } = useTranslation('graphql') return ( @@ -19,7 +16,7 @@ export function Query({ item }: Props) {

    {t('graphql.reference.type')}: - + {item.type}

    diff --git a/src/graphql/components/Table.tsx b/src/graphql/components/Table.tsx index 75618fe077e4..46ae3bf4a5ca 100644 --- a/src/graphql/components/Table.tsx +++ b/src/graphql/components/Table.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { Notice } from './Notice' import { useTranslation } from 'src/languages/components/useTranslation' @@ -10,8 +8,6 @@ type Props = { } export function Table({ fields }: Props) { - const { locale } = useRouter() - const { t } = useTranslation('graphql') const tableName = t('reference.name') const tableDescription = t('reference.description') @@ -33,7 +29,7 @@ export function Table({ fields }: Props) { {field.type} @@ -80,7 +76,7 @@ export function Table({ fields }: Props) {

    {argument.name} ( - + {argument.type.name} diff --git a/src/graphql/components/Union.tsx b/src/graphql/components/Union.tsx index 44efea0aa533..2f44084c6009 100644 --- a/src/graphql/components/Union.tsx +++ b/src/graphql/components/Union.tsx @@ -1,5 +1,3 @@ -import { useRouter } from 'next/router' - import { Link } from 'src/frame/components/Link' import { GraphqlItem } from './GraphqlItem' import { useTranslation } from 'src/languages/components/useTranslation' @@ -10,7 +8,6 @@ type Props = { } export function Union({ item }: Props) { - const { locale } = useRouter() const { t } = useTranslation('graphql') const heading = t('reference.possible_types').replace('{{ GraphQLItemTitle }}', item.name) @@ -19,7 +16,7 @@ export function Union({ item }: Props) {

      {item.possibleTypes.map((type) => (
    • - + {type.name}
    • diff --git a/src/graphql/tests/server-rendering.js b/src/graphql/tests/server-rendering.js index bc9dded7931e..991fec9c5d74 100644 --- a/src/graphql/tests/server-rendering.js +++ b/src/graphql/tests/server-rendering.js @@ -1,4 +1,7 @@ import { getDOM } from '#src/tests/helpers/e2etest.js' +import { loadPages } from '#src/frame/lib/page-data.js' + +const pageList = await loadPages(undefined, ['en']) describe('server rendering certain GraphQL pages', () => { test('minitoc hrefs on breaking-changes match', async () => { @@ -16,4 +19,31 @@ describe('server rendering certain GraphQL pages', () => { } expect.assertions(hrefs.length + 1) }) + + const autogeneratedPages = pageList.filter( + (page) => page.autogenerated === 'graphql' && page.relativePath.includes('reference'), + ) + const nonFPTPermalinks = autogeneratedPages + .map((page) => + page.permalinks.find((permalink) => permalink.pageVersion !== 'free-pro-team@latest'), + ) + .filter(Boolean) + const nonFPTPermalinksHrefs = nonFPTPermalinks.map((permalink) => { + return permalink.href + }) + + test.each(nonFPTPermalinksHrefs)( + 'all links keep locale and version in %s', + async (permalinkHref) => { + const $ = await getDOM(permalinkHref) + const internalLinks = $('#article-contents a[href^="/"]') + const hrefs = internalLinks.map((i, link) => $(link).attr('href')).get() + const [, pageLocale, pageVersion] = permalinkHref.split('/') + for (const href of hrefs) { + const [, locale, version] = href.split('/') + expect(locale).toBe(pageLocale) + expect(version).toBe(pageVersion) + } + }, + ) })