bigNumberWidget.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import styled from '@emotion/styled';
  2. import {space} from 'sentry/styles/space';
  3. import {defined} from 'sentry/utils';
  4. import {
  5. BigNumberWidgetVisualization,
  6. type BigNumberWidgetVisualizationProps,
  7. } from 'sentry/views/dashboards/widgets/bigNumberWidget/bigNumberWidgetVisualization';
  8. import {
  9. WidgetFrame,
  10. type WidgetFrameProps,
  11. } from 'sentry/views/dashboards/widgets/common/widgetFrame';
  12. import {MISSING_DATA_MESSAGE, NON_FINITE_NUMBER_MESSAGE} from '../common/settings';
  13. import type {DataProps, StateProps} from '../common/types';
  14. import {DEEMPHASIS_COLOR_NAME, LOADING_PLACEHOLDER} from './settings';
  15. interface Props
  16. extends DataProps,
  17. StateProps,
  18. Omit<WidgetFrameProps, 'children'>,
  19. Omit<BigNumberWidgetVisualizationProps, 'value' | 'previousPeriodValue'> {}
  20. export function BigNumberWidget(props: Props) {
  21. const {data, previousPeriodData} = props;
  22. // TODO: Instrument getting more than one data key back as an error
  23. // e.g., with data that looks like `[{'apdex()': 0.8}] this pulls out `"apdex()"` or `undefined`
  24. const field = Object.keys(data?.[0] ?? {})[0];
  25. const value = data?.[0]?.[field];
  26. const previousPeriodValue = previousPeriodData?.[0]?.[field];
  27. if (props.isLoading) {
  28. return (
  29. <WidgetFrame title={props.title} description={props.description}>
  30. <LoadingPlaceholder>{LOADING_PLACEHOLDER}</LoadingPlaceholder>
  31. </WidgetFrame>
  32. );
  33. }
  34. let parsingError: string | undefined = undefined;
  35. if (!defined(value)) {
  36. parsingError = MISSING_DATA_MESSAGE;
  37. } else if (
  38. (typeof value === 'number' && !Number.isFinite(value)) ||
  39. Number.isNaN(value)
  40. ) {
  41. parsingError = NON_FINITE_NUMBER_MESSAGE;
  42. }
  43. const error = props.error ?? parsingError;
  44. return (
  45. <WidgetFrame
  46. title={props.title}
  47. description={props.description}
  48. actions={props.actions}
  49. error={error}
  50. onRetry={props.onRetry}
  51. >
  52. {defined(value) && (
  53. <BigNumberResizeWrapper>
  54. <BigNumberWidgetVisualization
  55. value={value}
  56. previousPeriodValue={previousPeriodValue}
  57. field={field}
  58. maximumValue={props.maximumValue}
  59. preferredPolarity={props.preferredPolarity}
  60. meta={props.meta}
  61. thresholds={props.thresholds}
  62. />
  63. </BigNumberResizeWrapper>
  64. )}
  65. </WidgetFrame>
  66. );
  67. }
  68. const BigNumberResizeWrapper = styled('div')`
  69. position: relative;
  70. flex-grow: 1;
  71. margin-top: ${space(1)};
  72. `;
  73. const LoadingPlaceholder = styled('span')`
  74. color: ${p => p.theme[DEEMPHASIS_COLOR_NAME]};
  75. font-size: ${p => p.theme.fontSizeLarge};
  76. `;