This commit is contained in:
parent
ee5b6d22a3
commit
d476b42857
16 changed files with 447 additions and 231 deletions
|
@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiPopover, EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui';
|
||||
import { NativeRenderer } from '../../native_renderer';
|
||||
import { Action } from './state_management';
|
||||
import { DragContext } from '../../drag_drop';
|
||||
import { DragContext, Dragging } from '../../drag_drop';
|
||||
import { StateSetter, FramePublicAPI, DatasourceDataPanelProps, Datasource } from '../../types';
|
||||
import { Query, Filter } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
|
@ -26,6 +26,8 @@ interface DataPanelWrapperProps {
|
|||
query: Query;
|
||||
dateRange: FramePublicAPI['dateRange'];
|
||||
filters: Filter[];
|
||||
dropOntoWorkspace: (field: Dragging) => void;
|
||||
hasSuggestionForField: (field: Dragging) => boolean;
|
||||
}
|
||||
|
||||
export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => {
|
||||
|
@ -51,6 +53,8 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => {
|
|||
dateRange: props.dateRange,
|
||||
filters: props.filters,
|
||||
showNoDataPopover: props.showNoDataPopover,
|
||||
dropOntoWorkspace: props.dropOntoWorkspace,
|
||||
hasSuggestionForField: props.hasSuggestionForField,
|
||||
};
|
||||
|
||||
const [showDatasourceSwitcher, setDatasourceSwitcher] = useState(false);
|
||||
|
|
|
@ -632,16 +632,19 @@ describe('editor_frame', () => {
|
|||
);
|
||||
});
|
||||
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
.setState;
|
||||
|
||||
mockDatasource.renderDataPanel.mockClear();
|
||||
|
||||
const updatedState = {
|
||||
title: 'shazm',
|
||||
};
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
.setState;
|
||||
act(() => {
|
||||
setDatasourceState(updatedState);
|
||||
});
|
||||
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenCalledTimes(2);
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.renderDataPanel).toHaveBeenLastCalledWith(
|
||||
expect.any(Element),
|
||||
expect.objectContaining({
|
||||
|
|
|
@ -16,14 +16,19 @@ import { FrameLayout } from './frame_layout';
|
|||
import { SuggestionPanel } from './suggestion_panel';
|
||||
import { WorkspacePanel } from './workspace_panel';
|
||||
import { Document } from '../../persistence/saved_object_store';
|
||||
import { RootDragDropProvider } from '../../drag_drop';
|
||||
import { Dragging, RootDragDropProvider } from '../../drag_drop';
|
||||
import { getSavedObjectFormat } from './save';
|
||||
import { generateId } from '../../id_generator';
|
||||
import { Filter, Query, SavedQuery } from '../../../../../../src/plugins/data/public';
|
||||
import { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public';
|
||||
import { EditorFrameStartPlugins } from '../service';
|
||||
import { initializeDatasources, createDatasourceLayers } from './state_helpers';
|
||||
import { applyVisualizeFieldSuggestions } from './suggestion_helpers';
|
||||
import {
|
||||
applyVisualizeFieldSuggestions,
|
||||
getTopSuggestionForField,
|
||||
switchToSuggestion,
|
||||
} from './suggestion_helpers';
|
||||
import { trackUiEvent } from '../../lens_ui_telemetry';
|
||||
|
||||
export interface EditorFrameProps {
|
||||
doc?: Document;
|
||||
|
@ -254,6 +259,53 @@ export function EditorFrame(props: EditorFrameProps) {
|
|||
]
|
||||
);
|
||||
|
||||
const getSuggestionForField = React.useCallback(
|
||||
(field: Dragging) => {
|
||||
const { activeDatasourceId, datasourceStates } = state;
|
||||
const activeVisualizationId = state.visualization.activeId;
|
||||
const visualizationState = state.visualization.state;
|
||||
const { visualizationMap, datasourceMap } = props;
|
||||
|
||||
if (!field || !activeDatasourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return getTopSuggestionForField(
|
||||
datasourceLayers,
|
||||
activeVisualizationId,
|
||||
visualizationMap,
|
||||
visualizationState,
|
||||
datasourceMap[activeDatasourceId],
|
||||
datasourceStates,
|
||||
field
|
||||
);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
state.visualization.state,
|
||||
props.datasourceMap,
|
||||
props.visualizationMap,
|
||||
state.activeDatasourceId,
|
||||
state.datasourceStates,
|
||||
]
|
||||
);
|
||||
|
||||
const hasSuggestionForField = React.useCallback(
|
||||
(field: Dragging) => getSuggestionForField(field) !== undefined,
|
||||
[getSuggestionForField]
|
||||
);
|
||||
|
||||
const dropOntoWorkspace = React.useCallback(
|
||||
(field) => {
|
||||
const suggestion = getSuggestionForField(field);
|
||||
if (suggestion) {
|
||||
trackUiEvent('drop_onto_workspace');
|
||||
switchToSuggestion(dispatch, suggestion, 'SWITCH_VISUALIZATION');
|
||||
}
|
||||
},
|
||||
[getSuggestionForField]
|
||||
);
|
||||
|
||||
return (
|
||||
<RootDragDropProvider>
|
||||
<FrameLayout
|
||||
|
@ -277,6 +329,8 @@ export function EditorFrame(props: EditorFrameProps) {
|
|||
dateRange={props.dateRange}
|
||||
filters={props.filters}
|
||||
showNoDataPopover={props.showNoDataPopover}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
}
|
||||
configPanel={
|
||||
|
@ -310,6 +364,7 @@ export function EditorFrame(props: EditorFrameProps) {
|
|||
core={props.core}
|
||||
plugins={props.plugins}
|
||||
visualizeTriggerFieldContext={visualizeTriggerFieldContext}
|
||||
getSuggestionForField={getSuggestionForField}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getSuggestions } from './suggestion_helpers';
|
||||
import { getSuggestions, getTopSuggestionForField } from './suggestion_helpers';
|
||||
import { createMockVisualization, createMockDatasource, DatasourceMock } from '../mocks';
|
||||
import { TableSuggestion, DatasourceSuggestion } from '../../types';
|
||||
import { TableSuggestion, DatasourceSuggestion, Visualization } from '../../types';
|
||||
import { PaletteOutput } from 'src/plugins/charts/public';
|
||||
|
||||
const generateSuggestion = (state = {}, layerId: string = 'first'): DatasourceSuggestion => ({
|
||||
|
@ -472,4 +472,133 @@ describe('suggestion helpers', () => {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('getTopSuggestionForField', () => {
|
||||
let mockVisualization1: jest.Mocked<Visualization>;
|
||||
let mockVisualization2: jest.Mocked<Visualization>;
|
||||
let mockDatasourceState: unknown;
|
||||
let defaultParams: Parameters<typeof getTopSuggestionForField>;
|
||||
beforeEach(() => {
|
||||
datasourceMap.mock.getDatasourceSuggestionsForField.mockReturnValue([
|
||||
{
|
||||
state: {},
|
||||
table: {
|
||||
isMultiRow: true,
|
||||
layerId: '1',
|
||||
columns: [],
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization1 = createMockVisualization();
|
||||
mockVisualization1.getSuggestions.mockReturnValue([
|
||||
{
|
||||
score: 0.3,
|
||||
title: 'second suggestion',
|
||||
state: { second: true },
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'top suggestion',
|
||||
state: { first: true },
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
mockVisualization2 = createMockVisualization();
|
||||
mockVisualization2.getSuggestions.mockReturnValue([
|
||||
{
|
||||
score: 0.8,
|
||||
title: 'other vis suggestion',
|
||||
state: {},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
mockDatasourceState = { myDatasourceState: true };
|
||||
defaultParams = [
|
||||
{
|
||||
'1': {
|
||||
getTableSpec: () => [{ columnId: 'col1' }],
|
||||
datasourceId: '',
|
||||
getOperationForColumnId: jest.fn(),
|
||||
},
|
||||
},
|
||||
'vis1',
|
||||
{ vis1: mockVisualization1 },
|
||||
{},
|
||||
datasourceMap.mock,
|
||||
{
|
||||
mockindexpattern: { state: mockDatasourceState, isLoading: false },
|
||||
},
|
||||
{ id: 'myfield' },
|
||||
];
|
||||
});
|
||||
|
||||
it('should return top suggestion for field', () => {
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
expect(result!.title).toEqual('top suggestion');
|
||||
expect(datasourceMap.mock.getDatasourceSuggestionsForField).toHaveBeenCalledWith(
|
||||
mockDatasourceState,
|
||||
{
|
||||
id: 'myfield',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return nothing if visualization does not produce suggestions', () => {
|
||||
mockVisualization1.getSuggestions.mockReturnValue([]);
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return nothing if datasource does not produce suggestions', () => {
|
||||
datasourceMap.mock.getDatasourceSuggestionsForField.mockReturnValue([]);
|
||||
defaultParams[2] = {
|
||||
vis1: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should not consider suggestion from other visualization if there is data', () => {
|
||||
defaultParams[2] = {
|
||||
vis1: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should consider top suggestion from other visualization if there is no data', () => {
|
||||
const mockVisualization3 = createMockVisualization();
|
||||
defaultParams[0] = {
|
||||
'1': {
|
||||
getTableSpec: () => [],
|
||||
datasourceId: '',
|
||||
getOperationForColumnId: jest.fn(),
|
||||
},
|
||||
};
|
||||
mockVisualization1.getSuggestions.mockReturnValue([]);
|
||||
mockVisualization3.getSuggestions.mockReturnValue([
|
||||
{
|
||||
score: 0.1,
|
||||
title: 'low ranking suggestion',
|
||||
state: {},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
defaultParams[2] = {
|
||||
vis1: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
vis3: mockVisualization3,
|
||||
};
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
expect(result!.title).toEqual('other vis suggestion');
|
||||
expect(mockVisualization1.getSuggestions).toHaveBeenCalled();
|
||||
expect(mockVisualization2.getSuggestions).toHaveBeenCalled();
|
||||
expect(mockVisualization3.getSuggestions).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,8 +16,10 @@ import {
|
|||
TableChangeType,
|
||||
TableSuggestion,
|
||||
DatasourceSuggestion,
|
||||
DatasourcePublicAPI,
|
||||
} from '../../types';
|
||||
import { Action } from './state_management';
|
||||
import { Dragging } from '../../drag_drop';
|
||||
|
||||
export interface Suggestion {
|
||||
visualizationId: string;
|
||||
|
@ -221,3 +223,35 @@ export function switchToSuggestion(
|
|||
|
||||
dispatch(action);
|
||||
}
|
||||
|
||||
export function getTopSuggestionForField(
|
||||
datasourceLayers: Record<string, DatasourcePublicAPI>,
|
||||
activeVisualizationId: string | null,
|
||||
visualizationMap: Record<string, Visualization<unknown>>,
|
||||
visualizationState: unknown,
|
||||
datasource: Datasource,
|
||||
datasourceStates: Record<string, { state: unknown; isLoading: boolean }>,
|
||||
field: Dragging
|
||||
) {
|
||||
const hasData = Object.values(datasourceLayers).some(
|
||||
(datasourceLayer) => datasourceLayer.getTableSpec().length > 0
|
||||
);
|
||||
|
||||
const mainPalette =
|
||||
activeVisualizationId && visualizationMap[activeVisualizationId]?.getMainPalette
|
||||
? visualizationMap[activeVisualizationId].getMainPalette?.(visualizationState)
|
||||
: undefined;
|
||||
const suggestions = getSuggestions({
|
||||
datasourceMap: { [datasource.id]: datasource },
|
||||
datasourceStates,
|
||||
visualizationMap:
|
||||
hasData && activeVisualizationId
|
||||
? { [activeVisualizationId]: visualizationMap[activeVisualizationId] }
|
||||
: visualizationMap,
|
||||
activeVisualizationId,
|
||||
visualizationState,
|
||||
field,
|
||||
mainPalette,
|
||||
});
|
||||
return suggestions.find((s) => s.visualizationId === activeVisualizationId) || suggestions[0];
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { ReactExpressionRendererProps } from '../../../../../../../src/plugins/expressions/public';
|
||||
import { FramePublicAPI, TableSuggestion, Visualization } from '../../../types';
|
||||
import { FramePublicAPI, Visualization } from '../../../types';
|
||||
import {
|
||||
createMockVisualization,
|
||||
createMockDatasource,
|
||||
|
@ -85,6 +85,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -108,6 +109,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -131,6 +133,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -168,6 +171,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -241,6 +245,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -284,6 +289,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -335,6 +341,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -415,6 +422,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -471,6 +479,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -528,6 +537,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -569,6 +579,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -612,6 +623,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -652,6 +664,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -690,6 +703,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -734,6 +748,7 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={() => undefined}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -756,6 +771,7 @@ describe('workspace_panel', () => {
|
|||
|
||||
describe('suggestions from dropping in workspace panel', () => {
|
||||
let mockDispatch: jest.Mock;
|
||||
let mockGetSuggestionForField: jest.Mock;
|
||||
let frame: jest.Mocked<FramePublicAPI>;
|
||||
|
||||
const draggedField = { id: 'field' };
|
||||
|
@ -763,6 +779,7 @@ describe('workspace_panel', () => {
|
|||
beforeEach(() => {
|
||||
frame = createMockFramePublicAPI();
|
||||
mockDispatch = jest.fn();
|
||||
mockGetSuggestionForField = jest.fn();
|
||||
});
|
||||
|
||||
function initComponent(draggingContext = draggedField) {
|
||||
|
@ -790,43 +807,23 @@ describe('workspace_panel', () => {
|
|||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock, data: dataMock }}
|
||||
getSuggestionForField={mockGetSuggestionForField}
|
||||
/>
|
||||
</ChildDragDropProvider>
|
||||
);
|
||||
}
|
||||
|
||||
it('should immediately transition if exactly one suggestion is returned', () => {
|
||||
const expectedTable: TableSuggestion = {
|
||||
isMultiRow: true,
|
||||
layerId: '1',
|
||||
columns: [],
|
||||
changeType: 'unchanged',
|
||||
};
|
||||
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
|
||||
{
|
||||
state: {},
|
||||
table: expectedTable,
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'my title',
|
||||
state: {},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
mockGetSuggestionForField.mockReturnValue({
|
||||
visualizationId: 'vis',
|
||||
datasourceState: {},
|
||||
datasourceId: 'mock',
|
||||
visualizationState: {},
|
||||
});
|
||||
initComponent();
|
||||
|
||||
instance.find(DragDrop).prop('onDrop')!(draggedField);
|
||||
|
||||
expect(mockDatasource.getDatasourceSuggestionsForField).toHaveBeenCalledTimes(1);
|
||||
expect(mockVisualization.getSuggestions).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
table: expectedTable,
|
||||
})
|
||||
);
|
||||
expect(mockDispatch).toHaveBeenCalledWith({
|
||||
type: 'SWITCH_VISUALIZATION',
|
||||
newVisualizationId: 'vis',
|
||||
|
@ -837,80 +834,12 @@ describe('workspace_panel', () => {
|
|||
});
|
||||
|
||||
it('should allow to drop if there are suggestions', () => {
|
||||
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
|
||||
{
|
||||
state: {},
|
||||
table: {
|
||||
isMultiRow: true,
|
||||
layerId: '1',
|
||||
columns: [],
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'my title',
|
||||
state: {},
|
||||
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: {
|
||||
isMultiRow: true,
|
||||
layerId: '1',
|
||||
columns: [],
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization2.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'my title',
|
||||
state: {},
|
||||
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: {
|
||||
isMultiRow: true,
|
||||
layerId: '1',
|
||||
columns: [],
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'my title',
|
||||
state: {},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
mockGetSuggestionForField.mockReturnValue({
|
||||
visualizationId: 'vis',
|
||||
datasourceState: {},
|
||||
datasourceId: 'mock',
|
||||
visualizationState: {},
|
||||
});
|
||||
initComponent();
|
||||
expect(instance.find(DragDrop).prop('droppable')).toBeTruthy();
|
||||
});
|
||||
|
@ -919,61 +848,5 @@ describe('workspace_panel', () => {
|
|||
initComponent();
|
||||
expect(instance.find(DragDrop).prop('droppable')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should immediately transition to the first suggestion if there are multiple', () => {
|
||||
mockDatasource.getDatasourceSuggestionsForField.mockReturnValueOnce([
|
||||
{
|
||||
state: {},
|
||||
table: {
|
||||
isMultiRow: true,
|
||||
columns: [],
|
||||
layerId: '1',
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
{
|
||||
state: {},
|
||||
table: {
|
||||
isMultiRow: true,
|
||||
columns: [],
|
||||
layerId: '1',
|
||||
changeType: 'unchanged',
|
||||
},
|
||||
keptLayerIds: [],
|
||||
},
|
||||
]);
|
||||
mockVisualization.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.5,
|
||||
title: 'second suggestion',
|
||||
state: {},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
mockVisualization.getSuggestions.mockReturnValueOnce([
|
||||
{
|
||||
score: 0.8,
|
||||
title: 'first suggestion',
|
||||
state: {
|
||||
isFirst: true,
|
||||
},
|
||||
previewIcon: 'empty',
|
||||
},
|
||||
]);
|
||||
|
||||
initComponent();
|
||||
instance.find(DragDrop).prop('onDrop')!(draggedField);
|
||||
|
||||
expect(mockDispatch).toHaveBeenCalledWith({
|
||||
type: 'SWITCH_VISUALIZATION',
|
||||
newVisualizationId: 'vis',
|
||||
initialState: {
|
||||
isFirst: true,
|
||||
},
|
||||
datasourceState: {},
|
||||
datasourceId: 'mock',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,8 +39,8 @@ import {
|
|||
isLensFilterEvent,
|
||||
isLensEditEvent,
|
||||
} from '../../../types';
|
||||
import { DragDrop, DragContext } from '../../../drag_drop';
|
||||
import { getSuggestions, switchToSuggestion } from '../suggestion_helpers';
|
||||
import { DragDrop, DragContext, Dragging } from '../../../drag_drop';
|
||||
import { Suggestion, switchToSuggestion } from '../suggestion_helpers';
|
||||
import { buildExpression } from '../expression_helpers';
|
||||
import { debouncedComponent } from '../../../debounced_component';
|
||||
import { trackUiEvent } from '../../../lens_ui_telemetry';
|
||||
|
@ -75,6 +75,7 @@ export interface WorkspacePanelProps {
|
|||
plugins: { uiActions?: UiActionsStart; data: DataPublicPluginStart };
|
||||
title?: string;
|
||||
visualizeTriggerFieldContext?: VisualizeFieldContext;
|
||||
getSuggestionForField: (field: Dragging) => Suggestion | undefined;
|
||||
}
|
||||
|
||||
interface WorkspaceState {
|
||||
|
@ -97,43 +98,11 @@ export function WorkspacePanel({
|
|||
ExpressionRenderer: ExpressionRendererComponent,
|
||||
title,
|
||||
visualizeTriggerFieldContext,
|
||||
getSuggestionForField,
|
||||
}: WorkspacePanelProps) {
|
||||
const dragDropContext = useContext(DragContext);
|
||||
|
||||
const suggestionForDraggedField = useMemo(
|
||||
() => {
|
||||
if (!dragDropContext.dragging || !activeDatasourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasData = Object.values(framePublicAPI.datasourceLayers).some(
|
||||
(datasource) => datasource.getTableSpec().length > 0
|
||||
);
|
||||
|
||||
const mainPalette =
|
||||
activeVisualizationId &&
|
||||
visualizationMap[activeVisualizationId] &&
|
||||
visualizationMap[activeVisualizationId].getMainPalette
|
||||
? visualizationMap[activeVisualizationId].getMainPalette!(visualizationState)
|
||||
: undefined;
|
||||
const suggestions = getSuggestions({
|
||||
datasourceMap: { [activeDatasourceId]: datasourceMap[activeDatasourceId] },
|
||||
datasourceStates,
|
||||
visualizationMap:
|
||||
hasData && activeVisualizationId
|
||||
? { [activeVisualizationId]: visualizationMap[activeVisualizationId] }
|
||||
: visualizationMap,
|
||||
activeVisualizationId,
|
||||
visualizationState,
|
||||
field: dragDropContext.dragging,
|
||||
mainPalette,
|
||||
});
|
||||
|
||||
return suggestions.find((s) => s.visualizationId === activeVisualizationId) || suggestions[0];
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[dragDropContext.dragging]
|
||||
);
|
||||
const suggestionForDraggedField = getSuggestionForField(dragDropContext.dragging);
|
||||
|
||||
const [localState, setLocalState] = useState<WorkspaceState>({
|
||||
expressionBuildError: undefined,
|
||||
|
|
|
@ -260,6 +260,8 @@ describe('IndexPattern Data Panel', () => {
|
|||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
showNoDataPopover: jest.fn(),
|
||||
dropOntoWorkspace: jest.fn(),
|
||||
hasSuggestionForField: jest.fn(() => false),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ export function IndexPatternDataPanel({
|
|||
changeIndexPattern,
|
||||
charts,
|
||||
showNoDataPopover,
|
||||
dropOntoWorkspace,
|
||||
hasSuggestionForField,
|
||||
}: Props) {
|
||||
const { indexPatternRefs, indexPatterns, currentIndexPatternId } = state;
|
||||
const onChangeIndexPattern = useCallback(
|
||||
|
@ -193,6 +195,8 @@ export function IndexPatternDataPanel({
|
|||
onChangeIndexPattern={onChangeIndexPattern}
|
||||
existingFields={state.existingFields}
|
||||
existenceFetchFailed={state.existenceFetchFailed}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -241,6 +245,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
|
|||
data,
|
||||
existingFields,
|
||||
charts,
|
||||
dropOntoWorkspace,
|
||||
hasSuggestionForField,
|
||||
}: Omit<DatasourceDataPanelProps, 'state' | 'setState' | 'showNoDataPopover'> & {
|
||||
data: DataPublicPluginStart;
|
||||
currentIndexPatternId: string;
|
||||
|
@ -593,6 +599,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
|
|||
currentIndexPatternId={currentIndexPatternId}
|
||||
existenceFetchFailed={existenceFetchFailed}
|
||||
existFieldsInIndex={!!allFields.length}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
max-width: 300px;
|
||||
}
|
||||
|
||||
.lnsFieldItem__buttonGroup {
|
||||
// Enforce lowercase for buttons or else some browsers inherit all caps from flyout title
|
||||
.lnsFieldItem__fieldPanelTitle {
|
||||
text-transform: none;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,8 @@ describe('IndexPattern Field Item', () => {
|
|||
},
|
||||
exists: true,
|
||||
chartsThemeService,
|
||||
dropOntoWorkspace: () => {},
|
||||
hasSuggestionForField: () => false,
|
||||
};
|
||||
|
||||
data.fieldFormats = ({
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
import './field_item.scss';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import DateMath from '@elastic/datemath';
|
||||
import {
|
||||
EuiButtonGroup,
|
||||
EuiButtonIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIconTip,
|
||||
|
@ -18,7 +19,9 @@ import {
|
|||
EuiPopoverFooter,
|
||||
EuiPopoverTitle,
|
||||
EuiProgress,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
|
@ -45,7 +48,7 @@ import {
|
|||
import { FieldButton } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import { DraggedField } from './indexpattern';
|
||||
import { DragDrop } from '../drag_drop';
|
||||
import { DragDrop, Dragging } from '../drag_drop';
|
||||
import { DatasourceDataPanelProps, DataType } from '../types';
|
||||
import { BucketedAggregation, FieldStatsResponse } from '../../common';
|
||||
import { IndexPattern, IndexPatternField } from './types';
|
||||
|
@ -66,6 +69,8 @@ export interface FieldItemProps {
|
|||
chartsThemeService: ChartsPluginSetup['theme'];
|
||||
filters: Filter[];
|
||||
hideDetails?: boolean;
|
||||
dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace'];
|
||||
hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField'];
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -95,10 +100,19 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) {
|
|||
dateRange,
|
||||
filters,
|
||||
hideDetails,
|
||||
dropOntoWorkspace,
|
||||
} = props;
|
||||
|
||||
const [infoIsOpen, setOpen] = useState(false);
|
||||
|
||||
const dropOntoWorkspaceAndClose = useCallback(
|
||||
(droppedField: Dragging) => {
|
||||
dropOntoWorkspace(droppedField);
|
||||
setOpen(false);
|
||||
},
|
||||
[dropOntoWorkspace, setOpen]
|
||||
);
|
||||
|
||||
const [state, setState] = useState<State>({
|
||||
isLoading: false,
|
||||
});
|
||||
|
@ -142,10 +156,6 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) {
|
|||
}
|
||||
|
||||
function togglePopover() {
|
||||
if (hideDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(!infoIsOpen);
|
||||
if (!infoIsOpen) {
|
||||
trackUiEvent('indexpattern_field_info_click');
|
||||
|
@ -227,8 +237,13 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) {
|
|||
closePopover={() => setOpen(false)}
|
||||
anchorPosition="rightUp"
|
||||
panelClassName="lnsFieldItem__fieldPanel"
|
||||
initialFocus=".lnsFieldItem__fieldPanel"
|
||||
>
|
||||
<FieldItemPopoverContents {...state} {...props} />
|
||||
<FieldItemPopoverContents
|
||||
{...state}
|
||||
{...props}
|
||||
dropOntoWorkspace={dropOntoWorkspaceAndClose}
|
||||
/>
|
||||
</EuiPopover>
|
||||
</li>
|
||||
);
|
||||
|
@ -236,6 +251,40 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) {
|
|||
|
||||
export const FieldItem = debouncedComponent(InnerFieldItem);
|
||||
|
||||
function FieldPanelHeader({
|
||||
indexPatternId,
|
||||
field,
|
||||
hasSuggestionForField,
|
||||
dropOntoWorkspace,
|
||||
}: {
|
||||
field: IndexPatternField;
|
||||
indexPatternId: string;
|
||||
hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField'];
|
||||
dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace'];
|
||||
}) {
|
||||
const draggableField = {
|
||||
indexPatternId,
|
||||
id: field.name,
|
||||
field,
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xxs">
|
||||
<h5 className="lnsFieldItem__fieldPanelTitle">{field.displayName}</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
|
||||
<DragToWorkspaceButton
|
||||
isEnabled={hasSuggestionForField(draggableField)}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
field={draggableField}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
function FieldItemPopoverContents(props: State & FieldItemProps) {
|
||||
const {
|
||||
histogram,
|
||||
|
@ -247,6 +296,9 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
|
|||
sampledValues,
|
||||
chartsThemeService,
|
||||
data: { fieldFormats },
|
||||
dropOntoWorkspace,
|
||||
hasSuggestionForField,
|
||||
hideDetails,
|
||||
} = props;
|
||||
|
||||
const chartTheme = chartsThemeService.useChartsTheme();
|
||||
|
@ -270,6 +322,19 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
|
|||
|
||||
const [showingHistogram, setShowingHistogram] = useState(histogramDefault);
|
||||
|
||||
const panelHeader = (
|
||||
<FieldPanelHeader
|
||||
indexPatternId={indexPattern.id}
|
||||
field={field}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
);
|
||||
|
||||
if (hideDetails) {
|
||||
return panelHeader;
|
||||
}
|
||||
|
||||
let formatter: { convert: (data: unknown) => string };
|
||||
if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) {
|
||||
const FormatType = fieldFormats.getType(indexPattern.fieldFormatMap[field.name].id);
|
||||
|
@ -300,12 +365,16 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
|
|||
(!props.topValues || props.topValues.buckets.length === 0)
|
||||
) {
|
||||
return (
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', {
|
||||
defaultMessage:
|
||||
'This field is empty because it doesn’t exist in the 500 sampled documents. Adding this field to the configuration may result in a blank chart.',
|
||||
})}
|
||||
</EuiText>
|
||||
<>
|
||||
<EuiPopoverTitle>{panelHeader}</EuiPopoverTitle>
|
||||
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', {
|
||||
defaultMessage:
|
||||
'This field is empty because it doesn’t exist in the 500 sampled documents. Adding this field to the configuration may result in a blank chart.',
|
||||
})}
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -340,31 +409,40 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
|
|||
);
|
||||
} else if (field.type === 'date') {
|
||||
title = (
|
||||
<>
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldTimeDistributionLabel', {
|
||||
defaultMessage: 'Time distribution',
|
||||
})}
|
||||
</>
|
||||
<EuiTitle size="xxxs">
|
||||
<h6>
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldTimeDistributionLabel', {
|
||||
defaultMessage: 'Time distribution',
|
||||
})}
|
||||
</h6>
|
||||
</EuiTitle>
|
||||
);
|
||||
} else if (topValues && topValues.buckets.length) {
|
||||
title = (
|
||||
<>
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldTopValuesLabel', {
|
||||
defaultMessage: 'Top values',
|
||||
})}
|
||||
</>
|
||||
<EuiTitle size="xxxs">
|
||||
<h6>
|
||||
{i18n.translate('xpack.lens.indexPattern.fieldTopValuesLabel', {
|
||||
defaultMessage: 'Top values',
|
||||
})}
|
||||
</h6>
|
||||
</EuiTitle>
|
||||
);
|
||||
}
|
||||
|
||||
function wrapInPopover(el: React.ReactElement) {
|
||||
return (
|
||||
<>
|
||||
{title ? <EuiPopoverTitle>{title}</EuiPopoverTitle> : <></>}
|
||||
<EuiPopoverTitle>{panelHeader}</EuiPopoverTitle>
|
||||
|
||||
{title ? title : <></>}
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
{el}
|
||||
|
||||
{props.totalDocuments ? (
|
||||
<EuiPopoverFooter>
|
||||
<EuiText size="xs" textAlign="center">
|
||||
<EuiText color="subdued" size="xs">
|
||||
{props.sampledDocuments && (
|
||||
<>
|
||||
{i18n.translate('xpack.lens.indexPattern.percentageOfLabel', {
|
||||
|
@ -552,3 +630,44 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
|
|||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const DragToWorkspaceButton = ({
|
||||
field,
|
||||
dropOntoWorkspace,
|
||||
isEnabled,
|
||||
}: {
|
||||
field: {
|
||||
indexPatternId: string;
|
||||
id: string;
|
||||
field: IndexPatternField;
|
||||
};
|
||||
dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace'];
|
||||
isEnabled: boolean;
|
||||
}) => {
|
||||
const buttonTitle = isEnabled
|
||||
? i18n.translate('xpack.lens.indexPattern.moveToWorkspace', {
|
||||
defaultMessage: 'Add {field} to workspace',
|
||||
values: {
|
||||
field: field.field.name,
|
||||
},
|
||||
})
|
||||
: i18n.translate('xpack.lens.indexPattern.moveToWorkspaceDisabled', {
|
||||
defaultMessage:
|
||||
"This field can't be added to the workspace automatically. You can still use it directly in the configuration panel.",
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip content={buttonTitle}>
|
||||
<EuiButtonIcon
|
||||
aria-label={buttonTitle}
|
||||
isDisabled={!isEnabled}
|
||||
iconType="plusInCircle"
|
||||
onClick={() => {
|
||||
dropOntoWorkspace(field);
|
||||
}}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import { FieldItem } from './field_item';
|
|||
import { NoFieldsCallout } from './no_fields_callout';
|
||||
import { IndexPatternField } from './types';
|
||||
import { FieldItemSharedProps, FieldsAccordion } from './fields_accordion';
|
||||
import { DatasourceDataPanelProps } from '../types';
|
||||
const PAGINATION_SIZE = 50;
|
||||
|
||||
export type FieldGroups = Record<
|
||||
|
@ -48,6 +49,8 @@ export function FieldList({
|
|||
filter,
|
||||
currentIndexPatternId,
|
||||
existFieldsInIndex,
|
||||
dropOntoWorkspace,
|
||||
hasSuggestionForField,
|
||||
}: {
|
||||
exists: (field: IndexPatternField) => boolean;
|
||||
fieldGroups: FieldGroups;
|
||||
|
@ -60,6 +63,8 @@ export function FieldList({
|
|||
};
|
||||
currentIndexPatternId: string;
|
||||
existFieldsInIndex: boolean;
|
||||
dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace'];
|
||||
hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField'];
|
||||
}) {
|
||||
const [pageSize, setPageSize] = useState(PAGINATION_SIZE);
|
||||
const [scrollContainer, setScrollContainer] = useState<Element | undefined>(undefined);
|
||||
|
@ -137,6 +142,8 @@ export function FieldList({
|
|||
field={field}
|
||||
hideDetails={true}
|
||||
key={field.name}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
@ -147,6 +154,8 @@ export function FieldList({
|
|||
.map(([key, fieldGroup]) => (
|
||||
<Fragment key={key}>
|
||||
<FieldsAccordion
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
initialIsOpen={Boolean(accordionState[key])}
|
||||
key={key}
|
||||
id={`lnsIndexPattern${key}`}
|
||||
|
|
|
@ -72,6 +72,8 @@ describe('Fields Accordion', () => {
|
|||
fieldProps,
|
||||
renderCallout: <div id="lens-test-callout">Callout</div>,
|
||||
exists: () => true,
|
||||
dropOntoWorkspace: () => {},
|
||||
hasSuggestionForField: () => false,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@ export interface FieldsAccordionProps {
|
|||
exists: (field: IndexPatternField) => boolean;
|
||||
showExistenceFetchError?: boolean;
|
||||
hideDetails?: boolean;
|
||||
dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace'];
|
||||
hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField'];
|
||||
}
|
||||
|
||||
export const InnerFieldsAccordion = function InnerFieldsAccordion({
|
||||
|
@ -67,6 +69,8 @@ export const InnerFieldsAccordion = function InnerFieldsAccordion({
|
|||
exists,
|
||||
hideDetails,
|
||||
showExistenceFetchError,
|
||||
dropOntoWorkspace,
|
||||
hasSuggestionForField,
|
||||
}: FieldsAccordionProps) {
|
||||
const renderField = useCallback(
|
||||
(field: IndexPatternField) => (
|
||||
|
@ -76,9 +80,11 @@ export const InnerFieldsAccordion = function InnerFieldsAccordion({
|
|||
field={field}
|
||||
exists={exists(field)}
|
||||
hideDetails={hideDetails}
|
||||
dropOntoWorkspace={dropOntoWorkspace}
|
||||
hasSuggestionForField={hasSuggestionForField}
|
||||
/>
|
||||
),
|
||||
[fieldProps, exists, hideDetails]
|
||||
[fieldProps, exists, hideDetails, dropOntoWorkspace, hasSuggestionForField]
|
||||
);
|
||||
|
||||
const titleClassname = classNames({
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
Datatable,
|
||||
SerializedFieldFormat,
|
||||
} from '../../../../src/plugins/expressions/public';
|
||||
import { DragContextState } from './drag_drop';
|
||||
import { DragContextState, Dragging } from './drag_drop';
|
||||
import { Document } from './persistence';
|
||||
import { DateRange } from '../common';
|
||||
import { Query, Filter, SavedQuery, IFieldFormat } from '../../../../src/plugins/data/public';
|
||||
|
@ -217,6 +217,8 @@ export interface DatasourceDataPanelProps<T = unknown> {
|
|||
query: Query;
|
||||
dateRange: DateRange;
|
||||
filters: Filter[];
|
||||
dropOntoWorkspace: (field: Dragging) => void;
|
||||
hasSuggestionForField: (field: Dragging) => boolean;
|
||||
}
|
||||
|
||||
interface SharedDimensionProps {
|
||||
|
|
Loading…
Reference in a new issue