1
0
mirror of synced 2025-12-23 11:54:18 -05:00

New nested param table styles for REST (#30862)

This commit is contained in:
Robert Sese
2022-09-20 14:57:49 -05:00
committed by GitHub
parent 3b1a1eff2f
commit 3d45346c34
9 changed files with 249 additions and 143 deletions

View File

@@ -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;
}
}
}

View File

@@ -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>

View 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}
/>
)}
</>
)
}

View 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;
}
}

View File

@@ -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 (
<>
{heading && (
<h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}>
<a href={`#${slug}--parameters`}>{t('rest.reference.parameters')}</a>
<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>
{headers.length > 0 && (
<>
{headers.map((header, index) => (
<ParameterRow
rowParams={{
name: 'accept',
type: 'string',
description: `<p>Setting to <code>application/vnd.github+json</code> is recommended.</p>`,
isRequired: false,
}}
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) => (

View 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[]
}

View File

@@ -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}
/>
)}
</>
)
}

View File

@@ -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}
/>

View File

@@ -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