Browse Source

feat(ddm): Reduce add button size (#62306)

Reduce add button size
Disable delete if there is only one widget
Disable add button if there is an empty widget (no mri selected).

- closes https://github.com/getsentry/sentry/issues/62305
- closes https://github.com/getsentry/sentry/issues/62307
ArthurKnaus 1 year ago
parent
commit
39e59b2320
2 changed files with 48 additions and 23 deletions
  1. 12 2
      static/app/views/ddm/contextMenu.tsx
  2. 36 21
      static/app/views/ddm/scratchpad.tsx

+ 12 - 2
static/app/views/ddm/contextMenu.tsx

@@ -33,7 +33,7 @@ export function MetricWidgetContextMenu({
   widgetIndex,
 }: ContextMenuProps) {
   const organization = useOrganization();
-  const {removeWidget, duplicateWidget} = useDDMContext();
+  const {removeWidget, duplicateWidget, widgets} = useDDMContext();
   const createAlert = useCreateAlert(organization, metricsQuery);
   const createDashboardWidget = useCreateDashboardWidget(
     organization,
@@ -41,6 +41,8 @@ export function MetricWidgetContextMenu({
     displayType
   );
 
+  const canDelete = widgets.length > 1;
+
   const items = useMemo<MenuItemProps[]>(
     () => [
       {
@@ -67,10 +69,18 @@ export function MetricWidgetContextMenu({
         leadingItems: [<IconDelete key="icon" />],
         key: 'delete',
         label: t('Delete'),
+        disabled: !canDelete,
         onAction: () => removeWidget(widgetIndex),
       },
     ],
-    [createAlert, createDashboardWidget, duplicateWidget, removeWidget, widgetIndex]
+    [
+      createAlert,
+      createDashboardWidget,
+      duplicateWidget,
+      removeWidget,
+      widgetIndex,
+      canDelete,
+    ]
   );
 
   if (!hasDDMFeature(organization)) {

+ 36 - 21
static/app/views/ddm/scratchpad.tsx

@@ -1,10 +1,11 @@
-import {useCallback} from 'react';
+import {useCallback, useLayoutEffect} from 'react';
 import styled from '@emotion/styled';
 import * as echarts from 'echarts/core';
 
 import {Button} from 'sentry/components/button';
 import Panel from 'sentry/components/panels/panel';
 import {IconAdd} from 'sentry/icons';
+import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {trackAnalytics} from 'sentry/utils/analytics';
 import {MetricWidgetQueryParams} from 'sentry/utils/metrics';
@@ -21,6 +22,11 @@ export function MetricScratchpad() {
   const {selection} = usePageFilters();
   const organization = useOrganization();
 
+  // Make sure all charts are connected to the same group whenever the widgets definition changes
+  useLayoutEffect(() => {
+    echarts.connect(DDM_CHART_GROUP);
+  }, [widgets]);
+
   const handleChange = useCallback(
     (index: number, widget: Partial<MetricWidgetQueryParams>) => {
       updateWidget(index, widget);
@@ -28,11 +34,10 @@ export function MetricScratchpad() {
     [updateWidget]
   );
 
+  const hasEmptyWidget = widgets.length === 0 || widgets.some(widget => !widget.mri);
   const Wrapper =
     widgets.length === 1 ? StyledSingleWidgetWrapper : StyledMetricDashboard;
 
-  echarts.connect(DDM_CHART_GROUP);
-
   return (
     <Wrapper>
       {widgets.map((widget, index) => (
@@ -49,15 +54,22 @@ export function MetricScratchpad() {
         />
       ))}
       <AddWidgetPanel
-        onClick={() => {
-          trackAnalytics('ddm.widget.add', {
-            organization,
-          });
-
-          addWidget();
-        }}
+        disabled={hasEmptyWidget}
+        onClick={
+          !hasEmptyWidget
+            ? () => {
+                trackAnalytics('ddm.widget.add', {
+                  organization,
+                });
+
+                addWidget();
+              }
+            : undefined
+        }
       >
-        <Button icon={<IconAdd isCircled />}>Add widget</Button>
+        <Button disabled={hasEmptyWidget} icon={<IconAdd isCircled />}>
+          {t('Add widget')}
+        </Button>
       </AddWidgetPanel>
     </Wrapper>
   );
@@ -74,7 +86,7 @@ const StyledMetricDashboard = styled('div')`
   @media (max-width: ${props => props.theme.breakpoints.xlarge}) {
     grid-template-columns: repeat(1, minmax(${MIN_WIDGET_WIDTH}px, 1fr));
   }
-  grid-auto-rows: 1fr;
+  grid-auto-rows: auto;
 `;
 
 const StyledSingleWidgetWrapper = styled('div')`
@@ -87,11 +99,10 @@ const StyledSingleWidgetWrapper = styled('div')`
 
   gap: ${space(2)};
 
-  grid-auto-rows: 1fr;
+  grid-auto-rows: auto;
 `;
 
-const AddWidgetPanel = styled(Panel)`
-  width: 100%;
+const AddWidgetPanel = styled(Panel)<{disabled: boolean}>`
   height: 100%;
   margin-bottom: 0;
   padding: ${space(4)};
@@ -99,10 +110,14 @@ const AddWidgetPanel = styled(Panel)`
   display: flex;
   justify-content: center;
   align-items: center;
-  border: 1px dashed ${p => p.theme.border};
-
-  &:hover {
-    background-color: ${p => p.theme.backgroundSecondary};
-    cursor: pointer;
-  }
+  border: 1px dashed ${p => (p.disabled ? p.theme.disabledBorder : p.theme.border)};
+
+  ${p =>
+    !p.disabled &&
+    `
+    &:hover {
+      background-color: ${p.theme.backgroundSecondary};
+      cursor: pointer;
+    }
+  `}
 `;