images.stories.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import {useEffect, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import SizingWindow from 'sentry/components/stories/sizingWindow';
  4. import TextOverflow from 'sentry/components/textOverflow';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import storyBook from 'sentry/stories/storyBook';
  7. import {space} from 'sentry/styles/space';
  8. const toCamelCase = function camalize(str) {
  9. return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_m, chr) => chr.toUpperCase());
  10. };
  11. const nameOfFile = (file: string) => {
  12. return file.split('/').at(-1)?.split('.').at(0);
  13. };
  14. function imagesContext() {
  15. const context = require.context('sentry-images', true, /\.(svg|gif|png)$/, 'lazy');
  16. return {
  17. files: () => context.keys().map(file => file.replace(/^\.\//, 'sentry-images/')),
  18. importImage: (filename: string) =>
  19. context(filename.replace(/^sentry-images\//, './')),
  20. };
  21. }
  22. function LazyImage({file, module}: {file: string; module: Promise<string>}) {
  23. const [imgSrc, setImgSrc] = useState<string | undefined>(undefined);
  24. useEffect(() => {
  25. module.then(mod => {
  26. setImgSrc(mod);
  27. });
  28. }, [module]);
  29. return (
  30. <SizingWindow>
  31. <img alt={file} src={imgSrc ?? ''} />
  32. </SizingWindow>
  33. );
  34. }
  35. export default storyBook('sentry-image/*', story => {
  36. const context = imagesContext();
  37. const allFiles = context.files();
  38. const spotImages: string[] = [];
  39. const patternImages: string[] = [];
  40. const otherImages: string[] = [];
  41. allFiles.forEach(file => {
  42. if (file.startsWith('sentry-images/spot/')) {
  43. spotImages.push(file);
  44. } else if (file.startsWith('sentry-images/pattern/')) {
  45. patternImages.push(file);
  46. } else {
  47. otherImages.push(file);
  48. }
  49. });
  50. const section = (title: string, images: string[]) => {
  51. story(title, () => (
  52. <Grid>
  53. {images.map(file => (
  54. <GridCell key={file}>
  55. <Tooltip
  56. isHoverable
  57. title={`import ${toCamelCase(nameOfFile(file))} from '${file}';`}
  58. >
  59. <TextOverflow>
  60. <code>{nameOfFile(file)}</code>
  61. </TextOverflow>
  62. <LazyImage file={file} module={context.importImage(file)} />
  63. </Tooltip>
  64. </GridCell>
  65. ))}
  66. </Grid>
  67. ));
  68. };
  69. section('sentry-images/spot/*', spotImages);
  70. section('sentry-images/pattern/*', patternImages);
  71. section('Other', otherImages);
  72. });
  73. const Grid = styled('ul')`
  74. display: grid;
  75. grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  76. grid-template-rows: masonry;
  77. gap: ${space(1)};
  78. align-items: flex-start;
  79. margin: 0;
  80. padding: 0;
  81. & > li {
  82. margin: 0;
  83. padding: 0;
  84. list-style: none;
  85. }
  86. `;
  87. const GridCell = styled('li')`
  88. display: flex;
  89. align-content: flex-start;
  90. `;