scoreCard.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import styled from '@emotion/styled';
  2. import {Panel} from 'sentry/components/panels';
  3. import QuestionTooltip from 'sentry/components/questionTooltip';
  4. import TextOverflow from 'sentry/components/textOverflow';
  5. import space from 'sentry/styles/space';
  6. import {defined} from 'sentry/utils';
  7. import {Theme} from 'sentry/utils/theme';
  8. type Props = {
  9. title: React.ReactNode;
  10. className?: string;
  11. help?: React.ReactNode;
  12. score?: React.ReactNode;
  13. trend?: React.ReactNode;
  14. trendStatus?: 'good' | 'bad';
  15. };
  16. function ScoreCard({title, score, help, trend, trendStatus, className}: Props) {
  17. return (
  18. <ScorePanel className={className}>
  19. <HeaderTitle>
  20. <Title>{title}</Title>
  21. {help && <QuestionTooltip title={help} size="sm" position="top" />}
  22. </HeaderTitle>
  23. <ScoreWrapper>
  24. <Score>{score ?? '\u2014'}</Score>
  25. {defined(trend) && (
  26. <Trend trendStatus={trendStatus}>
  27. <TextOverflow>{trend}</TextOverflow>
  28. </Trend>
  29. )}
  30. </ScoreWrapper>
  31. </ScorePanel>
  32. );
  33. }
  34. function getTrendColor(p: TrendProps & {theme: Theme}) {
  35. switch (p.trendStatus) {
  36. case 'good':
  37. return p.theme.green300;
  38. case 'bad':
  39. return p.theme.red300;
  40. default:
  41. return p.theme.gray300;
  42. }
  43. }
  44. export const ScorePanel = styled(Panel)`
  45. display: flex;
  46. flex-direction: column;
  47. justify-content: space-between;
  48. padding: ${space(2)} ${space(3)};
  49. min-height: 96px;
  50. `;
  51. export const HeaderTitle = styled('div')`
  52. display: inline-grid;
  53. grid-auto-flow: column;
  54. gap: ${space(1)};
  55. align-items: center;
  56. width: fit-content;
  57. `;
  58. export const Title = styled('div')`
  59. font-size: ${p => p.theme.fontSizeLarge};
  60. color: ${p => p.theme.headingColor};
  61. ${p => p.theme.overflowEllipsis};
  62. font-weight: 600;
  63. `;
  64. export const ScoreWrapper = styled('div')`
  65. display: flex;
  66. flex-direction: row;
  67. align-items: flex-end;
  68. max-width: 100%;
  69. `;
  70. export const Score = styled('span')`
  71. flex-shrink: 1;
  72. font-size: 32px;
  73. line-height: 1;
  74. color: ${p => p.theme.headingColor};
  75. white-space: nowrap;
  76. `;
  77. type TrendProps = {trendStatus: Props['trendStatus']};
  78. export const Trend = styled('div')<TrendProps>`
  79. color: ${getTrendColor};
  80. margin-left: ${space(1)};
  81. line-height: 1;
  82. overflow: hidden;
  83. `;
  84. export default ScoreCard;