visualizationStep.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import {CSSProperties, useCallback, useEffect, useState} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import debounce from 'lodash/debounce';
  5. import isEqual from 'lodash/isEqual';
  6. import {TableCell} from 'sentry/components/charts/simpleTableChart';
  7. import Field from 'sentry/components/forms/field';
  8. import SelectControl from 'sentry/components/forms/selectControl';
  9. import {PanelAlert} from 'sentry/components/panels';
  10. import {DEFAULT_DEBOUNCE_DURATION} from 'sentry/constants';
  11. import {t} from 'sentry/locale';
  12. import space from 'sentry/styles/space';
  13. import {Organization, PageFilters, SelectValue} from 'sentry/types';
  14. import usePrevious from 'sentry/utils/usePrevious';
  15. import {DashboardFilters, DisplayType, Widget} from 'sentry/views/dashboardsV2/types';
  16. import WidgetCard, {WidgetCardPanel} from '../../widgetCard';
  17. import {displayTypes} from '../utils';
  18. import {BuildStep} from './buildStep';
  19. interface Props {
  20. displayType: DisplayType;
  21. onChange: (displayType: DisplayType) => void;
  22. organization: Organization;
  23. pageFilters: PageFilters;
  24. widget: Widget;
  25. dashboardFilters?: DashboardFilters;
  26. error?: string;
  27. noDashboardsMEPProvider?: boolean;
  28. }
  29. export function VisualizationStep({
  30. organization,
  31. pageFilters,
  32. displayType,
  33. error,
  34. onChange,
  35. widget,
  36. noDashboardsMEPProvider,
  37. dashboardFilters,
  38. }: Props) {
  39. const [debouncedWidget, setDebouncedWidget] = useState(widget);
  40. const previousWidget = usePrevious(widget);
  41. // Disabling for now because we use debounce to avoid excessively hitting
  42. // our endpoints, but useCallback wants an inline function and not one
  43. // returned from debounce
  44. // eslint-disable-next-line react-hooks/exhaustive-deps
  45. const debounceWidget = useCallback(
  46. debounce((value: Widget, shouldCancelUpdates: boolean) => {
  47. if (shouldCancelUpdates) {
  48. return;
  49. }
  50. setDebouncedWidget(value);
  51. }, DEFAULT_DEBOUNCE_DURATION),
  52. []
  53. );
  54. useEffect(() => {
  55. let shouldCancelUpdates = false;
  56. if (!isEqual(previousWidget, widget)) {
  57. debounceWidget(widget, shouldCancelUpdates);
  58. }
  59. return () => {
  60. shouldCancelUpdates = true;
  61. };
  62. }, [widget, previousWidget, debounceWidget]);
  63. const displayOptions = Object.keys(displayTypes).map(value => ({
  64. label: displayTypes[value],
  65. value,
  66. }));
  67. return (
  68. <BuildStep
  69. title={t('Choose your visualization')}
  70. description={t(
  71. 'This is a preview of how your widget will appear in the dashboard.'
  72. )}
  73. >
  74. <Field error={error} inline={false} flexibleControlStateSize stacked>
  75. <SelectControl
  76. name="displayType"
  77. options={displayOptions}
  78. value={displayType}
  79. onChange={(option: SelectValue<DisplayType>) => {
  80. onChange(option.value);
  81. }}
  82. styles={{
  83. singleValue: (provided: CSSProperties) => ({
  84. ...provided,
  85. width: `calc(100% - ${space(1)})`,
  86. }),
  87. }}
  88. />
  89. </Field>
  90. <VisualizationWrapper displayType={displayType}>
  91. <WidgetCard
  92. organization={organization}
  93. selection={pageFilters}
  94. widget={debouncedWidget}
  95. dashboardFilters={dashboardFilters}
  96. isEditing={false}
  97. widgetLimitReached={false}
  98. renderErrorMessage={errorMessage =>
  99. typeof errorMessage === 'string' && (
  100. <PanelAlert type="error">{errorMessage}</PanelAlert>
  101. )
  102. }
  103. isSorting={false}
  104. currentWidgetDragging={false}
  105. noLazyLoad
  106. showStoredAlert
  107. noDashboardsMEPProvider={noDashboardsMEPProvider}
  108. />
  109. </VisualizationWrapper>
  110. </BuildStep>
  111. );
  112. }
  113. const VisualizationWrapper = styled('div')<{displayType: DisplayType}>`
  114. padding-right: ${space(2)};
  115. ${WidgetCardPanel} {
  116. height: initial;
  117. }
  118. ${p =>
  119. p.displayType === DisplayType.TABLE &&
  120. css`
  121. overflow: hidden;
  122. ${TableCell} {
  123. /* 24px ActorContainer height + 16px top and bottom padding + 1px border = 41px */
  124. height: 41px;
  125. }
  126. ${WidgetCardPanel} {
  127. /* total size of a table, if it would display 5 rows of content */
  128. height: 301px;
  129. }
  130. `};
  131. `;