|
1 | 1 | import styled from '@emotion/styled';
|
| 2 | +import qs from 'query-string'; |
2 | 3 |
|
3 | 4 | import {LinkButton} from 'sentry/components/core/button/linkButton';
|
4 | 5 | import {Flex} from 'sentry/components/core/layout';
|
5 | 6 | import {IconArrow} from 'sentry/icons';
|
| 7 | +import {useStoryBookFilesByCategory} from 'sentry/stories/view/storySidebar'; |
| 8 | +import type {StoryTreeNode} from 'sentry/stories/view/storyTree'; |
| 9 | +import type {StoryDescriptor} from 'sentry/stories/view/useStoriesLoader'; |
6 | 10 | import {useStory} from 'sentry/stories/view/useStory';
|
7 | 11 | import {space} from 'sentry/styles/space';
|
| 12 | +import {useLocation} from 'sentry/utils/useLocation'; |
8 | 13 |
|
9 | 14 | export function StoryFooter() {
|
| 15 | + const location = useLocation(); |
| 16 | + |
10 | 17 | const {story} = useStory();
|
11 |
| - if (!story.filename.endsWith('.mdx')) return null; |
12 |
| - const {prev, next} = story.exports.frontmatter ?? {}; |
| 18 | + const categories = useStoryBookFilesByCategory(); |
| 19 | + const pagination = findPreviousAndNextStory(story, categories); |
| 20 | + |
| 21 | + const prevLocationDescriptor = qs.stringify({ |
| 22 | + ...location.query, |
| 23 | + name: pagination?.prev?.story.filesystemPath, |
| 24 | + }); |
| 25 | + const nextLocationDescriptor = qs.stringify({ |
| 26 | + ...location.query, |
| 27 | + name: pagination?.next?.story.filesystemPath, |
| 28 | + }); |
| 29 | + |
13 | 30 | return (
|
14 | 31 | <Flex align="center" justify="space-between" gap={space(2)}>
|
15 |
| - {prev && ( |
16 |
| - <Card to={prev.link} icon={<IconArrow direction="left" />}> |
| 32 | + {pagination?.prev && ( |
| 33 | + <Card |
| 34 | + to={`/stories/?${prevLocationDescriptor}`} |
| 35 | + icon={<IconArrow direction="left" />} |
| 36 | + > |
17 | 37 | <CardLabel>Previous</CardLabel>
|
18 |
| - <CardTitle>{prev.label}</CardTitle> |
| 38 | + <CardTitle>{pagination.prev.story.label}</CardTitle> |
19 | 39 | </Card>
|
20 | 40 | )}
|
21 |
| - {next && ( |
22 |
| - <Card data-flip to={next.link} icon={<IconArrow direction="right" />}> |
| 41 | + {pagination?.next && ( |
| 42 | + <Card |
| 43 | + data-flip |
| 44 | + to={`/stories/?${nextLocationDescriptor}`} |
| 45 | + icon={<IconArrow direction="right" />} |
| 46 | + > |
23 | 47 | <CardLabel>Next</CardLabel>
|
24 |
| - <CardTitle>{next.label}</CardTitle> |
| 48 | + <CardTitle>{pagination.next.story.label}</CardTitle> |
25 | 49 | </Card>
|
26 | 50 | )}
|
27 | 51 | </Flex>
|
28 | 52 | );
|
29 | 53 | }
|
30 | 54 |
|
| 55 | +function findPreviousAndNextStory( |
| 56 | + story: StoryDescriptor, |
| 57 | + categories: ReturnType<typeof useStoryBookFilesByCategory> |
| 58 | +): { |
| 59 | + next: {category: string; story: StoryTreeNode} | undefined; |
| 60 | + prev: {category: string; story: StoryTreeNode} | undefined; |
| 61 | +} | null { |
| 62 | + let prev: {category: string; story: StoryTreeNode} | undefined; |
| 63 | + let next: {category: string; story: StoryTreeNode} | undefined; |
| 64 | + |
| 65 | + const stories: Array<{category: string; story: StoryTreeNode}> = []; |
| 66 | + |
| 67 | + // Flatten into a single list so we don't have to deal with overflowing index |
| 68 | + // categories and can simplify the search procedure. |
| 69 | + for (const key in categories) { |
| 70 | + const category = categories[key as keyof typeof categories]; |
| 71 | + for (const s of category) { |
| 72 | + stories.push({category: key, story: s}); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + for (let i = 0; i < stories.length; i++) { |
| 77 | + const s = stories[i]!; |
| 78 | + if (s.story.filesystemPath === story.filename) { |
| 79 | + prev = stories[i - 1]; |
| 80 | + next = stories[i + 1]; |
| 81 | + return {prev, next}; |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + return null; |
| 86 | +} |
| 87 | + |
31 | 88 | const Card = styled(LinkButton)`
|
32 | 89 | display: flex;
|
33 | 90 | flex-direction: column;
|
|
0 commit comments