Skip to content

Commit

Permalink
Merge pull request #23334 from storybookjs/revert-23333-revert-23142-…
Browse files Browse the repository at this point in the history
…shilman/toc-proof-of-concept

Revert "Revert "Addon-docs: Add opt-in table of contents""
  • Loading branch information
shilman authored Jul 6, 2023
2 parents b89e3a5 + 184ba5b commit 51d6e46
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 6 deletions.
22 changes: 22 additions & 0 deletions code/addons/docs/template/stories/toc/basic.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { global as globalThis } from '@storybook/global';

export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
parameters: {
chromatic: { disable: true },
docs: { toc: {} },
},
};

export const One = {
args: { label: 'One' },
};

export const Two = {
args: { label: 'Two' },
};

export const Three = {
args: { label: 'Two' },
};
14 changes: 14 additions & 0 deletions code/addons/docs/template/stories/toc/custom-selector.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { global as globalThis } from '@storybook/global';
import { One, Two, Three } from './basic.stories';

export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
parameters: {
chromatic: { disable: true },
// Select all the headings in the document
docs: { toc: { headingSelector: 'h1, h2, h3' } },
},
};

export { One, Two, Three };
14 changes: 14 additions & 0 deletions code/addons/docs/template/stories/toc/custom-title.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { global as globalThis } from '@storybook/global';
import { One, Two, Three } from './basic.stories';

export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
parameters: {
chromatic: { disable: true },
// Custom title label
docs: { toc: { title: 'Contents' } },
},
};

export { One, Two, Three };
14 changes: 14 additions & 0 deletions code/addons/docs/template/stories/toc/ignore-selector.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { global as globalThis } from '@storybook/global';
import { One, Two, Three } from './basic.stories';

export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
parameters: {
chromatic: { disable: true },
// Skip the first story in the TOC
docs: { toc: { ignoreSelector: '#one' } },
},
};

export { One, Two, Three };
1 change: 0 additions & 1 deletion code/builders/builder-manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ const starter: StarterFunction = async function* starterGeneratorFn({
}
});
router.use(`/index.html`, ({ path }, res) => {
console.log({ path });
res.status(200).send(html);
});

