import React, { FC, useState } from 'react'; import { styled } from '@storybook/theming'; import memoize from 'memoizerific'; import uniq from 'lodash/uniq'; import { PropSummaryValue } from './types'; import { WithTooltipPure } from '../../tooltip/WithTooltip'; import { Icons } from '../../icon/icon'; import { SyntaxHighlighter } from '../../syntaxhighlighter/syntaxhighlighter'; import { codeCommon } from '../../typography/shared'; interface ArgValueProps { value?: PropSummaryValue; initialExpandedArgs?: boolean; } interface ArgTextProps { text: string; } interface ArgSummaryProps { value: PropSummaryValue; initialExpandedArgs?: boolean; } const ITEMS_BEFORE_EXPANSION = 8; const Summary = styled.div<{ isExpanded?: boolean }>(({ isExpanded }) => ({ display: 'flex', flexDirection: isExpanded ? 'column' : 'row', flexWrap: 'wrap', alignItems: 'flex-start', marginBottom: '-4px', minWidth: 100, })); const Text = styled.span<{}>(codeCommon, ({ theme }) => ({ flex: '0 0 auto', fontFamily: theme.typography.fonts.mono, fontSize: theme.typography.size.s1, wordBreak: 'break-word', margin: 0, marginRight: '4px', marginBottom: '4px', paddingTop: '2px', paddingBottom: '2px', lineHeight: '13px', whiteSpace: 'normal', maxWidth: '100%', })); const ExpandButton = styled.button<{}>(({ theme }) => ({ fontFamily: theme.typography.fonts.mono, color: theme.color.secondary, marginBottom: '4px', background: 'none', border: 'none', })); const Expandable = styled.div<{}>(codeCommon, ({ theme }) => ({ fontFamily: theme.typography.fonts.mono, color: theme.color.secondary, fontSize: theme.typography.size.s1, // overrides codeCommon margin: 0, whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', })); const Detail = styled.div<{ width: string }>(({ theme, width }) => ({ width, minWidth: 200, maxWidth: 800, padding: 15, // Dont remove the mono fontFamily here even if it seem useless, this is used by the browser to calculate the length of a "ch" unit. fontFamily: theme.typography.fonts.mono, fontSize: theme.typography.size.s1, // Most custom stylesheet will reset the box-sizing to "border-box" and will break the tooltip. boxSizing: 'content-box', '& code': { padding: '0 !important', }, })); const ArrowIcon = styled(Icons)({ height: 10, width: 10, minWidth: 10, marginLeft: 4, }); const EmptyArg = () => { return -; }; const ArgText: FC = ({ text }) => { return {text}; }; const calculateDetailWidth = memoize(1000)((detail: string): string => { const lines = detail.split(/\r?\n/); return `${Math.max(...lines.map((x) => x.length))}ch`; }); const getSummaryItems = (summary: string) => { if (!summary) return [summary]; const splittedItems = summary.split('|'); const summaryItems = splittedItems.map((value) => value.trim()); return uniq(summaryItems); }; const renderSummaryItems = (summaryItems: string[], isExpanded = true) => { let items = summaryItems; if (!isExpanded) { items = summaryItems.slice(0, ITEMS_BEFORE_EXPANSION); } return items.map((item) => ); }; const ArgSummary: FC = ({ value, initialExpandedArgs }) => { const { summary, detail } = value; const [isOpen, setIsOpen] = useState(false); const [isExpanded, setIsExpanded] = useState(initialExpandedArgs || false); if (summary === undefined || summary === null) return null; // summary is used for the default value // below check fixes not displaying default values for boolean typescript vars const summaryAsString = typeof summary.toString === 'function' ? summary.toString() : summary; if (detail == null) { const summaryItems = getSummaryItems(summaryAsString); const itemsCount = summaryItems.length; const hasManyItems = itemsCount > ITEMS_BEFORE_EXPANSION; return hasManyItems ? ( {renderSummaryItems(summaryItems, isExpanded)} setIsExpanded(!isExpanded)}> {isExpanded ? 'Show less...' : `Show ${itemsCount - ITEMS_BEFORE_EXPANSION} more...`} ) : ( {renderSummaryItems(summaryItems)} ); } return ( { setIsOpen(isVisible); }} tooltip={ {detail} } > {summaryAsString} ); }; export const ArgValue: FC = ({ value, initialExpandedArgs }) => { return value == null ? ( ) : ( ); };