Move more files into automated pipelines directory (#37415)
This commit is contained in:
58
src/automated-pipelines/components/AutomatedPage.tsx
Normal file
58
src/automated-pipelines/components/AutomatedPage.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { DefaultLayout } from 'components/DefaultLayout'
|
||||
import { ArticleTitle } from 'components/article/ArticleTitle'
|
||||
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
||||
import { Lead } from 'components/ui/Lead'
|
||||
import { PermissionsStatement } from 'components/ui/PermissionsStatement'
|
||||
import { ArticleGridLayout } from '../../../components/article/ArticleGridLayout'
|
||||
import { MiniTocs } from 'components/ui/MiniTocs'
|
||||
import { useAutomatedPageContext } from 'src/automated-pipelines/components/AutomatedPageContext'
|
||||
import { ClientSideHighlight } from 'components/ClientSideHighlight'
|
||||
import { Callout } from 'components/ui/Callout'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const AutomatedPage = ({ children }: Props) => {
|
||||
const { title, intro, renderedPage, miniTocItems, product, permissions } =
|
||||
useAutomatedPageContext()
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
<ClientSideHighlight />
|
||||
|
||||
<div className="container-xl px-3 px-md-6 my-4">
|
||||
<ArticleGridLayout
|
||||
topper={<ArticleTitle>{title}</ArticleTitle>}
|
||||
intro={
|
||||
<>
|
||||
{intro && (
|
||||
<Lead data-testid="lead" data-search="lead">
|
||||
{intro}
|
||||
</Lead>
|
||||
)}
|
||||
|
||||
{permissions && <PermissionsStatement permissions={permissions} />}
|
||||
|
||||
{product && (
|
||||
<Callout
|
||||
variant="success"
|
||||
className="mb-4"
|
||||
dangerouslySetInnerHTML={{ __html: product }}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
toc={miniTocItems.length > 1 && <MiniTocs miniTocItems={miniTocItems} />}
|
||||
>
|
||||
<div id="article-contents">
|
||||
{renderedPage && (
|
||||
<MarkdownContent className="pt-3 pb-4">{renderedPage}</MarkdownContent>
|
||||
)}
|
||||
{children && <MarkdownContent className="pt-3 pb-4">{children}</MarkdownContent>}
|
||||
</div>
|
||||
</ArticleGridLayout>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
)
|
||||
}
|
||||
38
src/automated-pipelines/components/AutomatedPageContext.tsx
Normal file
38
src/automated-pipelines/components/AutomatedPageContext.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
import type { MiniTocItem } from 'components/context/ArticleContext'
|
||||
|
||||
export type AutomatedPageContextT = {
|
||||
title: string
|
||||
intro: string
|
||||
renderedPage: string | JSX.Element[]
|
||||
miniTocItems: Array<MiniTocItem>
|
||||
product?: string
|
||||
permissions?: string
|
||||
}
|
||||
|
||||
export const AutomatedPageContext = createContext<AutomatedPageContextT | null>(null)
|
||||
|
||||
export const useAutomatedPageContext = (): AutomatedPageContextT => {
|
||||
const context = useContext(AutomatedPageContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'"useAutomatedPageContext" may only be used inside "AutomatedPageContext.Provider"'
|
||||
)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
export const getAutomatedPageContextFromRequest = (req: any): AutomatedPageContextT => {
|
||||
const page = req.context.page
|
||||
|
||||
return {
|
||||
title: page.title,
|
||||
intro: page.intro,
|
||||
renderedPage: req.context.renderedPage || '',
|
||||
miniTocItems: req.context.miniTocItems || [],
|
||||
product: page.product || '',
|
||||
permissions: page.permissions || '',
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
.childBodyParametersRows {
|
||||
details tr {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
// Remove any default markdown article padding for property cells
|
||||
details tr td {
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
// Set the left border for in the nested property tables. Also need to override
|
||||
// a default markdown file style that sets a table's font size based on
|
||||
// percentage which would cause the table font size to shrink more and more
|
||||
// as the properties nested more and more.
|
||||
td {
|
||||
table {
|
||||
border-left: 4px solid var(--color-border-muted);
|
||||
font-size: inherit !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { ParameterRow } from './ParameterRow'
|
||||
import type { ChildParameter } from './types'
|
||||
|
||||
import styles from './ChildBodyParametersRows.module.scss'
|
||||
|
||||
type Props = {
|
||||
open: boolean
|
||||
slug: string
|
||||
childParamsGroups: ChildParameter[]
|
||||
parentName: string
|
||||
parentType: string
|
||||
oneOfObject?: boolean
|
||||
}
|
||||
|
||||
export function ChildBodyParametersRows({
|
||||
open,
|
||||
slug,
|
||||
parentName,
|
||||
parentType,
|
||||
childParamsGroups,
|
||||
oneOfObject = false,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
return (
|
||||
<tr className={cx(styles.childBodyParametersRows, 'color-bg-subtle border-top-0')}>
|
||||
<td colSpan={4} className="has-nested-table">
|
||||
<details className="box px-3 ml-1 mb-0" open={open}>
|
||||
<summary role="button" aria-expanded="false" className="mb-2 keyboard-focus">
|
||||
{oneOfObject ? (
|
||||
<span id={`${slug}-${parentName}-${parentType}`}>Can be one of these objects:</span>
|
||||
) : (
|
||||
<span id={`${slug}-${parentName}-${parentType}`}>
|
||||
Properties of <code>{parentName}</code>
|
||||
</span>
|
||||
)}
|
||||
</summary>
|
||||
<table id={`${parentName}-object`} className="mb-4 color-bg-subtle">
|
||||
<thead className="visually-hidden">
|
||||
<tr>
|
||||
<th>{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{childParamsGroups.map((childParam, index) => {
|
||||
return (
|
||||
<ParameterRow
|
||||
rowParams={childParam}
|
||||
slug={slug}
|
||||
isChild={true}
|
||||
key={`${childParam.name}-${index}`}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { KeyboardEventHandler } from 'react'
|
||||
import { ChildBodyParametersRows } from './ChildBodyParametersRows'
|
||||
import type { ChildParameter } from './types'
|
||||
|
||||
type Props = {
|
||||
rowParams: ChildParameter
|
||||
slug: string
|
||||
numPreviews?: number
|
||||
isChild?: boolean
|
||||
rowIndex?: number
|
||||
bodyParamExpandCallback?: KeyboardEventHandler<HTMLButtonElement> | undefined
|
||||
clickedBodyParameterName?: string | undefined
|
||||
}
|
||||
|
||||
// Webhooks have these same properties in common that we describe separately in its
|
||||
// own section on the webhooks page:
|
||||
//
|
||||
// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#webhook-payload-object-common-properties
|
||||
//
|
||||
// Since there's more details for these particular properties, we chose not
|
||||
// show their child properties for each webhook and we also don't grab this
|
||||
// information from the schema.
|
||||
//
|
||||
// We use this list of common properties to make sure we don't try and request
|
||||
// the child properties for these specific properties.
|
||||
const NO_CHILD_WEBHOOK_PROPERTIES = [
|
||||
'action',
|
||||
'enterprise',
|
||||
'installation',
|
||||
'organization',
|
||||
'repository',
|
||||
'sender',
|
||||
]
|
||||
|
||||
export function ParameterRow({
|
||||
rowParams,
|
||||
slug,
|
||||
numPreviews = 0,
|
||||
isChild = false,
|
||||
rowIndex = 0,
|
||||
bodyParamExpandCallback = undefined,
|
||||
clickedBodyParameterName = undefined,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
|
||||
// This will be true if `rowParams` does not have a key called `default`
|
||||
// and it will be true if it does and its actual value is `undefined`.
|
||||
const hasDefault = rowParams.default !== undefined
|
||||
return (
|
||||
<>
|
||||
<tr className={`${isChild ? 'color-bg-subtle' : ''}`}>
|
||||
<td className={`${isChild ? 'px-3' : ''}`}>
|
||||
<div
|
||||
className={cx(
|
||||
'pl-0 pt-1 pr-1 pb-1',
|
||||
`${rowIndex > 0 && isChild ? 'pt-3 border-top color-border-muted' : ''}`
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
{rowParams.name ? (
|
||||
<>
|
||||
<code className={`text-bold f5`}>{rowParams.name}</code>
|
||||
{/* This whitespace is important otherwise, when the CSS is
|
||||
ignored, the plain text becomes `foobar` if the HTML
|
||||
was `<code>foo</code><span>bar</span>`.
|
||||
*/}{' '}
|
||||
<span className="color-fg-muted pl-2 f5">
|
||||
{Array.isArray(rowParams.type) ? rowParams.type.join(' or ') : rowParams.type}
|
||||
</span>
|
||||
{/* Ditto about the important explicit whitespace */}{' '}
|
||||
{rowParams.isRequired ? (
|
||||
<span className="color-fg-attention f5 pl-3">{t('required')}</span>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="color-fg-muted pl-1 f5">
|
||||
{Array.isArray(rowParams.type) ? rowParams.type.join(' or ') : rowParams.type}
|
||||
</span>
|
||||
{/* Ditto about the important explicit whitespace */}{' '}
|
||||
{rowParams.isRequired ? (
|
||||
<span className="color-fg-attention f5 pl-3">{t('required')}</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={cx('pl-1 f5', `${rowParams.description ? 'pt-2' : 'pt-0'}`)}>
|
||||
<div dangerouslySetInnerHTML={{ __html: rowParams.description }} />
|
||||
{numPreviews > 0 && (
|
||||
<a href={`#${slug}-preview-notices`} className="d-inline">
|
||||
{numPreviews > 1 ? ` ${t('see_preview_notices')}` : ` ${t('see_preview_notice')}`}
|
||||
</a>
|
||||
)}
|
||||
<div className={cx(`${hasDefault || rowParams.enum ? 'pt-2' : 'pt-0'}`)}>
|
||||
{hasDefault && (
|
||||
<p>
|
||||
<span>{t('default')}: </span>
|
||||
<code>
|
||||
{typeof rowParams.default === 'string'
|
||||
? // In the schema, the default value for strings can
|
||||
// potentially be the empty string so we handle this case
|
||||
// in particular by rendering it as "". Otherwise we would
|
||||
// display an empty code block which could be confusing.
|
||||
rowParams.default || '""'
|
||||
: JSON.stringify(rowParams.default)}
|
||||
</code>
|
||||
</p>
|
||||
)}
|
||||
{rowParams.enum && rowParams.enum.length && (
|
||||
<p>
|
||||
<span>
|
||||
{rowParams.enum.length === 1
|
||||
? t('single_enum_description')
|
||||
: t('enum_description_title')}
|
||||
:{' '}
|
||||
</span>
|
||||
{rowParams.enum.map((item, index, array) => (
|
||||
<span key={`${item}${index}`}>
|
||||
<code>{item === null ? <i>null</i> : item}</code>
|
||||
{index !== array.length - 1 && ','}{' '}
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{rowParams.childParamsGroups && rowParams.childParamsGroups.length > 0 && (
|
||||
<ChildBodyParametersRows
|
||||
slug={slug}
|
||||
parentName={rowParams.name}
|
||||
parentType={Array.isArray(rowParams.type) ? rowParams.type.join(' or ') : rowParams.type}
|
||||
childParamsGroups={rowParams.childParamsGroups}
|
||||
open={rowParams.name === clickedBodyParameterName}
|
||||
oneOfObject={rowParams.oneOfObject}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* These conditions tell us:
|
||||
|
||||
1. the param is an object or array AND:
|
||||
2. the param has no child param groups AND:
|
||||
3. the param isn't one of the common webhook properties
|
||||
|
||||
If all these are true, then that means we haven't yet loaded the
|
||||
nested parameters so we show a stub <details> element that triggers
|
||||
an API request to get the nested parameter data.
|
||||
*/}
|
||||
{rowParams.type &&
|
||||
(rowParams.type === 'object' || rowParams.type.includes('array of')) &&
|
||||
rowParams.childParamsGroups &&
|
||||
rowParams.childParamsGroups.length === 0 &&
|
||||
!NO_CHILD_WEBHOOK_PROPERTIES.includes(rowParams.name) ? (
|
||||
<tr className="border-top-0">
|
||||
<td colSpan={4} className="has-nested-table">
|
||||
<details
|
||||
data-nested-param-id={rowParams.name}
|
||||
className="box px-3 ml-1 mb-0"
|
||||
onToggle={bodyParamExpandCallback}
|
||||
>
|
||||
<summary role="button" aria-expanded="false" className="mb-2 keyboard-focus">
|
||||
{rowParams.oneOfObject ? (
|
||||
<span id={`${slug}-${rowParams.name}`}>
|
||||
Can be one of these objects: <code>{rowParams.name}</code>
|
||||
</span>
|
||||
) : (
|
||||
<span id={`${slug}-${rowParams.name}`}>
|
||||
Properties of <code>{rowParams.name}</code>
|
||||
</span>
|
||||
)}
|
||||
</summary>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.parameterTable {
|
||||
// this is for the child parameter table row that contains the top level
|
||||
// properties details toggle element because we want it to match the
|
||||
// background color of the top level parameter rows. We need the !important
|
||||
// because the child parameter rows (the nested expanded properties) otherwise
|
||||
// have the same background color.
|
||||
& > tbody > tr {
|
||||
background: var(--color-canvas-default) !important;
|
||||
}
|
||||
|
||||
// also for the top level child parameter table row, we want the details toggle
|
||||
// to align with the top level parameter rows. Child parameter rows have some
|
||||
// left padding so they can indent as they nest but we don't want that in
|
||||
// this case. We need the !important to override general default markdown
|
||||
// article styling.
|
||||
& > tbody > tr > td > details {
|
||||
padding-left: 0px !important;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
& > tbody > tr > td > * {
|
||||
width: auto;
|
||||
display: block;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import cx from 'classnames'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { KeyboardEventHandler } from 'react'
|
||||
|
||||
import { ParameterRow } from './ParameterRow'
|
||||
import { BodyParameter, ChildParameter, Parameter } from './types'
|
||||
|
||||
import styles from './ParameterTable.module.scss'
|
||||
|
||||
type Props = {
|
||||
slug: string
|
||||
numPreviews?: number
|
||||
heading?: string
|
||||
headers?: Array<ChildParameter>
|
||||
parameters?: Array<Parameter>
|
||||
bodyParameters: Array<BodyParameter>
|
||||
bodyParamExpandCallback?: KeyboardEventHandler<HTMLButtonElement> | undefined
|
||||
clickedBodyParameterName?: string | undefined
|
||||
}
|
||||
|
||||
export function ParameterTable({
|
||||
slug,
|
||||
numPreviews = 0,
|
||||
heading = '',
|
||||
headers = [],
|
||||
parameters = [],
|
||||
bodyParameters,
|
||||
bodyParamExpandCallback = undefined,
|
||||
clickedBodyParameterName = '',
|
||||
}: Props) {
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
const queryParams = parameters.filter((param) => param.in === 'query')
|
||||
const pathParams = parameters.filter((param) => param.in === 'path')
|
||||
|
||||
return (
|
||||
<>
|
||||
{heading && (
|
||||
<h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}>
|
||||
<a href={`#${slug}--parameters`}>{heading}</a>
|
||||
</h3>
|
||||
)}
|
||||
|
||||
<table
|
||||
className={cx(styles.parameterTable)}
|
||||
summary="Column one has the type of parameter and the other columns show the name, type, and description of the parameters"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
id="header"
|
||||
scope="col"
|
||||
className={cx(headers.length === 0 && 'visually-hidden', 'text-bold pl-0')}
|
||||
>
|
||||
{t('headers')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="col">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{headers.length > 0 && (
|
||||
<>
|
||||
{headers.map((header, index) => (
|
||||
<ParameterRow
|
||||
rowParams={header}
|
||||
slug={slug}
|
||||
numPreviews={numPreviews}
|
||||
key={`${index}-${header.name}`}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{pathParams.length > 0 && (
|
||||
<>
|
||||
<tr className="border-top-0">
|
||||
<th className="text-bold pl-0 border-top-0" scope="colgroup">
|
||||
{t('path')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
{pathParams.map((param, index) => (
|
||||
<ParameterRow
|
||||
rowParams={{
|
||||
name: param.name,
|
||||
type: param.schema.type,
|
||||
description: param.description,
|
||||
isRequired: param.required,
|
||||
default: param.schema.default,
|
||||
enum: param.schema.enum,
|
||||
}}
|
||||
slug={slug}
|
||||
key={`${index}-${param}`}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{queryParams.length > 0 && (
|
||||
<>
|
||||
<tr className="border-top-0">
|
||||
<th className="text-bold pl-0" scope="colgroup">
|
||||
{t('query')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
|
||||
{queryParams.map((param, index) => (
|
||||
<ParameterRow
|
||||
rowParams={{
|
||||
name: param.name,
|
||||
type: param.schema.type,
|
||||
description: param.description,
|
||||
isRequired: param.required,
|
||||
default: param.schema.default,
|
||||
enum: param.schema.enum,
|
||||
}}
|
||||
slug={slug}
|
||||
key={`${index}-${param}`}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{bodyParameters.length > 0 && (
|
||||
<>
|
||||
<tr className="border-top-0">
|
||||
{/* webhooks don't have a 'Parameters' table heading text so
|
||||
we adjust the size of the body params heading in that case */}
|
||||
<th scope="colgroup" className={cx(heading ? 'text-bold' : 'h4', 'pl-0')}>
|
||||
{t('body')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
|
||||
{bodyParameters.map((param, index) => (
|
||||
<ParameterRow
|
||||
bodyParamExpandCallback={bodyParamExpandCallback}
|
||||
clickedBodyParameterName={clickedBodyParameterName}
|
||||
rowParams={param}
|
||||
slug={slug}
|
||||
key={`${index}-${param}`}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
)
|
||||
}
|
||||
33
src/automated-pipelines/components/parameter-table/types.ts
Normal file
33
src/automated-pipelines/components/parameter-table/types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export interface Parameter {
|
||||
in: string
|
||||
name: string
|
||||
description: string
|
||||
required: boolean
|
||||
schema: {
|
||||
type: string
|
||||
default?: string
|
||||
enum?: Array<string>
|
||||
}
|
||||
}
|
||||
|
||||
export interface BodyParameter {
|
||||
in: string
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
isRequired?: boolean
|
||||
default?: string
|
||||
enum?: Array<string>
|
||||
childParamsGroups?: Array<ChildParameter>
|
||||
}
|
||||
|
||||
export interface ChildParameter {
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
isRequired?: boolean
|
||||
enum?: Array<string | null>
|
||||
default?: string | boolean | number | undefined | string[]
|
||||
childParamsGroups?: ChildParameter[]
|
||||
oneOfObject?: boolean
|
||||
}
|
||||
Reference in New Issue
Block a user