Picker improvements (#21765)
* close Language and ArticleVersion pickers after click * cleanup ArticleGridLayout due to VersionPicker changes * fix tsc errors resulting from primer upgrade * fix / update tests * cleanup mobile pickers visual consistency * use btn-sm on VersionPicker * update translation and close on click for enterprise releases Co-authored-by: Kevin Heis <heiskr@users.noreply.github.com>
This commit is contained in:
@@ -37,7 +37,7 @@ export function Link(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextLink href={href || ''} locale={locale || false}>
|
<NextLink href={locale ? `/${locale}${href}` : href || ''} locale={locale || false}>
|
||||||
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
||||||
<a rel={isExternal ? 'noopener' : ''} {...restProps} />
|
<a rel={isExternal ? 'noopener' : ''} {...restProps} />
|
||||||
</NextLink>
|
</NextLink>
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export function Search({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{results.length > 0 ? (
|
{results.length > 0 ? (
|
||||||
<ol data-testid="search-results" className="d-block mt-2">
|
<ol data-testid="search-results" className="d-block mt-4">
|
||||||
{results.map(({ url, breadcrumbs, heading, title, content }, index) => {
|
{results.map(({ url, breadcrumbs, heading, title, content }, index) => {
|
||||||
const isActive = index === activeHit
|
const isActive = index === activeHit
|
||||||
return (
|
return (
|
||||||
|
|||||||
126
components/VersionPicker.tsx
Normal file
126
components/VersionPicker.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import cx from 'classnames'
|
||||||
|
import { Dropdown, Heading, Details, Box, Text, useDetails } from '@primer/components'
|
||||||
|
import { ArrowRightIcon, ChevronDownIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
|
import { Link } from 'components/Link'
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
import { useVersion } from 'components/hooks/useVersion'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
hideLabel?: boolean
|
||||||
|
variant?: 'default' | 'compact' | 'inline'
|
||||||
|
popoverVariant?: 'inline' | 'dropdown'
|
||||||
|
}
|
||||||
|
export const VersionPicker = ({ variant = 'default', popoverVariant, hideLabel }: Props) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentVersion } = useVersion()
|
||||||
|
const { allVersions, page, enterpriseServerVersions } = useMainContext()
|
||||||
|
const { getDetailsProps, setOpen } = useDetails({ closeOnOutsideClick: true })
|
||||||
|
const { t } = useTranslation('pages')
|
||||||
|
|
||||||
|
if (page.permalinks && page.permalinks.length <= 1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!hideLabel && (
|
||||||
|
<Heading as="span" fontSize={1} className="d-none d-xl-inline-block mb-1">
|
||||||
|
{t('article_version')}
|
||||||
|
</Heading>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<Details
|
||||||
|
{...getDetailsProps()}
|
||||||
|
className={cx(
|
||||||
|
'position-relative details-reset',
|
||||||
|
variant === 'inline' ? 'd-block' : 'd-inline-block'
|
||||||
|
)}
|
||||||
|
data-testid="article-version-picker"
|
||||||
|
>
|
||||||
|
{(variant === 'compact' || variant === 'inline') && (
|
||||||
|
<summary
|
||||||
|
className="d-block btn btn-invisible color-text-primary"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-label="Toggle version list"
|
||||||
|
>
|
||||||
|
{variant === 'inline' ? (
|
||||||
|
<div className="d-flex flex-items-center flex-justify-between">
|
||||||
|
<Text>{allVersions[currentVersion].versionTitle}</Text>
|
||||||
|
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Text>{allVersions[currentVersion].versionTitle}</Text>
|
||||||
|
<Dropdown.Caret />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</summary>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{variant === 'default' && (
|
||||||
|
<summary aria-haspopup="true" className="btn btn-sm">
|
||||||
|
<Text>{allVersions[currentVersion].versionTitle}</Text>
|
||||||
|
<Dropdown.Caret />
|
||||||
|
</summary>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{popoverVariant === 'inline' ? (
|
||||||
|
<Box py="2">
|
||||||
|
{(page.permalinks || []).map((permalink) => {
|
||||||
|
return (
|
||||||
|
<Dropdown.Item key={permalink.href} onClick={() => setOpen(false)}>
|
||||||
|
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Box mt={1}>
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
href={`/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`}
|
||||||
|
className="f6 no-underline color-text-tertiary pl-3 pr-2 no-wrap"
|
||||||
|
>
|
||||||
|
{t('all_enterprise_releases')}{' '}
|
||||||
|
<ArrowRightIcon verticalAlign="middle" size={15} className="mr-2" />
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Dropdown.Menu direction="sw" style={{ width: 'unset' }}>
|
||||||
|
{(page.permalinks || []).map((permalink) => {
|
||||||
|
return (
|
||||||
|
<Dropdown.Item key={permalink.href} onClick={() => setOpen(false)}>
|
||||||
|
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Box
|
||||||
|
borderColor="border.default"
|
||||||
|
borderTopWidth={1}
|
||||||
|
borderTopStyle="solid"
|
||||||
|
mt={2}
|
||||||
|
pt={2}
|
||||||
|
pb={1}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
href={`/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`}
|
||||||
|
className="f6 no-underline color-text-tertiary pl-3 pr-2 no-wrap"
|
||||||
|
>
|
||||||
|
{t('all_enterprise_releases')}{' '}
|
||||||
|
<ArrowRightIcon verticalAlign="middle" size={15} className="mr-2" />
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
</Dropdown.Menu>
|
||||||
|
)}
|
||||||
|
</Details>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
@import "@primer/css/layout/index.scss";
|
|
||||||
@import "@primer/css/support/variables/layout.scss";
|
|
||||||
@import "@primer/css/marketing/support/variables.scss";
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 720px;
|
|
||||||
|
|
||||||
@include breakpoint(xl) {
|
|
||||||
max-width: none;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto 1fr;
|
|
||||||
grid-template-columns: minmax(500px, 720px) minmax(220px, 1fr);
|
|
||||||
grid-template-areas:
|
|
||||||
"top right-sidebar"
|
|
||||||
"bottom right-sidebar";
|
|
||||||
column-gap: $spacer-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include breakpoint(xl) {
|
|
||||||
column-gap: $spacer-9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
grid-area: right-sidebar;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarContent {
|
|
||||||
@include breakpoint(xl) {
|
|
||||||
position: sticky;
|
|
||||||
top: $spacer-4;
|
|
||||||
max-height: calc(100vh - #{$spacer-4});
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-bottom: $spacer-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.head {
|
|
||||||
grid-area: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
grid-area: bottom;
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,78 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import cx from 'classnames'
|
import styled from 'styled-components'
|
||||||
import styles from './ArticleGridLayout.module.scss'
|
import { Box, themeGet } from '@primer/components'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
head?: React.ReactNode
|
intro?: React.ReactNode
|
||||||
|
topperSidebar?: React.ReactNode
|
||||||
|
topper?: React.ReactNode
|
||||||
toc?: React.ReactNode
|
toc?: React.ReactNode
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
export const ArticleGridLayout = ({ head, toc, children, className }: Props) => {
|
export const ArticleGridLayout = ({
|
||||||
|
intro,
|
||||||
|
topperSidebar,
|
||||||
|
topper,
|
||||||
|
toc,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className={cx(styles.container, className)}>
|
<Container className={className}>
|
||||||
{/* head */}
|
{topper && <Box gridArea="topper">{topper}</Box>}
|
||||||
{head && <div className={styles.head}>{head}</div>}
|
{topperSidebar && <Box gridArea="topper-sidebar">{topperSidebar}</Box>}
|
||||||
|
|
||||||
{/* toc */}
|
|
||||||
{toc && (
|
{toc && (
|
||||||
<div className={cx(styles.sidebar, 'border-bottom border-xl-0 pb-4 mb-5 pb-xl-0 mb-xl-0')}>
|
<SidebarContent
|
||||||
<div className={styles.sidebarContent}>{toc}</div>
|
gridArea="sidebar"
|
||||||
</div>
|
alignSelf="flex-start"
|
||||||
|
className="border-bottom border-xl-0 pb-4 mb-5 pb-xl-0 mb-xl-0"
|
||||||
|
>
|
||||||
|
{toc}
|
||||||
|
</SidebarContent>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* content */}
|
{intro && <Box gridArea="intro">{intro}</Box>}
|
||||||
<div data-search="article-body" className={styles.content}>
|
|
||||||
|
<Box gridArea="content" data-search="article-body">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</Box>
|
||||||
</div>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Container = styled(Box)`
|
||||||
|
max-width: 720px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
'topper'
|
||||||
|
'topper-sidebar'
|
||||||
|
'intro'
|
||||||
|
'sidebar'
|
||||||
|
'content';
|
||||||
|
|
||||||
|
row-gap: ${themeGet('space.2')};
|
||||||
|
|
||||||
|
@media (min-width: ${themeGet('breakpoints.3')}) {
|
||||||
|
max-width: none;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
grid-template-columns: minmax(500px, 720px) minmax(220px, 1fr);
|
||||||
|
grid-template-areas:
|
||||||
|
'topper topper-sidebar'
|
||||||
|
'intro sidebar'
|
||||||
|
'content sidebar';
|
||||||
|
column-gap: ${themeGet('space.9')};
|
||||||
|
row-gap: 0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const SidebarContent = styled(Box)`
|
||||||
|
@media (min-width: ${themeGet('breakpoints.3')}) {
|
||||||
|
position: sticky;
|
||||||
|
padding-top: ${themeGet('space.4')};
|
||||||
|
top: 0;
|
||||||
|
max-height: calc(100vh - ${themeGet('space.4')});
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: ${themeGet('space.4')};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
|
import { Heading } from '@primer/components'
|
||||||
|
|
||||||
import { ZapIcon, InfoIcon } from '@primer/octicons-react'
|
import { ZapIcon, InfoIcon } from '@primer/octicons-react'
|
||||||
import { Callout } from 'components/ui/Callout'
|
import { Callout } from 'components/ui/Callout'
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
import { Link } from 'components/Link'
|
||||||
import { DefaultLayout } from 'components/DefaultLayout'
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
import { ArticleTopper } from 'components/article/ArticleTopper'
|
|
||||||
import { ArticleTitle } from 'components/article/ArticleTitle'
|
import { ArticleTitle } from 'components/article/ArticleTitle'
|
||||||
import { useArticleContext } from 'components/context/ArticleContext'
|
import { useArticleContext } from 'components/context/ArticleContext'
|
||||||
import { useTranslation } from 'components/hooks/useTranslation'
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
@@ -14,6 +14,8 @@ import { LearningTrackNav } from './LearningTrackNav'
|
|||||||
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
||||||
import { Lead } from 'components/ui/Lead'
|
import { Lead } from 'components/ui/Lead'
|
||||||
import { ArticleGridLayout } from './ArticleGridLayout'
|
import { ArticleGridLayout } from './ArticleGridLayout'
|
||||||
|
import { VersionPicker } from 'components/VersionPicker'
|
||||||
|
import { Breadcrumbs } from 'components/Breadcrumbs'
|
||||||
|
|
||||||
// Mapping of a "normal" article to it's interactive counterpart
|
// Mapping of a "normal" article to it's interactive counterpart
|
||||||
const interactiveAlternatives: Record<string, { href: string }> = {
|
const interactiveAlternatives: Record<string, { href: string }> = {
|
||||||
@@ -44,12 +46,11 @@ export const ArticlePage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<div className="container-xl px-3 px-md-6 my-4 my-lg-4">
|
<div className="container-xl px-3 px-md-6 my-4">
|
||||||
<ArticleTopper />
|
|
||||||
|
|
||||||
<ArticleGridLayout
|
<ArticleGridLayout
|
||||||
className="mt-7"
|
topper={<Breadcrumbs />}
|
||||||
head={
|
topperSidebar={<VersionPicker />}
|
||||||
|
intro={
|
||||||
<>
|
<>
|
||||||
<ArticleTitle>{title}</ArticleTitle>
|
<ArticleTitle>{title}</ArticleTitle>
|
||||||
|
|
||||||
@@ -124,11 +125,11 @@ export const ArticlePage = () => {
|
|||||||
)}
|
)}
|
||||||
{miniTocItems.length > 1 && (
|
{miniTocItems.length > 1 && (
|
||||||
<>
|
<>
|
||||||
<h2 id="in-this-article" className="f5 mb-2">
|
<Heading as="h2" fontSize={1} id="in-this-article" className="mb-1">
|
||||||
<a className="Link--primary" href="#in-this-article">
|
<a className="Link--primary" href="#in-this-article">
|
||||||
{t('miniToc')}
|
{t('miniToc')}
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</Heading>
|
||||||
<ul className="list-style-none pl-0 f5 mb-0">
|
<ul className="list-style-none pl-0 f5 mb-0">
|
||||||
{miniTocItems.map((item) => {
|
{miniTocItems.map((item) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type Props = {
|
|||||||
export const ArticleTitle = ({ children }: Props) => {
|
export const ArticleTitle = ({ children }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="d-flex flex-items-baseline flex-justify-between">
|
<div className="d-flex flex-items-baseline flex-justify-between">
|
||||||
<h1 className="my-4 border-bottom-0">{children}</h1>
|
<h1 className="mt-4 border-bottom-0">{children}</h1>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Breadcrumbs } from 'components/Breadcrumbs'
|
|
||||||
import { ArticleVersionPicker } from 'components/article/ArticleVersionPicker'
|
|
||||||
|
|
||||||
export const ArticleTopper = () => {
|
|
||||||
return (
|
|
||||||
<div className="d-lg-flex flex-justify-between">
|
|
||||||
<div className="d-block d-lg-none mb-2">
|
|
||||||
<ArticleVersionPicker />
|
|
||||||
</div>
|
|
||||||
<div className="d-flex flex-items-center">
|
|
||||||
<Breadcrumbs />
|
|
||||||
</div>
|
|
||||||
<div className="d-none d-lg-block">
|
|
||||||
<ArticleVersionPicker />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Dropdown } from '@primer/components'
|
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
|
||||||
import { useMainContext } from 'components/context/MainContext'
|
|
||||||
import { useVersion } from 'components/hooks/useVersion'
|
|
||||||
import { useTranslation } from 'components/hooks/useTranslation'
|
|
||||||
|
|
||||||
export const ArticleVersionPicker = () => {
|
|
||||||
const router = useRouter()
|
|
||||||
const { currentVersion } = useVersion()
|
|
||||||
const { allVersions, page, enterpriseServerVersions } = useMainContext()
|
|
||||||
const { t } = useTranslation('pages')
|
|
||||||
|
|
||||||
if (page.permalinks && page.permalinks.length <= 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
css={`
|
|
||||||
ul {
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
data-testid="article-version-picker"
|
|
||||||
>
|
|
||||||
<summary className="btn btn-outline p-2 outline-none">
|
|
||||||
<span className="d-md-none d-xl-inline-block">{t('article_version')}</span>{' '}
|
|
||||||
{allVersions[currentVersion].versionTitle}
|
|
||||||
<Dropdown.Caret />
|
|
||||||
</summary>
|
|
||||||
<Dropdown.Menu direction="sw">
|
|
||||||
{(page.permalinks || []).map((permalink) => {
|
|
||||||
return (
|
|
||||||
<Dropdown.Item key={permalink.href}>
|
|
||||||
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link>
|
|
||||||
</Dropdown.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="pb-1">
|
|
||||||
<Link
|
|
||||||
href={`/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`}
|
|
||||||
className="f6 no-underline color-text-tertiary pl-3 pr-2 no-wrap"
|
|
||||||
>
|
|
||||||
See all Enterprise releases
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Dropdown.Menu>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import cx from 'classnames'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Dropdown, Details, useDetails } from '@primer/components'
|
|
||||||
import { ChevronDownIcon } from '@primer/octicons-react'
|
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
|
||||||
import { useMainContext } from 'components/context/MainContext'
|
|
||||||
import { useVersion } from 'components/hooks/useVersion'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
variant?: 'inline'
|
|
||||||
}
|
|
||||||
export const HomepageVersionPicker = ({ variant }: Props) => {
|
|
||||||
const router = useRouter()
|
|
||||||
const { currentVersion } = useVersion()
|
|
||||||
const { getDetailsProps } = useDetails({})
|
|
||||||
const { allVersions, page, enterpriseServerVersions } = useMainContext()
|
|
||||||
|
|
||||||
if (page.permalinks && page.permalinks.length <= 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = allVersions[currentVersion].versionTitle
|
|
||||||
|
|
||||||
if (variant === 'inline') {
|
|
||||||
return (
|
|
||||||
<Details {...getDetailsProps()} className="details-reset">
|
|
||||||
<summary className="outline-none" aria-label="Toggle language list">
|
|
||||||
<div className="d-flex flex-items-center flex-justify-between py-2">
|
|
||||||
<span>{label}</span>
|
|
||||||
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
|
||||||
</div>
|
|
||||||
</summary>
|
|
||||||
<div>
|
|
||||||
{(page.permalinks || []).map((permalink) => {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={permalink.href}
|
|
||||||
href={permalink.href}
|
|
||||||
className={cx(
|
|
||||||
'd-block py-2',
|
|
||||||
permalink.href === router.asPath
|
|
||||||
? 'color-text-link text-underline active'
|
|
||||||
: 'Link--primary no-underline'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{permalink.pageVersionTitle}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Link
|
|
||||||
href={`/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`}
|
|
||||||
className="f6 no-underline color-text-tertiary no-wrap"
|
|
||||||
>
|
|
||||||
See all Enterprise releases
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Details>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
css={`
|
|
||||||
ul {
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<summary>
|
|
||||||
{label}
|
|
||||||
<Dropdown.Caret />
|
|
||||||
</summary>
|
|
||||||
<Dropdown.Menu direction="sw">
|
|
||||||
{(page.permalinks || []).map((permalink) => {
|
|
||||||
return (
|
|
||||||
<Dropdown.Item key={permalink.href}>
|
|
||||||
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link>
|
|
||||||
</Dropdown.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="pb-1">
|
|
||||||
<Link
|
|
||||||
href={`/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`}
|
|
||||||
className="f6 no-underline color-text-tertiary pl-3 pr-2 no-wrap"
|
|
||||||
>
|
|
||||||
See all Enterprise releases
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</Dropdown.Menu>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { DefaultLayout } from 'components/DefaultLayout'
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
import { TableOfContents } from 'components/landing/TableOfContents'
|
import { TableOfContents } from 'components/landing/TableOfContents'
|
||||||
import { useTocLandingContext } from 'components/context/TocLandingContext'
|
import { useTocLandingContext } from 'components/context/TocLandingContext'
|
||||||
import { ArticleTopper } from 'components/article/ArticleTopper'
|
import { VersionPicker } from 'components/VersionPicker'
|
||||||
|
import { Breadcrumbs } from 'components/Breadcrumbs'
|
||||||
import { ArticleTitle } from 'components/article/ArticleTitle'
|
import { ArticleTitle } from 'components/article/ArticleTitle'
|
||||||
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
||||||
import { ArticleList } from 'components/landing/ArticleList'
|
import { ArticleList } from 'components/landing/ArticleList'
|
||||||
@@ -9,7 +10,7 @@ import { useTranslation } from 'components/hooks/useTranslation'
|
|||||||
import { ArticleGridLayout } from 'components/article/ArticleGridLayout'
|
import { ArticleGridLayout } from 'components/article/ArticleGridLayout'
|
||||||
import { Callout } from 'components/ui/Callout'
|
import { Callout } from 'components/ui/Callout'
|
||||||
import { Lead } from 'components/ui/Lead'
|
import { Lead } from 'components/ui/Lead'
|
||||||
import { LearningTrackNav } from '../article/LearningTrackNav'
|
import { LearningTrackNav } from 'components/article/LearningTrackNav'
|
||||||
|
|
||||||
export const TocLanding = () => {
|
export const TocLanding = () => {
|
||||||
const {
|
const {
|
||||||
@@ -26,10 +27,8 @@ export const TocLanding = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<div className="container-xl px-3 px-md-6 my-4 my-lg-4">
|
<div className="container-xl px-3 px-md-6 my-4">
|
||||||
<ArticleTopper />
|
<ArticleGridLayout topper={<Breadcrumbs />} topperSidebar={<VersionPicker />}>
|
||||||
|
|
||||||
<ArticleGridLayout className="mt-7">
|
|
||||||
<ArticleTitle>{title}</ArticleTitle>
|
<ArticleTitle>{title}</ArticleTitle>
|
||||||
|
|
||||||
{introPlainText && <Lead>{introPlainText}</Lead>}
|
{introPlainText && <Lead>{introPlainText}</Lead>}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useState } from 'react'
|
|||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { MarkGithubIcon, ThreeBarsIcon, XIcon } from '@primer/octicons-react'
|
import { MarkGithubIcon, ThreeBarsIcon, XIcon } from '@primer/octicons-react'
|
||||||
import { ButtonOutline } from '@primer/components'
|
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
import { Link } from 'components/Link'
|
||||||
import { useMainContext } from 'components/context/MainContext'
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
@@ -10,8 +9,8 @@ import { LanguagePicker } from './LanguagePicker'
|
|||||||
import { HeaderNotifications } from 'components/page-header/HeaderNotifications'
|
import { HeaderNotifications } from 'components/page-header/HeaderNotifications'
|
||||||
import { ProductPicker } from 'components/page-header/ProductPicker'
|
import { ProductPicker } from 'components/page-header/ProductPicker'
|
||||||
import { useTranslation } from 'components/hooks/useTranslation'
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
import { HomepageVersionPicker } from 'components/landing/HomepageVersionPicker'
|
|
||||||
import { Search } from 'components/Search'
|
import { Search } from 'components/Search'
|
||||||
|
import { VersionPicker } from 'components/VersionPicker'
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -31,25 +30,23 @@ export const Header = () => {
|
|||||||
<div className="border-bottom color-border-secondary no-print">
|
<div className="border-bottom color-border-secondary no-print">
|
||||||
{error !== '404' && <HeaderNotifications />}
|
{error !== '404' && <HeaderNotifications />}
|
||||||
|
|
||||||
<header
|
<header className={cx('container-xl px-3 px-md-6 pt-3 pb-3 position-relative z-3')}>
|
||||||
className="container-xl px-3 px-md-6 pt-3 pb-3 position-relative"
|
|
||||||
style={{ zIndex: 2 }}
|
|
||||||
>
|
|
||||||
{/* desktop header */}
|
{/* desktop header */}
|
||||||
<div className="d-none d-lg-flex flex-justify-end" data-testid="desktop-header">
|
<div
|
||||||
|
className="d-none d-lg-flex flex-justify-end flex-items-center"
|
||||||
|
data-testid="desktop-header"
|
||||||
|
>
|
||||||
{showVersionPicker && (
|
{showVersionPicker && (
|
||||||
<div className="py-2 mr-4">
|
<div className="mr-2">
|
||||||
<HomepageVersionPicker />
|
<VersionPicker hideLabel={true} variant="compact" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="py-2">
|
<LanguagePicker />
|
||||||
<LanguagePicker />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
|
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
|
||||||
{relativePath !== 'index.md' && error !== '404' && (
|
{relativePath !== 'index.md' && error !== '404' && (
|
||||||
<div className="d-inline-block ml-4">
|
<div className="d-inline-block ml-3">
|
||||||
<Search updateSearchParams={updateSearchParams} isOverlay={true} />
|
<Search updateSearchParams={updateSearchParams} isOverlay={true} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -72,14 +69,14 @@ export const Header = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ButtonOutline
|
<button
|
||||||
|
className="btn"
|
||||||
data-testid="mobile-menu-button"
|
data-testid="mobile-menu-button"
|
||||||
css
|
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
aria-label="Navigation Menu"
|
aria-label="Navigation Menu"
|
||||||
>
|
>
|
||||||
{isMenuOpen ? <XIcon size="small" /> : <ThreeBarsIcon size="small" />}
|
{isMenuOpen ? <XIcon size="small" /> : <ThreeBarsIcon size="small" />}
|
||||||
</ButtonOutline>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -87,31 +84,33 @@ export const Header = () => {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
'width-full position-absolute left-0 right-0 color-shadow-large color-bg-primary px-3 px-md-6 pb-3',
|
'width-full position-absolute left-0 right-0 color-shadow-large color-bg-primary px-2 px-md-4 pb-3',
|
||||||
isMenuOpen ? 'd-block' : 'd-none'
|
isMenuOpen ? 'd-block' : 'd-none'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="mt-3 mb-2">
|
<div className="mt-3 mb-2">
|
||||||
<h4 className="f5 text-normal color-text-secondary">{t('explore_by_product')}</h4>
|
<h4 className="f5 text-normal color-text-secondary ml-3">
|
||||||
|
{t('explore_by_product')}
|
||||||
|
</h4>
|
||||||
|
|
||||||
<ProductPicker />
|
<ProductPicker />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <!-- Versions picker that only appears in the header on landing pages --> */}
|
{/* <!-- Versions picker that only appears in the header on landing pages --> */}
|
||||||
{showVersionPicker && (
|
{showVersionPicker && (
|
||||||
<div className="border-top py-2">
|
<>
|
||||||
<HomepageVersionPicker variant="inline" />
|
<div className="border-top my-2 mx-3" />
|
||||||
</div>
|
<VersionPicker hideLabel={true} variant="inline" popoverVariant={'inline'} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* <!-- Language picker - 'English', 'Japanese', etc --> */}
|
{/* <!-- Language picker - 'English', 'Japanese', etc --> */}
|
||||||
<div className="border-top py-2">
|
<div className="border-top my-2 mx-3" />
|
||||||
<LanguagePicker variant="inline" />
|
<LanguagePicker variant="inline" />
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
|
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
|
||||||
{relativePath !== 'index.md' && error !== '404' && (
|
{relativePath !== 'index.md' && error !== '404' && (
|
||||||
<div className="pt-3 border-top">
|
<div className="my-2 pt-3 mx-3">
|
||||||
<Search updateSearchParams={updateSearchParams} />
|
<Search updateSearchParams={updateSearchParams} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import cx from 'classnames'
|
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Dropdown, Details, useDetails } from '@primer/components'
|
import { Box, Dropdown, Details, Text, useDetails } from '@primer/components'
|
||||||
import { ChevronDownIcon } from '@primer/octicons-react'
|
import { ChevronDownIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
import { Link } from 'components/Link'
|
||||||
@@ -12,76 +11,63 @@ type Props = {
|
|||||||
export const LanguagePicker = ({ variant }: Props) => {
|
export const LanguagePicker = ({ variant }: Props) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { languages } = useLanguages()
|
const { languages } = useLanguages()
|
||||||
const { getDetailsProps } = useDetails({})
|
const { getDetailsProps, setOpen } = useDetails({ closeOnOutsideClick: true })
|
||||||
const locale = router.locale || 'en'
|
const locale = router.locale || 'en'
|
||||||
const langs = Object.values(languages)
|
const langs = Object.values(languages)
|
||||||
const selectedLang = languages[locale]
|
const selectedLang = languages[locale]
|
||||||
|
|
||||||
if (variant === 'inline') {
|
if (variant === 'inline') {
|
||||||
return (
|
return (
|
||||||
<Details {...getDetailsProps()} className="details-reset">
|
<Details {...getDetailsProps()} data-testid="language-picker">
|
||||||
<summary className="outline-none" aria-label="Toggle language list">
|
<summary
|
||||||
<div className="d-flex flex-items-center flex-justify-between py-2">
|
className="d-block btn btn-invisible color-text-primary"
|
||||||
<span>{selectedLang.nativeName || selectedLang.name}</span>
|
aria-label="Toggle language list"
|
||||||
|
>
|
||||||
|
<div className="d-flex flex-items-center flex-justify-between">
|
||||||
|
<Text>{selectedLang.nativeName || selectedLang.name}</Text>
|
||||||
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
||||||
</div>
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
<div>
|
<Box mt={1}>
|
||||||
{langs.map((lang) => {
|
{langs.map((lang) => {
|
||||||
if (lang.wip) {
|
if (lang.wip) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Dropdown.Item onClick={() => setOpen(false)} key={lang.code}>
|
||||||
key={lang.code}
|
<Link href={router.asPath} locale={lang.code}>
|
||||||
href={router.asPath}
|
{lang.nativeName ? (
|
||||||
locale={lang.code}
|
<>
|
||||||
disableClientTransition={true}
|
{lang.nativeName} ({lang.name})
|
||||||
className={cx(
|
</>
|
||||||
'd-block py-2',
|
) : (
|
||||||
lang.code === router.locale
|
lang.name
|
||||||
? 'color-text-link text-underline active'
|
)}
|
||||||
: 'Link--primary no-underline'
|
</Link>
|
||||||
)}
|
</Dropdown.Item>
|
||||||
>
|
|
||||||
{lang.nativeName ? (
|
|
||||||
<>
|
|
||||||
{lang.nativeName} ({lang.name})
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
lang.name
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</Box>
|
||||||
</Details>
|
</Details>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Details {...getDetailsProps()} data-testid="language-picker" className="position-relative">
|
||||||
css={`
|
<summary className="d-block btn btn-invisible color-text-primary">
|
||||||
ul {
|
<Text>{selectedLang.nativeName || selectedLang.name}</Text>
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
data-testid="language-picker"
|
|
||||||
>
|
|
||||||
<summary>
|
|
||||||
{selectedLang.nativeName || selectedLang.name}
|
|
||||||
<Dropdown.Caret />
|
<Dropdown.Caret />
|
||||||
</summary>
|
</summary>
|
||||||
<Dropdown.Menu direction="sw">
|
<Dropdown.Menu direction="sw" style={{ width: 'unset' }}>
|
||||||
{langs.map((lang) => {
|
{langs.map((lang) => {
|
||||||
if (lang.wip) {
|
if (lang.wip) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown.Item key={lang.code}>
|
<Dropdown.Item key={lang.code} onClick={() => setOpen(false)}>
|
||||||
<Link href={router.asPath} locale={lang.code} disableClientTransition={true}>
|
<Link href={router.asPath} locale={lang.code}>
|
||||||
{lang.nativeName ? (
|
{lang.nativeName ? (
|
||||||
<>
|
<>
|
||||||
{lang.nativeName} ({lang.name})
|
{lang.nativeName} ({lang.name})
|
||||||
@@ -94,6 +80,6 @@ export const LanguagePicker = ({ variant }: Props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Details>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,48 @@
|
|||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import cx from 'classnames'
|
|
||||||
|
|
||||||
import { Link } from 'components/Link'
|
import { Link } from 'components/Link'
|
||||||
import { useMainContext } from 'components/context/MainContext'
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
import { ChevronDownIcon, LinkExternalIcon } from '@primer/octicons-react'
|
import { ChevronDownIcon, LinkExternalIcon } from '@primer/octicons-react'
|
||||||
import { Details, useDetails } from '@primer/components'
|
import { Box, Dropdown, Details, useDetails } from '@primer/components'
|
||||||
|
|
||||||
|
// Product Picker - GitHub.com, Enterprise Server, etc
|
||||||
export const ProductPicker = () => {
|
export const ProductPicker = () => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { activeProducts, currentProduct } = useMainContext()
|
const { activeProducts, currentProduct } = useMainContext()
|
||||||
const { getDetailsProps } = useDetails({})
|
const { getDetailsProps, setOpen } = useDetails({ closeOnOutsideClick: true })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Details {...getDetailsProps()} className="details-reset">
|
<Details {...getDetailsProps()} className="details-reset">
|
||||||
<summary
|
<summary
|
||||||
className="color-text-link outline-none"
|
className="d-block color-text-primary btn btn-invisible"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="Toggle products list"
|
aria-label="Toggle products list"
|
||||||
>
|
>
|
||||||
<div id="current-product" className="d-flex flex-items-center flex-justify-between py-2">
|
<div
|
||||||
{/* <!-- Product switcher - GitHub.com, Enterprise Server, etc -->
|
data-testid="current-product"
|
||||||
<!-- 404 and 500 error layouts are not real pages so we need to hardcode the name for those --> */}
|
data-current-product-path={currentProduct?.href}
|
||||||
|
className="d-flex flex-items-center flex-justify-between"
|
||||||
|
>
|
||||||
<span>{currentProduct?.name || 'All Products'}</span>
|
<span>{currentProduct?.name || 'All Products'}</span>
|
||||||
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
||||||
</div>
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
<div id="homepages" style={{ zIndex: 6 }}>
|
<Box data-testid="product-picker-list" py="2" style={{ zIndex: 6 }}>
|
||||||
{activeProducts.map((product) => {
|
{activeProducts.map((product) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Dropdown.Item key={product.id} onClick={() => setOpen(false)}>
|
||||||
key={product.id}
|
<Link href={`${product.external ? '' : `/${router.locale}`}${product.href}`}>
|
||||||
href={`${product.external ? '' : `/${router.locale}`}${product.href}`}
|
{product.name}
|
||||||
className={cx(
|
{product.external && (
|
||||||
'd-block py-2',
|
<span className="ml-1">
|
||||||
product.id === currentProduct?.id
|
<LinkExternalIcon size="small" />
|
||||||
? 'color-text-link text-underline active'
|
</span>
|
||||||
: 'Link--primary no-underline'
|
)}
|
||||||
)}
|
</Link>
|
||||||
>
|
</Dropdown.Item>
|
||||||
{product.name}
|
|
||||||
{product.external && (
|
|
||||||
<span className="ml-1">
|
|
||||||
<LinkExternalIcon size="small" />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</Box>
|
||||||
</Details>
|
</Details>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,17 +31,16 @@ export const CodeLanguagePicker = ({ variant }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectMenu css className="position-relative">
|
<SelectMenu className="position-relative">
|
||||||
<Button as="summary" css>
|
<Button as="summary">
|
||||||
{currentLanguage.label} <Dropdown.Caret />
|
{currentLanguage.label} <Dropdown.Caret />
|
||||||
</Button>
|
</Button>
|
||||||
<SelectMenu.Modal css style={{ minWidth: 300 }} align="right">
|
<SelectMenu.Modal style={{ minWidth: 300 }} align="right">
|
||||||
<SelectMenu.Header css>Programming Language</SelectMenu.Header>
|
<SelectMenu.Header>Programming Language</SelectMenu.Header>
|
||||||
<SelectMenu.List>
|
<SelectMenu.List>
|
||||||
{codeLanguages.map((language) => (
|
{codeLanguages.map((language) => (
|
||||||
<SelectMenu.Item
|
<SelectMenu.Item
|
||||||
key={language.id}
|
key={language.id}
|
||||||
css
|
|
||||||
as="a"
|
as="a"
|
||||||
href={`${routePath}?langId=${language.id}`}
|
href={`${routePath}?langId=${language.id}`}
|
||||||
selected={language.id === currentLanguage.id}
|
selected={language.id === currentLanguage.id}
|
||||||
|
|||||||
@@ -40,9 +40,10 @@ toc:
|
|||||||
guides: Guides
|
guides: Guides
|
||||||
whats_new: What's new
|
whats_new: What's new
|
||||||
pages:
|
pages:
|
||||||
article_version: 'Article version:'
|
article_version: 'Article version'
|
||||||
miniToc: In this article
|
miniToc: In this article
|
||||||
contributor_callout: This article is contributed and maintained by
|
contributor_callout: This article is contributed and maintained by
|
||||||
|
all_enterprise_releases: All Enterprise releases
|
||||||
errors:
|
errors:
|
||||||
oops: Ooops!
|
oops: Ooops!
|
||||||
something_went_wrong: It looks like something went wrong.
|
something_went_wrong: It looks like something went wrong.
|
||||||
|
|||||||
412
package-lock.json
generated
412
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||||
"@hapi/accept": "^5.0.2",
|
"@hapi/accept": "^5.0.2",
|
||||||
"@primer/components": "^28.5.0",
|
"@primer/components": "^29.1.1",
|
||||||
"@primer/css": "^17.9.0",
|
"@primer/css": "^17.9.0",
|
||||||
"@primer/octicons": "^15.1.0",
|
"@primer/octicons": "^15.1.0",
|
||||||
"@primer/octicons-react": "^15.1.0",
|
"@primer/octicons-react": "^15.1.0",
|
||||||
|
|||||||
@@ -68,6 +68,9 @@
|
|||||||
.z-2 {
|
.z-2 {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
.z-3 {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
/* Blue primary button
|
/* Blue primary button
|
||||||
------------------------------------------------------------------------------*/
|
------------------------------------------------------------------------------*/
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ describe('header', () => {
|
|||||||
|
|
||||||
test('includes localized meta tags', async () => {
|
test('includes localized meta tags', async () => {
|
||||||
const $ = await getDOM('/en')
|
const $ = await getDOM('/en')
|
||||||
expect($('meta[name="next-head-count"]').length).toBe(1)
|
expect($('link[rel="alternate"]').length).toBeGreaterThan(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("includes a link to the homepage (in the current page's language)", async () => {
|
test("includes a link to the homepage (in the current page's language)", async () => {
|
||||||
@@ -26,26 +26,30 @@ describe('header', () => {
|
|||||||
)
|
)
|
||||||
expect(
|
expect(
|
||||||
$(
|
$(
|
||||||
'[data-testid=language-picker] a[href="/ja/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule"]'
|
'[data-testid=desktop-header] [data-testid=language-picker] a[href="/ja/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule"]'
|
||||||
).length
|
).length
|
||||||
).toBe(1)
|
).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('display the native name and the English name for each translated language', async () => {
|
test('display the native name and the English name for each translated language', async () => {
|
||||||
const $ = await getDOM('/en')
|
const $ = await getDOM('/en')
|
||||||
expect($('[data-testid=language-picker] a[href="/en/"]').text().trim()).toBe('English')
|
|
||||||
expect($('[data-testid=language-picker] a[href="/cn/"]').text().trim()).toBe(
|
expect(
|
||||||
'简体中文 (Simplified Chinese)'
|
$('[data-testid=desktop-header] [data-testid=language-picker] a[href="/en"]').text().trim()
|
||||||
)
|
).toBe('English')
|
||||||
expect($('[data-testid=language-picker] a[href="/ja/"]').text().trim()).toBe(
|
expect(
|
||||||
'日本語 (Japanese)'
|
$('[data-testid=desktop-header] [data-testid=language-picker] a[href="/cn"]').text().trim()
|
||||||
)
|
).toBe('简体中文 (Simplified Chinese)')
|
||||||
|
expect(
|
||||||
|
$('[data-testid=desktop-header] [data-testid=language-picker] a[href="/ja"]').text().trim()
|
||||||
|
).toBe('日本語 (Japanese)')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('emphasize the current language', async () => {
|
test('emphasize the current language', async () => {
|
||||||
const $ = await getDOM('/en')
|
const $ = await getDOM('/en')
|
||||||
expect($('[data-testid=language-picker] a[href="/en/"]').length).toBe(1)
|
expect($('[data-testid=desktop-header] [data-testid=language-picker] summary').text()).toBe(
|
||||||
expect($('[data-testid=language-picker] a[href="/ja/"]').length).toBe(1)
|
'English'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -136,15 +140,15 @@ describe('header', () => {
|
|||||||
const $ = await getDOM(
|
const $ = await getDOM(
|
||||||
'/en/github/importing-your-projects-to-github/importing-source-code-to-github/about-github-importer'
|
'/en/github/importing-your-projects-to-github/importing-source-code-to-github/about-github-importer'
|
||||||
)
|
)
|
||||||
const github = $('#homepages a.active[href="/en/github"]')
|
const github = $('[data-testid=current-product][data-current-product-path="/github"]')
|
||||||
expect(github.length).toBe(1)
|
expect(github.length).toBe(1)
|
||||||
expect(github.text().trim()).toBe('GitHub')
|
expect(github.text().trim()).toBe('GitHub')
|
||||||
expect(github.attr('class').includes('active')).toBe(true)
|
|
||||||
|
|
||||||
const ghe = $(`#homepages a[href="/en/enterprise-server@${latest}/admin"]`)
|
const ghe = $(
|
||||||
|
`[data-testid=product-picker-list] a[href="/en/enterprise-server@${latest}/admin"]`
|
||||||
|
)
|
||||||
expect(ghe.length).toBe(1)
|
expect(ghe.length).toBe(1)
|
||||||
expect(ghe.text().trim()).toBe('Enterprise administrators')
|
expect(ghe.text().trim()).toBe('Enterprise administrators')
|
||||||
expect(ghe.attr('class').includes('active')).toBe(false)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Skipped. See issues/923
|
// Skipped. See issues/923
|
||||||
@@ -152,17 +156,21 @@ describe('header', () => {
|
|||||||
const $ = await getDOM(
|
const $ = await getDOM(
|
||||||
'/ja/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests'
|
'/ja/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests'
|
||||||
)
|
)
|
||||||
expect($('#homepages a.active[href="/ja/repositories"]').length).toBe(1)
|
expect(
|
||||||
expect($(`#homepages a[href="/ja/enterprise-server@${latest}/admin"]`).length).toBe(1)
|
$('[data-testid=current-product][data-current-product-path="/repositories"]').length
|
||||||
|
).toBe(1)
|
||||||
|
expect(
|
||||||
|
$(`[data-testid=product-picker-list] a[href="/ja/enterprise-server@${latest}/admin"]`)
|
||||||
|
.length
|
||||||
|
).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('emphasizes the product that corresponds to the current page', async () => {
|
test('emphasizes the product that corresponds to the current page', async () => {
|
||||||
const $ = await getDOM(
|
const $ = await getDOM(
|
||||||
`/en/enterprise/${oldestSupported}/user/github/importing-your-projects-to-github/importing-source-code-to-github/importing-a-git-repository-using-the-command-line`
|
`/en/enterprise-server@${oldestSupported}/github/importing-your-projects-to-github/importing-source-code-to-github/importing-a-git-repository-using-the-command-line`
|
||||||
)
|
)
|
||||||
expect($(`#homepages a.active[href="/en/enterprise-server@${latest}/admin"]`).length).toBe(0)
|
|
||||||
expect($('#homepages a[href="/en/github"]').length).toBe(1)
|
expect($('[data-testid=current-product]').text()).toBe('GitHub')
|
||||||
expect($('#homepages a.active[href="/en/github"]').length).toBe(1)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -545,7 +545,7 @@ describe('server', () => {
|
|||||||
$(
|
$(
|
||||||
`[data-testid=article-version-picker] a[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`
|
`[data-testid=article-version-picker] a[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`
|
||||||
).length
|
).length
|
||||||
).toBe(2)
|
).toBe(1)
|
||||||
// 2.13 predates this feature, so it should be excluded:
|
// 2.13 predates this feature, so it should be excluded:
|
||||||
expect(
|
expect(
|
||||||
$(`[data-testid=article-version-picker] a[href="/en/enterprise/2.13/user/${articlePath}"]`)
|
$(`[data-testid=article-version-picker] a[href="/en/enterprise/2.13/user/${articlePath}"]`)
|
||||||
|
|||||||
@@ -25,28 +25,34 @@ describe('products module', () => {
|
|||||||
describe('mobile-only products nav', () => {
|
describe('mobile-only products nav', () => {
|
||||||
test('renders current product on various product pages for each product', async () => {
|
test('renders current product on various product pages for each product', async () => {
|
||||||
// Note the unversioned homepage at `/` does not have a product selected in the mobile dropdown
|
// Note the unversioned homepage at `/` does not have a product selected in the mobile dropdown
|
||||||
expect((await getDOM('/github'))('#current-product').text().trim()).toBe('GitHub')
|
expect((await getDOM('/github'))('[data-testid=current-product]').text().trim()).toBe('GitHub')
|
||||||
|
|
||||||
// Enterprise server
|
// Enterprise server
|
||||||
expect((await getDOM('/en/enterprise/admin'))('#current-product').text().trim()).toBe(
|
expect(
|
||||||
'Enterprise administrators'
|
(await getDOM('/en/enterprise/admin'))('[data-testid=current-product]').text().trim()
|
||||||
)
|
).toBe('Enterprise administrators')
|
||||||
expect(
|
expect(
|
||||||
(
|
(
|
||||||
await getDOM(
|
await getDOM(
|
||||||
'/en/enterprise/user/github/importing-your-projects-to-github/importing-source-code-to-github/importing-a-git-repository-using-the-command-line'
|
'/en/enterprise/user/github/importing-your-projects-to-github/importing-source-code-to-github/importing-a-git-repository-using-the-command-line'
|
||||||
)
|
)
|
||||||
)('#current-product')
|
)('[data-testid=current-product]')
|
||||||
.text()
|
.text()
|
||||||
.trim()
|
.trim()
|
||||||
).toBe('GitHub')
|
).toBe('GitHub')
|
||||||
|
|
||||||
expect((await getDOM('/desktop'))('#current-product').text().trim()).toBe('GitHub Desktop')
|
expect((await getDOM('/desktop'))('[data-testid=current-product]').text().trim()).toBe(
|
||||||
|
'GitHub Desktop'
|
||||||
|
)
|
||||||
|
|
||||||
expect((await getDOM('/actions'))('#current-product').text().trim()).toBe('GitHub Actions')
|
expect((await getDOM('/actions'))('[data-testid=current-product]').text().trim()).toBe(
|
||||||
|
'GitHub Actions'
|
||||||
|
)
|
||||||
|
|
||||||
// localized
|
// localized
|
||||||
expect((await getDOM('/ja/desktop'))('#current-product').text().trim()).toBe('GitHub Desktop')
|
expect((await getDOM('/ja/desktop'))('[data-testid=current-product]').text().trim()).toBe(
|
||||||
|
'GitHub Desktop'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user