index.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import {useState} from 'react';
  2. import type {RouteComponentProps} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import Input from 'sentry/components/input';
  5. import {space} from 'sentry/styles/space';
  6. import EmptyStory from 'sentry/views/stories/emptyStory';
  7. import ErrorStory from 'sentry/views/stories/errorStory';
  8. import storiesContext from 'sentry/views/stories/storiesContext';
  9. import StoryFile from 'sentry/views/stories/storyFile';
  10. import StoryHeader from 'sentry/views/stories/storyHeader';
  11. import StoryTree from 'sentry/views/stories/storyTree';
  12. import type {StoriesQuery} from 'sentry/views/stories/types';
  13. import useStoriesLoader from 'sentry/views/stories/useStoriesLoader';
  14. type Props = RouteComponentProps<{}, {}, any, StoriesQuery>;
  15. export default function Stories({location}: Props) {
  16. const story = useStoriesLoader({filename: location.query.name});
  17. const [searchTerm, setSearchTerm] = useState('');
  18. return (
  19. <Layout>
  20. <div
  21. style={{
  22. gridArea: 'aside',
  23. display: 'flex',
  24. gap: `${space(2)}`,
  25. flexDirection: 'column',
  26. }}
  27. >
  28. <Input
  29. placeholder="Search files by name"
  30. onChange={e => setSearchTerm(e.target.value.toLowerCase())}
  31. />
  32. <Sidebar>
  33. <StoryTree
  34. files={storiesContext()
  35. .files()
  36. .filter(s => s.toLowerCase().includes(searchTerm))}
  37. />
  38. </Sidebar>
  39. </div>
  40. <StoryHeader style={{gridArea: 'head'}} />
  41. {story.error ? (
  42. <VerticalScroll style={{gridArea: 'body'}}>
  43. <ErrorStory error={story.error} />
  44. </VerticalScroll>
  45. ) : story.resolved ? (
  46. <Main style={{gridArea: 'body'}}>
  47. <StoryFile filename={story.filename} resolved={story.resolved} />
  48. </Main>
  49. ) : (
  50. <VerticalScroll style={{gridArea: 'body'}}>
  51. <EmptyStory />
  52. </VerticalScroll>
  53. )}
  54. </Layout>
  55. );
  56. }
  57. const Layout = styled('div')`
  58. --stories-grid-space: ${space(2)};
  59. display: grid;
  60. grid-template:
  61. 'head head' max-content
  62. 'aside body' auto/ ${p => p.theme.settings.sidebarWidth} 1fr;
  63. gap: var(--stories-grid-space);
  64. place-items: stretch;
  65. height: 100vh;
  66. padding: var(--stories-grid-space);
  67. `;
  68. const Sidebar = styled('aside')`
  69. overflow: auto;
  70. `;
  71. const VerticalScroll = styled('main')`
  72. overflow-x: hidden;
  73. overflow-y: scroll;
  74. `;
  75. /**
  76. * Avoid <Panel> here because nested panels will have a modified theme.
  77. * Therefore stories will look different in prod.
  78. */
  79. const Main = styled(VerticalScroll)`
  80. background: ${p => p.theme.background};
  81. border-radius: ${p => p.theme.panelBorderRadius};
  82. border: 1px solid ${p => p.theme.border};
  83. padding: var(--stories-grid-space);
  84. overflow-x: hidden;
  85. overflow-y: scroll;
  86. position: relative;
  87. `;