From ce1f652c847a957228f865e04326a8851ab0a581 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 5 Aug 2019 14:11:09 +0200 Subject: [PATCH] [Lens] Allow only current visualization on field drop in workspace (#42344) --- .../editor_frame/editor_frame.test.tsx | 5 + .../editor_frame/workspace_panel.test.tsx | 171 +++++++++++++----- .../editor_frame/workspace_panel.tsx | 34 ++-- 3 files changed, 149 insertions(+), 61 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx index 24cb07e6d62f7..e63373f5614fb 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx @@ -1250,6 +1250,11 @@ describe('editor_frame', () => { ...mockDatasource, getDatasourceSuggestionsForField: () => [generateSuggestion()], getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()], + renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => { + if (dragging !== 'draggedField') { + setDragging('draggedField'); + } + }, }, }} initialDatasourceId="testDatasource" diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx index 5a7b50851e847..9bcfb82e4d19b 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { ExpressionRendererProps } from '../../../../../../../src/legacy/core_plugins/data/public'; -import { Visualization } from '../../types'; +import { Visualization, FramePublicAPI } from '../../types'; import { createMockVisualization, createMockDatasource, @@ -18,13 +18,14 @@ import { import { InnerWorkspacePanel, WorkspacePanelProps } from './workspace_panel'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { ReactWrapper } from 'enzyme'; -import { DragDrop } from '../../drag_drop'; +import { DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { Ast } from '@kbn/interpreter/common'; const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); describe('workspace_panel', () => { let mockVisualization: jest.Mocked; + let mockVisualization2: jest.Mocked; let mockDatasource: DatasourceMock; let expressionRendererMock: jest.Mock; @@ -33,6 +34,7 @@ describe('workspace_panel', () => { beforeEach(() => { mockVisualization = createMockVisualization(); + mockVisualization2 = createMockVisualization(); mockDatasource = createMockDatasource(); @@ -452,32 +454,42 @@ describe('workspace_panel', () => { describe('suggestions from dropping in workspace panel', () => { let mockDispatch: jest.Mock; + let frame: jest.Mocked; + + const draggedField: unknown = {}; beforeEach(() => { + frame = createMockFramePublicAPI(); mockDispatch = jest.fn(); + }); + + function initComponent(draggingContext: unknown = draggedField) { instance = mount( - + {}}> + + ); - }); + } it('should immediately transition if exactly one suggestion is returned', () => { const expectedTable = { @@ -501,13 +513,9 @@ describe('workspace_panel', () => { previewIcon: 'empty', }, ]); + initComponent(); - instance.find(DragDrop).prop('onDrop')!({ - name: '@timestamp', - type: 'date', - searchable: false, - aggregatable: false, - }); + instance.find(DragDrop).prop('onDrop')!(draggedField); expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1); expect(mockVisualization.getSuggestions).toHaveBeenCalledWith( @@ -523,6 +531,90 @@ describe('workspace_panel', () => { }); }); + it('should allow to drop if there are suggestions', () => { + mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([ + { + state: {}, + table: { + datasourceSuggestionId: 0, + isMultiRow: true, + layerId: '1', + columns: [], + }, + }, + ]); + mockVisualization.getSuggestions.mockReturnValueOnce([ + { + score: 0.5, + title: 'my title', + state: {}, + datasourceSuggestionId: 0, + previewIcon: 'empty', + }, + ]); + initComponent(); + expect(instance.find(DragDrop).prop('droppable')).toBeTruthy(); + }); + + it('should refuse to drop if there only suggestions from other visualizations if there are data tables', () => { + frame.datasourceLayers.a = mockDatasource.publicAPIMock; + mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'a' }]); + mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([ + { + state: {}, + table: { + datasourceSuggestionId: 0, + isMultiRow: true, + layerId: '1', + columns: [], + }, + }, + ]); + mockVisualization2.getSuggestions.mockReturnValueOnce([ + { + score: 0.5, + title: 'my title', + state: {}, + datasourceSuggestionId: 0, + previewIcon: 'empty', + }, + ]); + initComponent(); + expect(instance.find(DragDrop).prop('droppable')).toBeFalsy(); + }); + + it('should allow to drop if there are suggestions from active visualization even if there are data tables', () => { + frame.datasourceLayers.a = mockDatasource.publicAPIMock; + mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'a' }]); + mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([ + { + state: {}, + table: { + datasourceSuggestionId: 0, + isMultiRow: true, + layerId: '1', + columns: [], + }, + }, + ]); + mockVisualization.getSuggestions.mockReturnValueOnce([ + { + score: 0.5, + title: 'my title', + state: {}, + datasourceSuggestionId: 0, + previewIcon: 'empty', + }, + ]); + initComponent(); + expect(instance.find(DragDrop).prop('droppable')).toBeTruthy(); + }); + + it('should refuse to drop if there are no suggestions', () => { + initComponent(); + expect(instance.find(DragDrop).prop('droppable')).toBeFalsy(); + }); + it('should immediately transition to the first suggestion if there are multiple', () => { mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([ { @@ -563,12 +655,8 @@ describe('workspace_panel', () => { }, ]); - instance.find(DragDrop).prop('onDrop')!({ - name: '@timestamp', - type: 'date', - searchable: false, - aggregatable: false, - }); + initComponent(); + instance.find(DragDrop).prop('onDrop')!(draggedField); expect(mockDispatch).toHaveBeenCalledWith({ type: 'SWITCH_VISUALIZATION', @@ -579,18 +667,5 @@ describe('workspace_panel', () => { datasourceState: {}, }); }); - - it("should do nothing when the visualization can't use the suggestions", () => { - instance.find(DragDrop).prop('onDrop')!({ - name: '@timestamp', - type: 'date', - searchable: false, - aggregatable: false, - }); - - expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1); - expect(mockVisualization.getSuggestions).toHaveBeenCalledTimes(1); - expect(mockDispatch).not.toHaveBeenCalled(); - }); }); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx index eb0496605cc1c..bb3e8fc3b64cc 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx @@ -49,30 +49,38 @@ export function InnerWorkspacePanel({ ExpressionRenderer: ExpressionRendererComponent, }: WorkspacePanelProps) { const dragDropContext = useContext(DragContext); - function onDrop(item: unknown) { - if (!activeDatasourceId) { + + const suggestionForDraggedField = useMemo(() => { + if (!dragDropContext.dragging || !activeDatasourceId) { return; } const datasourceSuggestions = datasourceMap[ activeDatasourceId - ].getDatasourceSuggestionsForField(datasourceStates[activeDatasourceId].state, item); + ].getDatasourceSuggestionsForField( + datasourceStates[activeDatasourceId].state, + dragDropContext.dragging + ); + + const hasData = Object.values(framePublicAPI.datasourceLayers).some( + datasource => datasource.getTableSpec().length > 0 + ); const suggestions = getSuggestions( datasourceSuggestions, - visualizationMap, + hasData && activeVisualizationId + ? { [activeVisualizationId]: visualizationMap[activeVisualizationId] } + : visualizationMap, activeVisualizationId, visualizationState ); - if (suggestions.length === 0) { - // TODO specify and implement behavior in case of no valid suggestions - return; - } + return suggestions[0]; + }, [dragDropContext.dragging]); - const suggestion = suggestions[0]; - - // TODO heuristically present the suggestions in a modal instead of just picking the first one - dispatch(toSwitchAction(suggestion)); + function onDrop() { + if (suggestionForDraggedField) { + dispatch(toSwitchAction(suggestionForDraggedField)); + } } function renderEmptyWorkspace() { @@ -151,7 +159,7 @@ export function InnerWorkspacePanel({ } return ( - + {renderVisualization()} );