Skip to content

Commit

Permalink
feat(explore): Convert buttons into context menu
Browse files Browse the repository at this point in the history
  • Loading branch information
narsaynorath committed Nov 22, 2024
1 parent 4e8a6ba commit 0abd945
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 200 deletions.
68 changes: 8 additions & 60 deletions static/app/views/explore/charts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import type {Dispatch, SetStateAction} from 'react';
import {Fragment, useCallback, useEffect, useMemo} from 'react';
import styled from '@emotion/styled';

import Feature from 'sentry/components/acl/feature';
import {getInterval} from 'sentry/components/charts/utils';
import {CompactSelect} from 'sentry/components/compactSelect';
import {DropdownMenu} from 'sentry/components/dropdownMenu';
import {Tooltip} from 'sentry/components/tooltip';
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
import {IconClock, IconGraph, IconSubscribed} from 'sentry/icons';
import {IconClock, IconGraph} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {dedupeArray} from 'sentry/utils/dedupeArray';
Expand All @@ -18,12 +16,9 @@ import {
prettifyParsedFunction,
} from 'sentry/utils/discover/fields';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import useOrganization from 'sentry/utils/useOrganization';
import usePageFilters from 'sentry/utils/usePageFilters';
import useProjects from 'sentry/utils/useProjects';
import {formatVersion} from 'sentry/utils/versions/formatVersion';
import {Dataset} from 'sentry/views/alerts/rules/metric/types';
import {AddToDashboardButton} from 'sentry/views/explore/components/addToDashboardButton';
import ChartContextMenu from 'sentry/views/explore/components/chartContextMenu';
import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
import {useDataset} from 'sentry/views/explore/hooks/useDataset';
import {useVisualizes} from 'sentry/views/explore/hooks/useVisualizes';
Expand All @@ -33,7 +28,6 @@ import Chart, {
} from 'sentry/views/insights/common/components/chart';
import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
import {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries';
import {getAlertsUrl} from 'sentry/views/insights/common/utils/getAlertsUrl';
import {CHART_HEIGHT} from 'sentry/views/insights/database/settings';

import {useGroupBys} from '../hooks/useGroupBys';
Expand Down Expand Up @@ -67,9 +61,6 @@ export const EXPLORE_CHART_GROUP = 'explore-charts_group';
// TODO: Update to support aggregate mode and multiple queries / visualizations
export function ExploreCharts({query, setError}: ExploreChartsProps) {
const pageFilters = usePageFilters();
const organization = useOrganization();
const {projects} = useProjects();

const [dataset] = useDataset();
const [visualizes, setVisualizes] = useVisualizes();
const [interval, setInterval, intervalOptions] = useChartInterval();
Expand Down Expand Up @@ -184,29 +175,6 @@ export function ExploreCharts({query, setError}: ExploreChartsProps) {
? 'area'
: 'bar';

const project =
projects.length === 1
? projects[0]
: projects.find(p => p.id === `${pageFilters.selection.projects[0]}`);
const singleProject =
(pageFilters.selection.projects.length === 1 || projects.length === 1) &&
project;
const alertsUrls = singleProject
? visualizeYAxes.map(yAxis => ({
key: yAxis,
label: yAxis,
to: getAlertsUrl({
project,
query,
pageFilters: pageFilters.selection,
aggregate: yAxis,
orgSlug: organization.slug,
dataset: Dataset.EVENTS_ANALYTICS_PLATFORM,
interval,
}),
}))
: undefined;

const data = getSeries(dedupedYAxes, formattedYAxes);

const outputTypes = new Set(
Expand Down Expand Up @@ -251,32 +219,12 @@ export function ExploreCharts({query, setError}: ExploreChartsProps) {
options={intervalOptions}
/>
</Tooltip>
<Feature features="organizations:alerts-eap">
<Tooltip
title={
singleProject
? t('Create an alert for this chart')
: t('Cannot create an alert when multiple projects are selected')
}
>
<DropdownMenu
triggerProps={{
'aria-label': t('Create Alert'),
size: 'sm',
borderless: true,
showChevron: false,
icon: <IconSubscribed />,
}}
position="bottom-end"
items={alertsUrls ?? []}
menuTitle={t('Create an alert for')}
isDisabled={!alertsUrls || alertsUrls.length === 0}
/>
</Tooltip>
</Feature>
<Feature features="organizations:dashboards-eap">
<AddToDashboardButton visualizeIndex={index} />
</Feature>
<ChartContextMenu
visualizeYAxes={visualizeYAxes}
query={query}
interval={interval}
visualizeIndex={index}
/>
</ChartHeader>
<Chart
height={CHART_HEIGHT}
Expand Down
97 changes: 0 additions & 97 deletions static/app/views/explore/components/addToDashboardButton.tsx

This file was deleted.

96 changes: 96 additions & 0 deletions static/app/views/explore/components/chartContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {DropdownMenu} from 'sentry/components/dropdownMenu';
import {IconEllipsis} from 'sentry/icons';
import {t} from 'sentry/locale';
import useOrganization from 'sentry/utils/useOrganization';
import usePageFilters from 'sentry/utils/usePageFilters';
import useProjects from 'sentry/utils/useProjects';
import {Dataset} from 'sentry/views/alerts/rules/metric/types';
import {useAddToDashboard} from 'sentry/views/explore/hooks/useAddToDashboard';
import {getAlertsUrl} from 'sentry/views/insights/common/utils/getAlertsUrl';

function ChartContextMenu({
visualizeIndex,
visualizeYAxes,
query,
interval,
}: {
interval: string;
query: string;
visualizeIndex: number;
visualizeYAxes: string[];
}) {
const {addToDashboard} = useAddToDashboard();
const organization = useOrganization();

const {projects} = useProjects();
const pageFilters = usePageFilters();

const project =
projects.length === 1
? projects[0]
: projects.find(p => p.id === `${pageFilters.selection.projects[0]}`);
const singleProject =
(pageFilters.selection.projects.length === 1 || projects.length === 1) && project;

const alertsUrls = singleProject
? visualizeYAxes.map(yAxis => ({
key: yAxis,
label: yAxis,
to: getAlertsUrl({
project,
query,
pageFilters: pageFilters.selection,
aggregate: yAxis,
orgSlug: organization.slug,
dataset: Dataset.EVENTS_ANALYTICS_PLATFORM,
interval,
}),
}))
: undefined;

if (
!organization.features.includes('alerts-eap') &&
!organization.features.includes('dashboards-eap')
) {
return null;
}

return (
<DropdownMenu
triggerProps={{
size: 'sm',
borderless: true,
showChevron: false,
icon: <IconEllipsis />,
}}
position="bottom-end"
items={[
...(organization.features.includes('alerts-eap')
? [
{
key: 'create-alert',
label: t('Create an alert for'),
children: alertsUrls ?? [],
tooltip: !singleProject
? t('Cannot create an alert when multiple projects are selected')
: undefined,
disabled: !alertsUrls || alertsUrls.length === 0 || !singleProject,
isSubmenu: true,
},
]
: []),
...(organization.features.includes('dashboards-eap')
? [
{
key: 'add-to-dashboard',
label: t('Add to Dashboard'),
onAction: () => addToDashboard(visualizeIndex),
},
]
: []),
]}
/>
);
}

export default ChartContextMenu;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';

import {openAddToDashboardModal} from 'sentry/actionCreators/modal';
import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
import {AddToDashboardButton} from 'sentry/views/explore/components/addToDashboardButton';
import {useAddToDashboard} from 'sentry/views/explore/hooks/useAddToDashboard';
import {useResultMode} from 'sentry/views/explore/hooks/useResultsMode';
import {useVisualizes} from 'sentry/views/explore/hooks/useVisualizes';
import {ChartType} from 'sentry/views/insights/common/components/chart';
Expand All @@ -11,6 +11,11 @@ jest.mock('sentry/actionCreators/modal');
jest.mock('sentry/views/explore/hooks/useVisualizes');
jest.mock('sentry/views/explore/hooks/useResultsMode');

function TestPage({visualizeIndex}: {visualizeIndex: number}) {
const {addToDashboard} = useAddToDashboard();
return <button onClick={() => addToDashboard(visualizeIndex)}>Add to Dashboard</button>;
}

describe('AddToDashboardButton', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -29,15 +34,10 @@ describe('AddToDashboardButton', () => {
jest.mocked(useResultMode).mockReturnValue(['samples', jest.fn()]);
});

it('renders', async () => {
render(<AddToDashboardButton visualizeIndex={0} />);
await userEvent.hover(screen.getByLabelText('Add to Dashboard'));
expect(await screen.findByText('Add to Dashboard')).toBeInTheDocument();
});

it('opens the dashboard modal with the correct query for samples mode', async () => {
render(<AddToDashboardButton visualizeIndex={0} />);
await userEvent.click(screen.getByLabelText('Add to Dashboard'));
render(<TestPage visualizeIndex={0} />);

await userEvent.click(screen.getByText('Add to Dashboard'));

// The table columns are encoded as the fields for the defaultWidgetQuery
expect(openAddToDashboardModal).toHaveBeenCalledWith(
Expand Down Expand Up @@ -107,8 +107,8 @@ describe('AddToDashboardButton', () => {
jest.fn(),
]);

render(<AddToDashboardButton visualizeIndex={1} />);
await userEvent.click(screen.getByLabelText('Add to Dashboard'));
render(<TestPage visualizeIndex={1} />);
await userEvent.click(screen.getByText('Add to Dashboard'));

// The group by and the yAxes are encoded as the fields for the defaultTableQuery
expect(openAddToDashboardModal).toHaveBeenCalledWith(
Expand Down Expand Up @@ -163,8 +163,8 @@ describe('AddToDashboardButton', () => {
it('uses the yAxes for the aggregate mode', async () => {
jest.mocked(useResultMode).mockReturnValue(['aggregate', jest.fn()]);

render(<AddToDashboardButton visualizeIndex={0} />);
await userEvent.click(screen.getByLabelText('Add to Dashboard'));
render(<TestPage visualizeIndex={0} />);
await userEvent.click(screen.getByText('Add to Dashboard'));

expect(openAddToDashboardModal).toHaveBeenCalledWith(
expect.objectContaining({
Expand Down Expand Up @@ -219,8 +219,8 @@ describe('AddToDashboardButton', () => {
jest.fn(),
]);

render(<AddToDashboardButton visualizeIndex={0} />);
await userEvent.click(screen.getByLabelText('Add to Dashboard'));
render(<TestPage visualizeIndex={0} />);
await userEvent.click(screen.getByText('Add to Dashboard'));

expect(openAddToDashboardModal).toHaveBeenCalledWith(
expect.objectContaining({
Expand Down
Loading

0 comments on commit 0abd945

Please sign in to comment.