Nest expandable elements and refine styling

This commit is contained in:
domyen 2020-06-18 16:09:14 -04:00
parent 3a3c559ca1
commit 79a8c6cbd8
3 changed files with 169 additions and 141 deletions

View File

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

View File

@ -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}&nbsp;
<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.&nbsp;
<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>
))}

View File

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