Skip to content

Commit 32c8ad3

Browse files
authored
Merge pull request github#23886 from github/repo-sync
repo sync
2 parents a143b99 + e5370a5 commit 32c8ad3

20 files changed

+340
-199
lines changed

components/DefaultLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const DefaultLayout = (props: Props) => {
4242
</Head>
4343

4444
{/* For local site search indexing */}
45-
<div data-search="breadcrumbs">
45+
<div className="d-none d-xl-block" data-search="breadcrumbs">
4646
<Breadcrumbs />
4747
</div>
4848

components/article/ArticleGridLayout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const ArticleGridLayout = ({ intro, topper, toc, children, className }: P
1717
<SidebarContent
1818
gridArea="sidebar"
1919
alignSelf="flex-start"
20-
className="border-bottom border-xl-0 pb-4 mb-5 pb-xl-0 mb-xl-0"
20+
className="border-bottom border-lg-0 pb-4 mb-5 pb-xl-0 mb-xl-0"
2121
>
2222
{toc}
2323
</SidebarContent>
@@ -44,7 +44,7 @@ const Container = styled(Box)`
4444
4545
row-gap: ${themeGet('space.2')};
4646
47-
@media (min-width: ${themeGet('breakpoints.3')}) {
47+
@media (min-width: ${themeGet('breakpoints.2')}) {
4848
max-width: none;
4949
padding-top: ${themeGet('space.4')};
5050
grid-template-rows: auto 1fr;
@@ -59,7 +59,7 @@ const Container = styled(Box)`
5959
`
6060

6161
const SidebarContent = styled(Box)`
62-
@media (min-width: ${themeGet('breakpoints.3')}) {
62+
@media (min-width: ${themeGet('breakpoints.2')}) {
6363
position: sticky;
6464
padding-top: ${themeGet('space.4')};
6565
top: 5em;

components/article/ArticlePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const ArticlePage = () => {
5353
<ClientSideHighlight />
5454
{router.pathname.includes('/rest/') && <RestRedirect />}
5555
<div className="container-xl px-3 px-md-6 my-4">
56-
<div className={cx('my-3 mr-auto width-full')}>
56+
<div className={cx('d-none d-xl-block mt-3 mr-auto width-full')}>
5757
<Breadcrumbs />
5858
</div>
5959
<ArticleGridLayout

components/landing/ProductLanding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const ProductLanding = () => {
3434
<div data-search="article-body">
3535
{router.query.productId === 'rest' && <RestRedirect />}
3636
<LandingSection className="pt-3">
37-
<div className={cx('my-3 mr-auto width-full')}>
37+
<div className={cx('d-none d-xl-block my-3 mr-auto width-full')}>
3838
<Breadcrumbs />
3939
</div>
4040
<LandingHero />

components/landing/TocLanding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const TocLanding = () => {
3838
<ClientSideRedirects />
3939

4040
<div className="container-xl px-3 px-md-6 my-4">
41-
<div className={cx('my-3 mr-auto width-full')}>
41+
<div className={cx('d-none d-xl-block mt-3 mr-auto width-full')}>
4242
<Breadcrumbs />
4343
</div>
4444
<ArticleGridLayout>

components/page-header/Breadcrumbs.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import cx from 'classnames'
2-
import { useRouter } from 'next/router'
2+
33
import { useMainContext } from '../context/MainContext'
44
import { Link } from 'components/Link'
5+
56
import styles from './Breadcrumbs.module.scss'
67

8+
type Props = {
9+
inHeader?: boolean
10+
}
11+
712
export type BreadcrumbT = {
813
title: string
914
href?: string
1015
}
1116

12-
export const Breadcrumbs = () => {
13-
const router = useRouter()
14-
// remove query string and hash
15-
const pathWithLocale = `/${router.locale}${router.asPath.split('?')[0].split('#')[0]}`
17+
export const Breadcrumbs = ({ inHeader }: Props) => {
1618
const { breadcrumbs } = useMainContext()
1719

1820
return (
@@ -23,7 +25,7 @@ export const Breadcrumbs = () => {
2325
updating script/search/parse-page-sections-into-records.js.
2426
*/
2527
<nav
26-
data-testid="breadcrumbs"
28+
data-testid={inHeader ? 'breadcrumbs-header' : 'breadcrumbs-in-article'}
2729
className={cx('f5 breadcrumbs', styles.breadcrumbs)}
2830
aria-label="Breadcrumb"
2931
>
@@ -44,21 +46,19 @@ export const Breadcrumbs = () => {
4446
href={breadcrumb.href}
4547
title={title}
4648
className={cx(
47-
'pr-3',
48-
// Always show first and last, show middle on XL size
49-
i === 0 || i === arr.length - 1
50-
? 'd-inline-block'
51-
: 'd-none d-xl-inline-block',
52-
pathWithLocale === breadcrumb.href && 'color-fg-muted'
49+
'Link--primary mr-2 color-fg-muted',
50+
// Show the last breadcrumb if it's in the header, but not if it's in the article
51+
// If there's only 1 breadcrumb, show it
52+
!inHeader && i === arr.length - 1 && arr.length !== 1 && 'd-none'
5353
)}
5454
>
5555
{breadcrumb.title}
56-
{i !== arr.length - 1 ? (
57-
<span className="color-fg-muted pl-3" key={`${i}-slash`}>
58-
/
59-
</span>
60-
) : null}
6156
</Link>
57+
{i !== arr.length - 1 ? (
58+
<span className="color-fg-muted pr-2" key={`${i}-slash`}>
59+
/
60+
</span>
61+
) : null}
6262
</li>
6363
),
6464
]

components/page-header/Header.tsx

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { useCallback, useEffect, useRef, useState } from 'react'
22
import cx from 'classnames'
33
import { useRouter } from 'next/router'
4-
import { AnchoredOverlay, IconButton } from '@primer/react'
4+
import { AnchoredOverlay, Dialog, IconButton } from '@primer/react'
55
import {
66
KebabHorizontalIcon,
77
LinkExternalIcon,
88
MarkGithubIcon,
99
SearchIcon,
10+
ThreeBarsIcon,
1011
XIcon,
1112
} from '@primer/octicons-react'
1213

@@ -19,14 +20,17 @@ import { HeaderNotifications } from 'components/page-header/HeaderNotifications'
1920
import { ApiVersionPicker } from 'components/sidebar/ApiVersionPicker'
2021
import { useTranslation } from 'components/hooks/useTranslation'
2122
import { Search } from 'components/Search'
23+
import { Breadcrumbs } from 'components/page-header/Breadcrumbs'
2224
import { VersionPicker } from 'components/page-header/VersionPicker'
25+
import { SidebarNav } from 'components/sidebar/SidebarNav'
26+
import { AllProductsLink } from 'components/sidebar/AllProductsLink'
2327

2428
import styles from './Header.module.scss'
2529

2630
export const Header = () => {
2731
const router = useRouter()
2832
const { error } = useMainContext()
29-
const { currentProduct, allVersions } = useMainContext()
33+
const { isHomepageVersion, currentProduct, currentProductTree, allVersions } = useMainContext()
3034
const { currentVersion } = useVersion()
3135
const { t } = useTranslation(['header'])
3236
const isRestPage = currentProduct && currentProduct.id === 'rest'
@@ -36,12 +40,21 @@ export const Header = () => {
3640
const [isMenuOpen, setIsMenuOpen] = useState(false)
3741
const openMenuOverlay = useCallback(() => setIsMenuOpen(true), [setIsMenuOpen])
3842
const closeMenuOverlay = useCallback(() => setIsMenuOpen(false), [setIsMenuOpen])
43+
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
44+
const openSidebar = useCallback(() => setIsSidebarOpen(true), [isSidebarOpen])
45+
const closeSidebar = useCallback(() => setIsSidebarOpen(false), [isSidebarOpen])
3946
const isMounted = useRef(false)
4047
const menuButtonRef = useRef<HTMLButtonElement>(null)
41-
48+
const { asPath } = useRouter()
49+
const isSearchResultsPage = router.route === '/search'
4250
const signupCTAVisible =
4351
hasAccount === false && // don't show if `null`
4452
(currentVersion === DEFAULT_VERSION || currentVersion === 'enterprise-cloud@latest')
53+
const productTitle = currentProductTree?.shortTitle || currentProductTree?.title
54+
const [windowSize, setWindowSize] = useState(0)
55+
const handleWindowResize = useCallback(() => {
56+
setWindowSize(window.innerWidth)
57+
}, [])
4558

4659
useEffect(() => {
4760
function onScroll() {
@@ -75,6 +88,40 @@ export const Header = () => {
7588
}
7689
}, [isSearchOpen])
7790

91+
// When the sidebar overlay is opened, prevent the main content from being
92+
// scrollable.
93+
useEffect(() => {
94+
const bodyDiv = document.querySelector('body div') as HTMLElement
95+
const body = document.querySelector('body')
96+
if (bodyDiv && body) {
97+
// The full sidebar automatically shows at the xl window size so unlock
98+
// scrolling if the overlay was opened and the window size is increased to xl.
99+
body.style.overflow = isSidebarOpen && windowSize < 1280 ? 'hidden' : 'auto'
100+
}
101+
window.addEventListener('resize', handleWindowResize)
102+
return () => window.removeEventListener('resize', handleWindowResize)
103+
}, [isSidebarOpen, windowSize])
104+
105+
// with client side navigation clicking sidebar overlay links doesn't dismiss
106+
// the overlay so we close it ourselves when the path changes
107+
useEffect(() => {
108+
setIsSidebarOpen(false)
109+
}, [asPath])
110+
111+
// on REST pages there are sidebar links that are hash anchor links to different
112+
// sections on the same page so the sidebar overlay doesn't dismiss. we listen
113+
// for hash changes and close the overlay when the hash changes.
114+
useEffect(() => {
115+
const hashChangeHandler = () => {
116+
setIsSidebarOpen(false)
117+
}
118+
window.addEventListener('hashchange', hashChangeHandler)
119+
120+
return () => {
121+
window.removeEventListener('hashchange', hashChangeHandler)
122+
}
123+
}, [])
124+
78125
return (
79126
<>
80127
<div
@@ -86,12 +133,12 @@ export const Header = () => {
86133
{error !== '404' && <HeaderNotifications />}
87134
<header
88135
className={cx(
89-
'color-bg-default px-3 pt-3 pb-3 position-sticky top-0 z-1 border-bottom',
136+
'color-bg-default p-2 position-sticky top-0 z-1 border-bottom',
90137
scroll && 'color-shadow-small'
91138
)}
92139
>
93140
<div
94-
className="d-flex flex-justify-between flex-items-center flex-wrap"
141+
className="d-flex flex-justify-between p-2 flex-items-center flex-wrap"
95142
data-testid="desktop-header"
96143
>
97144
<div
@@ -170,8 +217,9 @@ export const Header = () => {
170217
/>
171218

172219
{/* The ... navigation menu at medium and smaller widths */}
173-
<nav>
220+
<div>
174221
<AnchoredOverlay
222+
anchorRef={menuButtonRef}
175223
renderAnchor={(anchorProps) => (
176224
<IconButton
177225
data-testid="mobile-menu"
@@ -205,7 +253,7 @@ export const Header = () => {
205253
</span>
206254
{isRestPage && allVersions[currentVersion].apiVersions.length > 0 && (
207255
<span className="pb-2 m-2 d-block">
208-
<ApiVersionPicker mediumOrLower={true} />
256+
<ApiVersionPicker />
209257
</span>
210258
)}
211259
{signupCTAVisible && (
@@ -222,9 +270,66 @@ export const Header = () => {
222270
)}
223271
</div>
224272
</AnchoredOverlay>
225-
</nav>
273+
</div>
226274
</div>
227275
</div>
276+
{!isHomepageVersion && !isSearchResultsPage && (
277+
<div className="d-flex flex-items-center d-xl-none mt-2">
278+
<div className={cx(styles.sidebarOverlayCloseButtonContainer, 'mr-2')}>
279+
<IconButton
280+
data-testid="sidebar-hamburger"
281+
className="color-fg-muted"
282+
variant="invisible"
283+
icon={ThreeBarsIcon}
284+
aria-label="Open Sidebar"
285+
onClick={openSidebar}
286+
/>
287+
<Dialog
288+
isOpen={isSidebarOpen}
289+
onDismiss={closeSidebar}
290+
aria-labelledby="menu-title"
291+
sx={{
292+
position: 'fixed',
293+
top: '0',
294+
left: '0',
295+
marginTop: '0',
296+
maxHeight: '100vh',
297+
width: 'auto !important',
298+
transform: 'none',
299+
borderRadius: '0',
300+
borderRight: '1px solid var(--color-border-default)',
301+
}}
302+
>
303+
<Dialog.Header
304+
style={{ paddingTop: '0px', background: 'none' }}
305+
id="sidebar-overlay-header"
306+
sx={{ display: 'block' }}
307+
>
308+
<AllProductsLink />
309+
{error === '404' ||
310+
!currentProduct ||
311+
isSearchResultsPage ||
312+
!currentProductTree ? null : (
313+
<div className="mt-3">
314+
<Link
315+
data-testid="sidebar-product-dialog"
316+
href={currentProductTree.href}
317+
className="d-block pl-1 mb-2 h3 color-fg-default no-underline"
318+
>
319+
{productTitle}
320+
</Link>
321+
</div>
322+
)}
323+
{isRestPage && <ApiVersionPicker />}
324+
</Dialog.Header>
325+
<SidebarNav variant="overlay" />
326+
</Dialog>
327+
</div>
328+
<div className="mr-auto width-full" data-search="breadcrumbs">
329+
<Breadcrumbs inHeader={true} />
330+
</div>
331+
</div>
332+
)}
228333
</header>
229334
</div>
230335
</>

components/page-header/HeaderNotifications.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const HeaderNotifications = () => {
9898
className={cx(
9999
'flash flash-banner',
100100
styles.container,
101-
'text-center f5 color-fg-default py-4 px-6',
101+
'text-center f5 color-fg-default py-4 px-6 z-1',
102102
type === NotificationType.TRANSLATION && 'color-bg-accent',
103103
type === NotificationType.RELEASE && 'color-bg-accent',
104104
type === NotificationType.EARLY_ACCESS && 'color-bg-danger',

components/page-header/LanguagePicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const LanguagePicker = ({ mediumOrLower }: Props) => {
8181
}}
8282
buttonBorder={mediumOrLower}
8383
dataTestId="default-language"
84-
ariaLabel="Select language"
84+
ariaLabel={`Select language: current language is ${selectedLang.name}`}
8585
alignment={mediumOrLower ? 'start' : 'end'}
8686
/>
8787
</div>

components/page-header/VersionPicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const VersionPicker = ({ mediumOrLower }: Props) => {
9090
pickerLabel="Version"
9191
dataTestId="field"
9292
buttonBorder={mediumOrLower}
93-
ariaLabel="Select GitHub product version"
93+
ariaLabel={`Select GitHub product version: current version is ${currentVersion}`}
9494
renderItem={(item) => {
9595
return (
9696
<div data-testid="version-picker-item" className={cx(styles.itemsWidth)}>

0 commit comments

Comments
 (0)