dashboardCard.tsx 3.3 KB

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