forked from storybookjs/storybook
/
PropValue.tsx
115 lines (99 loc) · 3.09 KB
/
PropValue.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import React, { FC, useState } from 'react';
import { isNil } from 'lodash';
import { styled } from '@storybook/theming';
import memoize from 'memoizerific';
import { PropSummaryValue } from './PropDef';
import { WithTooltipPure } from '../../tooltip/WithTooltip';
import { Icons } from '../../icon/icon';
import { SyntaxHighlighter } from '../../syntaxhighlighter/syntaxhighlighter';
import { codeCommon } from '../../typography/shared';
interface PropValueProps {
value?: PropSummaryValue;
}
interface PropTextProps {
text: string;
}
interface PropSummaryProps {
value: PropSummaryValue;
}
const Text = styled.span(({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
fontSize: theme.typography.size.s2 - 1,
}));
const Expandable = styled.div<{}>(codeCommon, ({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
color: theme.color.secondary,
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.s2 - 1,
// 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 EmptyProp = () => {
return <span>-</span>;
};
const PropText: FC<PropTextProps> = ({ text }) => {
return <Text>{text}</Text>;
};
const calculateDetailWidth = memoize(1000)((detail: string): string => {
const lines = detail.split(/\r?\n/);
return `${Math.max(...lines.map(x => x.length))}ch`;
});
const PropSummary: FC<PropSummaryProps> = ({ value }) => {
const { summary, detail } = value;
const [isOpen, setIsOpen] = useState(false);
// summary is used for the default value
// below check fixes not displaying default values for boolean typescript vars
const summaryAsString =
summary !== undefined && summary !== null && typeof summary.toString === 'function'
? summary.toString()
: summary;
if (isNil(detail)) {
return <PropText text={summaryAsString} />;
}
return (
<WithTooltipPure
closeOnClick
trigger="click"
placement="bottom"
tooltipShown={isOpen}
onVisibilityChange={isVisible => {
setIsOpen(isVisible);
}}
tooltip={
<Detail width={calculateDetailWidth(detail)}>
<SyntaxHighlighter language="jsx" format={false}>
{detail}
</SyntaxHighlighter>
</Detail>
}
>
<Expandable className="sbdocs-expandable">
<span>{summaryAsString}</span>
<ArrowIcon icon={isOpen ? 'arrowup' : 'arrowdown'} />
</Expandable>
</WithTooltipPure>
);
};
export const PropValue: FC<PropValueProps> = ({ value }) => {
return isNil(value) ? <EmptyProp /> : <PropSummary value={value} />;
};