dashboardCard.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import {useTheme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import {ActivityAvatar} from 'sentry/components/activity/item/avatar';
  4. import Card from 'sentry/components/card';
  5. import InteractionStateLayer from 'sentry/components/interactionStateLayer';
  6. import type {LinkProps} from 'sentry/components/links/link';
  7. import Link from 'sentry/components/links/link';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {User} from 'sentry/types/user';
  11. interface Props {
  12. detail: React.ReactNode;
  13. renderWidgets: () => React.ReactNode;
  14. title: string;
  15. to: LinkProps['to'];
  16. createdBy?: User;
  17. dateStatus?: React.ReactNode;
  18. onEventClick?: () => void;
  19. renderContextMenu?: () => React.ReactNode;
  20. }
  21. function DashboardCard({
  22. title,
  23. detail,
  24. createdBy,
  25. renderWidgets,
  26. dateStatus,
  27. to,
  28. onEventClick,
  29. renderContextMenu,
  30. }: Props) {
  31. function onClick() {
  32. onEventClick?.();
  33. }
  34. // Fetch the theme to set the `InteractionStateLayer` color. Otherwise it will
  35. // use the `currentColor` of the `Link`, which is blue, and not correct
  36. const theme = useTheme();
  37. return (
  38. <CardWithoutMargin>
  39. <CardLink
  40. data-test-id={`card-${title}`}
  41. onClick={onClick}
  42. to={to}
  43. aria-label={title}
  44. >
  45. <InteractionStateLayer as="div" color={theme.textColor} />
  46. <CardHeader>
  47. <CardContent>
  48. <Title>{title}</Title>
  49. <Detail>{detail}</Detail>
  50. </CardContent>
  51. <AvatarWrapper>
  52. {createdBy ? (
  53. <ActivityAvatar type="user" user={createdBy} size={34} />
  54. ) : (
  55. <ActivityAvatar type="system" size={34} />
  56. )}
  57. </AvatarWrapper>
  58. </CardHeader>
  59. <CardBody>{renderWidgets()}</CardBody>
  60. <CardFooter>
  61. <DateSelected>
  62. {dateStatus ? (
  63. <DateStatus>
  64. {t('Created')} {dateStatus}
  65. </DateStatus>
  66. ) : (
  67. <DateStatus />
  68. )}
  69. </DateSelected>
  70. </CardFooter>
  71. </CardLink>
  72. <ContextMenuWrapper>{renderContextMenu?.()}</ContextMenuWrapper>
  73. </CardWithoutMargin>
  74. );
  75. }
  76. const AvatarWrapper = styled('span')`
  77. border: 3px solid ${p => p.theme.border};
  78. border-radius: 50%;
  79. height: min-content;
  80. `;
  81. const CardContent = styled('div')`
  82. flex-grow: 1;
  83. overflow: hidden;
  84. margin-right: ${space(1)};
  85. `;
  86. const CardWithoutMargin = styled(Card)`
  87. margin: 0;
  88. `;
  89. const Title = styled('div')`
  90. ${p => p.theme.text.cardTitle};
  91. color: ${p => p.theme.headingColor};
  92. ${p => p.theme.overflowEllipsis};
  93. font-weight: ${p => p.theme.fontWeightNormal};
  94. `;
  95. const CardLink = styled(Link)`
  96. position: relative;
  97. display: flex;
  98. flex-direction: column;
  99. color: ${p => p.theme.textColor};
  100. &:focus,
  101. &:hover {
  102. color: ${p => p.theme.textColor};
  103. ${Title} {
  104. text-decoration: underline;
  105. }
  106. }
  107. `;
  108. const CardHeader = styled('div')`
  109. display: flex;
  110. padding: ${space(1.5)} ${space(2)};
  111. `;
  112. const Detail = styled('div')`
  113. font-family: ${p => p.theme.text.familyMono};
  114. font-size: ${p => p.theme.fontSizeSmall};
  115. color: ${p => p.theme.gray300};
  116. ${p => p.theme.overflowEllipsis};
  117. line-height: 1.5;
  118. `;
  119. const CardBody = styled('div')`
  120. background: ${p => p.theme.gray100};
  121. padding: ${space(1.5)} ${space(2)};
  122. max-height: 150px;
  123. min-height: 150px;
  124. overflow: hidden;
  125. border-bottom: 1px solid ${p => p.theme.gray100};
  126. `;
  127. const CardFooter = styled('div')`
  128. display: flex;
  129. justify-content: space-between;
  130. align-items: center;
  131. padding: ${space(1)} ${space(2)};
  132. height: 42px;
  133. `;
  134. const DateSelected = styled('div')`
  135. font-size: ${p => p.theme.fontSizeSmall};
  136. display: grid;
  137. grid-column-gap: ${space(1)};
  138. color: ${p => p.theme.textColor};
  139. ${p => p.theme.overflowEllipsis};
  140. `;
  141. const DateStatus = styled('span')`
  142. color: ${p => p.theme.subText};
  143. padding-left: ${space(1)};
  144. `;
  145. const ContextMenuWrapper = styled('div')`
  146. position: absolute;
  147. right: ${space(2)};
  148. bottom: ${space(1)};
  149. `;
  150. export default DashboardCard;