feat(docs-ui): improve documentation components
- Add ReactAriaLink component for linking to React Aria docs - Make PropsTable columns configurable - Improve Chip styling to support multiline content - Allow ReactNode in PropDef descriptions Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
display: inline;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
padding: 0px 8px;
|
||||
height: 24px;
|
||||
padding: 4px 8px;
|
||||
margin-right: 4px;
|
||||
background-color: #f0f0f0;
|
||||
color: #5d5d5d;
|
||||
|
||||
@@ -6,77 +6,115 @@ import { TypePopup } from './TypePopup';
|
||||
|
||||
import { PropDef } from '@/utils/propDefs';
|
||||
|
||||
// Use the proper PropDef type
|
||||
type PropData = PropDef;
|
||||
|
||||
// Modify the PropsTable component to use the new type
|
||||
type ColumnType = 'prop' | 'type' | 'default' | 'description' | 'responsive';
|
||||
|
||||
interface ColumnConfig {
|
||||
key: ColumnType;
|
||||
width: string;
|
||||
}
|
||||
|
||||
const defaultColumns: ColumnConfig[] = [
|
||||
{ key: 'prop', width: '16%' },
|
||||
{ key: 'type', width: '50%' },
|
||||
{ key: 'default', width: '20%' },
|
||||
{ key: 'responsive', width: '14%' },
|
||||
];
|
||||
|
||||
const columnLabels: Record<ColumnType, string> = {
|
||||
prop: 'Prop',
|
||||
type: 'Type',
|
||||
default: 'Default',
|
||||
description: 'Description',
|
||||
responsive: 'Responsive',
|
||||
};
|
||||
|
||||
export const PropsTable = <T extends Record<string, PropData>>({
|
||||
data,
|
||||
columns = defaultColumns,
|
||||
}: {
|
||||
data: T;
|
||||
columns?: ColumnConfig[];
|
||||
}) => {
|
||||
if (!data) return null;
|
||||
|
||||
const renderCell = (
|
||||
propName: string,
|
||||
propData: PropData,
|
||||
column: ColumnType,
|
||||
) => {
|
||||
const enumValues =
|
||||
Array.isArray(propData.values) &&
|
||||
propData.values.map(t => <Chip key={t}>{t}</Chip>);
|
||||
|
||||
switch (column) {
|
||||
case 'prop':
|
||||
return <Chip head>{propName}</Chip>;
|
||||
|
||||
case 'type':
|
||||
return (
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}>
|
||||
{propData.type === 'string' && <Chip>string</Chip>}
|
||||
{propData.type === 'number' && <Chip>number</Chip>}
|
||||
{propData.type === 'boolean' && <Chip>boolean</Chip>}
|
||||
{propData.type === 'enum' && enumValues}
|
||||
{propData.type === 'spacing' && (
|
||||
<>
|
||||
<Chip>0.5, 1, 1.5, 2, 3, ..., 14</Chip>
|
||||
<Chip>string</Chip>
|
||||
</>
|
||||
)}
|
||||
{propData.type === 'complex' && propData.complexType && (
|
||||
<TypePopup
|
||||
complexType={propData.complexType}
|
||||
name={propData.complexType.name}
|
||||
/>
|
||||
)}
|
||||
{propData.type === 'enum | string' && (
|
||||
<>
|
||||
{enumValues}
|
||||
<Chip>string</Chip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'default':
|
||||
return propData.default ? <Chip>{propData.default}</Chip> : null;
|
||||
|
||||
case 'description':
|
||||
return propData.description || null;
|
||||
|
||||
case 'responsive':
|
||||
return <Chip>{propData.responsive ? 'Yes' : 'No'}</Chip>;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell style={{ width: '16%' }}>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell style={{ width: '50%' }}>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell style={{ width: '20%' }}>Default</Table.HeaderCell>
|
||||
<Table.HeaderCell style={{ width: '14%' }}>
|
||||
Responsive
|
||||
</Table.HeaderCell>
|
||||
{columns.map(col => (
|
||||
<Table.HeaderCell key={col.key} style={{ width: col.width }}>
|
||||
{columnLabels[col.key]}
|
||||
</Table.HeaderCell>
|
||||
))}
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(data).map(n => {
|
||||
const enumValues =
|
||||
Array.isArray(data[n].values) &&
|
||||
data[n].values.map(t => <Chip key={t}>{t}</Chip>);
|
||||
|
||||
return (
|
||||
<Table.Row key={n}>
|
||||
<Table.Cell style={{ width: '16%' }}>
|
||||
<Chip head>{n}</Chip>
|
||||
{Object.keys(data).map(n => (
|
||||
<Table.Row key={n}>
|
||||
{columns.map(col => (
|
||||
<Table.Cell key={col.key} style={{ width: col.width }}>
|
||||
{renderCell(n, data[n], col.key)}
|
||||
</Table.Cell>
|
||||
<Table.Cell style={{ width: '50%' }}>
|
||||
<div
|
||||
style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}
|
||||
>
|
||||
{data[n].type === 'string' && <Chip>string</Chip>}
|
||||
{data[n].type === 'number' && <Chip>number</Chip>}
|
||||
{data[n].type === 'boolean' && <Chip>boolean</Chip>}
|
||||
{data[n].type === 'enum' && enumValues}
|
||||
{data[n].type === 'spacing' && (
|
||||
<>
|
||||
<Chip>0.5, 1, 1.5, 2, 3, ..., 14</Chip>
|
||||
<Chip>string</Chip>
|
||||
</>
|
||||
)}
|
||||
{data[n].type === 'complex' && data[n].complexType && (
|
||||
<TypePopup
|
||||
complexType={data[n].complexType}
|
||||
name={data[n].complexType.name}
|
||||
/>
|
||||
)}
|
||||
{data[n].type === 'enum | string' && (
|
||||
<>
|
||||
{enumValues}
|
||||
<Chip>string</Chip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell style={{ width: '20%' }}>
|
||||
<Chip>{data[n].default ? data[n].default : '-'}</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell style={{ width: '14%' }}>
|
||||
<Chip>{data[n].responsive ? 'Yes' : 'No'}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import styles from './styles.module.css';
|
||||
|
||||
interface ReactAriaLinkProps {
|
||||
/** The React Aria component name (e.g., "Cell", "Table") */
|
||||
component: string;
|
||||
/** The documentation URL */
|
||||
href: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a standardized note linking to React Aria documentation.
|
||||
*
|
||||
* Usage:
|
||||
* <ReactAriaLink component="Cell" href="https://react-spectrum.adobe.com/react-aria/Table.html" />
|
||||
*/
|
||||
export function ReactAriaLink({ component, href }: ReactAriaLinkProps) {
|
||||
return (
|
||||
<p className={styles.note}>
|
||||
Inherits all{' '}
|
||||
<a href={href} target="_blank" rel="noopener noreferrer">
|
||||
React Aria {component}
|
||||
</a>{' '}
|
||||
props.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { ReactAriaLink } from './ReactAriaLink';
|
||||
@@ -0,0 +1,15 @@
|
||||
.note {
|
||||
font-size: 1rem;
|
||||
color: var(--secondary);
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.note a {
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.note a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Breakpoint } from '@backstage/ui/src/types';
|
||||
|
||||
const breakpoints = ['initial', 'xs', 'sm', 'md', 'lg', 'xl'] as Breakpoint[];
|
||||
@@ -28,7 +29,7 @@ export type PropDef = {
|
||||
default?: string;
|
||||
required?: boolean;
|
||||
responsive?: boolean;
|
||||
description?: string;
|
||||
description?: ReactNode;
|
||||
};
|
||||
|
||||
export { breakpoints };
|
||||
|
||||
Reference in New Issue
Block a user