dataSetStep.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import {useEffect, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import Alert from 'sentry/components/alert';
  4. import FeatureBadge from 'sentry/components/badge/featureBadge';
  5. import {Button} from 'sentry/components/button';
  6. import type {RadioGroupProps} from 'sentry/components/forms/controls/radioGroup';
  7. import RadioGroup from 'sentry/components/forms/controls/radioGroup';
  8. import ExternalLink from 'sentry/components/links/externalLink';
  9. import {IconClose} from 'sentry/icons';
  10. import {t, tct} from 'sentry/locale';
  11. import {space} from 'sentry/styles/space';
  12. import {DatasetSource} from 'sentry/utils/discover/types';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {DisplayType, type WidgetType} from 'sentry/views/dashboards/types';
  15. import {hasDatasetSelector} from 'sentry/views/dashboards/utils';
  16. import {DATASET_LABEL_MAP} from 'sentry/views/discover/savedQuery/datasetSelectorTabs';
  17. import {DataSet} from '../utils';
  18. import {BuildStep} from './buildStep';
  19. function DiscoverSplitAlert({onDismiss, splitDecision}: any) {
  20. const splitAlertMessage = splitDecision
  21. ? tct(
  22. "We're splitting our datasets up to make it a bit easier to digest. We defaulted this widget to [splitDecision]. Edit as you see fit.",
  23. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  24. {splitDecision: DATASET_LABEL_MAP[splitDecision]}
  25. )
  26. : null;
  27. return (
  28. <Alert
  29. type="warning"
  30. showIcon
  31. trailingItems={
  32. <StyledCloseButton
  33. icon={<IconClose size="sm" />}
  34. aria-label={t('Close')}
  35. onClick={onDismiss}
  36. size="zero"
  37. borderless
  38. />
  39. }
  40. >
  41. {splitAlertMessage}
  42. </Alert>
  43. );
  44. }
  45. interface Props {
  46. dataSet: DataSet;
  47. displayType: DisplayType;
  48. onChange: (dataSet: DataSet) => void;
  49. source?: DatasetSource;
  50. splitDecision?: WidgetType;
  51. }
  52. export function DataSetStep({
  53. dataSet,
  54. onChange,
  55. displayType,
  56. splitDecision,
  57. source,
  58. }: Props) {
  59. const [showSplitAlert, setShowSplitAlert] = useState<boolean>(
  60. source === DatasetSource.FORCED
  61. );
  62. const organization = useOrganization();
  63. const disabledChoices: RadioGroupProps<string>['disabledChoices'] = [];
  64. const hasDatasetSelectorFeature = hasDatasetSelector(organization);
  65. useEffect(() => {
  66. setShowSplitAlert(!!splitDecision);
  67. }, [splitDecision]);
  68. if (displayType !== DisplayType.TABLE) {
  69. disabledChoices.push([
  70. DataSet.ISSUES,
  71. t('This dataset is restricted to tabular visualization.'),
  72. ]);
  73. }
  74. const datasetChoices = new Map<string, string | React.ReactNode>();
  75. if (hasDatasetSelectorFeature) {
  76. // TODO: Finalize description copy
  77. datasetChoices.set(DataSet.ERRORS, t('Errors (TypeError, InvalidSearchQuery, etc)'));
  78. datasetChoices.set(DataSet.TRANSACTIONS, t('Transactions'));
  79. }
  80. if (!hasDatasetSelectorFeature) {
  81. datasetChoices.set(DataSet.EVENTS, t('Errors and Transactions'));
  82. }
  83. if (organization.features.includes('dashboards-eap')) {
  84. datasetChoices.set(
  85. DataSet.SPANS,
  86. <FeatureBadgeAlignmentWrapper aria-label={t('Spans')}>
  87. {t('Spans')}{' '}
  88. <FeatureBadge
  89. type="beta"
  90. title={t('This feature is available for early adopters and the UX may change')}
  91. />
  92. </FeatureBadgeAlignmentWrapper>
  93. );
  94. }
  95. datasetChoices.set(DataSet.ISSUES, t('Issues (States, Assignment, Time, etc.)'));
  96. datasetChoices.set(DataSet.RELEASES, t('Releases (Sessions, Crash rates)'));
  97. return (
  98. <BuildStep
  99. title={t('Choose your dataset')}
  100. description={tct(
  101. `This reflects the type of information you want to use. To learn more, [link: read the docs].`,
  102. {
  103. link: (
  104. <ExternalLink href="https://docs.sentry.io/product/dashboards/widget-builder/#choose-your-dataset" />
  105. ),
  106. }
  107. )}
  108. >
  109. {showSplitAlert && hasDatasetSelectorFeature && (
  110. <DiscoverSplitAlert
  111. onDismiss={() => setShowSplitAlert(false)}
  112. splitDecision={splitDecision}
  113. />
  114. )}
  115. <DataSetChoices
  116. label="dataSet"
  117. value={dataSet}
  118. choices={[...datasetChoices.entries()]}
  119. disabledChoices={disabledChoices}
  120. onChange={newDataSet => {
  121. onChange(newDataSet as DataSet);
  122. }}
  123. orientInline
  124. />
  125. </BuildStep>
  126. );
  127. }
  128. const DataSetChoices = styled(RadioGroup)`
  129. display: flex;
  130. flex-wrap: wrap;
  131. gap: ${space(2)};
  132. `;
  133. const StyledCloseButton = styled(Button)`
  134. background-color: transparent;
  135. transition: opacity 0.1s linear;
  136. &:hover,
  137. &:focus {
  138. background-color: transparent;
  139. opacity: 1;
  140. }
  141. `;
  142. const FeatureBadgeAlignmentWrapper = styled('div')`
  143. ${FeatureBadge} {
  144. position: relative;
  145. top: -1px;
  146. }
  147. `;