Move mini tocs rest + collapsible (#26069)

* starting minitocs change

* moving rest mini tocs and adding functionality to collapse

* adding some comments

* update margins

* minor updates: fix scroll to top button add padding to resthttpmethod verbs and background color for nested arrows in minitocs

* Update components/rest/RestReferencePage.tsx

Co-authored-by: Rachael Sewell <rachmari@github.com>

* Update components/rest/RestReferencePage.tsx

Co-authored-by: Rachael Sewell <rachmari@github.com>

* fix comment

* fix wording and z index

* bring back articlegridlayout

* updating margins for lg and xl

Co-authored-by: Rachael Sewell <rachmari@github.com>
This commit is contained in:
Grace Park 2022-03-14 10:50:53 -07:00 коммит произвёл GitHub
Родитель 3b8b6e0e61
Коммит 21b59b5805
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 119 добавлений и 59 удалений

Просмотреть файл

@ -93,7 +93,7 @@ export const DefaultLayout = (props: Props) => {
style={{ height: '100vh' }}
>
<Header />
<main id="main-content">
<main id="main-content" style={{ scrollMarginTop: '5rem' }}>
<DeprecationBanner />
<RestBanner />

Просмотреть файл

@ -50,7 +50,7 @@ export function CodeBlock({ verb, headingLang, codeBlock, highlight }: Props) {
>
<code>
{verb && (
<span className="color-bg-accent-emphasis color-fg-on-emphasis rounded-1 text-uppercase">
<span className="color-bg-accent-emphasis color-fg-on-emphasis rounded-1 text-uppercase p-1">
{verb}
</span>
)}{' '}

Просмотреть файл

@ -1,5 +1,6 @@
.parameterTable {
table-layout: fixed !important;
z-index: 0;
thead {
tr {

Просмотреть файл

@ -1,17 +1,18 @@
import { useState, useEffect } from 'react'
import React, { useState, useEffect } from 'react'
import cx from 'classnames'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import { DefaultLayout } from 'components/DefaultLayout'
import { ArticleTitle } from 'components/article/ArticleTitle'
import { useMainContext } from 'components/context/MainContext'
import { MarkdownContent } from 'components/ui/MarkdownContent'
import { Lead } from 'components/ui/Lead'
import { ArticleGridLayout } from 'components/article/ArticleGridLayout'
import { MiniTocItem } from 'components/context/ArticleContext'
import { RestCategoryOperationsT } from './types'
import { RestOperation } from './RestOperation'
import { MiniTocs } from 'components/ui/MiniTocs'
import { ChevronDownIcon, ChevronUpIcon, SearchIcon } from '@primer/octicons-react'
import { useTranslation } from 'components/hooks/useTranslation'
import { ActionList } from '@primer/react'
const ClientSideHighlightJS = dynamic(() => import('components/article/ClientSideHighlightJS'), {
ssr: false,
@ -37,9 +38,11 @@ export const RestReferencePage = ({
restOperations,
miniTocItems,
}: StructuredContentT) => {
const { t } = useTranslation('pages')
const { asPath } = useRouter()
const { page } = useMainContext()
const subcategories = Object.keys(restOperations)
const [collapsed, setCollapsed] = useState({} as Record<number, boolean>)
// We have some one-off redirects for rest api docs
// currently those are limited to the repos page, but
@ -106,6 +109,43 @@ export const RestReferencePage = ({
// consecutive one does.
}, [asPath])
// Resetting the collapsed array when we move to another REST page
useEffect(() => {
setCollapsed({})
}, [asPath])
const handleClick = (param: number) => {
setCollapsed((prevState) => {
return { ...prevState, [param]: !prevState[param] }
})
}
const renderTocItem = (item: MiniTocItem, index: number) => {
return (
<ActionList.Item
as="li"
key={item.contents}
className={item.platform}
sx={{ listStyle: 'none', padding: '2px' }}
>
<div className={cx('lh-condensed d-block width-full')}>
<div className="d-inline-flex" dangerouslySetInnerHTML={{ __html: item.contents }} />
{item.items && item.items.length > 0 && (
<button
className="background-transparent border-0 ml-1"
onClick={() => handleClick(index)}
>
{!collapsed[index] ? <ChevronDownIcon /> : <ChevronUpIcon />}
</button>
)}
{collapsed[index] && item.items && item.items.length > 0 ? (
<ul className="ml-3">{item.items.map(renderTocItem)}</ul>
) : null}
</div>
</ActionList.Item>
)
}
return (
<DefaultLayout>
{/* Doesn't matter *where* this is included because it will
@ -113,47 +153,60 @@ export const RestReferencePage = ({
{loadClientsideRedirectExceptions && <ClientSideRedirectExceptions />}
{lazyLoadHighlightJS && <ClientSideHighlightJS />}
<div className="container-xl px-3 px-md-6 my-4">
<ArticleGridLayout
topper={<ArticleTitle>{page.title}</ArticleTitle>}
intro={
<>
{page.introPlainText && (
<Lead data-testid="lead" data-search="lead">
{page.introPlainText}
</Lead>
)}
</>
}
toc={
<>
{miniTocItems && miniTocItems.length > 1 && (
<MiniTocs pageTitle={page.title} miniTocItems={miniTocItems} />
)}
</>
}
>
<div key={`restCategory-introContent`}>
<div dangerouslySetInnerHTML={{ __html: introContent }} />
<div className="container-xl px-3 px-md-6 my-4 mx-xl-12 mx-lg-12">
<h1>{page.title}</h1>
{page.introPlainText && (
<Lead data-testid="lead" data-search="lead" className="markdown-body">
{page.introPlainText}
</Lead>
)}
<div key={`restCategory-introContent`}>
<div dangerouslySetInnerHTML={{ __html: introContent }} />
</div>
<div className="my-3 d-flex">
<div className="pr-3 mt-1">
<Circle className="color-fg-on-emphasis color-bg-emphasis">
<SearchIcon className="" size={15} />
</Circle>
</div>
<div id="article-contents">
<MarkdownContent>
{subcategories.map((subcategory, index) => (
<div key={`restCategory-${index}`}>
<div dangerouslySetInnerHTML={{ __html: descriptions[subcategory] }} />
{restOperations[subcategory].map((operation, index) => (
<RestOperation
key={`restOperation-${index}`}
operation={operation}
index={index}
/>
))}
</div>
))}
</MarkdownContent>
<h3>{t('miniToc')}</h3>
{miniTocItems && (
<ActionList
key={page.title}
items={miniTocItems.map((items, i) => {
return {
key: page.title + i,
text: page.title,
renderItem: () => <ul>{renderTocItem(items, i)}</ul>,
}
})}
/>
)}
</div>
</ArticleGridLayout>
</div>
<MarkdownContent>
{subcategories.map((subcategory, index) => (
<div key={`restCategory-${index}`}>
<div dangerouslySetInnerHTML={{ __html: descriptions[subcategory] }} />
{restOperations[subcategory].map((operation, index) => (
<RestOperation key={`restOperation-${index}`} operation={operation} index={index} />
))}
</div>
))}
</MarkdownContent>
</div>
</DefaultLayout>
)
}
const Circle = ({ className, children }: { className?: string; children?: React.ReactNode }) => {
return (
<div
className={cx('circle d-flex flex-justify-center flex-items-center', className)}
style={{ width: 24, height: 24 }}
>
{children}
</div>
)
}

Просмотреть файл

@ -11,26 +11,26 @@ export const ScrollButton = ({ className, ariaLabel }: ScrollButtonPropsT) => {
const [show, setShow] = useState(false)
useEffect(() => {
// show scroll button only when view is scrolled down
const onScroll = function () {
const y = document.documentElement.scrollTop // get the height from page top
if (y < 100) {
setShow(false)
} else if (y >= 100) {
setShow(true)
}
}
window.addEventListener('scroll', onScroll)
// We cannot determine document.documentElement.scrollTop height because we set the height: 100vh and set overflow to auto to keep the header sticky
// That means window.scrollTop height is always 0
// Using IntersectionObserver we can detemine if the h1 header is in view or not. If not, we show the scroll to top button, if so, we hide it
const observer = new IntersectionObserver(
function (entries) {
if (entries[0].isIntersecting === false) {
setShow(true)
} else {
setShow(false)
}
},
{ threshold: [0] }
)
return () => {
window.removeEventListener('scroll', onScroll)
}
observer.observe(document.getElementsByTagName('h1')[0])
}, [])
const onClick = () => {
window.scrollTo(0, 0)
const topOfPage = document.getElementById('github-logo')
if (topOfPage) topOfPage.focus()
document?.getElementById('github-logo')?.focus()
document?.getElementById('main-content')?.scrollIntoView()
}
return (

Просмотреть файл

@ -78,6 +78,12 @@
border-color: transparent !important;
}
/* Background colors
------------------------------------------------------------------------------*/
.background-transparent {
background-color: transparent;
}
/* Widths / Heights
------------------------------------------------------------------------------*/
.max-w-xs {