packages/docusaurus-theme/src/components/prop_table/prop_table.tsx (228 lines of code) (raw):
import {
EuiBasicTable,
EuiMarkdownFormat,
EuiBasicTableColumn,
EuiTextColor,
EuiFlexGroup,
EuiCode,
UseEuiTheme,
useEuiMemoizedStyles,
EuiLink,
EuiPanel,
} from '@elastic/eui';
import {
ProcessedComponent,
ProcessedComponentProp,
} from '@elastic/eui-docgen';
import { useCallback, useMemo } from 'react';
import { css } from '@emotion/react';
import Heading from '@theme/Heading';
import types from '@elastic/eui-docgen/dist/types.json';
import { JSONOutput } from 'typedoc';
import { PropTableExtendedTypes } from './extended_types';
export interface PropTableProps {
definition: ProcessedComponent;
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
showTitle?: boolean;
}
const getPropId = (prop: ProcessedComponentProp, componentName: string) =>
`${encodeURIComponent(componentName)}-prop-${prop.name}`;
const getPropTableStyles = ({ euiTheme }: UseEuiTheme) => ({
propTable: css`
margin-block: ${euiTheme.size.xl};
`,
header: css`
// Increased specificity is needed here to override default
// content heading styles
&& h1,
&& h2,
&& h3,
&& h4,
&& h5,
&& h6 {
margin-block: 0;
}
`,
table: css`
vertical-align: top;
`,
propName: css`
font-family: ${euiTheme.font.familyCode};
font-weight: ${euiTheme.font.weight.semiBold};
`,
description: css`
p,
ul {
font-size: var(--eui-font-size-s);
}
p {
margin-block-end: var(--eui-size-s);
}
p:first-child {
margin-block-start: 0;
}
`,
required: css`
font-family: ${euiTheme.font.familyCode};
color: ${euiTheme.colors.textDanger};
`,
type: css`
font-weight: ${euiTheme.font.weight.semiBold};
`,
tableNameLink: css`
display: none;
margin-inline-start: ${euiTheme.size.xs};
`,
tableRow: css`
scroll-margin-block-start: calc(var(--ifm-navbar-height) + 0.5rem);
&:hover .propLink {
display: inline-block;
}
`,
tableCell: css`
vertical-align: text-top;
`,
});
export const PropTable = ({
definition,
headingLevel = 'h3',
showTitle = true,
}: PropTableProps) => {
const styles = useEuiMemoizedStyles(getPropTableStyles);
const tableItems = useMemo<Array<ProcessedComponentProp>>(
() =>
Object.values(definition.props).sort(
(a, b) => +b.isRequired - +a.isRequired
),
[definition.props]
);
const columns = useMemo<Array<EuiBasicTableColumn<ProcessedComponentProp>>>(
() => [
{
field: 'name',
name: 'Prop',
width: '150',
render(
value: ProcessedComponentProp['name'],
prop: ProcessedComponentProp
) {
return (
<span css={styles.propName}>
{value}
<EuiLink
href={`#${getPropId(prop, definition.displayName)}`}
css={styles.tableNameLink}
className="propLink"
aria-label={`Direct link to the ${prop.name} prop`}
title={`Direct link to the ${prop.name} prop`}
>
#
</EuiLink>
</span>
);
},
},
{
field: 'description',
name: 'Description and type',
render(
value: ProcessedComponentProp['description'],
prop: ProcessedComponentProp
) {
const result = value
.replace(/{@link (\w+)}/g, (_, componentName) => {
const componentSource = (
types as unknown as JSONOutput.ProjectReflection
).children?.find((item) => item.name === componentName)
?.sources?.[0];
if (componentSource) {
const { fileName, line } = componentSource;
return `[${componentName}](https://github.com/elastic/eui/tree/main/packages/${fileName}#L${line})`;
} else {
return `\`${componentName}\``;
}
})
.trim();
return (
<EuiFlexGroup
direction="column"
alignItems="flexStart"
gutterSize="s"
>
{result && (
<EuiMarkdownFormat css={styles.description}>
{result}
</EuiMarkdownFormat>
)}
{prop.type && (
<span css={styles.type}>
Type:{' '}
<EuiCode language="ts">
{prop.type.raw || prop.type.name}
</EuiCode>
</span>
)}
</EuiFlexGroup>
);
},
},
{
field: 'defaultValue',
name: 'Default value',
width: '120',
render(
value: ProcessedComponentProp['defaultValue'],
prop: ProcessedComponentProp
) {
if (prop.isRequired && !value?.trim().length) {
return <EuiTextColor css={styles.required}>Required</EuiTextColor>;
}
return value && <EuiCode>{value}</EuiCode>;
},
},
],
[]
);
const rowProps = useCallback(
(item: ProcessedComponentProp) => ({
id: getPropId(item, definition.displayName),
css: styles.tableRow,
}),
[definition.displayName]
);
const cellProps = useCallback(
(item: ProcessedComponentProp) => ({
id: getPropId(item, definition.displayName),
css: styles.tableCell,
}),
[definition.displayName]
);
return (
<EuiFlexGroup
aria-label={`Component properties table for ${definition.displayName}`}
gutterSize="s"
direction="column"
css={styles.propTable}
>
<header css={styles.header}>
{showTitle && (
<Heading as={headingLevel} id={definition.displayName}>
{definition.displayName}
</Heading>
)}
<PropTableExtendedTypes definition={definition} />
</header>
<EuiPanel color="plain" hasBorder>
<EuiBasicTable
css={styles.table}
width="100%"
items={tableItems}
columns={columns}
rowProps={rowProps}
cellProps={cellProps}
compressed
/>
</EuiPanel>
</EuiFlexGroup>
);
};