Browse Source

feat(new-widget-builder-experience): Open overwrite modal when changes - [DD-555] (#32137)

Covers when queries are being changed, title and display type will be a follow up because
the userHasModified flag should not be set by those fields currently
Nar Saynorath 3 years ago
parent
commit
6bfb843c23

+ 10 - 0
static/app/actionCreators/modal.tsx

@@ -7,6 +7,7 @@ import {DashboardWidgetLibraryModalOptions} from 'sentry/components/modals/dashb
 import type {DashboardWidgetQuerySelectorModalOptions} from 'sentry/components/modals/dashboardWidgetQuerySelectorModal';
 import type {DashboardWidgetQuerySelectorModalOptions} from 'sentry/components/modals/dashboardWidgetQuerySelectorModal';
 import {InviteRow} from 'sentry/components/modals/inviteMembersModal/types';
 import {InviteRow} from 'sentry/components/modals/inviteMembersModal/types';
 import type {ReprocessEventModalOptions} from 'sentry/components/modals/reprocessEventModal';
 import type {ReprocessEventModalOptions} from 'sentry/components/modals/reprocessEventModal';
+import {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
 import type {WidgetViewerModalOptions} from 'sentry/components/modals/widgetViewerModal';
 import type {WidgetViewerModalOptions} from 'sentry/components/modals/widgetViewerModal';
 import {
 import {
   Group,
   Group,
@@ -241,6 +242,15 @@ export async function openAddDashboardWidgetModal(options: DashboardWidgetModalO
   openModal(deps => <Modal {...deps} {...options} />, {backdrop: 'static', modalCss});
   openModal(deps => <Modal {...deps} {...options} />, {backdrop: 'static', modalCss});
 }
 }
 
 
+export async function openWidgetBuilderOverwriteModal(
+  options: OverwriteWidgetModalProps
+) {
+  const mod = await import('sentry/components/modals/widgetBuilder/overwriteWidgetModal');
+  const {default: Modal, modalCss} = mod;
+
+  openModal(deps => <Modal {...deps} {...options} />, {backdrop: 'static', modalCss});
+}
+
 export async function openReprocessEventModal({
 export async function openReprocessEventModal({
   onClose,
   onClose,
   ...options
   ...options

+ 2 - 0
static/app/views/dashboardsV2/widgetBuilder/widgetBuilder.tsx

@@ -962,8 +962,10 @@ function WidgetBuilder({
                     dataSet: prebuiltWidget.widgetType
                     dataSet: prebuiltWidget.widgetType
                       ? WIDGET_TYPE_TO_DATA_SET[prebuiltWidget.widgetType]
                       ? WIDGET_TYPE_TO_DATA_SET[prebuiltWidget.widgetType]
                       : DataSet.EVENTS,
                       : DataSet.EVENTS,
+                    userHasModified: false,
                   })
                   })
                 }
                 }
+                bypassOverwriteModal={!state.userHasModified}
               />
               />
             </Side>
             </Side>
           </Body>
           </Body>

+ 12 - 12
static/app/views/dashboardsV2/widgetBuilder/widgetLibrary/index.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import {useTheme} from '@emotion/react';
 import {useTheme} from '@emotion/react';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 
 
-import {openModal} from 'sentry/actionCreators/modal';
+import {openWidgetBuilderOverwriteModal} from 'sentry/actionCreators/modal';
 import {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
 import {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
 import {t} from 'sentry/locale';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
 import space from 'sentry/styles/space';
@@ -14,23 +14,23 @@ import {
 import {Card} from './card';
 import {Card} from './card';
 
 
 type Props = {
 type Props = {
+  bypassOverwriteModal: boolean;
   onWidgetSelect: (widget: WidgetTemplate) => void;
   onWidgetSelect: (widget: WidgetTemplate) => void;
 };
 };
 
 
-export async function openWidgetBuilderOverwriteModal(
-  options: OverwriteWidgetModalProps
-) {
-  const mod = await import('sentry/components/modals/widgetBuilder/overwriteWidgetModal');
-  const {default: Modal, modalCss} = mod;
-
-  openModal(deps => <Modal {...deps} {...options} />, {backdrop: 'static', modalCss});
-}
-
-export function WidgetLibrary({onWidgetSelect}: Props) {
+export function WidgetLibrary({bypassOverwriteModal, onWidgetSelect}: Props) {
   const theme = useTheme();
   const theme = useTheme();
 
 
-  function getLibrarySelectionHandler(widget, iconColor) {
+  function getLibrarySelectionHandler(
+    widget: OverwriteWidgetModalProps['widget'],
+    iconColor: OverwriteWidgetModalProps['iconColor']
+  ) {
     return function handleWidgetSelect() {
     return function handleWidgetSelect() {
+      if (bypassOverwriteModal) {
+        onWidgetSelect(widget);
+        return;
+      }
+
       openWidgetBuilderOverwriteModal({
       openWidgetBuilderOverwriteModal({
         onConfirm: () => onWidgetSelect(widget),
         onConfirm: () => onWidgetSelect(widget),
         widget,
         widget,

+ 24 - 0
tests/js/spec/views/dashboardsV2/widgetBuilder/widgetBuilder.spec.tsx

@@ -11,6 +11,7 @@ import {
 import {textWithMarkupMatcher} from 'sentry-test/utils';
 import {textWithMarkupMatcher} from 'sentry-test/utils';
 
 
 import * as indicators from 'sentry/actionCreators/indicator';
 import * as indicators from 'sentry/actionCreators/indicator';
+import {openWidgetBuilderOverwriteModal} from 'sentry/actionCreators/modal';
 import {
 import {
   DashboardDetails,
   DashboardDetails,
   DashboardWidgetSource,
   DashboardWidgetSource,
@@ -20,6 +21,8 @@ import {
 import * as dashboardsTypes from 'sentry/views/dashboardsV2/types';
 import * as dashboardsTypes from 'sentry/views/dashboardsV2/types';
 import WidgetBuilder, {WidgetBuilderProps} from 'sentry/views/dashboardsV2/widgetBuilder';
 import WidgetBuilder, {WidgetBuilderProps} from 'sentry/views/dashboardsV2/widgetBuilder';
 
 
+jest.mock('sentry/actionCreators/modal');
+
 function renderTestComponent({
 function renderTestComponent({
   widget,
   widget,
   dashboard,
   dashboard,
@@ -910,6 +913,27 @@ describe('WidgetBuilder', function () {
       renderTestComponent();
       renderTestComponent();
       expect(await screen.findByText('Widget Library')).toBeInTheDocument();
       expect(await screen.findByText('Widget Library')).toBeInTheDocument();
     });
     });
+
+    it('only opens the modal when the query data is changed', async function () {
+      renderTestComponent();
+      await screen.findByText('Widget Library');
+
+      userEvent.click(screen.getByText('Duration Distribution'));
+
+      // Widget Library, Builder title, and Chart title
+      expect(await screen.findAllByText('Duration Distribution')).toHaveLength(3);
+
+      // Confirm modal doesn't open because no changes were made
+      expect(openWidgetBuilderOverwriteModal).not.toHaveBeenCalled();
+
+      expect(screen.getAllByLabelText('Remove this Y-Axis')).toHaveLength(3);
+      userEvent.click(screen.getAllByLabelText('Remove this Y-Axis')[0]);
+      userEvent.click(screen.getByText('High Throughput Transactions'));
+
+      // Should not have overwritten widget data, and confirm modal should open
+      expect(await screen.findAllByText('Duration Distribution')).toHaveLength(3);
+      expect(openWidgetBuilderOverwriteModal).toHaveBeenCalled();
+    });
   });
   });
 
 
   it('disables dashboards with max widgets', async function () {
   it('disables dashboards with max widgets', async function () {