scoreCard.tsx 2.6 KB

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