differenceToPreviousPeriodValue.tsx 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import styled from '@emotion/styled';
  2. import isNumber from 'lodash/isNumber';
  3. import {
  4. ColorizedRating,
  5. getPolarity,
  6. getPolarityRating,
  7. type Polarity,
  8. } from 'sentry/components/percentChange';
  9. import {IconArrow} from 'sentry/icons';
  10. import {space} from 'sentry/styles/space';
  11. import {
  12. DEEMPHASIS_COLOR_NAME,
  13. LOADING_PLACEHOLDER,
  14. } from 'sentry/views/dashboards/widgets/bigNumberWidget/settings';
  15. import type {TableData} from 'sentry/views/dashboards/widgets/common/types';
  16. import {DEFAULT_FIELD} from '../common/settings';
  17. interface DifferenceToPreviousPeriodValueProps {
  18. previousPeriodValue: number;
  19. renderer: (datum: TableData[number]) => React.ReactNode;
  20. value: number;
  21. field?: string;
  22. preferredPolarity?: Polarity;
  23. }
  24. export function DifferenceToPreviousPeriodValue({
  25. value: currentValue,
  26. previousPeriodValue: previousValue,
  27. preferredPolarity = '',
  28. field = DEFAULT_FIELD,
  29. renderer,
  30. }: DifferenceToPreviousPeriodValueProps) {
  31. if (!isNumber(currentValue) || !isNumber(previousValue)) {
  32. return <Deemphasize>{LOADING_PLACEHOLDER}</Deemphasize>;
  33. }
  34. const difference = currentValue - previousValue;
  35. const polarity = getPolarity(difference);
  36. const rating = getPolarityRating(polarity, preferredPolarity);
  37. const directionMarker = getDifferenceDirectionMarker(difference);
  38. // Create a fake data row so we can pass it to field renderers. Omit the +/- sign since the direction marker will indicate it
  39. const differenceAsDatum = {
  40. [field ?? 'unknown']: Math.abs(difference),
  41. };
  42. return (
  43. <Difference rating={rating}>
  44. <Text>{directionMarker}</Text>
  45. <Text>{renderer(differenceAsDatum)}</Text>
  46. </Difference>
  47. );
  48. }
  49. const Difference = styled(ColorizedRating)`
  50. display: flex;
  51. gap: ${space(0.25)};
  52. margin-bottom: 6cqh;
  53. @container (min-height: 50px) {
  54. margin-bottom: 10cqh;
  55. }
  56. `;
  57. const Text = styled('div')`
  58. font-size: 14px;
  59. @container (min-height: 50px) {
  60. font-size: clamp(14px, calc(10px + 4cqi), 30cqh);
  61. }
  62. `;
  63. const Deemphasize = styled('span')`
  64. color: ${p => p.theme[DEEMPHASIS_COLOR_NAME]};
  65. `;
  66. function getDifferenceDirectionMarker(difference: number) {
  67. if (difference > 0) {
  68. return <IconArrow direction="up" size="xs" />;
  69. }
  70. if (difference < 0) {
  71. return <IconArrow direction="down" size="xs" />;
  72. }
  73. return null;
  74. }