Expand Down
1 change: 1 addition & 0 deletions code/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ export const parameters = {
},
docs: {
theme: themes.light,
toc: {},
},
controls: {
presetColors: [
Expand Down
1 change: 1 addition & 0 deletions code/ui/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"polished": "^4.2.2",
"react-colorful": "^5.1.2",
"telejson": "^7.0.3",
"tocbot": "^4.20.1",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2"
},
Expand Down
17 changes: 16 additions & 1 deletion code/ui/blocks/src/blocks/DocsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { DocsContextProps } from './DocsContext';
import { DocsContext } from './DocsContext';
import { SourceContainer } from './SourceContainer';
import { scrollToElement } from './utils';
import { TableOfContents } from '../components/TableOfContents';

const { document, window: globalWindow } = global;

Expand All @@ -22,6 +23,16 @@ export const DocsContainer: FC<PropsWithChildren<DocsContainerProps>> = ({
theme,
children,
}) => {
let toc;

try {
const meta = context.resolveOf('meta', ['meta']);
toc = meta.preparedMeta.parameters?.docs?.toc;
} catch (err) {
// No meta, falling back to project annotations
toc = context?.projectAnnotations?.parameters?.docs?.toc;
}

useEffect(() => {
let url;
try {
Expand All @@ -44,7 +55,11 @@ export const DocsContainer: FC<PropsWithChildren<DocsContainerProps>> = ({
<DocsContext.Provider value={context}>
<SourceContainer channel={context.channel}>
<ThemeProvider theme={ensureTheme(theme)}>
<DocsPageWrapper>{children}</DocsPageWrapper>
<DocsPageWrapper
toc={toc ? <TableOfContents className="sbdocs sbdocs-toc--custom" {...toc} /> : null}
>
{children}
</DocsPageWrapper>
</ThemeProvider>
</SourceContainer>
</DocsContext.Provider>
Expand Down
11 changes: 7 additions & 4 deletions code/ui/blocks/src/components/DocsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { transparentize } from 'polished';
import { withReset } from '@storybook/components';
import type { CSSObject } from '@storybook/theming';
import { styled } from '@storybook/theming';
import { transparentize } from 'polished';
import type { FC } from 'react';
import React from 'react';

/**
* This selector styles all raw elements inside the DocsPage like this example with a `<div/>`:
Expand Down Expand Up @@ -429,16 +429,19 @@ export const DocsWrapper = styled.div(({ theme }) => ({
padding: '4rem 20px',
minHeight: '100vh',
boxSizing: 'border-box',
gap: '3rem',

[`@media (min-width: ${breakpoint}px)`]: {},
}));

interface DocsPageWrapperProps {
children?: React.ReactNode;
toc?: React.ReactNode;
}

export const DocsPageWrapper: FC<DocsPageWrapperProps> = ({ children }) => (
export const DocsPageWrapper: FC<DocsPageWrapperProps> = ({ children, toc }) => (
<DocsWrapper className="sbdocs sbdocs-wrapper">
<DocsContent className="sbdocs sbdocs-content">{children}</DocsContent>
{toc}
</DocsWrapper>
);
181 changes: 181 additions & 0 deletions code/ui/blocks/src/components/TableOfContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import React, { useEffect } from 'react';
import type { FC, ReactElement } from 'react';
import { styled } from '@storybook/theming';
import tocbot from 'tocbot';

export interface TocParameters {
/** CSS selector for the container to search for headings. */
contentsSelector?: string;

/**
* When true, hide the TOC. We still show the empty container
* (as opposed to showing nothing at all) because it affects the
* page layout and we want to preserve the layout across pages.
*/
disable?: boolean;

/** CSS selector to match headings to list in the TOC. */
headingSelector?: string;

/** Headings that match the ignoreSelector will be skipped. */
ignoreSelector?: string;

/** Custom title ReactElement or string to display above the TOC. */
title?: ReactElement | string | null;

/**
* TocBot options, not guaranteed to be available in future versions.
* @see tocbot docs {@link https://tscanlin.github.io/tocbot/#usage}
*/
unsafeTocbotOptions?: tocbot.IStaticOptions;
}

const Wrapper = styled.div(({ theme }) => ({
width: '10rem',

'@media (max-width: 768px)': {
display: 'none',
},
}));

const Content = styled.div(({ theme }) => ({
position: 'fixed',
top: 0,
width: '10rem',
paddingTop: '4rem',

fontFamily: theme.typography.fonts.base,
fontSize: theme.typography.size.s2,

WebkitFontSmoothing: 'antialiased',
MozOsxFontSmoothing: 'grayscale',
WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)',
WebkitOverflowScrolling: 'touch',

'& *': {
boxSizing: 'border-box',
},

'& > .toc-wrapper > .toc-list': {
paddingLeft: 0,
borderLeft: `solid 2px ${theme.color.mediumlight}`,

'.toc-list': {
paddingLeft: 0,
borderLeft: `solid 2px ${theme.color.mediumlight}`,

'.toc-list': {
paddingLeft: 0,
borderLeft: `solid 2px ${theme.color.mediumlight}`,
},
},
},
'& .toc-list-item': {
position: 'relative',
listStyleType: 'none',
marginLeft: 20,
paddingTop: 3,
paddingBottom: 3,
},
'& .toc-list-item::before': {
content: '""',
position: 'absolute',
height: '100%',
top: 0,
left: 0,
transform: `translateX(calc(-2px - 20px))`,
borderLeft: `solid 2px ${theme.color.mediumdark}`,
opacity: 0,
transition: 'opacity 0.2s',
},
'& .toc-list-item.is-active-li::before': {
opacity: 1,
},
'& .toc-list-item > a': {
color: theme.color.defaultText,
textDecoration: 'none',
},
'& .toc-list-item.is-active-li > a': {
fontWeight: 600,
color: theme.color.secondary,
textDecoration: 'none',
},
}));

const Heading = styled.p(({ theme }) => ({
fontWeight: 600,
fontSize: '0.875em',
color: theme.textColor,
textTransform: 'uppercase',
marginBottom: 10,
}));

type TableOfContentsProps = React.PropsWithChildren<
TocParameters & {
className?: string;
}
>;

const OptionalTitle: FC<{ title: TableOfContentsProps['title'] }> = ({ title }) => {
if (title === null) {
return null;
}
if (typeof title === 'string') {
return <Heading>{title}</Heading>;
}
return title;
};

export const TableOfContents = ({
title,
disable,
headingSelector,
contentsSelector,
ignoreSelector,
unsafeTocbotOptions,
}: TableOfContentsProps) => {
useEffect(() => {
const configuration = {
tocSelector: '.toc-wrapper',
contentSelector: contentsSelector ?? '.sbdocs-content',
headingSelector: headingSelector ?? 'h3',
ignoreSelector: ignoreSelector ?? '.skip-toc',
headingsOffset: 40,
scrollSmoothOffset: -40,
/**
* Ignore headings that did not
* come from the main markdown code.
*/
// ignoreSelector: ':not(.sbdocs), .hide-from-toc',
orderedList: false,
/**
* Prevent default linking behavior,
* leaving only the smooth scrolling.
*/
onClick: () => false,
...unsafeTocbotOptions,
};

/**
* Wait for the DOM to be ready.
*/
const timeout = setTimeout(() => tocbot.init(configuration), 100);
return () => {
clearTimeout(timeout);
tocbot.destroy();
};
}, [disable]);

return (
<>
<Wrapper>
{!disable ? (
<Content>
<OptionalTitle title={title || null} />
<div className="toc-wrapper" />
</Content>
) : null}
</Wrapper>
</>
);
};
8 changes: 8 additions & 0 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5802,6 +5802,7 @@ __metadata:
polished: ^4.2.2
react-colorful: ^5.1.2
telejson: ^7.0.3
tocbot: ^4.20.1
ts-dedent: ^2.0.0
util-deprecate: ^1.0.2
peerDependencies:
Expand Down Expand Up @@ -29218,6 +29219,13 @@ __metadata:
languageName: node
linkType: hard

"tocbot@npm:^4.20.1":
version: 4.21.0
resolution: "tocbot@npm:4.21.0"
checksum: 877d99df40c07ec5e5c2259b820be9c8af9a9f52d582a61b7bed3d43daff820f23031bc613a5cc3bb14ecc34b79c1a45349dcbae8f3a79de7ecc127f366ed3c6
languageName: node
linkType: hard

"toggle-selection@npm:^1.0.6":
version: 1.0.6
resolution: "toggle-selection@npm:1.0.6"
Expand Down

0 comments on commit 51d6e46

Please sign in to comment.