New nested param table styles for REST (#30862)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
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 = {
|
||||
slug: string
|
||||
childParamsGroups: ChildParameter[]
|
||||
@@ -15,27 +19,23 @@ export function ChildBodyParametersRows({
|
||||
parentType,
|
||||
childParamsGroups,
|
||||
}: Props) {
|
||||
const { t } = useTranslation('products')
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
|
||||
return (
|
||||
<tr className="border-top-0">
|
||||
<tr className={cx(styles.childBodyParametersRows, 'color-bg-subtle border-top-0')}>
|
||||
<td colSpan={4} className="has-nested-table">
|
||||
<details className="ml-1">
|
||||
<summary role="button" aria-expanded="false" className="keyboard-focus color-fg-muted">
|
||||
<span className="d-inline-block mb-3" id={`${slug}-${parentName}-${parentType}`}>
|
||||
Properties of the
|
||||
<code>{parentName}</code>
|
||||
{parentType}
|
||||
</span>
|
||||
<details className="box px-3 ml-1 mb-0">
|
||||
<summary
|
||||
role="button"
|
||||
aria-expanded="false"
|
||||
className="mb-2 keyboard-focus color-fg-muted"
|
||||
>
|
||||
<span id={`${slug}-${parentName}-${parentType}`}>Properties of {parentName}</span>
|
||||
</summary>
|
||||
<table id={`${parentName}-object`} className="mb-4 mt-2 color-bg-subtle">
|
||||
<thead className="visually-hidden">
|
||||
<tr>
|
||||
<th>
|
||||
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
|
||||
'rest.reference.description'
|
||||
)}`}
|
||||
</th>
|
||||
<th>{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
94
components/parameter-table/ParameterRow.tsx
Normal file
94
components/parameter-table/ParameterRow.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { ChildBodyParametersRows } from './ChildBodyParametersRows'
|
||||
import type { ChildParameter } from './types'
|
||||
|
||||
type Props = {
|
||||
rowParams: ChildParameter
|
||||
slug: string
|
||||
numPreviews?: number
|
||||
isChild?: boolean
|
||||
rowIndex?: number
|
||||
}
|
||||
|
||||
export function ParameterRow({
|
||||
rowParams,
|
||||
slug,
|
||||
numPreviews = 0,
|
||||
rowIndex = 0,
|
||||
isChild = false,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
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>
|
||||
<code className={`text-bold f5`}>{rowParams.name}</code>
|
||||
<span className="color-fg-muted pl-2 f5">{rowParams.type}</span>
|
||||
{rowParams.isRequired ? (
|
||||
<span className="color-fg-attention f5 pl-3">{t('required')}</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cx('pl-1 color-fg-muted 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(`${rowParams.default || rowParams.enum ? 'pt-2' : 'pt-0'}`)}>
|
||||
{rowParams.default && (
|
||||
<p>
|
||||
<span>{t('default')}: </span>
|
||||
<code>{rowParams.default}</code>
|
||||
</p>
|
||||
)}
|
||||
{rowParams.enum && rowParams.enum.length && (
|
||||
<p>
|
||||
{rowParams.enum.length > 1 ? (
|
||||
<>
|
||||
<span>{t('enum_description_title')}: </span>
|
||||
{rowParams.enum.map((item, index, array) => (
|
||||
<span key={item + index}>
|
||||
<code className="color-bg-muted">{item}</code>
|
||||
{index !== array.length - 1 && ','}{' '}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>{t('single_enum_description')}: </span>
|
||||
<span key={rowParams.enum[0]}>
|
||||
<code>{rowParams.enum[0]}</code>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{rowParams.childParamsGroups && rowParams.childParamsGroups.length > 0 && (
|
||||
<ChildBodyParametersRows
|
||||
slug={slug}
|
||||
parentName={rowParams.name}
|
||||
parentType={rowParams.type}
|
||||
childParamsGroups={rowParams.childParamsGroups}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
20
components/parameter-table/ParameterTable.module.scss
Normal file
20
components/parameter-table/ParameterTable.module.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
.parameterTable {
|
||||
// this is for the child parameter table row that contiains 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;
|
||||
}
|
||||
}
|
||||
@@ -2,68 +2,77 @@ import cx from 'classnames'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
|
||||
import { ParameterRow } from './ParameterRow'
|
||||
import { BodyParameter, Parameter } from './types'
|
||||
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>
|
||||
}
|
||||
|
||||
export function RestParameterTable({ slug, numPreviews, parameters, bodyParameters }: Props) {
|
||||
const { t } = useTranslation('products')
|
||||
export function ParameterTable({
|
||||
slug,
|
||||
numPreviews,
|
||||
heading = '',
|
||||
headers = [],
|
||||
parameters,
|
||||
bodyParameters,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(['parameter_table', 'products'])
|
||||
const queryParams = parameters.filter((param) => param.in === 'query')
|
||||
const pathParams = parameters.filter((param) => param.in === 'path')
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}>
|
||||
<a href={`#${slug}--parameters`}>{t('rest.reference.parameters')}</a>
|
||||
</h3>
|
||||
{heading && (
|
||||
<h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}>
|
||||
<a href={`#${slug}--parameters`}>{heading}</a>
|
||||
</h3>
|
||||
)}
|
||||
|
||||
<table
|
||||
className={cx('d-block')}
|
||||
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="text-bold pl-0">
|
||||
{t('rest.reference.headers')}
|
||||
{t('headers')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="col">
|
||||
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
|
||||
'rest.reference.description'
|
||||
)}`}
|
||||
</th>
|
||||
<th scope="col">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<ParameterRow
|
||||
rowParams={{
|
||||
name: 'accept',
|
||||
type: 'string',
|
||||
description: `<p>Setting to <code>application/vnd.github+json</code> is recommended.</p>`,
|
||||
isRequired: false,
|
||||
}}
|
||||
slug={slug}
|
||||
numPreviews={numPreviews}
|
||||
/>
|
||||
{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('rest.reference.path')}
|
||||
{t('path')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">
|
||||
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
|
||||
'rest.reference.description'
|
||||
)}`}
|
||||
</th>
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
{pathParams.map((param, index) => (
|
||||
<ParameterRow
|
||||
@@ -86,15 +95,11 @@ export function RestParameterTable({ slug, numPreviews, parameters, bodyParamete
|
||||
<>
|
||||
<tr className="border-top-0">
|
||||
<th className="text-bold pl-0" scope="colgroup">
|
||||
{t('rest.reference.query')}
|
||||
{t('query')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">
|
||||
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
|
||||
'rest.reference.description'
|
||||
)}`}
|
||||
</th>
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
|
||||
{queryParams.map((param, index) => (
|
||||
@@ -118,15 +123,11 @@ export function RestParameterTable({ slug, numPreviews, parameters, bodyParamete
|
||||
<>
|
||||
<tr className="border-top-0">
|
||||
<th scope="colgroup" className="text-bold pl-0">
|
||||
{t('rest.reference.body')}
|
||||
{t('body')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr className="visually-hidden">
|
||||
<th scope="colgroup">
|
||||
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
|
||||
'rest.reference.description'
|
||||
)}`}
|
||||
</th>
|
||||
<th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
|
||||
</tr>
|
||||
|
||||
{bodyParameters.map((param, index) => (
|
||||
32
components/parameter-table/types.ts
Normal file
32
components/parameter-table/types.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
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>
|
||||
default?: string
|
||||
childParamsGroups?: ChildParameter[]
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { ChildBodyParametersRows } from './ChildBodyParametersRows'
|
||||
import type { ChildParameter } from './types'
|
||||
|
||||
type Props = {
|
||||
rowParams: ChildParameter
|
||||
slug: string
|
||||
numPreviews?: number
|
||||
isChild?: boolean
|
||||
}
|
||||
|
||||
export function ParameterRow({ rowParams, slug, numPreviews = 0, isChild = false }: Props) {
|
||||
const { t } = useTranslation('products')
|
||||
return (
|
||||
<>
|
||||
<tr className={`${isChild ? 'color-bg-subtle' : ''}`}>
|
||||
<td className={`${isChild ? 'px-3' : ''}`}>
|
||||
<div>
|
||||
<code className={`text-bold ${isChild ? 'f6' : 'f5'}`}>{rowParams.name}</code>
|
||||
<span className="color-fg-muted pl-2 f5">{rowParams.type}</span>
|
||||
{rowParams.isRequired ? (
|
||||
<span className={`color-fg-attention f5 ${isChild ? 'pl-3' : 'float-right'}`}>
|
||||
{t('rest.reference.required')}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="pl-1 pt-2 color-fg-muted f5">
|
||||
<div dangerouslySetInnerHTML={{ __html: rowParams.description }} />
|
||||
{numPreviews > 0 && (
|
||||
<a href={`#${slug}-preview-notices`} className="d-inline">
|
||||
{numPreviews > 1
|
||||
? ` ${t('rest.reference.see_preview_notices')}`
|
||||
: ` ${t('rest.reference.see_preview_notice')}`}
|
||||
</a>
|
||||
)}
|
||||
<div className="pt-2">
|
||||
{rowParams.default && (
|
||||
<p>
|
||||
<span>{t('rest.reference.default')}: </span>
|
||||
<code>{rowParams.default}</code>
|
||||
</p>
|
||||
)}
|
||||
{rowParams.enum && rowParams.enum.length && (
|
||||
<p>
|
||||
<span>{t('rest.reference.enum_description_title')}: </span>
|
||||
|
||||
{rowParams.enum.map((item, index, array) => {
|
||||
return index !== array.length - 1 ? (
|
||||
<span key={item + index}>
|
||||
<code>{item}</code>,{' '}
|
||||
</span>
|
||||
) : (
|
||||
<span key={item + index}>
|
||||
<code>{item}</code>
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{rowParams.childParamsGroups && rowParams.childParamsGroups.length > 0 && (
|
||||
<ChildBodyParametersRows
|
||||
slug={slug}
|
||||
parentName={rowParams.name}
|
||||
parentType={rowParams.type}
|
||||
childParamsGroups={rowParams.childParamsGroups}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { Link } from 'components/Link'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { RestPreviewNotice } from './RestPreviewNotice'
|
||||
import styles from './RestOperation.module.scss'
|
||||
import { RestParameterTable } from './RestParameterTable'
|
||||
import { ParameterTable } from 'components/parameter-table/ParameterTable'
|
||||
import { RestCodeSamples } from './RestCodeSamples'
|
||||
import { RestStatusCodes } from './RestStatusCodes'
|
||||
import { Operation } from './types'
|
||||
@@ -17,11 +17,20 @@ type Props = {
|
||||
operation: Operation
|
||||
}
|
||||
|
||||
// all REST operations have this accept header by default
|
||||
const DEFAULT_ACCEPT_HEADER = {
|
||||
name: 'accept',
|
||||
type: 'string',
|
||||
description: `<p>Setting to <code>application/vnd.github+json</code> is recommended.</p>`,
|
||||
isRequired: false,
|
||||
}
|
||||
|
||||
export function RestOperation({ operation }: Props) {
|
||||
const slug = slugger.slug(operation.title)
|
||||
const { t } = useTranslation('products')
|
||||
const router = useRouter()
|
||||
|
||||
const headers = [DEFAULT_ACCEPT_HEADER]
|
||||
const numPreviews = operation.previews.length
|
||||
const hasStatusCodes = operation.statusCodes.length > 0
|
||||
const hasCodeSamples = operation.codeExamples.length > 0
|
||||
@@ -54,9 +63,11 @@ export function RestOperation({ operation }: Props) {
|
||||
/>
|
||||
|
||||
{hasParameters && (
|
||||
<RestParameterTable
|
||||
<ParameterTable
|
||||
slug={slug}
|
||||
numPreviews={numPreviews}
|
||||
heading={t('rest.reference.parameters')}
|
||||
headers={headers}
|
||||
parameters={operation.parameters}
|
||||
bodyParameters={operation.bodyParameters}
|
||||
/>
|
||||
|
||||
24
data/ui.yml
24
data/ui.yml
@@ -87,6 +87,19 @@ contribution_cta:
|
||||
button: Make a contribution
|
||||
or: Or,
|
||||
to_guidelines: learn how to contribute.
|
||||
parameter_table:
|
||||
body: Body parameters
|
||||
default: Default
|
||||
description: Description
|
||||
enum_description_title: Can be one of
|
||||
headers: Headers
|
||||
name: Name
|
||||
path: Path parameters
|
||||
query: Query parameters
|
||||
required: Required
|
||||
see_preview_notice: See preview notice
|
||||
see_preview_notices: See preview notices
|
||||
type: Type
|
||||
products:
|
||||
graphql:
|
||||
reference:
|
||||
@@ -111,10 +124,7 @@ products:
|
||||
updates: Updates
|
||||
rest:
|
||||
reference:
|
||||
default: Default
|
||||
name: Name
|
||||
in: In
|
||||
type: Type
|
||||
description: Description
|
||||
notes: Notes
|
||||
parameters: Parameters
|
||||
@@ -126,18 +136,10 @@ products:
|
||||
code_samples: Code samples
|
||||
preview_notice: Preview notice
|
||||
preview_notices: Preview notices
|
||||
see_preview_notice: See preview notice
|
||||
see_preview_notices: See preview notices
|
||||
preview_header_is_required: This header is <strong>required</strong>
|
||||
preview_notice_to_change: This API is under preview and subject to change
|
||||
works_with: Works with
|
||||
api_reference: REST API reference
|
||||
enum_description_title: Can be one of
|
||||
required: Required
|
||||
headers: Headers
|
||||
query: Query parameters
|
||||
path: Path parameters
|
||||
body: Body parameters
|
||||
footer:
|
||||
all_rights_reserved: All rights reserved
|
||||
terms: Terms
|
||||
|
||||
Reference in New Issue
Block a user