Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repo sync #16143

Merged
merged 11 commits into from
Mar 9, 2022
4 changes: 2 additions & 2 deletions .github/workflows/azure-preview-env-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ jobs:
rsync -rptovR ./user-code/assets/./**/*.png ./assets
rsync -rptovR ./user-code/data/./**/*.{yml,md} ./data
rsync -rptovR ./user-code/components/./**/*.{ts,tsx} ./components
rsync -rptovR ./user-code/lib/./**/*.{js,ts,json} ./lib
rsync -rptovR ./user-code/middleware/./**/*.{js,ts} ./middleware
rsync -rptovR --ignore-missing-args ./user-code/lib/./**/*.{js,ts} ./lib
rsync -rptovR --ignore-missing-args ./user-code/middleware/./**/*.{js,ts} ./middleware
rsync -rptovR ./user-code/pages/./**/*.{tsx} ./pages
rsync -rptovR ./user-code/stylesheets/./**/*.{scss} ./stylesheets

Expand Down
106 changes: 8 additions & 98 deletions components/article/ArticlePage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import { useState, useEffect, ReactNode } from 'react'
import { useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import dynamic from 'next/dynamic'
import cx from 'classnames'
import { ActionList, Heading } from '@primer/react'

import { ZapIcon, InfoIcon, ShieldLockIcon } from '@primer/octicons-react'
import { Callout } from 'components/ui/Callout'

import { Link } from 'components/Link'
import { DefaultLayout } from 'components/DefaultLayout'
import { ArticleTitle } from 'components/article/ArticleTitle'
import { MiniTocItem, useArticleContext } from 'components/context/ArticleContext'
import { useArticleContext } from 'components/context/ArticleContext'
import { useTranslation } from 'components/hooks/useTranslation'
import { LearningTrackNav } from './LearningTrackNav'
import { MarkdownContent } from 'components/ui/MarkdownContent'
import { Lead } from 'components/ui/Lead'
import { ArticleGridLayout } from './ArticleGridLayout'
import { PlatformPicker } from 'components/article/PlatformPicker'
import { ToolPicker } from 'components/article/ToolPicker'
import { MiniTocs } from 'components/ui/MiniTocs'

const ClientSideRedirectExceptions = dynamic(() => import('./ClientsideRedirectExceptions'), {
ssr: false,
})
const ClientSideHighlightJS = dynamic(() => import('./ClientSideHighlightJS'), { ssr: false })

// Mapping of a "normal" article to it's interactive counterpart
Expand Down Expand Up @@ -49,12 +45,11 @@ const interactiveAlternatives: Record<string, { href: string }> = {
href: '/codespaces/setting-up-your-project-for-codespaces/setting-up-your-project-for-codespaces?langId=py',
},
}

