index.tsx 3.0 KB

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