thresholdsIndicator.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import {useTheme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import type {Polarity} from 'sentry/components/percentChange';
  4. import {normalizeUnit} from '../../utils';
  5. import {ThresholdsHoverWrapper} from '../../widgetBuilder/buildSteps/thresholdsStep/thresholdsHoverWrapper';
  6. import type {ThresholdsConfig} from '../../widgetBuilder/buildSteps/thresholdsStep/thresholdsStep';
  7. type ValidThresholds = {
  8. max_values: {
  9. max1: number;
  10. max2: number;
  11. };
  12. unit?: string;
  13. };
  14. interface ThresholdsIndicatorProps {
  15. thresholds: ValidThresholds;
  16. type: string;
  17. unit: string;
  18. value: number;
  19. preferredPolarity?: Polarity;
  20. }
  21. export function ThresholdsIndicator({
  22. thresholds,
  23. value,
  24. type,
  25. preferredPolarity = '+',
  26. unit: valueUnit,
  27. }: ThresholdsIndicatorProps) {
  28. const theme = useTheme();
  29. const {max_values, unit: thresholdUnit} = thresholds;
  30. const {max1, max2} = max_values;
  31. const normalizedValue = normalizeUnit(value, valueUnit, type);
  32. const normalizedMax1 = thresholdUnit ? normalizeUnit(max1, thresholdUnit, type) : max1;
  33. const normalizedMax2 = thresholdUnit ? normalizeUnit(max2, thresholdUnit, type) : max2;
  34. const state = getThresholdState(
  35. normalizedValue,
  36. normalizedMax1,
  37. normalizedMax2,
  38. preferredPolarity
  39. );
  40. const colorName = COLOR_NAME_FOR_STATE[state];
  41. const thresholdsConfig: ThresholdsConfig = {
  42. unit: thresholdUnit ?? null,
  43. max_values: {
  44. max1: max1 ?? null,
  45. max2: max2 ?? null,
  46. },
  47. };
  48. return (
  49. <ThresholdsHoverWrapper thresholds={thresholdsConfig} type={type}>
  50. <Circle role="status" aria-label={state} color={theme[colorName]} />
  51. </ThresholdsHoverWrapper>
  52. );
  53. }
  54. const Circle = styled('div')<{color: string}>`
  55. display: inline-block;
  56. height: clamp(12px, 20cqh, 50px);
  57. width: clamp(12px, 20cqh, 50px);
  58. position: relative;
  59. align-self: center;
  60. flex-shrink: 0;
  61. border-radius: 50%;
  62. background: ${p => p.color};
  63. `;
  64. type ThresholdState = 'poor' | 'meh' | 'good';
  65. const COLOR_NAME_FOR_STATE: Record<ThresholdState, string> = {
  66. poor: 'red300',
  67. meh: 'yellow300',
  68. good: 'green300',
  69. };
  70. function getThresholdState(
  71. value: number,
  72. max1: number,
  73. max2: number,
  74. preferredPolarity: Polarity
  75. ): string {
  76. const [belowMax1, belowMax2, aboveMax2] =
  77. preferredPolarity === '+' ? ['poor', 'meh', 'good'] : ['good', 'meh', 'poor'];
  78. if (value <= max1) {
  79. return belowMax1;
  80. }
  81. if (value <= max2) {
  82. return belowMax2;
  83. }
  84. return aboveMax2;
  85. }