diff --git a/extension/src/plots/webview/messages.ts b/extension/src/plots/webview/messages.ts index 1a4373ce68..36c6c0b1e1 100644 --- a/extension/src/plots/webview/messages.ts +++ b/extension/src/plots/webview/messages.ts @@ -110,6 +110,12 @@ export class WebviewMessages { return this.attemptToRefreshSelectedData(message.payload) case MessageFromWebviewType.TOGGLE_EXPERIMENT: return this.setExperimentStatus(message.payload) + case MessageFromWebviewType.ZOOM_PLOT: + return sendTelemetryEvent( + EventName.VIEWS_PLOTS_ZOOM_PLOT, + undefined, + undefined + ) default: Logger.error(`Unexpected message: ${JSON.stringify(message)}`) } diff --git a/extension/src/telemetry/constants.ts b/extension/src/telemetry/constants.ts index c6a0a3cb4e..45b2db2381 100644 --- a/extension/src/telemetry/constants.ts +++ b/extension/src/telemetry/constants.ts @@ -74,6 +74,7 @@ export const EventName = Object.assign( VIEWS_PLOTS_SECTION_TOGGLE: 'views.plots.toggleSection', VIEWS_PLOTS_SELECT_EXPERIMENTS: 'view.plots.selectExperiments', VIEWS_PLOTS_SELECT_PLOTS: 'view.plots.selectPlots', + VIEWS_PLOTS_ZOOM_PLOT: 'views.plots.zoomPlot', VIEWS_REORDER_PLOTS_CUSTOM: 'views.plots.customReordered', VIEWS_REORDER_PLOTS_METRICS: 'views.plots.metricsReordered', VIEWS_REORDER_PLOTS_TEMPLATES: 'views.plots.templatesReordered', @@ -261,6 +262,7 @@ export interface IEventNamePropertyMapping { [EventName.VIEWS_PLOTS_SELECT_EXPERIMENTS]: undefined [EventName.VIEWS_PLOTS_SELECT_PLOTS]: undefined [EventName.VIEWS_PLOTS_EXPERIMENT_TOGGLE]: undefined + [EventName.VIEWS_PLOTS_ZOOM_PLOT]: undefined [EventName.VIEWS_REORDER_PLOTS_METRICS]: undefined [EventName.VIEWS_REORDER_PLOTS_CUSTOM]: undefined [EventName.VIEWS_REORDER_PLOTS_TEMPLATES]: undefined diff --git a/extension/src/test/suite/plots/index.test.ts b/extension/src/test/suite/plots/index.test.ts index fd38a1db90..34b4531410 100644 --- a/extension/src/test/suite/plots/index.test.ts +++ b/extension/src/test/suite/plots/index.test.ts @@ -510,6 +510,26 @@ suite('Plots Test Suite', () => { ) }).timeout(WEBVIEW_TEST_TIMEOUT) + it('should handle a plot zoomed message from the webview', async () => { + const { plots } = await buildPlots(disposable, plotsDiffFixture) + + const webview = await plots.showWebview() + + const mockSendTelemetryEvent = stub(Telemetry, 'sendTelemetryEvent') + const mockMessageReceived = getMessageReceivedEmitter(webview) + + mockMessageReceived.fire({ + type: MessageFromWebviewType.ZOOM_PLOT + }) + + expect(mockSendTelemetryEvent).to.be.calledOnce + expect(mockSendTelemetryEvent).to.be.calledWithExactly( + EventName.VIEWS_PLOTS_ZOOM_PLOT, + undefined, + undefined + ) + }).timeout(WEBVIEW_TEST_TIMEOUT) + it('should handle a custom plots reordered message from the webview', async () => { const { plots, plotsModel, messageSpy } = await buildPlots( disposable, diff --git a/extension/src/webview/contract.ts b/extension/src/webview/contract.ts index 10866d5bc0..ec638fa710 100644 --- a/extension/src/webview/contract.ts +++ b/extension/src/webview/contract.ts @@ -66,7 +66,8 @@ export enum MessageFromWebviewType { INITIALIZE_GIT = 'initialize-git', SHOW_SCM_PANEL = 'show-scm-panel', INSTALL_DVC = 'install-dvc', - SETUP_WORKSPACE = 'setup-workspace' + SETUP_WORKSPACE = 'setup-workspace', + ZOOM_PLOT = 'zoom-plot' } export type ColumnResizePayload = { @@ -236,6 +237,7 @@ export type MessageFromWebview = | { type: MessageFromWebviewType.OPEN_STUDIO_PROFILE } | { type: MessageFromWebviewType.SAVE_STUDIO_TOKEN } | { type: MessageFromWebviewType.ADD_CONFIGURATION } + | { type: MessageFromWebviewType.ZOOM_PLOT } export type MessageToWebview = { type: MessageToWebviewType.SET_DATA diff --git a/webview/src/plots/components/App.test.tsx b/webview/src/plots/components/App.test.tsx index d63b75db77..2438563982 100644 --- a/webview/src/plots/components/App.test.tsx +++ b/webview/src/plots/components/App.test.tsx @@ -1404,6 +1404,22 @@ describe('App', () => { expect(screen.getByTestId('modal')).toBeInTheDocument() }) + it('should send a message to the extension when a plot is opened in a modal', () => { + renderAppWithOptionalData({ + template: complexTemplatePlotsFixture + }) + + expect(screen.queryByTestId('modal')).not.toBeInTheDocument() + + const plot = within(screen.getAllByTestId(/^plot_/)[0]).getByRole('button') + + fireEvent.click(plot) + + expect(mockPostMessage).toHaveBeenCalledWith({ + type: MessageFromWebviewType.ZOOM_PLOT + }) + }) + it('should open a modal with the plot zoomed in when clicking a checkpoint plot', () => { renderAppWithOptionalData({ checkpoint: checkpointPlotsFixture diff --git a/webview/src/plots/components/ZoomablePlot.tsx b/webview/src/plots/components/ZoomablePlot.tsx index a1d79ad182..043741c0c6 100644 --- a/webview/src/plots/components/ZoomablePlot.tsx +++ b/webview/src/plots/components/ZoomablePlot.tsx @@ -7,6 +7,7 @@ import VegaLite, { VegaLiteProps } from 'react-vega/lib/VegaLite' import { setZoomedInPlot } from './webviewSlice' import styles from './styles.module.scss' import { config } from './constants' +import { zoomPlot } from './messages' import { useGetPlot } from '../hooks/useGetPlot' import { GripIcon } from '../../shared/components/dragDrop/GripIcon' @@ -47,7 +48,10 @@ export const ZoomablePlot: React.FC = ({ ) }, [data, spec, dispatch, id]) - const handleOnClick = () => dispatch(setZoomedInPlot({ id, plot: plotProps })) + const handleOnClick = () => { + zoomPlot() + return dispatch(setZoomedInPlot({ id, plot: plotProps })) + } if (!data && !spec) { return null diff --git a/webview/src/plots/components/messages.ts b/webview/src/plots/components/messages.ts new file mode 100644 index 0000000000..aa37198cd0 --- /dev/null +++ b/webview/src/plots/components/messages.ts @@ -0,0 +1,5 @@ +import { MessageFromWebviewType } from 'dvc/src/webview/contract' +import { sendMessage } from '../../shared/vscode' + +export const zoomPlot = () => + sendMessage({ type: MessageFromWebviewType.ZOOM_PLOT })