mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-09 00:19:13 +08:00
Nest expandable elements and refine styling
This commit is contained in:
parent
3a3c559ca1
commit
79a8c6cbd8
@ -13,6 +13,7 @@ export interface ArgRowProps {
|
||||
arg: any;
|
||||
updateArgs?: (args: Args) => void;
|
||||
compact?: boolean;
|
||||
expandable?: boolean;
|
||||
}
|
||||
|
||||
const Name = styled.span({ fontWeight: 'bold' });
|
||||
@ -55,8 +56,12 @@ const TypeWithJsDoc = styled.div<{ hasDescription: boolean }>(({ theme, hasDescr
|
||||
marginBottom: 12,
|
||||
}));
|
||||
|
||||
const StyledTd = styled.td<{ expandable: boolean }>(({ theme, expandable }) => ({
|
||||
paddingLeft: expandable ? '40px !important' : '20px !important',
|
||||
}));
|
||||
|
||||
export const ArgRow: FC<ArgRowProps> = (props) => {
|
||||
const { row, updateArgs, compact } = props;
|
||||
const { row, updateArgs, compact, expandable } = props;
|
||||
const { name, description } = row;
|
||||
const table = (row.table || {}) as TableAnnotation;
|
||||
const type = table.type || row.type;
|
||||
@ -66,10 +71,10 @@ export const ArgRow: FC<ArgRowProps> = (props) => {
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<StyledTd expandable={expandable}>
|
||||
<Name>{name}</Name>
|
||||
{required ? <Required title="Required">*</Required> : null}
|
||||
</td>
|
||||
</StyledTd>
|
||||
{compact ? null : (
|
||||
<td>
|
||||
{hasDescription && (
|
||||
|
@ -5,141 +5,138 @@ import { ArgRow, ArgRowProps } from './ArgRow';
|
||||
import { SectionRow, SectionRowProps } from './SectionRow';
|
||||
import { ArgType, ArgTypes, Args } from './types';
|
||||
import { EmptyBlock } from '../EmptyBlock';
|
||||
import { Link } from '../../typography/link/link';
|
||||
import { ResetWrapper } from '../../typography/DocumentFormatting';
|
||||
|
||||
export const TableWrapper = styled.table<{ compact?: boolean; expandable: boolean }>(
|
||||
({ theme, compact, expandable }) => ({
|
||||
'&&': {
|
||||
// Resets for cascading/system styles
|
||||
borderCollapse: 'collapse',
|
||||
borderSpacing: 0,
|
||||
color: theme.color.defaultText,
|
||||
tr: {
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
export const TableWrapper = styled.table<{ compact?: boolean }>(({ theme, compact }) => ({
|
||||
'&&': {
|
||||
// Resets for cascading/system styles
|
||||
borderCollapse: 'collapse',
|
||||
borderSpacing: 0,
|
||||
color: theme.color.defaultText,
|
||||
tr: {
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
},
|
||||
|
||||
'td, th': {
|
||||
padding: 0,
|
||||
border: 'none',
|
||||
verticalAlign: 'top',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
// End Resets
|
||||
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
lineHeight: '20px',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
|
||||
// Margin collapse
|
||||
marginTop: 25,
|
||||
marginBottom: 40,
|
||||
|
||||
'thead th:first-of-type, td:first-of-type': {
|
||||
width: '30%',
|
||||
},
|
||||
|
||||
'th:first-of-type, td:first-of-type': {
|
||||
paddingLeft: 20,
|
||||
},
|
||||
|
||||
'th:last-of-type, td:last-of-type': {
|
||||
paddingRight: 20,
|
||||
...(compact ? null : { width: '20%' }),
|
||||
},
|
||||
|
||||
th: {
|
||||
color:
|
||||
theme.base === 'light'
|
||||
? transparentize(0.25, theme.color.defaultText)
|
||||
: transparentize(0.45, theme.color.defaultText),
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
|
||||
'&:not(:first-of-type)': {
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
},
|
||||
},
|
||||
|
||||
td: {
|
||||
paddingTop: '10px',
|
||||
paddingBottom: '10px',
|
||||
|
||||
'&:not(:first-of-type)': {
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
},
|
||||
|
||||
'td, th': {
|
||||
padding: 0,
|
||||
border: 'none',
|
||||
verticalAlign: 'top',
|
||||
},
|
||||
// End Resets
|
||||
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
lineHeight: '20px',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
|
||||
// Margin collapse
|
||||
marginTop: 25,
|
||||
marginBottom: 40,
|
||||
|
||||
'thead th:first-of-type, td:first-of-type': {
|
||||
width: '30%',
|
||||
},
|
||||
|
||||
'th:first-of-type, td:first-of-type': {
|
||||
paddingLeft: 20,
|
||||
},
|
||||
|
||||
'th:last-of-type, td:last-of-type': {
|
||||
'&:last-of-type': {
|
||||
paddingRight: 20,
|
||||
...(compact ? null : { width: '20%' }),
|
||||
},
|
||||
},
|
||||
|
||||
th: {
|
||||
color:
|
||||
theme.base === 'light'
|
||||
? transparentize(0.25, theme.color.defaultText)
|
||||
: transparentize(0.45, theme.color.defaultText),
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
// Table "block" styling
|
||||
// Emphasize tbody's background and set borderRadius
|
||||
// Calling out because styling tables is finicky
|
||||
|
||||
'&:not(:first-of-type)': {
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
// Makes border alignment consistent w/other DocBlocks
|
||||
marginLeft: 1,
|
||||
marginRight: 1,
|
||||
|
||||
[`tr:first-child${ignoreSsrWarning}`]: {
|
||||
[`td:first-child${ignoreSsrWarning}, th:first-child${ignoreSsrWarning}`]: {
|
||||
borderTopLeftRadius: theme.appBorderRadius,
|
||||
},
|
||||
[`td:last-child${ignoreSsrWarning}, th:last-child${ignoreSsrWarning}`]: {
|
||||
borderTopRightRadius: theme.appBorderRadius,
|
||||
},
|
||||
},
|
||||
|
||||
[`tr:last-child${ignoreSsrWarning}`]: {
|
||||
[`td:first-child${ignoreSsrWarning}, th:first-child${ignoreSsrWarning}`]: {
|
||||
borderBottomLeftRadius: theme.appBorderRadius,
|
||||
},
|
||||
[`td:last-child${ignoreSsrWarning}, th:last-child${ignoreSsrWarning}`]: {
|
||||
borderBottomRightRadius: theme.appBorderRadius,
|
||||
},
|
||||
},
|
||||
|
||||
tbody: {
|
||||
// slightly different than the other DocBlock shadows to account for table styling gymnastics
|
||||
boxShadow:
|
||||
theme.base === 'light'
|
||||
? `rgba(0, 0, 0, 0.10) 0 1px 3px 1px,
|
||||
${transparentize(0.035, theme.appBorderColor)} 0 0 0 1px`
|
||||
: `rgba(0, 0, 0, 0.20) 0 2px 5px 1px,
|
||||
${opacify(0.05, theme.appBorderColor)} 0 0 0 1px`,
|
||||
borderRadius: theme.appBorderRadius,
|
||||
|
||||
tr: {
|
||||
background: 'transparent',
|
||||
overflow: 'hidden',
|
||||
[`&:not(:first-child${ignoreSsrWarning})`]: {
|
||||
borderTopWidth: 1,
|
||||
borderTopStyle: 'solid',
|
||||
borderTopColor:
|
||||
theme.base === 'light'
|
||||
? darken(0.1, theme.background.content)
|
||||
: lighten(0.05, theme.background.content),
|
||||
},
|
||||
},
|
||||
|
||||
td: {
|
||||
paddingTop: '10px',
|
||||
paddingBottom: '10px',
|
||||
|
||||
':first-of-type': {
|
||||
paddingLeft: expandable ? '40px' : '20px',
|
||||
},
|
||||
|
||||
'&:not(:first-of-type)': {
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
},
|
||||
|
||||
'&:last-of-type': {
|
||||
paddingRight: 20,
|
||||
},
|
||||
background: theme.background.content,
|
||||
},
|
||||
|
||||
// Table "block" styling
|
||||
// Emphasize tbody's background and set borderRadius
|
||||
// Calling out because styling tables is finicky
|
||||
|
||||
// Makes border alignment consistent w/other DocBlocks
|
||||
marginLeft: 1,
|
||||
marginRight: 1,
|
||||
|
||||
[`tr:first-child${ignoreSsrWarning}`]: {
|
||||
[`td:first-child${ignoreSsrWarning}, th:first-child${ignoreSsrWarning}`]: {
|
||||
borderTopLeftRadius: theme.appBorderRadius,
|
||||
},
|
||||
[`td:last-child${ignoreSsrWarning}, th:last-child${ignoreSsrWarning}`]: {
|
||||
borderTopRightRadius: theme.appBorderRadius,
|
||||
},
|
||||
},
|
||||
|
||||
[`tr:last-child${ignoreSsrWarning}`]: {
|
||||
[`td:first-child${ignoreSsrWarning}, th:first-child${ignoreSsrWarning}`]: {
|
||||
borderBottomLeftRadius: theme.appBorderRadius,
|
||||
},
|
||||
[`td:last-child${ignoreSsrWarning}, th:last-child${ignoreSsrWarning}`]: {
|
||||
borderBottomRightRadius: theme.appBorderRadius,
|
||||
},
|
||||
},
|
||||
|
||||
tbody: {
|
||||
// slightly different than the other DocBlock shadows to account for table styling gymnastics
|
||||
boxShadow:
|
||||
theme.base === 'light'
|
||||
? `rgba(0, 0, 0, 0.10) 0 1px 3px 1px,
|
||||
${transparentize(0.035, theme.appBorderColor)} 0 0 0 1px`
|
||||
: `rgba(0, 0, 0, 0.20) 0 2px 5px 1px,
|
||||
${opacify(0.05, theme.appBorderColor)} 0 0 0 1px`,
|
||||
borderRadius: theme.appBorderRadius,
|
||||
|
||||
tr: {
|
||||
background: 'transparent',
|
||||
overflow: 'hidden',
|
||||
[`&:not(:first-child${ignoreSsrWarning})`]: {
|
||||
borderTopWidth: 1,
|
||||
borderTopStyle: 'solid',
|
||||
borderTopColor:
|
||||
theme.base === 'light'
|
||||
? darken(0.1, theme.background.content)
|
||||
: lighten(0.05, theme.background.content),
|
||||
},
|
||||
},
|
||||
|
||||
td: {
|
||||
background: theme.background.content,
|
||||
},
|
||||
},
|
||||
// End finicky table styling
|
||||
},
|
||||
})
|
||||
);
|
||||
// End finicky table styling
|
||||
},
|
||||
}));
|
||||
|
||||
export enum ArgsTableError {
|
||||
NO_COMPONENT = 'No component found',
|
||||
NO_COMPONENT = 'No component found.',
|
||||
ARGS_UNSUPPORTED = 'Args unsupported. See Args documentation for your framework.',
|
||||
}
|
||||
|
||||
@ -197,15 +194,29 @@ const groupRows = (rows: ArgTypes) => {
|
||||
export const ArgsTable: FC<ArgsTableProps> = (props) => {
|
||||
const { error } = props as ArgsTableErrorProps;
|
||||
if (error) {
|
||||
return <EmptyBlock>{error}</EmptyBlock>;
|
||||
return (
|
||||
<EmptyBlock>
|
||||
{error}
|
||||
<Link href="http://storybook.js.org/docs/" target="_blank" withArrow>
|
||||
Read the docs
|
||||
</Link>
|
||||
</EmptyBlock>
|
||||
);
|
||||
}
|
||||
|
||||
const { rows, args, updateArgs, compact } = props as ArgsTableRowProps;
|
||||
|
||||
const groups = groupRows(rows);
|
||||
|
||||
if (Object.keys(groups).length === 0) {
|
||||
return <EmptyBlock>No props found for this component</EmptyBlock>;
|
||||
if (groups.ungrouped.length === 0 && Object.entries(groups.sections).length === 0) {
|
||||
return (
|
||||
<EmptyBlock>
|
||||
No inputs found for this component.
|
||||
<Link href="http://storybook.js.org/docs/" target="_blank" withArrow>
|
||||
Read the docs
|
||||
</Link>
|
||||
</EmptyBlock>
|
||||
);
|
||||
}
|
||||
|
||||
let colSpan = 1;
|
||||
@ -216,8 +227,8 @@ export const ArgsTable: FC<ArgsTableProps> = (props) => {
|
||||
const common = { updateArgs, compact };
|
||||
return (
|
||||
<ResetWrapper>
|
||||
<TableWrapper {...{ compact, expandable }} className="docblock-propstable">
|
||||
<thead className="docblock-propstable-head">
|
||||
<TableWrapper {...{ compact }} className="docblock-argstable">
|
||||
<thead className="docblock-argstable-head">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
{compact ? null : <th>Description</th>}
|
||||
@ -225,10 +236,11 @@ export const ArgsTable: FC<ArgsTableProps> = (props) => {
|
||||
{updateArgs ? <th>Control</th> : null}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="docblock-propstable-body">
|
||||
<tbody className="docblock-argstable-body">
|
||||
{groups.ungrouped.map((row) => (
|
||||
<ArgRow key={row.key} row={row} arg={args && args[row.key]} {...common} />
|
||||
))}
|
||||
|
||||
{Object.entries(groups.sections).map(([category, section]) => (
|
||||
<SectionRow key={category} label={category} level="section" colSpan={colSpan}>
|
||||
{section.ungrouped.map((row) => (
|
||||
@ -242,7 +254,13 @@ export const ArgsTable: FC<ArgsTableProps> = (props) => {
|
||||
colSpan={colSpan}
|
||||
>
|
||||
{subsection.map((row) => (
|
||||
<ArgRow key={row.key} row={row} arg={args && args[row.key]} {...common} />
|
||||
<ArgRow
|
||||
key={row.key}
|
||||
row={row}
|
||||
arg={args && args[row.key]}
|
||||
expandable={expandable}
|
||||
{...common}
|
||||
/>
|
||||
))}
|
||||
</SectionRow>
|
||||
))}
|
||||
|
@ -34,7 +34,6 @@ const FlexWrapper = styled.span<{}>(({ theme }) => ({
|
||||
|
||||
const commonStyles = {
|
||||
width: '100%',
|
||||
cursor: 'row-resize',
|
||||
};
|
||||
|
||||
const Section = styled.td<{}>(({ theme }) => ({
|
||||
@ -48,10 +47,8 @@ const Section = styled.td<{}>(({ theme }) => ({
|
||||
? transparentize(0.4, theme.color.defaultText)
|
||||
: transparentize(0.6, theme.color.defaultText),
|
||||
background: `${theme.background.app} !important`,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: `${theme.background.hoverable} !important`,
|
||||
boxShadow: `${theme.color.mediumlight} 0 - 1px 0 0 inset`,
|
||||
'& ~ td': {
|
||||
background: `${theme.background.app} !important`,
|
||||
},
|
||||
}));
|
||||
|
||||
@ -60,10 +57,13 @@ const Subsection = styled.td<{}>(({ theme }) => ({
|
||||
fontWeight: theme.typography.weight.bold,
|
||||
fontSize: theme.typography.size.s2 - 1,
|
||||
background: theme.background.content,
|
||||
}));
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.background.hoverable,
|
||||
const StyledTr = styled.tr<{}>(({ theme }) => ({
|
||||
'&:hover > td': {
|
||||
backgroundColor: `${theme.background.hoverable} !important`,
|
||||
boxShadow: `${theme.color.mediumlight} 0 - 1px 0 0 inset`,
|
||||
cursor: 'row-resize',
|
||||
},
|
||||
}));
|
||||
|
||||
@ -78,19 +78,24 @@ export const SectionRow: FC<SectionRowProps> = ({
|
||||
const Level = level === 'subsection' ? Subsection : Section;
|
||||
// @ts-ignore
|
||||
const itemCount = children?.length || 0;
|
||||
const caption = level === 'subsection' ? `${itemCount} items` : '';
|
||||
const caption = level === 'subsection' ? `${itemCount} item${itemCount !== 1 ? 's' : ''}` : '';
|
||||
const icon = expanded ? 'arrowdown' : 'arrowright';
|
||||
|
||||
const helperText = `${expanded ? 'Hide' : 'Side'} ${
|
||||
level === 'subsection' ? itemCount : label
|
||||
} item${itemCount !== 1 ? 's' : ''}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr onClick={(e) => setExpanded(!expanded)}>
|
||||
<Level colSpan={colSpan}>
|
||||
<StyledTr onClick={(e) => setExpanded(!expanded)} title={helperText}>
|
||||
<Level colSpan={1}>
|
||||
<FlexWrapper>
|
||||
<ExpanderIcon icon={icon} />
|
||||
{label}
|
||||
{caption}
|
||||
</FlexWrapper>
|
||||
</Level>
|
||||
</tr>
|
||||
<td colSpan={colSpan - 1}>{expanded ? null : caption}</td>
|
||||
</StyledTr>
|
||||
{expanded ? children : null}
|
||||
</>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user