docsLinks.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import styled from '@emotion/styled';
  2. /* eslint-disable-next-line import/default */
  3. import LinkTo from '@storybook/addon-links/react';
  4. import {IconArrow} from 'sentry/icons';
  5. import space from 'sentry/styles/space';
  6. import {Theme} from 'sentry/utils/theme';
  7. type Link = {
  8. img?: {
  9. src: string;
  10. alt: string;
  11. };
  12. title: string;
  13. desc?: string;
  14. /**
  15. * props to pass to LinkTo:
  16. *
  17. * if the target is a docs page (.mdx file),
  18. * then kind = page title (e.g. 'Core/Overview', as defined in the Meta tag),
  19. * and story = 'page'
  20. *
  21. * if the target is a story (.js(x)/.ts(x) file),
  22. * then kind = story page title (from the title key in the default export)
  23. * and story = a story name (defined with .storyName) or just 'default'
  24. */
  25. kind: string;
  26. story: string;
  27. };
  28. type LinkProps = Link;
  29. type Props = {
  30. links: Link[];
  31. theme: Theme;
  32. };
  33. /**
  34. * kind defaults to 'Core/Overview', so an empty link will just
  35. * lead back to the home page
  36. */
  37. const DocsLink = ({img, title, desc, kind, story}: LinkProps) => (
  38. <LinkWrap kind={kind} story={story}>
  39. {img && (
  40. <ImgWrap>
  41. <Img src={img.src} alt={img.alt} />
  42. </ImgWrap>
  43. )}
  44. <TitleWrap>
  45. <Title>{title}</Title>
  46. <IconWrap>
  47. <IconArrow color="textColor" direction="right" size="sm" />
  48. </IconWrap>
  49. </TitleWrap>
  50. {desc && <Desc>{desc}</Desc>}
  51. </LinkWrap>
  52. );
  53. const DocsLinks = ({links}: Props) => (
  54. <Wrapper>
  55. {links.map((link, i) => (
  56. <DocsLink key={i} {...link} />
  57. ))}
  58. </Wrapper>
  59. );
  60. export default DocsLinks;
  61. const Wrapper = styled('div')`
  62. display: flex;
  63. flex-wrap: wrap;
  64. justify-content: flex-start;
  65. gap: ${space(2)};
  66. width: 100%;
  67. margin: ${space(2)} auto;
  68. `;
  69. const LinkWrap = styled(LinkTo)`
  70. width: calc((100% - ${space(2)} * 2) / 3);
  71. cursor: pointer;
  72. margin: ${space(1)} 0;
  73. @media only screen and (max-width: ${p => p.theme.breakpoints[0]}) {
  74. width: calc((100% - ${space(2)}) / 2);
  75. }
  76. `;
  77. const ImgWrap = styled('div')`
  78. position: relative;
  79. width: 100%;
  80. padding-top: 50%;
  81. border: solid 1px ${p => p.theme.border};
  82. border-radius: ${p => p.theme.borderRadius};
  83. overflow: hidden;
  84. transition: 0.2s ease-out;
  85. ${/* sc-selector */ LinkWrap}:hover & {
  86. border: solid 1px ${p => p.theme.border};
  87. }
  88. `;
  89. const Img = styled('img')`
  90. position: absolute;
  91. top: 0;
  92. left: 0;
  93. width: 100%;
  94. height: 100%;
  95. object-fit: cover;
  96. `;
  97. const TitleWrap = styled('div')`
  98. display: flex;
  99. align-items: center;
  100. margin-top: ${space(2)};
  101. `;
  102. const Title = styled('p')`
  103. font-weight: 600;
  104. line-height: 1;
  105. margin-bottom: 0;
  106. margin-right: ${space(1)};
  107. `;
  108. const IconWrap = styled('div')`
  109. display: flex;
  110. align-items: center;
  111. transition: 0.2s ease-out;
  112. ${/* sc-selector */ LinkWrap}:hover & {
  113. transform: translateX(${space(0.5)});
  114. }
  115. `;
  116. const Desc = styled('p')`
  117. margin-top: ${space(0.5)};
  118. font-size: 0.875rem;
  119. color: ${p => p.theme.subText};
  120. `;