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 { useTranslation } from 'components/hooks/useTranslation'
import { ParameterRow } from './ParameterRow' import { ParameterRow } from './ParameterRow'
import type { ChildParameter } from './types' import type { ChildParameter } from './types'
import styles from './ChildBodyParametersRows.module.scss'
type Props = { type Props = {
slug: string slug: string
childParamsGroups: ChildParameter[] childParamsGroups: ChildParameter[]
@@ -15,27 +19,23 @@ export function ChildBodyParametersRows({
parentType, parentType,
childParamsGroups, childParamsGroups,
}: Props) { }: Props) {
const { t } = useTranslation('products') const { t } = useTranslation(['parameter_table', 'products'])
return ( return (
<tr className="border-top-0"> <tr className={cx(styles.childBodyParametersRows, 'color-bg-subtle border-top-0')}>
<td colSpan={4} className="has-nested-table"> <td colSpan={4} className="has-nested-table">
<details className="ml-1"> <details className="box px-3 ml-1 mb-0">
<summary role="button" aria-expanded="false" className="keyboard-focus color-fg-muted"> <summary
<span className="d-inline-block mb-3" id={`${slug}-${parentName}-${parentType}`}> role="button"
Properties of the aria-expanded="false"
<code>{parentName}</code> className="mb-2 keyboard-focus color-fg-muted"
{parentType} >
</span> <span id={`${slug}-${parentName}-${parentType}`}>Properties of {parentName}</span>
</summary> </summary>
<table id={`${parentName}-object`} className="mb-4 mt-2 color-bg-subtle"> <table id={`${parentName}-object`} className="mb-4 mt-2 color-bg-subtle">
<thead className="visually-hidden"> <thead className="visually-hidden">
<tr> <tr>
<th> <th>{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
'rest.reference.description'
)}`}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <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 { useTranslation } from 'components/hooks/useTranslation'
import { ParameterRow } from './ParameterRow' import { ParameterRow } from './ParameterRow'
import { BodyParameter, Parameter } from './types' import { BodyParameter, ChildParameter, Parameter } from './types'
import styles from './ParameterTable.module.scss'
type Props = { type Props = {
slug: string slug: string
numPreviews: number numPreviews: number
heading: string
headers: Array<ChildParameter>
parameters: Array<Parameter> parameters: Array<Parameter>
bodyParameters: Array<BodyParameter> bodyParameters: Array<BodyParameter>
} }
export function RestParameterTable({ slug, numPreviews, parameters, bodyParameters }: Props) { export function ParameterTable({
const { t } = useTranslation('products') slug,
numPreviews,
heading = '',
headers = [],
parameters,
bodyParameters,
}: Props) {
const { t } = useTranslation(['parameter_table', 'products'])
const queryParams = parameters.filter((param) => param.in === 'query') const queryParams = parameters.filter((param) => param.in === 'query')
const pathParams = parameters.filter((param) => param.in === 'path') const pathParams = parameters.filter((param) => param.in === 'path')
return ( return (
<> <>
<h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}> {heading && (
<a href={`#${slug}--parameters`}>{t('rest.reference.parameters')}</a> <h3 className="mt-4 mb-3 pt-3 h4" id={`${slug}--parameters`}>
</h3> <a href={`#${slug}--parameters`}>{heading}</a>
</h3>
)}
<table <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" summary="Column one has the type of parameter and the other columns show the name, type, and description of the parameters"
> >
<thead> <thead>
<tr> <tr>
<th id="header" scope="col" className="text-bold pl-0"> <th id="header" scope="col" className="text-bold pl-0">
{t('rest.reference.headers')} {t('headers')}
</th> </th>
</tr> </tr>
<tr className="visually-hidden"> <tr className="visually-hidden">
<th scope="col"> <th scope="col">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
'rest.reference.description'
)}`}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ParameterRow {headers.length > 0 && (
rowParams={{ <>
name: 'accept', {headers.map((header, index) => (
type: 'string', <ParameterRow
description: `<p>Setting to <code>application/vnd.github+json</code> is recommended.</p>`, rowParams={header}
isRequired: false, slug={slug}
}} numPreviews={numPreviews}
slug={slug} key={`${index}-${header.name}`}
numPreviews={numPreviews} />
/> ))}
</>
)}
{pathParams.length > 0 && ( {pathParams.length > 0 && (
<> <>
<tr className="border-top-0"> <tr className="border-top-0">
<th className="text-bold pl-0 border-top-0" scope="colgroup"> <th className="text-bold pl-0 border-top-0" scope="colgroup">
{t('rest.reference.path')} {t('path')}
</th> </th>
</tr> </tr>
<tr className="visually-hidden"> <tr className="visually-hidden">
<th scope="colgroup"> <th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
'rest.reference.description'
)}`}
</th>
</tr> </tr>
{pathParams.map((param, index) => ( {pathParams.map((param, index) => (
<ParameterRow <ParameterRow
@@ -86,15 +95,11 @@ export function RestParameterTable({ slug, numPreviews, parameters, bodyParamete
<> <>
<tr className="border-top-0"> <tr className="border-top-0">
<th className="text-bold pl-0" scope="colgroup"> <th className="text-bold pl-0" scope="colgroup">
{t('rest.reference.query')} {t('query')}
</th> </th>
</tr> </tr>
<tr className="visually-hidden"> <tr className="visually-hidden">
<th scope="colgroup"> <th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
'rest.reference.description'
)}`}
</th>
</tr> </tr>
{queryParams.map((param, index) => ( {queryParams.map((param, index) => (
@@ -118,15 +123,11 @@ export function RestParameterTable({ slug, numPreviews, parameters, bodyParamete
<> <>
<tr className="border-top-0"> <tr className="border-top-0">
<th scope="colgroup" className="text-bold pl-0"> <th scope="colgroup" className="text-bold pl-0">
{t('rest.reference.body')} {t('body')}
</th> </th>
</tr> </tr>
<tr className="visually-hidden"> <tr className="visually-hidden">
<th scope="colgroup"> <th scope="colgroup">{`${t('name')}, ${t('type')}, ${t('description')}`}</th>
{`${t('rest.reference.name')}, ${t('rest.reference.type')}, ${t(
'rest.reference.description'
)}`}
</th>
</tr> </tr>
{bodyParameters.map((param, index) => ( {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 { useTranslation } from 'components/hooks/useTranslation'
import { RestPreviewNotice } from './RestPreviewNotice' import { RestPreviewNotice } from './RestPreviewNotice'
import styles from './RestOperation.module.scss' import styles from './RestOperation.module.scss'
import { RestParameterTable } from './RestParameterTable' import { ParameterTable } from 'components/parameter-table/ParameterTable'
import { RestCodeSamples } from './RestCodeSamples' import { RestCodeSamples } from './RestCodeSamples'
import { RestStatusCodes } from './RestStatusCodes' import { RestStatusCodes } from './RestStatusCodes'
import { Operation } from './types' import { Operation } from './types'
@@ -17,11 +17,20 @@ type Props = {
operation: Operation 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) { export function RestOperation({ operation }: Props) {
const slug = slugger.slug(operation.title) const slug = slugger.slug(operation.title)
const { t } = useTranslation('products') const { t } = useTranslation('products')
const router = useRouter() const router = useRouter()
const headers = [DEFAULT_ACCEPT_HEADER]
const numPreviews = operation.previews.length const numPreviews = operation.previews.length
const hasStatusCodes = operation.statusCodes.length > 0 const hasStatusCodes = operation.statusCodes.length > 0
const hasCodeSamples = operation.codeExamples.length > 0 const hasCodeSamples = operation.codeExamples.length > 0
@@ -54,9 +63,11 @@ export function RestOperation({ operation }: Props) {
/> />
{hasParameters && ( {hasParameters && (
<RestParameterTable <ParameterTable
slug={slug} slug={slug}
numPreviews={numPreviews} numPreviews={numPreviews}
heading={t('rest.reference.parameters')}
headers={headers}
parameters={operation.parameters} parameters={operation.parameters}
bodyParameters={operation.bodyParameters} bodyParameters={operation.bodyParameters}
/> />

View File

@@ -87,6 +87,19 @@ contribution_cta:
button: Make a contribution button: Make a contribution
or: Or, or: Or,
to_guidelines: learn how to contribute. 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: products:
graphql: graphql:
reference: reference:
@@ -111,10 +124,7 @@ products:
updates: Updates updates: Updates
rest: rest:
reference: reference:
default: Default
name: Name
in: In in: In
type: Type
description: Description description: Description
notes: Notes notes: Notes
parameters: Parameters parameters: Parameters
@@ -126,18 +136,10 @@ products:
code_samples: Code samples code_samples: Code samples
preview_notice: Preview notice preview_notice: Preview notice
preview_notices: Preview notices 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_header_is_required: This header is <strong>required</strong>
preview_notice_to_change: This API is under preview and subject to change preview_notice_to_change: This API is under preview and subject to change
works_with: Works with works_with: Works with
api_reference: REST API reference 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: footer:
all_rights_reserved: All rights reserved all_rights_reserved: All rights reserved
terms: Terms terms: Terms