|
@@ -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;
|
|
|
+ }
|
|
|
+ `}
|
|
|
`;
|