vitalCard.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import InteractionStateLayer from 'sentry/components/interactionStateLayer';
  4. import QuestionTooltip from 'sentry/components/questionTooltip';
  5. import {space} from 'sentry/styles/space';
  6. import {PERFORMANCE_SCORE_COLORS} from 'sentry/views/insights/browser/webVitals/utils/performanceScoreColors';
  7. type Props = {
  8. description: string;
  9. formattedValue: string | undefined;
  10. status: string | undefined;
  11. statusLabel: string | undefined;
  12. title: string;
  13. onClick?: () => void;
  14. };
  15. function VitalCard({
  16. description,
  17. formattedValue,
  18. status,
  19. statusLabel,
  20. title,
  21. onClick,
  22. }: Props) {
  23. return (
  24. <Fragment>
  25. <MeterBarContainer clickable={onClick !== undefined} onClick={onClick}>
  26. {onClick && <InteractionStateLayer />}
  27. <MeterBarBody>
  28. {description && (
  29. <StyledQuestionTooltip
  30. isHoverable
  31. size="xs"
  32. title={<span>{description}</span>}
  33. />
  34. )}
  35. <MeterHeader>{title}</MeterHeader>
  36. <MeterValueText>{formattedValue ?? '-'}</MeterValueText>
  37. </MeterBarBody>
  38. <MeterBarFooter
  39. label={statusLabel}
  40. status={status as keyof typeof PERFORMANCE_SCORE_COLORS}
  41. />
  42. </MeterBarContainer>
  43. </Fragment>
  44. );
  45. }
  46. const MeterBarContainer = styled('div')<{clickable?: boolean}>`
  47. flex: 1;
  48. position: relative;
  49. padding: 0;
  50. cursor: ${p => (p.clickable ? 'pointer' : 'default')};
  51. min-width: 140px;
  52. `;
  53. const MeterBarBody = styled('div')`
  54. border: 1px solid ${p => p.theme.gray200};
  55. border-radius: ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0 0;
  56. border-bottom: none;
  57. padding: ${space(1)} 0 ${space(0.5)} 0;
  58. `;
  59. const MeterHeader = styled('div')`
  60. font-size: ${p => p.theme.fontSizeSmall};
  61. color: ${p => p.theme.textColor};
  62. display: inline-block;
  63. text-align: center;
  64. width: 100%;
  65. `;
  66. const MeterValueText = styled('div')`
  67. display: flex;
  68. justify-content: center;
  69. align-items: center;
  70. font-size: ${p => p.theme.headerFontSize};
  71. color: ${p => p.theme.textColor};
  72. flex: 1;
  73. text-align: center;
  74. `;
  75. function MeterBarFooter({
  76. label,
  77. status,
  78. }: {
  79. label: string | undefined;
  80. status: keyof typeof PERFORMANCE_SCORE_COLORS | undefined;
  81. }) {
  82. return (
  83. <MeterBarFooterContainer status={status || 'none'}>
  84. {label || '-'}
  85. </MeterBarFooterContainer>
  86. );
  87. }
  88. const MeterBarFooterContainer = styled('div')<{
  89. status: keyof typeof PERFORMANCE_SCORE_COLORS;
  90. }>`
  91. color: ${p => p.theme[PERFORMANCE_SCORE_COLORS[p.status].normal]};
  92. border-radius: 0 0 ${p => p.theme.borderRadius} ${p => p.theme.borderRadius};
  93. background-color: ${p =>
  94. p.status === 'none' ? 'none' : p.theme[PERFORMANCE_SCORE_COLORS[p.status].light]};
  95. border: solid 1px ${p => p.theme[PERFORMANCE_SCORE_COLORS[p.status].light]};
  96. font-size: ${p => p.theme.fontSizeExtraSmall};
  97. padding: ${space(0.5)};
  98. text-align: center;
  99. `;
  100. const StyledQuestionTooltip = styled(QuestionTooltip)`
  101. position: absolute;
  102. right: ${space(1)};
  103. `;
  104. export default VitalCard;