export type StructuredContentT = {
structuredContent?: ReactNode
type Props = {
children?: React.ReactNode
}

export const ArticlePage = ({ structuredContent }: StructuredContentT) => {
export const ArticlePage = ({ children }: Props) => {
const { asPath } = useRouter()
const {
title,
Expand All @@ -69,58 +64,9 @@ export const ArticlePage = ({ structuredContent }: StructuredContentT) => {
miniTocItems,
currentLearningTrack,
} = useArticleContext()
const renderedContent = structuredContent || renderedPage
const { t } = useTranslation('pages')
const currentPath = asPath.split('?')[0]

const renderTocItem = (item: MiniTocItem) => {
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 dangerouslySetInnerHTML={{ __html: item.contents }} />
{item.items && item.items.length > 0 ? (
<ul className="ml-3">{item.items.map(renderTocItem)}</ul>
) : null}
</div>
</ActionList.Item>
)
}

// We have some one-off redirects for rest api docs
// currently those are limited to the repos page, but
// that will grow soon as we restructure the rest api docs.
// This is a workaround to updating the hardcoded links
// directly in the REST API code in a separate repo, which
// requires many file changes and teams to sign off.
// While the organization is turbulent, we can do this.
// Once it's more settled, we can refactor the rest api code
// to leverage the OpenAPI urls rather than hardcoded urls.
// The code below determines if we should bother loading this redirecting
// component at all.
// The reason this isn't done at the server-level is because there you
// can't possibly access the URL hash. That's only known in client-side
// code.
const [loadClientsideRedirectExceptions, setLoadClientsideRedirectExceptions] = useState(false)
useEffect(() => {
const { hash, pathname } = window.location
// Today, Jan 2022, it's known explicitly what the pathname.
// In the future there might be more.
// Hopefully, we can some day delete all of this and no longer
// be dependent on the URL hash to do the redirect.
if (
hash &&
(pathname.endsWith('/rest/reference/repos') ||
pathname.endsWith('/rest/reference/enterprise-admin'))
) {
setLoadClientsideRedirectExceptions(true)
}
}, [])

// If the page contains `[data-highlight]` blocks, these pages need
// syntax highlighting. But not every page needs it, so it's conditionally
// lazy-loaded on the client.
Expand All @@ -139,29 +85,8 @@ export const ArticlePage = ({ structuredContent }: StructuredContentT) => {
// consecutive one does.
}, [asPath])

// Scrollable code blocks in our REST API docs and elsewhere aren't accessible
// via keyboard navigation without setting tabindex="0". But we don't want to set
// this attribute on every `<pre>` code block, only the ones where there are scroll
// bars because the content isn't all visible.
useEffect(() => {
const codeBlocks = document.querySelectorAll<HTMLPreElement>('pre')

codeBlocks.forEach((codeBlock) => {
if (
codeBlock.scrollWidth > codeBlock.clientWidth ||
codeBlock.scrollHeight > codeBlock.clientHeight
) {
codeBlock.setAttribute('tabindex', '0')
}
})
}, [])

return (
<DefaultLayout>
{/* Doesn't matter *where* this is included because it will
never render anything. It always just return null. */}
{loadClientsideRedirectExceptions && <ClientSideRedirectExceptions />}

{/* Doesn't matter *where* this is included because it will
never render anything. It always just return null. */}
{lazyLoadHighlightJS && <ClientSideHighlightJS />}
Expand Down Expand Up @@ -220,28 +145,13 @@ export const ArticlePage = ({ structuredContent }: StructuredContentT) => {
</div>
)}
{miniTocItems.length > 1 && (
<>
<Heading as="h2" id="in-this-article" className="mb-1" sx={{ fontSize: 1 }}>
<Link href="#in-this-article">{t('miniToc')}</Link>
</Heading>

<ActionList
key={title}
items={miniTocItems.map((items, i) => {
return {
key: title + i,
text: title,
renderItem: () => <ul>{renderTocItem(items)}</ul>,
}
})}
/>
</>
<MiniTocs pageTitle={title} miniTocItems={miniTocItems} />
)}
</>
}
>
<div id="article-contents">
<MarkdownContent>{renderedContent}</MarkdownContent>
<MarkdownContent>{children || renderedPage}</MarkdownContent>
{effectiveDate && (
<div className="mt-4" id="effectiveDate">
Effective as of:{' '}
Expand Down
4 changes: 4 additions & 0 deletions components/rest/RestParameterTable.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
}

tbody {
tr td {
width: auto;
}

tr td:first-child {
font-weight: bold;
}
Expand Down
159 changes: 159 additions & 0 deletions components/rest/RestReferencePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { useState, useEffect } from 'react'
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'

const ClientSideHighlightJS = dynamic(() => import('components/article/ClientSideHighlightJS'), {
ssr: false,
})

const ClientSideRedirectExceptions = dynamic(
() => import('components/article/ClientsideRedirectExceptions'),
{
ssr: false,
}
)

export type StructuredContentT = {
descriptions: any
introContent: string
restOperations: RestCategoryOperationsT
miniTocItems?: MiniTocItem[]
}

export const RestReferencePage = ({
descriptions,
introContent,
restOperations,
miniTocItems,
}: StructuredContentT) => {
const { asPath } = useRouter()
const { page } = useMainContext()
const subcategories = Object.keys(restOperations)

// We have some one-off redirects for rest api docs
// currently those are limited to the repos page, but
// that will grow soon as we restructure the rest api docs.
// This is a workaround to updating the hardcoded links
// directly in the REST API code in a separate repo, which
// requires many file changes and teams to sign off.
// While the organization is turbulent, we can do this.
// Once it's more settled, we can refactor the rest api code
// to leverage the OpenAPI urls rather than hardcoded urls.
// The code below determines if we should bother loading this redirecting
// component at all.
// The reason this isn't done at the server-level is because there you
// can't possibly access the URL hash. That's only known in client-side
// code.
const [loadClientsideRedirectExceptions, setLoadClientsideRedirectExceptions] = useState(false)
useEffect(() => {
const { hash, pathname } = window.location
// Today, Jan 2022, it's known explicitly what the pathname.
// In the future there might be more.
// Hopefully, we can some day delete all of this and no longer
// be dependent on the URL hash to do the redirect.
if (
hash &&
(pathname.endsWith('/rest/reference/repos') ||
pathname.endsWith('/rest/reference/enterprise-admin'))
) {
setLoadClientsideRedirectExceptions(true)
}
}, [])

// Scrollable code blocks in our REST API docs and elsewhere aren't accessible
// via keyboard navigation without setting tabindex="0". But we don't want to set
// this attribute on every `<pre>` code block, only the ones where there are scroll
// bars because the content isn't all visible.
useEffect(() => {
const codeBlocks = document.querySelectorAll<HTMLPreElement>('pre')

codeBlocks.forEach((codeBlock) => {
if (
codeBlock.scrollWidth > codeBlock.clientWidth ||
codeBlock.scrollHeight > codeBlock.clientHeight
) {
codeBlock.setAttribute('tabindex', '0')
}
})
}, [])

// If the page contains `[data-highlight]` blocks, these pages need
// syntax highlighting. But not every page needs it, so it's conditionally
// lazy-loaded on the client.
const [lazyLoadHighlightJS, setLazyLoadHighlightJS] = useState(false)
useEffect(() => {
// It doesn't need to use querySelector because all we care about is if
// there is greater than zero of these in the DOM.
// Note! This "core selector", which determines whether to bother
// or not, needs to match what's used inside ClientSideHighlightJS.tsx
if (document.querySelector('[data-highlight]')) {
setLazyLoadHighlightJS(true)
}

// Important to depend on the current path because the first page you
// load, before any client-side navigation, might not need it, but the
// consecutive one does.
}, [asPath])

return (
<DefaultLayout>
{/* Doesn't matter *where* this is included because it will
never render anything. It always just return null. */}
{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>
<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>
</div>
</ArticleGridLayout>
</div>
</DefaultLayout>
)
}
4 changes: 4 additions & 0 deletions components/rest/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@ export interface ChildParameter {
description: string
type: string
}

export interface RestCategoryOperationsT {
[subcategory: string]: Operation[]
}
Loading