Skip to content

Commit 4011490

Browse files
authored
Add more documentation (#45)
1 parent 861c688 commit 4011490

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+4682
-143
lines changed

.storybook/docs/CodeBlock.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ import './highlight.css';
1212

1313
interface CodeBlockProps extends HTMLChakraProps<'pre'> {
1414
className?: string;
15-
text: string;
15+
children: string;
1616
}
1717

1818
const Pre = chakra('pre');
1919

20-
export function CodeBlock({ text, ...rest }: CodeBlockProps) {
20+
export function CodeBlock({
21+
children: text,
22+
className,
23+
...rest
24+
}: CodeBlockProps) {
2125
const ref = useRef<HTMLPreElement>(null);
2226

2327
const [copied, setCopied] = useState(false);
@@ -27,9 +31,15 @@ export function CodeBlock({ text, ...rest }: CodeBlockProps) {
2731

2832
useEffect(() => {
2933
if (ref.current) {
30-
ref.current.innerHTML = Prism.highlight(text, Prism.languages.tsx, 'tsx');
34+
const language = className?.replace('language-', '') ?? 'tsx';
35+
36+
ref.current.innerHTML = Prism.highlight(
37+
text,
38+
Prism.languages[language],
39+
language
40+
);
3141
}
32-
}, [text]);
42+
}, [className, text]);
3343

3444
async function handleCopy() {
3545
await copy(text);

.storybook/docs/DocsContainerWrapper.tsx

Lines changed: 172 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
List,
99
Text,
1010
VStack,
11+
type HeadingProps,
1112
type HTMLChakraProps,
1213
} from '@chakra-ui/react';
1314
import { MDXProvider } from '@mdx-js/react';
@@ -17,15 +18,24 @@ import {
1718
useContext,
1819
useEffect,
1920
useMemo,
21+
useRef,
22+
useState,
2023
type MouseEvent,
2124
type PropsWithChildren,
2225
type ReactNode,
2326
} from 'react';
2427
import { NAVIGATE_URL } from 'storybook/internal/core-events';
2528
import type { DocsContextProps } from 'storybook/internal/types';
2629
import { styled, ThemeProvider } from 'storybook/theming';
30+
import { useCopyToClipboard } from 'usehooks-ts';
2731

28-
import { Table } from '../../src';
32+
import {
33+
ArrowUpRightIcon,
34+
ClipboardIcon,
35+
IconButton,
36+
Table,
37+
Tooltip,
38+
} from '../../src';
2939
import { storybookTheme } from '../storybookTheme';
3040
import { StorybookThemeProvider } from '../StorybookThemeProvider';
3141
import { getThemes } from '../themes';
@@ -60,7 +70,7 @@ const StorybookOverrides = styled.div`
6070
border: 1px solid;
6171
border-radius: var(--teleport-radii-md);
6272
63-
& > :first-child {
73+
& > :first-of-type {
6474
margin-top: 0;
6575
}
6676
@@ -76,6 +86,15 @@ const StorybookOverrides = styled.div`
7686
.octicon {
7787
fill: var(--teleport-colors-interactive-solid-accent-default);
7888
}
89+
90+
a {
91+
color: var(--teleport-colors-alpha-800);
92+
text-decoration: underline;
93+
94+
&:hover {
95+
color: var(--teleport-colors-text-main);
96+
}
97+
}
7998
}
8099
81100
.markdown-alert-tip {
@@ -122,6 +141,8 @@ const StorybookOverrides = styled.div`
122141

123142
export function DocsLink(props: HTMLChakraProps<'a'>) {
124143
const context = useContext(DocsContext);
144+
const url = props.href ?? '';
145+
const isExternal = url.startsWith('http://') || url.startsWith('https://');
125146

126147
function handleClick(event: MouseEvent) {
127148
const LEFT_BUTTON = 0;
@@ -134,16 +155,99 @@ export function DocsLink(props: HTMLChakraProps<'a'>) {
134155

135156
if (isLeftClick) {
136157
event.preventDefault();
137-
context.channel.emit(
138-
NAVIGATE_URL,
139-
event.currentTarget.getAttribute('href') ?? ''
140-
);
158+
context.channel.emit(NAVIGATE_URL, url);
141159
}
142160
}
143161

162+
if (isExternal) {
163+
const { children, ...rest } = props;
164+
165+
return (
166+
<Link {...rest} target="_blank">
167+
{children}
168+
169+
<ArrowUpRightIcon />
170+
</Link>
171+
);
172+
}
173+
144174
return <Link {...props} onClick={handleClick} />;
145175
}
146176

177+
function HeadingWrapper({ id, mt, mb, my, children, ...props }: HeadingProps) {
178+
const [copied, setCopied] = useState(false);
179+
const [, copy] = useCopyToClipboard();
180+
181+
const timeoutRef = useRef<number | null>(null);
182+
183+
const location = new URL(window.location.href);
184+
const docId = location.searchParams.get('id') ?? '';
185+
const idValue = id ?? '';
186+
const url = location.origin + '/?path=/docs/' + docId + '#' + idValue;
187+
188+
async function handleCopy() {
189+
await copy(url);
190+
191+
setCopied(true);
192+
193+
if (timeoutRef.current) {
194+
window.clearTimeout(timeoutRef.current);
195+
}
196+
197+
timeoutRef.current = window.setTimeout(() => {
198+
setCopied(false);
199+
}, 1000);
200+
}
201+
202+
useEffect(() => {
203+
return () => {
204+
if (timeoutRef.current) {
205+
window.clearTimeout(timeoutRef.current);
206+
}
207+
};
208+
}, []);
209+
210+
return (
211+
<Box
212+
display="flex"
213+
alignItems="center"
214+
ml="-32px"
215+
mt={mt}
216+
my={my}
217+
mb={mb}
218+
className="group"
219+
>
220+
<Tooltip
221+
content={copied ? 'Copied' : 'Copy link to clipboard'}
222+
openDelay={0}
223+
positioning={{
224+
placement: 'top',
225+
}}
226+
closeDelay={0}
227+
closeOnClick={false}
228+
>
229+
<IconButton
230+
onClick={() => {
231+
void handleCopy();
232+
}}
233+
size="sm"
234+
fill="minimal"
235+
intent="neutral"
236+
mr="8px"
237+
opacity={0}
238+
_groupHover={{ opacity: 1 }}
239+
>
240+
<ClipboardIcon />
241+
</IconButton>
242+
</Tooltip>
243+
244+
<Heading {...props} id={id} cursor="pointer">
245+
<a href={url}>{children}</a>
246+
</Heading>
247+
</Box>
248+
);
249+
}
250+
147251
export function DocsContainerWrapper({
148252
children,
149253
context,
@@ -161,33 +265,55 @@ export function DocsContainerWrapper({
161265
useEffect(() => {
162266
let timeout: number | null = null;
163267

164-
try {
165-
const url = new URL(window.parent.location.toString());
166-
167-
if (url.hash) {
168-
const element = document.getElementById(
169-
decodeURIComponent(url.hash.substring(1))
170-
);
171-
172-
if (element) {
173-
timeout = window.setTimeout(() => {
174-
element.scrollIntoView({
175-
behavior: 'smooth',
176-
block: 'start',
177-
inline: 'nearest',
178-
});
179-
}, 200);
268+
function scrollToHash(ms: number) {
269+
if (timeout) {
270+
window.clearTimeout(timeout);
271+
timeout = null;
272+
}
273+
274+
try {
275+
const url = new URL(window.parent.location.toString());
276+
277+
if (url.hash) {
278+
const element = document.getElementById(
279+
decodeURIComponent(url.hash.substring(1))
280+
);
281+
282+
if (element) {
283+
timeout = window.setTimeout(() => {
284+
const elementPosition =
285+
element.getBoundingClientRect().top + window.pageYOffset;
286+
const offsetPosition = elementPosition - 20;
287+
288+
window.scrollTo({
289+
top: offsetPosition,
290+
behavior: 'smooth',
291+
});
292+
}, ms);
293+
}
180294
}
295+
} catch {
296+
// pass
181297
}
182-
} catch {
183-
// pass
184298
}
185299

300+
scrollToHash(200);
301+
302+
function handleChange() {
303+
scrollToHash(0);
304+
}
305+
306+
window.parent.addEventListener('popstate', handleChange);
307+
window.parent.addEventListener('hashchange', handleChange);
308+
186309
return () => {
187310
if (timeout) {
188311
window.clearTimeout(timeout);
189312
timeout = null;
190313
}
314+
315+
window.parent.removeEventListener('popstate', handleChange);
316+
window.parent.removeEventListener('hashchange', handleChange);
191317
};
192318
}, []);
193319

@@ -197,18 +323,32 @@ export function DocsContainerWrapper({
197323
<MDXProvider
198324
components={{
199325
h1: props => (
200-
<Heading {...props} as="h1" mt={3} mb={4} size="3xl" />
326+
<HeadingWrapper {...props} as="h1" mt={3} mb={4} size="3xl" />
201327
),
202328
h2: props => (
203-
<Heading {...props} as="h2" mt={6} mb={4} size="2xl" />
329+
<HeadingWrapper {...props} as="h2" mt={6} mb={4} size="2xl" />
330+
),
331+
h3: props => (
332+
<HeadingWrapper {...props} as="h3" mt={3} mb={3} size="xl" />
333+
),
334+
h4: props => (
335+
<HeadingWrapper
336+
{...props}
337+
as="h4"
338+
mt={4}
339+
mb={2}
340+
size="lg"
341+
fontSize="16px"
342+
/>
343+
),
344+
h5: props => (
345+
<HeadingWrapper {...props} as="h5" mt={4} mb={0} size="md" />
204346
),
205-
h3: props => <Heading {...props} as="h3" mt={3} mb={3} size="xl" />,
206-
h4: props => <Heading {...props} as="h4" mt={2} mb={2} size="md" />,
207-
h5: props => <Heading {...props} as="h5" size="sm" />,
208-
h6: props => <Heading {...props} as="h6" size="xs" />,
347+
h6: props => <HeadingWrapper {...props} as="h6" size="sm" />,
209348
p: props => <Text {...props} mb={2} />,
210-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
211-
pre: props => <CodeBlock text={props.children.props.children} />,
349+
em: props => <Text as="em" fontStyle="italic" {...props} />,
350+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
351+
pre: props => <CodeBlock {...props.children.props} />,
212352
ul: props => <List.Root mt={4} mb={6} pl={4} {...props} />,
213353
li: props => <List.Item pl={1} {...props} />,
214354
a: props => <DocsLink {...props} />,

.storybook/docs/DocsNavigation.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ export function DocsNavigation() {
6767
}
6868

6969
const entries = Object.values(store.storyIndex.entries);
70-
const orderedEntries = entries.toSorted(
71-
preview.parameters.options.storySort
72-
);
70+
const orderedEntries = entries
71+
.filter(entry => entry.type === 'docs')
72+
.filter(entry => entry.tags?.includes('dev'))
73+
.toSorted(preview.parameters.options.storySort);
7374

7475
const currentIndex = orderedEntries.findIndex(
7576
entry => entry.id === currentId

.storybook/docs/TableOfContents.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ export function TableOfContents({ channel }: TableOfContentsProps) {
9494
const configuration: tocbot.IStaticOptions = {
9595
tocSelector: '.toc-wrapper',
9696
contentSelector: '[data-docs-content]',
97-
headingSelector: 'h2, h3',
97+
headingSelector: 'h2, h3, h4',
9898
ignoreSelector: '.docs-story *, .skip-toc',
9999
headingsOffset: 20,
100-
scrollSmoothOffset: -10,
100+
scrollSmoothOffset: -20,
101101
orderedList: false,
102102
scrollHandlerType: 'throttle',
103103
scrollHandlerTimeout: 10,
@@ -126,21 +126,11 @@ export function TableOfContents({ channel }: TableOfContentsProps) {
126126

127127
const headingId = useId();
128128

129-
// if (
130-
// !entry ||
131-
// CATEGORIES_TO_HIDE.some(category => entry.title.startsWith(category))
132-
// ) {
133-
// return <Box />;
134-
// }
135-
136129
return (
137130
<Aside>
138131
<Nav
139132
aria-labelledby={headingId}
140133
css={{
141-
'& .toc-wrapper': {
142-
mt: 2,
143-
},
144134
'& > .toc-wrapper > .toc-list': {
145135
ml: 0,
146136
},
@@ -150,7 +140,8 @@ export function TableOfContents({ channel }: TableOfContentsProps) {
150140
'& .toc-list-item': {
151141
position: 'relative',
152142
listStyleType: 'none',
153-
py: 1,
143+
pt: 2,
144+
lineHeight: '2',
154145
},
155146
'& .toc-list-item.is-active-li::before': {
156147
opacity: 1,
@@ -159,11 +150,12 @@ export function TableOfContents({ channel }: TableOfContentsProps) {
159150
color: 'text.slightlyMuted',
160151
textDecoration: 'none',
161152
display: 'block',
153+
lineHeight: '1.5',
162154
// Add slight letter-spacing to compensate for bold weight difference
163-
letterSpacing: '0.01em',
155+
letterSpacing: '0.02em',
164156
},
165157
'& .toc-list-item.is-active-li > a': {
166-
fontWeight: 600,
158+
fontWeight: 500,
167159
color: 'text.main',
168160
textDecoration: 'none',
169161
// Remove letter-spacing when bold to maintain similar width

0 commit comments

Comments
 (0)