@@ -51,7 +51,11 @@ export const Breadcrumbs = ({ variant = 'default' }: Props) => {
|
||||
{breadcrumb.title}
|
||||
</Link>
|
||||
),
|
||||
i !== arr.length - 1 ? <span className="color-text-tertiary">/</span> : null,
|
||||
i !== arr.length - 1 ? (
|
||||
<span className="color-text-tertiary" key={`${i}-slash`}>
|
||||
/
|
||||
</span>
|
||||
) : null,
|
||||
]
|
||||
})}
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Head from 'next/head'
|
||||
|
||||
import { SidebarNav } from 'components/SidebarNav'
|
||||
import { SidebarNav } from 'components/sidebar/SidebarNav'
|
||||
import { Header } from 'components/page-header/Header'
|
||||
import { SmallFooter } from 'components/page-footer/SmallFooter'
|
||||
import { ScrollButton } from 'components/ScrollButton'
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { LinkExternalIcon, MarkGithubIcon } from '@primer/octicons-react'
|
||||
|
||||
import { Link } from 'components/Link'
|
||||
import { useTranslation } from './hooks/useTranslation'
|
||||
import { useMainContext } from './context/MainContext'
|
||||
import { SidebarProduct } from './product/SidebarProduct'
|
||||
import { AllProductsLink } from './product/AllProductsLink'
|
||||
import { useVersion } from './hooks/useVersion'
|
||||
|
||||
export const SidebarNav = () => {
|
||||
const router = useRouter()
|
||||
const { error, relativePath, isFPT } = useMainContext()
|
||||
const { t } = useTranslation('header')
|
||||
|
||||
return (
|
||||
<div className="d-none d-lg-block color-bg-tertiary position-sticky top-0 overflow-y-auto root">
|
||||
<div
|
||||
className="d-flex flex-items-center p-4 position-sticky top-0 color-bg-tertiary"
|
||||
style={{ zIndex: 3 }}
|
||||
id="github-logo"
|
||||
role="banner"
|
||||
>
|
||||
<Link
|
||||
href={`/${router.locale}`}
|
||||
className="color-text-primary"
|
||||
aria-hidden="true"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<MarkGithubIcon size={32} />
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${router.locale}`}
|
||||
className="h4-mktg color-text-primary no-underline no-wrap pl-2 flex-auto"
|
||||
>
|
||||
{t('github_docs')}
|
||||
</Link>
|
||||
</div>
|
||||
<nav>
|
||||
{error === '404' || relativePath === 'index.md' ? (
|
||||
<ul className="sidebar-products mt-4">
|
||||
{!isFPT && <AllProductsLink />}
|
||||
<SidebarHomepage />
|
||||
</ul>
|
||||
) : (
|
||||
<ul className="sidebar-products">
|
||||
<SidebarProduct />
|
||||
</ul>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.root {
|
||||
width: 286px;
|
||||
height: 100vh;
|
||||
flex-shrink: 0;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SidebarHomepage = () => {
|
||||
const router = useRouter()
|
||||
const { currentVersion } = useVersion()
|
||||
const { activeProducts, isFPT } = useMainContext()
|
||||
|
||||
return (
|
||||
<>
|
||||
{activeProducts.map((product) => {
|
||||
if (!isFPT && !product.versions?.includes(currentVersion) && !product.external) {
|
||||
return null
|
||||
}
|
||||
|
||||
const href = `${!product.external ? `/${router.locale}` : ''}${
|
||||
product.versions?.includes(currentVersion) && !isFPT
|
||||
? `/${currentVersion}/${product.id}`
|
||||
: product.href
|
||||
}`
|
||||
|
||||
return (
|
||||
<li
|
||||
key={product.id}
|
||||
title={`${product.name}${product.external ? '(External Site)' : ''}`}
|
||||
className="sidebar-product"
|
||||
>
|
||||
<a
|
||||
href={href}
|
||||
target={product.external ? '_blank' : undefined}
|
||||
className="f4 pl-4 pr-5 py-2 color-text-primary"
|
||||
>
|
||||
{product.name}
|
||||
{product.external && (
|
||||
<span className="ml-1">
|
||||
<LinkExternalIcon size="small" />
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -207,38 +207,11 @@ function initExitEvent() {
|
||||
document.addEventListener('visibilitychange', sendExit)
|
||||
}
|
||||
|
||||
function initNavigateEvent() {
|
||||
if (!document.querySelector('.sidebar-products')) return
|
||||
|
||||
Array.from(document.querySelectorAll('.sidebar-products details')).forEach((details) =>
|
||||
details.addEventListener('toggle', (evt) => {
|
||||
const target = evt.target as HTMLDetailsElement
|
||||
sendEvent({
|
||||
type: EventType.navigate,
|
||||
navigate_label: `details ${target.open ? 'open' : 'close'}: ${
|
||||
target?.querySelector('summary')?.innerText
|
||||
}`,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
document.querySelector('.sidebar-products')?.addEventListener('click', (evt) => {
|
||||
const target = evt.target as HTMLElement
|
||||
const link = target.closest('a') as HTMLAnchorElement
|
||||
if (!link) return
|
||||
sendEvent({
|
||||
type: EventType.navigate,
|
||||
navigate_label: `link: ${link.href}`,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default function initializeEvents() {
|
||||
initPageEvent() // must come first
|
||||
initExitEvent()
|
||||
initLinkEvent()
|
||||
initClipboardEvent()
|
||||
initNavigateEvent()
|
||||
// print event in ./print.js
|
||||
// survey event in ./survey.js
|
||||
// experiment event in ./experiment.js
|
||||
|
||||
53
components/sidebar/SidebarHomepage.tsx
Normal file
53
components/sidebar/SidebarHomepage.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { LinkExternalIcon } from '@primer/octicons-react'
|
||||
|
||||
import { useVersion } from 'components/hooks/useVersion'
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { Link } from 'components/Link'
|
||||
|
||||
import { AllProductsLink } from './AllProductsLink'
|
||||
|
||||
export const SidebarHomepage = () => {
|
||||
const router = useRouter()
|
||||
const { currentVersion } = useVersion()
|
||||
const { activeProducts, isFPT } = useMainContext()
|
||||
|
||||
return (
|
||||
<ul data-testid="sidebar" className="mt-4">
|
||||
{!isFPT && <AllProductsLink />}
|
||||
|
||||
{activeProducts.map((product) => {
|
||||
if (!isFPT && !product.versions?.includes(currentVersion) && !product.external) {
|
||||
return null
|
||||
}
|
||||
|
||||
const href = `${!product.external ? `/${router.locale}` : ''}${
|
||||
product.versions?.includes(currentVersion) && !isFPT
|
||||
? `/${currentVersion}/${product.id}`
|
||||
: product.href
|
||||
}`
|
||||
|
||||
return (
|
||||
<li
|
||||
key={product.id}
|
||||
title={`${product.name}${product.external ? '(External Site)' : ''}`}
|
||||
className="my-3"
|
||||
>
|
||||
<Link
|
||||
href={href}
|
||||
target={product.external ? '_blank' : undefined}
|
||||
className="f4 pl-4 pr-5 py-2 color-text-primary no-underline"
|
||||
>
|
||||
{product.name}
|
||||
{product.external && (
|
||||
<span className="ml-1">
|
||||
<LinkExternalIcon size="small" />
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
46
components/sidebar/SidebarNav.tsx
Normal file
46
components/sidebar/SidebarNav.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { MarkGithubIcon } from '@primer/octicons-react'
|
||||
|
||||
import { Link } from 'components/Link'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { SidebarProduct } from './SidebarProduct'
|
||||
import { SidebarHomepage } from './SidebarHomepage'
|
||||
|
||||
export const SidebarNav = () => {
|
||||
const router = useRouter()
|
||||
const { error, relativePath } = useMainContext()
|
||||
const { t } = useTranslation('header')
|
||||
|
||||
return (
|
||||
<div
|
||||
className="d-none d-lg-block color-bg-tertiary position-sticky top-0 overflow-y-auto flex-shrink-0 pb-5"
|
||||
style={{ width: 286, height: '100vh' }}
|
||||
>
|
||||
<div
|
||||
className="d-flex flex-items-center p-4 position-sticky top-0 color-bg-tertiary"
|
||||
style={{ zIndex: 3 }}
|
||||
id="github-logo"
|
||||
role="banner"
|
||||
>
|
||||
<Link
|
||||
href={`/${router.locale}`}
|
||||
className="color-text-primary"
|
||||
aria-hidden="true"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<MarkGithubIcon size={32} />
|
||||
</Link>
|
||||
<Link
|
||||
href={`/${router.locale}`}
|
||||
className="h4-mktg color-text-primary no-underline no-wrap pl-2 flex-auto"
|
||||
>
|
||||
{t('github_docs')}
|
||||
</Link>
|
||||
</div>
|
||||
<nav>
|
||||
{error === '404' || relativePath === 'index.md' ? <SidebarHomepage /> : <SidebarProduct />}
|
||||
</nav>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
13
components/sidebar/SidebarProduct.module.scss
Normal file
13
components/sidebar/SidebarProduct.module.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
.sidebarArticle::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
height: 100%;
|
||||
border-left: 1px solid var(--color-text-primary);
|
||||
width: 1px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.sidebarArticleActive::before {
|
||||
border-left-width: 2px;
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import cx from 'classnames'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, SyntheticEvent } from 'react'
|
||||
import { ChevronDownIcon } from '@primer/octicons-react'
|
||||
|
||||
import { Link } from 'components/Link'
|
||||
import { ProductTreeNode, useMainContext } from 'components/context/MainContext'
|
||||
import { AllProductsLink } from 'components/product/AllProductsLink'
|
||||
import { AllProductsLink } from 'components/sidebar/AllProductsLink'
|
||||
import { EventType, sendEvent } from 'components/lib/events'
|
||||
|
||||
import styles from './SidebarProduct.module.scss'
|
||||
|
||||
export const SidebarProduct = () => {
|
||||
const router = useRouter()
|
||||
const { currentProductTree } = useMainContext()
|
||||
|
||||
useEffect(() => {
|
||||
const activeArticle = document.querySelector('.sidebar-article.active')
|
||||
const activeArticle = document.querySelector('[data-is-current-page=true]')
|
||||
// Setting to the top doesn't give enough context of surrounding categories
|
||||
activeArticle?.scrollIntoView({ block: 'center' })
|
||||
// scrollIntoView affects some articles that are very low in the sidebar
|
||||
@@ -26,16 +30,16 @@ export const SidebarProduct = () => {
|
||||
|
||||
const productTitle = currentProductTree.renderedShortTitle || currentProductTree.renderedFullTitle
|
||||
const routePath = `/${router.locale}${router.asPath.split('?')[0]}` // remove query string
|
||||
const hasExactCategory = currentProductTree.childPages.find(({ href }) =>
|
||||
const hasExactCategory = !!currentProductTree.childPages.find(({ href }) =>
|
||||
routePath.includes(href)
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<ul data-testid="sidebar" className={styles.container}>
|
||||
<AllProductsLink />
|
||||
|
||||
{!currentProductTree.page.hidden && (
|
||||
<>
|
||||
<li title="" className="sidebar-product mb-2">
|
||||
<li data-testid="sidebar-product" title={productTitle} className="my-2">
|
||||
<Link
|
||||
href={currentProductTree.href}
|
||||
className="pl-4 pr-5 pb-1 f4 color-text-primary no-underline"
|
||||
@@ -44,25 +48,21 @@ export const SidebarProduct = () => {
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<ul className="sidebar-categories list-style-none">
|
||||
<li className="my-3">
|
||||
<ul className="list-style-none">
|
||||
{currentProductTree.childPages.map((childPage, i) => {
|
||||
const isStandaloneCategory = childPage.page.documentType === 'article'
|
||||
|
||||
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
|
||||
|
||||
const isActive = routePath.includes(`${childPage.href}/`)
|
||||
const isCurrent = routePath === childPage.href
|
||||
const defaultOpen = hasExactCategory ? isActive || isCurrent : false
|
||||
const isActive = routePath.includes(childPage.href) || routePath === childPage.href
|
||||
const defaultOpen = hasExactCategory ? isActive : false
|
||||
return (
|
||||
<li
|
||||
key={childPage.href + i}
|
||||
className={cx(
|
||||
'sidebar-category py-1',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page',
|
||||
isStandaloneCategory && 'standalone-category'
|
||||
)}
|
||||
data-is-active-category={isActive}
|
||||
data-is-current-page={isActive && isStandaloneCategory}
|
||||
className={cx('py-1', isActive && 'color-bg-info')}
|
||||
>
|
||||
{isStandaloneCategory ? (
|
||||
<Link
|
||||
@@ -86,7 +86,7 @@ export const SidebarProduct = () => {
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,8 +100,46 @@ const CollapsibleSection = (props: SectionProps) => {
|
||||
const { routePath, defaultOpen, title, page } = props
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen)
|
||||
|
||||
const onToggle = (e: SyntheticEvent) => {
|
||||
const newIsOpen = (e.target as HTMLDetailsElement).open
|
||||
setIsOpen(newIsOpen)
|
||||
sendEvent({
|
||||
type: EventType.navigate,
|
||||
navigate_label: `details ${newIsOpen ? 'open' : 'close'}: ${title}`,
|
||||
})
|
||||
}
|
||||
|
||||
// The lowest level page link displayed in the tree
|
||||
const renderTerminalPageLink = (page: ProductTreeNode) => {
|
||||
const title = page.renderedShortTitle || page.renderedFullTitle
|
||||
|
||||
const isCurrent = routePath === page.href
|
||||
return (
|
||||
<li
|
||||
data-testid="sidebar-article"
|
||||
data-is-current-page={isCurrent}
|
||||
key={page.href}
|
||||
className={cx(
|
||||
'position-relative',
|
||||
styles.sidebarArticle,
|
||||
isCurrent && ['text-bold', styles.sidebarArticleActive]
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href={page.href}
|
||||
className={cx(
|
||||
'd-block pl-6 pr-5 py-1 no-underline',
|
||||
isCurrent ? 'color-text-link' : 'color-text-primary'
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<details open={defaultOpen} onToggle={(e) => setIsOpen((e.target as HTMLDetailsElement).open)}>
|
||||
<details open={defaultOpen} onToggle={onToggle} className="details-reset">
|
||||
<summary>
|
||||
<div className="d-flex flex-justify-between">
|
||||
<div className="pl-4 pr-1 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary no-underline text-bold">
|
||||
@@ -115,9 +153,9 @@ const CollapsibleSection = (props: SectionProps) => {
|
||||
|
||||
{
|
||||
<>
|
||||
{/* <!-- some categories have maptopics with child articles --> */}
|
||||
{/* <!-- some pages have nested child pages (formerly known as a mapTopic) --> */}
|
||||
{page.childPages[0]?.page.documentType === 'mapTopic' ? (
|
||||
<ul className="sidebar-topics list-style-none position-relative">
|
||||
<ul className="list-style-none position-relative">
|
||||
{page.childPages.map((childPage, i) => {
|
||||
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
|
||||
|
||||
@@ -125,42 +163,17 @@ const CollapsibleSection = (props: SectionProps) => {
|
||||
const isCurrent = routePath === childPage.href
|
||||
|
||||
return (
|
||||
<li
|
||||
key={childPage.href + i}
|
||||
className={cx(
|
||||
'sidebar-maptopic',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<details open={isActive} onToggle={(e) => e.stopPropagation()}>
|
||||
<li key={childPage.href + i} data-is-current-page={isCurrent}>
|
||||
<details
|
||||
open={isActive}
|
||||
onToggle={(e) => e.stopPropagation()}
|
||||
className="details-reset"
|
||||
>
|
||||
<summary>
|
||||
<div className={cx('pl-4 pr-5 py-2 no-underline')}>{childTitle}</div>
|
||||
</summary>
|
||||
<ul className="sidebar-articles my-2">
|
||||
{childPage.childPages.map((grandchildPage, i, arr) => {
|
||||
const grandchildTitle =
|
||||
grandchildPage.renderedShortTitle || grandchildPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = routePath === grandchildPage.href
|
||||
return (
|
||||
<li
|
||||
key={grandchildPage.href + i}
|
||||
className={cx('sidebar-article', isActive && 'active')}
|
||||
>
|
||||
<Link
|
||||
href={grandchildPage.href}
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1 no-underline',
|
||||
isLast && 'pb-2',
|
||||
isActive && 'color-text-link'
|
||||
)}
|
||||
>
|
||||
{grandchildTitle}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
<ul data-testid="sidebar-article-group" className="my-2 pb-2">
|
||||
{childPage.childPages.map(renderTerminalPageLink)}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
@@ -168,36 +181,9 @@ const CollapsibleSection = (props: SectionProps) => {
|
||||
})}
|
||||
</ul>
|
||||
) : page.childPages[0]?.page.documentType === 'article' ? (
|
||||
<ul className="sidebar-articles list-style-none">
|
||||
<ul data-testid="sidebar-article-group" className="list-style-none pb-2">
|
||||
{/* <!-- some categories have no maptopics, only articles --> */}
|
||||
{page.childPages.map((childPage, i, arr) => {
|
||||
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
|
||||
const isActive = routePath.includes(childPage.href)
|
||||
const isCurrent = routePath === childPage.href
|
||||
return (
|
||||
<li
|
||||
key={childPage.href + i}
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href={childPage.href}
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1 no-underline',
|
||||
isLast && 'pb-2',
|
||||
isActive && 'color-text-link'
|
||||
)}
|
||||
>
|
||||
{childTitle}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
{page.childPages.map(renderTerminalPageLink)}
|
||||
</ul>
|
||||
) : null}
|
||||
</>
|
||||
@@ -1,4 +1,4 @@
|
||||
.procedural-image-wrapper {
|
||||
.markdown-body .procedural-image-wrapper {
|
||||
display: block;
|
||||
padding: 10px 0;
|
||||
margin: 20px auto 0 auto;
|
||||
@@ -6,7 +6,7 @@
|
||||
max-width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.procedural-image-wrapper img {
|
||||
.markdown-body .procedural-image-wrapper img {
|
||||
border-radius: 5px;
|
||||
border: 2px solid var(--color-auto-gray-2);
|
||||
width: auto;
|
||||
@@ -16,11 +16,10 @@
|
||||
}
|
||||
|
||||
// make sure images that contain emoji render at the expected size
|
||||
img[src*="https://github.githubassets.com/images/icons/emoji"]
|
||||
.markdown-body img[src*="https://github.githubassets.com/images/icons/emoji"]
|
||||
{
|
||||
height: 20;
|
||||
width: 20;
|
||||
align: absmiddle;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
|
||||
@@ -22,7 +22,6 @@ $marketing-font-path: "/assets/fonts/inter/";
|
||||
@import "release-notes.scss";
|
||||
@import "search.scss";
|
||||
@import "shadows.scss";
|
||||
@import "sidebar.scss";
|
||||
@import "summary.scss";
|
||||
@import "syntax-highlighting.scss";
|
||||
@import "tables.scss";
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-bottom: $spacer-5;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
flex-shrink: 0;
|
||||
|
||||
@include breakpoint(xl) {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-products > li {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.sidebar-products a,
|
||||
.sidebar-products .arrow {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.sidebar-category,
|
||||
.sidebar-product {
|
||||
> a,
|
||||
summary a {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-category.active {
|
||||
background-color: rgba(#959da5, 0.1); // --color-auto-gray-4
|
||||
// We can't do rgba(var(--color-auto-gray-4, 0.1) quite yet
|
||||
// as the browsers are still working on supporting that combination
|
||||
}
|
||||
|
||||
.sidebar-article {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: $spacer-4;
|
||||
height: 100%;
|
||||
border-left: 1px solid var(--color-text-primary);
|
||||
width: 1px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::before {
|
||||
border-left: 2px solid var(--color-text-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-current-page > a {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
// only display child lists of active elements in sidebar
|
||||
.sidebar-product.active > ul,
|
||||
.sidebar-category.active > ul,
|
||||
.sidebar-maptopic.active > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar-category {
|
||||
> ul {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-maptopic > ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-maptopic > details > summary > div,
|
||||
.sidebar-article > a {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.sidebar-maptopic > details > summary > div:hover,
|
||||
.sidebar-article > a:hover {
|
||||
color: var(--color-text-primary);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-category summary {
|
||||
list-style: none;
|
||||
}
|
||||
summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
@@ -462,7 +462,9 @@ describe.skip('next/link client-side navigation', () => {
|
||||
response.url().startsWith('http://localhost:4001/_next/data')
|
||||
),
|
||||
page.waitForNavigation({ waitUntil: 'networkidle2' }),
|
||||
page.click('.sidebar-articles:nth-child(2) .sidebar-article:nth-child(1) a'),
|
||||
page.click(
|
||||
'[data-testid=sidebar-article-group]:nth-child(2) [data-testid=sidebar-article]:nth-child(1) a'
|
||||
),
|
||||
])
|
||||
|
||||
expect(response.status()).toBe(200)
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('server', () => {
|
||||
|
||||
test('renders the homepage with links to exptected products in both the sidebar and page body', async () => {
|
||||
const $ = await getDOM('/en')
|
||||
const sidebarItems = $('.sidebar-products li a').get()
|
||||
const sidebarItems = $('[data-testid=sidebar] li a').get()
|
||||
const sidebarTitles = sidebarItems.map((el) => $(el).text().trim())
|
||||
const sidebarHrefs = sidebarItems.map((el) => $(el).attr('href'))
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('server', () => {
|
||||
|
||||
test('renders the Enterprise homepage with links to exptected products in both the sidebar and page body', async () => {
|
||||
const $ = await getDOM(`/en/enterprise-server@${enterpriseServerReleases.latest}`)
|
||||
const sidebarItems = $('.sidebar-products li a').get()
|
||||
const sidebarItems = $('[data-testid=sidebar] li a').get()
|
||||
const sidebarTitles = sidebarItems.map((el) => $(el).text().trim())
|
||||
const sidebarHrefs = sidebarItems.map((el) => $(el).attr('href'))
|
||||
|
||||
|
||||
@@ -15,34 +15,45 @@ describe('sidebar', () => {
|
||||
})
|
||||
|
||||
test('highlights active product on Enterprise pages', async () => {
|
||||
expect($enterprisePage('.sidebar-products li.sidebar-product').length).toBe(1)
|
||||
expect($enterprisePage('.sidebar-products li.sidebar-product > a').text().trim()).toBe(
|
||||
'Enterprise administrators'
|
||||
)
|
||||
expect($enterprisePage('[data-testid=sidebar] [data-testid=sidebar-product]').length).toBe(1)
|
||||
expect(
|
||||
$enterprisePage('[data-testid=sidebar] [data-testid=sidebar-product] > a').text().trim()
|
||||
).toBe('Enterprise administrators')
|
||||
})
|
||||
|
||||
test('highlights active product on GitHub pages', async () => {
|
||||
expect($githubPage('.sidebar-products li.sidebar-product').length).toBe(1)
|
||||
expect($githubPage('.sidebar-products li.sidebar-product > a').text().trim()).toBe('GitHub')
|
||||
expect($githubPage('[data-testid=sidebar] [data-testid=sidebar-product]').length).toBe(1)
|
||||
expect(
|
||||
$githubPage('[data-testid=sidebar] [data-testid=sidebar-product] > a').text().trim()
|
||||
).toBe('GitHub')
|
||||
})
|
||||
|
||||
test('includes links to external products like the CLI, Atom, Electron, and CodeQL', async () => {
|
||||
expect($homePage('.sidebar-products a[href="https://cli.github.com/manual"]')).toHaveLength(1)
|
||||
expect($homePage('.sidebar-products a[href="https://atom.io/docs"]')).toHaveLength(1)
|
||||
expect($homePage('.sidebar-products a[href="https://electronjs.org/docs"]')).toHaveLength(1)
|
||||
expect($homePage('.sidebar-products a[href="https://codeql.github.com/docs"]')).toHaveLength(1)
|
||||
expect($homePage('[data-testid=sidebar] a[href="https://cli.github.com/manual"]')).toHaveLength(
|
||||
1
|
||||
)
|
||||
expect($homePage('[data-testid=sidebar] a[href="https://atom.io/docs"]')).toHaveLength(1)
|
||||
expect($homePage('[data-testid=sidebar] a[href="https://electronjs.org/docs"]')).toHaveLength(1)
|
||||
expect(
|
||||
$homePage('[data-testid=sidebar] a[href="https://codeql.github.com/docs"]')
|
||||
).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('adds an `is-current-page` class to the sidebar link to the current page', async () => {
|
||||
test('adds `data-is-current-page` and `data-is-active-category` properties to the sidebar link for the current page', async () => {
|
||||
const url =
|
||||
'/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings'
|
||||
const $ = await getDOM(url)
|
||||
expect($('.sidebar-products .is-current-page').length).toBe(1)
|
||||
expect($('.sidebar-products .is-current-page a').attr('href')).toContain(url)
|
||||
expect($('[data-testid=sidebar] [data-is-active-category=true]').length).toBe(1)
|
||||
expect($('[data-testid=sidebar] [data-is-current-page=true]').length).toBe(1)
|
||||
expect($('[data-testid=sidebar] [data-is-current-page=true] a').attr('href')).toContain(url)
|
||||
})
|
||||
|
||||
test('does not display Early Access as a product', async () => {
|
||||
expect($homePage('.sidebar-products li.sidebar-product[title*="Early"]').length).toBe(0)
|
||||
expect($homePage('.sidebar-products li.sidebar-product[title*="early"]').length).toBe(0)
|
||||
expect(
|
||||
$homePage('[data-testid=sidebar] [data-testid=sidebar-product][title*="Early"]').length
|
||||
).toBe(0)
|
||||
expect(
|
||||
$homePage('[data-testid=sidebar] [data-testid=sidebar-product][title*="early"]').length
|
||||
).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user