storyBook.tsx 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import type {ReactNode} from 'react';
  2. import {Children, Fragment} from 'react';
  3. import styled from '@emotion/styled';
  4. import SideBySide from 'sentry/components/stories/sideBySide';
  5. import {space} from 'sentry/styles/space';
  6. import {StoryTypes} from 'sentry/views/stories/storyTypes';
  7. type StoryRenderFunction = () => ReactNode | ReactNode[];
  8. type StoryContext = (storyName: string, story: StoryRenderFunction) => void;
  9. type SetupFunction = (
  10. story: StoryContext,
  11. apiReference: (documentation: TypeLoader.ComponentDocWithFilename | undefined) => void
  12. ) => void;
  13. export default function storyBook(
  14. title: string,
  15. setup: SetupFunction
  16. ): StoryRenderFunction {
  17. const stories: Array<{
  18. name: string;
  19. render: StoryRenderFunction;
  20. }> = [];
  21. const APIDocumentation: Array<TypeLoader.ComponentDocWithFilename | undefined> = [];
  22. const storyFn: StoryContext = (name: string, render: StoryRenderFunction) => {
  23. stories.push({name, render});
  24. };
  25. const apiReferenceFn: (
  26. documentation: TypeLoader.ComponentDocWithFilename | undefined
  27. ) => void = (documentation: TypeLoader.ComponentDocWithFilename | undefined) => {
  28. APIDocumentation.push(documentation);
  29. };
  30. setup(storyFn, apiReferenceFn);
  31. return function RenderStory() {
  32. return (
  33. <Fragment>
  34. <StoryTitle>{title}</StoryTitle>
  35. {stories.map(({name, render}, i) => (
  36. <Story key={i} name={name} render={render} />
  37. ))}
  38. {APIDocumentation.map((documentation, i) => (
  39. <StoryTypes key={i} types={documentation} />
  40. ))}
  41. </Fragment>
  42. );
  43. };
  44. }
  45. function Story(props: {name: string; render: StoryRenderFunction}) {
  46. const children = props.render();
  47. const isOneChild = Children.count(children) === 1;
  48. return (
  49. <StorySection>
  50. <StoryTitle>{props.name}</StoryTitle>
  51. {isOneChild ? children : <SideBySide>{children}</SideBySide>}
  52. </StorySection>
  53. );
  54. }
  55. export const StorySection = styled('section')`
  56. margin-top: ${space(4)};
  57. & > p {
  58. margin: ${space(3)} 0;
  59. }
  60. `;
  61. export const StoryTitle = styled('h3')`
  62. border-bottom: 1px solid ${p => p.theme.border};
  63. scroll-margin-top: ${space(2)};
  64. `;