storyBook.tsx 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import {Children, JSXElementConstructor, ReactNode} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Flex} from 'sentry/components/profiling/flex';
  4. import SideBySide from 'sentry/components/stories/sideBySide';
  5. import {space} from 'sentry/styles/space';
  6. type RenderFn = () => ReactNode | ReactNode[];
  7. type StoryFn = (storyName: string, storyRender: RenderFn) => void;
  8. type SetupFn = (story: StoryFn) => void;
  9. type Context = {
  10. name: string;
  11. render: RenderFn;
  12. };
  13. export default function storyBook(
  14. bookContext: string | JSXElementConstructor<any>,
  15. setup: SetupFn
  16. ) {
  17. const contexts: Context[] = [];
  18. const storyFn: StoryFn = (name: string, render: RenderFn) => {
  19. contexts.push({name, render});
  20. };
  21. setup(storyFn);
  22. return function RenderStory() {
  23. return (
  24. <Flex column gap={space(4)}>
  25. <BookHeading bookContext={bookContext} />
  26. {contexts.map(({name, render}, i) => {
  27. const children = render();
  28. const isOneChild = Children.count(children) === 1;
  29. const key = `${i}_${name}`;
  30. return (
  31. <Story key={key}>
  32. <StoryTitle id={key}>{name}</StoryTitle>
  33. {isOneChild ? children : <SideBySide>{children}</SideBySide>}
  34. </Story>
  35. );
  36. })}
  37. </Flex>
  38. );
  39. };
  40. }
  41. function BookHeading({bookContext}) {
  42. if (typeof bookContext === 'string') {
  43. return <BookTitle>{bookContext}</BookTitle>;
  44. }
  45. const componentName =
  46. bookContext.displayName ?? bookContext.name ?? bookContext.constructor.name;
  47. if (!componentName) {
  48. return null;
  49. }
  50. return (
  51. <BookTitle>
  52. <code>{`<${componentName}/>`}</code>
  53. </BookTitle>
  54. );
  55. }
  56. const BookTitle = styled('h3')`
  57. margin: 0;
  58. `;
  59. const Story = styled('section')`
  60. & > p {
  61. margin: ${space(3)} 0;
  62. }
  63. `;
  64. const StoryTitle = styled('h4')`
  65. border-bottom: 1px solid ${p => p.theme.border};
  66. `;