newWidgetBuilder.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {Fragment, useEffect} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {AnimatePresence, motion} from 'framer-motion';
  5. import {CustomMeasurementsProvider} from 'sentry/utils/customMeasurements/customMeasurementsProvider';
  6. import EventView from 'sentry/utils/discover/eventView';
  7. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  8. import {MetricsCardinalityProvider} from 'sentry/utils/performance/contexts/metricsCardinality';
  9. import {MEPSettingProvider} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  10. import useKeyPress from 'sentry/utils/useKeyPress';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import usePageFilters from 'sentry/utils/usePageFilters';
  14. import {
  15. type DashboardDetails,
  16. type DashboardFilters,
  17. DisplayType,
  18. type Widget,
  19. } from 'sentry/views/dashboards/types';
  20. import WidgetBuilderSlideout from 'sentry/views/dashboards/widgetBuilder/components/widgetBuilderSlideout';
  21. import WidgetPreview from 'sentry/views/dashboards/widgetBuilder/components/widgetPreview';
  22. import {
  23. useWidgetBuilderContext,
  24. WidgetBuilderProvider,
  25. } from 'sentry/views/dashboards/widgetBuilder/contexts/widgetBuilderContext';
  26. import {DashboardsMEPProvider} from 'sentry/views/dashboards/widgetCard/dashboardsMEPContext';
  27. import {SpanTagsProvider} from 'sentry/views/explore/contexts/spanTagsContext';
  28. import {MetricsDataSwitcher} from 'sentry/views/performance/landing/metricsDataSwitcher';
  29. type WidgetBuilderV2Props = {
  30. dashboard: DashboardDetails;
  31. dashboardFilters: DashboardFilters;
  32. isOpen: boolean;
  33. onClose: () => void;
  34. onSave: ({index, widget}: {index: number; widget: Widget}) => void;
  35. };
  36. function WidgetBuilderV2({
  37. isOpen,
  38. onClose,
  39. onSave,
  40. dashboardFilters,
  41. dashboard,
  42. }: WidgetBuilderV2Props) {
  43. const escapeKeyPressed = useKeyPress('Escape');
  44. const organization = useOrganization();
  45. const {selection} = usePageFilters();
  46. useEffect(() => {
  47. if (escapeKeyPressed) {
  48. if (isOpen) {
  49. onClose?.();
  50. }
  51. }
  52. }, [escapeKeyPressed, isOpen, onClose]);
  53. return (
  54. <Fragment>
  55. {isOpen && <Backdrop style={{opacity: 0.5, pointerEvents: 'auto'}} />}
  56. <AnimatePresence>
  57. {isOpen && (
  58. <WidgetBuilderProvider>
  59. <CustomMeasurementsProvider organization={organization} selection={selection}>
  60. <SpanTagsProvider
  61. dataset={DiscoverDatasets.SPANS_EAP}
  62. enabled={organization.features.includes('dashboards-eap')}
  63. >
  64. <ContainerWithoutSidebar>
  65. <WidgetBuilderContainer>
  66. <WidgetBuilderSlideout
  67. isOpen={isOpen}
  68. onClose={onClose}
  69. onSave={onSave}
  70. />
  71. <WidgetPreviewContainer
  72. dashboardFilters={dashboardFilters}
  73. dashboard={dashboard}
  74. />
  75. </WidgetBuilderContainer>
  76. </ContainerWithoutSidebar>
  77. </SpanTagsProvider>
  78. </CustomMeasurementsProvider>
  79. </WidgetBuilderProvider>
  80. )}
  81. </AnimatePresence>
  82. </Fragment>
  83. );
  84. }
  85. export default WidgetBuilderV2;
  86. function WidgetPreviewContainer({
  87. dashboardFilters,
  88. dashboard,
  89. }: {
  90. dashboard: DashboardDetails;
  91. dashboardFilters: DashboardFilters;
  92. }) {
  93. const {state} = useWidgetBuilderContext();
  94. const organization = useOrganization();
  95. const location = useLocation();
  96. return (
  97. <DashboardsMEPProvider>
  98. <MetricsCardinalityProvider organization={organization} location={location}>
  99. <MetricsDataSwitcher
  100. organization={organization}
  101. location={location}
  102. hideLoadingIndicator
  103. eventView={EventView.fromLocation(location)}
  104. >
  105. {metricsDataSide => (
  106. <MEPSettingProvider
  107. location={location}
  108. forceTransactions={metricsDataSide.forceTransactionsOnly}
  109. >
  110. <SampleWidgetCard
  111. initial={{opacity: 0, x: '50%', y: 0}}
  112. animate={{opacity: 1, x: 0, y: 0}}
  113. exit={{opacity: 0, x: '50%', y: 0}}
  114. transition={{
  115. type: 'spring',
  116. stiffness: 500,
  117. damping: 50,
  118. }}
  119. isTable={state.displayType === DisplayType.TABLE}
  120. >
  121. <WidgetPreview
  122. dashboardFilters={dashboardFilters}
  123. dashboard={dashboard}
  124. />
  125. </SampleWidgetCard>
  126. </MEPSettingProvider>
  127. )}
  128. </MetricsDataSwitcher>
  129. </MetricsCardinalityProvider>
  130. </DashboardsMEPProvider>
  131. );
  132. }
  133. const fullPageCss = css`
  134. position: absolute;
  135. top: 0;
  136. right: 0;
  137. bottom: 0;
  138. left: 0;
  139. `;
  140. const Backdrop = styled('div')`
  141. ${fullPageCss};
  142. z-index: ${p => p.theme.zIndex.widgetBuilderDrawer};
  143. background: ${p => p.theme.black};
  144. will-change: opacity;
  145. transition: opacity 200ms;
  146. pointer-events: none;
  147. opacity: 0;
  148. `;
  149. const SampleWidgetCard = styled(motion.div)<{isTable: boolean}>`
  150. width: 30vw;
  151. min-width: 400px;
  152. height: ${p => (p.isTable ? 'auto' : '400px')};
  153. border: 2px dashed ${p => p.theme.border};
  154. border-radius: ${p => p.theme.borderRadius};
  155. background-color: ${p => p.theme.background};
  156. align-content: center;
  157. z-index: ${p => p.theme.zIndex.modal};
  158. position: relative;
  159. margin: auto;
  160. `;
  161. const ContainerWithoutSidebar = styled('div')`
  162. position: absolute;
  163. top: 0;
  164. left: 0;
  165. `;
  166. const WidgetBuilderContainer = styled('div')`
  167. z-index: ${p => p.theme.zIndex.widgetBuilderDrawer};
  168. display: flex;
  169. align-items: center;
  170. justify-content: space-between;
  171. height: 100vh;
  172. position: fixed;
  173. width: -webkit-fill-available; /* Chrome */
  174. width: -moz-available; /* Firefox */
  175. width: fill-available; /* others */
  176. `;