Skip to content

Commit d588dc8

Browse files
authored
Add a bunch of documentation (#39)
1 parent 0915143 commit d588dc8

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

+6974
-72
lines changed

.storybook/docs/DocsContainerWrapper.tsx

Lines changed: 155 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
1-
import { Box, Code, Heading, Text } from '@chakra-ui/react';
1+
import {
2+
Blockquote,
3+
Box,
4+
Code,
5+
Heading,
6+
HStack,
7+
Link,
8+
List,
9+
Text,
10+
VStack,
11+
type HTMLChakraProps,
12+
} from '@chakra-ui/react';
213
import { MDXProvider } from '@mdx-js/react';
314
import { DocsContext, SourceContainer } from '@storybook/addon-docs/blocks';
4-
import { useEffect, useMemo, type PropsWithChildren } from 'react';
15+
import {
16+
Children,
17+
useContext,
18+
useEffect,
19+
useMemo,
20+
type MouseEvent,
21+
type PropsWithChildren,
22+
type ReactNode,
23+
} from 'react';
24+
import { NAVIGATE_URL } from 'storybook/internal/core-events';
525
import type { DocsContextProps } from 'storybook/internal/types';
626
import { styled, ThemeProvider } from 'storybook/theming';
727

28+
import { Table } from '../../src';
829
import { storybookTheme } from '../storybookTheme';
930
import { StorybookThemeProvider } from '../StorybookThemeProvider';
1031
import { getThemes } from '../themes';
1132
import { CodeBlock } from './CodeBlock';
33+
import { DocsNavigation } from './DocsNavigation';
34+
import { TableOfContents } from './TableOfContents';
1235

1336
interface ContextOverride extends DocsContextProps {
1437
store: {
@@ -30,8 +53,97 @@ const StorybookOverrides = styled.div`
3053
color: var(--teleport-colors-text-main);
3154
margin: 0;
3255
}
56+
57+
.markdown-alert {
58+
padding: var(--teleport-spacing-2) var(--teleport-spacing-3);
59+
margin: var(--teleport-spacing-4) 0;
60+
border: 1px solid;
61+
border-radius: var(--teleport-radii-md);
62+
63+
& > :first-child {
64+
margin-top: 0;
65+
}
66+
67+
& > :last-child {
68+
margin-bottom: 0;
69+
}
70+
}
71+
72+
.markdown-alert-note {
73+
background: var(--teleport-colors-interactive-tonal-informational-2);
74+
border-color: var(--teleport-colors-interactive-solid-accent-default);
75+
76+
.octicon {
77+
fill: var(--teleport-colors-interactive-solid-accent-default);
78+
}
79+
}
80+
81+
.markdown-alert-tip {
82+
background: var(--teleport-colors-interactive-tonal-success-2);
83+
border-color: var(--teleport-colors-interactive-solid-success-default);
84+
85+
.octicon {
86+
fill: var(--teleport-colors-interactive-solid-success-default);
87+
}
88+
}
89+
90+
.markdown-alert-warning {
91+
background: var(--teleport-colors-interactive-tonal-alert-2);
92+
border-color: var(--teleport-colors-interactive-solid-alert-default);
93+
94+
.octicon {
95+
fill: var(--teleport-colors-interactive-solid-alert-default);
96+
}
97+
}
98+
99+
.markdown-alert-caution {
100+
background: var(--teleport-colors-interactive-tonal-danger-2);
101+
border-color: var(--teleport-colors-interactive-solid-danger-default);
102+
103+
.octicon {
104+
fill: var(--teleport-colors-interactive-solid-danger-default);
105+
}
106+
}
107+
108+
.markdown-alert-title {
109+
display: flex;
110+
margin-bottom: var(--teleport-spacing-2);
111+
align-items: center;
112+
font-weight: var(--teleport-font-weights-bold);
113+
}
114+
115+
.octicon {
116+
margin-right: var(--teleport-spacing-2);
117+
display: inline-block;
118+
overflow: visible !important;
119+
vertical-align: text-bottom;
120+
}
33121
`;
34122

123+
export function DocsLink(props: HTMLChakraProps<'a'>) {
124+
const context = useContext(DocsContext);
125+
126+
function handleClick(event: MouseEvent) {
127+
const LEFT_BUTTON = 0;
128+
const isLeftClick =
129+
event.button === LEFT_BUTTON &&
130+
!event.altKey &&
131+
!event.ctrlKey &&
132+
!event.metaKey &&
133+
!event.shiftKey;
134+
135+
if (isLeftClick) {
136+
event.preventDefault();
137+
context.channel.emit(
138+
NAVIGATE_URL,
139+
event.currentTarget.getAttribute('href') ?? ''
140+
);
141+
}
142+
}
143+
144+
return <Link {...props} onClick={handleClick} />;
145+
}
146+
35147
export function DocsContainerWrapper({
36148
children,
37149
context,
@@ -88,24 +200,59 @@ export function DocsContainerWrapper({
88200
<Heading {...props} as="h1" mt={3} mb={4} size="3xl" />
89201
),
90202
h2: props => (
91-
<Heading {...props} as="h2" mt={6} mb={3} size="2xl" />
203+
<Heading {...props} as="h2" mt={6} mb={4} size="2xl" />
92204
),
93-
h3: props => <Heading {...props} as="h3" mt={6} mb={3} size="xl" />,
94-
h4: props => <Heading {...props} as="h4" size="md" />,
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" />,
95207
h5: props => <Heading {...props} as="h5" size="sm" />,
96208
h6: props => <Heading {...props} as="h6" size="xs" />,
97209
p: props => <Text {...props} mb={2} />,
98210
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
99211
pre: props => <CodeBlock text={props.children.props.children} />,
212+
ul: props => <List.Root mt={4} mb={6} pl={4} {...props} />,
213+
li: props => <List.Item pl={1} {...props} />,
214+
a: props => <DocsLink {...props} />,
215+
table: props => (
216+
<Table.Root size="sm" variant="outline" mb={6} {...props} />
217+
),
218+
thead: props => <Table.Header {...props} />,
219+
td: props => <Table.Cell {...props} />,
220+
tr: props => <Table.Row {...props} />,
221+
th: props => <Table.ColumnHeader {...props} />,
222+
blockquote: ({ children, ...rest }) => {
223+
const child = Children.toArray(children as ReactNode[]).find(
224+
child => typeof child !== 'string'
225+
);
226+
227+
// @ts-expect-error TS2322 - need to figure out type here
228+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
229+
const content = child.props.children;
230+
231+
return (
232+
<Blockquote.Root {...rest} my={5}>
233+
<Blockquote.Content>{content}</Blockquote.Content>
234+
</Blockquote.Root>
235+
);
236+
},
100237
code: Code,
101238
}}
102239
>
103240
<DocsContext.Provider value={context}>
104241
<SourceContainer channel={context.channel}>
105242
<ThemeProvider theme={storybookTheme}>
106-
<Box py={4} px={10}>
107-
{children}
108-
</Box>
243+
<VStack flex="1">
244+
<Box maxW="7xl" w="100%" data-docs-content mr={-4}>
245+
<HStack gap={6} align="flex-start">
246+
<Box flex={1} py={4}>
247+
{children}
248+
249+
<DocsNavigation />
250+
</Box>
251+
252+
<TableOfContents channel={context.channel} />
253+
</HStack>
254+
</Box>
255+
</VStack>
109256
</ThemeProvider>
110257
</SourceContainer>
111258
</DocsContext.Provider>

.storybook/docs/DocsNavigation.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { HStack, Spacer, Text, VStack } from '@chakra-ui/react';
2+
import { DocsContext } from '@storybook/addon-docs/blocks';
3+
import { useContext, useMemo, type MouseEvent } from 'react';
4+
import { NAVIGATE_URL } from 'storybook/internal/core-events';
5+
import type { IndexEntry } from 'storybook/internal/types';
6+
7+
import { CaretLeftIcon, CaretRightIcon } from '../../src';
8+
import preview from '../preview';
9+
import { getIdFromUrl, type TypedDocsContext } from './utils';
10+
11+
interface NavigationItemProps {
12+
title: string;
13+
next: boolean;
14+
id: string;
15+
onClick: (event: MouseEvent) => void;
16+
}
17+
18+
function NavigationItem({ id, onClick, title, next }: NavigationItemProps) {
19+
const text = next ? 'Next' : 'Previous';
20+
const icon = next ? <CaretRightIcon /> : <CaretLeftIcon />;
21+
22+
return (
23+
<VStack
24+
asChild
25+
onClick={onClick}
26+
align={next ? 'flex-end' : 'flex-start'}
27+
borderRadius="md"
28+
border="1px solid {colors.interactive.tonal.neutral.1}"
29+
flex={1}
30+
px={4}
31+
gap={1}
32+
pt={3}
33+
pb={4}
34+
minWidth="200px"
35+
_hover={{ bg: 'interactive.tonal.neutral.1' }}
36+
>
37+
<a href={`/?path=/docs/${id}`}>
38+
<Text color="text.slightlyMuted" fontSize="sm">
39+
{text}
40+
</Text>
41+
42+
<HStack fontWeight="bold">
43+
{!next && icon}
44+
{title}
45+
{next && icon}
46+
</HStack>
47+
</a>
48+
</VStack>
49+
);
50+
}
51+
52+
function getItemTitle(item: IndexEntry) {
53+
const path = item.title.split('/');
54+
55+
return path[path.length - 1];
56+
}
57+
58+
export function DocsNavigation() {
59+
const context = useContext(DocsContext) as TypedDocsContext;
60+
const store = context.store;
61+
const currentId = getIdFromUrl();
62+
63+
// eslint-disable-next-line react-hooks/preserve-manual-memoization
64+
const { prev, next } = useMemo(() => {
65+
if (!store.storyIndex?.entries || !currentId) {
66+
return { prev: null, next: null };
67+
}
68+
69+
const entries = Object.values(store.storyIndex.entries);
70+
const orderedEntries = entries.toSorted(
71+
preview.parameters.options.storySort
72+
);
73+
74+
const currentIndex = orderedEntries.findIndex(
75+
entry => entry.id === currentId
76+
);
77+
78+
if (currentIndex === -1) {
79+
return { prev: null, next: null };
80+
}
81+
82+
return {
83+
prev: orderedEntries[currentIndex - 1] ?? null,
84+
next: orderedEntries[currentIndex + 1] ?? null,
85+
};
86+
}, [store.storyIndex?.entries, currentId]);
87+
88+
function handleClick(event: MouseEvent) {
89+
const LEFT_BUTTON = 0;
90+
const isLeftClick =
91+
event.button === LEFT_BUTTON &&
92+
!event.altKey &&
93+
!event.ctrlKey &&
94+
!event.metaKey &&
95+
!event.shiftKey;
96+
97+
if (isLeftClick) {
98+
event.preventDefault();
99+
// use the A element's href, which has been modified for
100+
// local paths without a `?path=` query param prefix
101+
context.channel.emit(
102+
NAVIGATE_URL,
103+
event.currentTarget.getAttribute('href') ?? ''
104+
);
105+
}
106+
}
107+
108+
return (
109+
<HStack w="100%" justify="space-between" gap={10} mt={8} mb={8}>
110+
{prev ? (
111+
<NavigationItem
112+
id={prev.id}
113+
title={getItemTitle(prev)}
114+
next={false}
115+
onClick={handleClick}
116+
/>
117+
) : (
118+
<Spacer />
119+
)}
120+
121+
{next ? (
122+
<NavigationItem
123+
id={next.id}
124+
title={getItemTitle(next)}
125+
next={true}
126+
onClick={handleClick}
127+
/>
128+
) : (
129+
<Spacer />
130+
)}
131+
</HStack>
132+
);
133+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { GithubLogoIcon } from '../../src';
2+
import { GitHubLink } from '../../src/storybook/components/GitHubLink';
3+
import { useCurrentEntry } from './utils';
4+
5+
export function EditInGitHubLink() {
6+
const entry = useCurrentEntry();
7+
8+
if (!entry) {
9+
return null;
10+
}
11+
12+
const importPath = entry.importPath.startsWith('./')
13+
? entry.importPath.slice(2)
14+
: entry.importPath;
15+
const githubUrl = `https://github.com/gravitational/design-system/blob/main/${importPath}`;
16+
17+
return (
18+
<GitHubLink fontSize="sm" href={githubUrl} icon={GithubLogoIcon}>
19+
Edit this page on GitHub
20+
</GitHubLink>
21+
);
22+
}

0 commit comments

Comments
 (0)