[Lens] move from slice to reducers/actions and simplify loading (#113324)
* structure changes * tests & fix for sessionId * share mocks in time_range_middleware * make switchVisualization and selectSuggestion one reducer as it's very similar * CR Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8e25f5cc0f
commit
6bfa2a4c2c
21 changed files with 845 additions and 1008 deletions
|
@ -13,7 +13,13 @@ import { App } from './app';
|
|||
import { LensAppProps, LensAppServices } from './types';
|
||||
import { EditorFrameInstance, EditorFrameProps } from '../types';
|
||||
import { Document } from '../persistence';
|
||||
import { visualizationMap, datasourceMap, makeDefaultServices, mountWithProvider } from '../mocks';
|
||||
import {
|
||||
visualizationMap,
|
||||
datasourceMap,
|
||||
makeDefaultServices,
|
||||
mountWithProvider,
|
||||
mockStoreDeps,
|
||||
} from '../mocks';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import {
|
||||
SavedObjectSaveModal,
|
||||
|
@ -92,9 +98,11 @@ describe('Lens App', () => {
|
|||
};
|
||||
}
|
||||
|
||||
const makeDefaultServicesForApp = () => makeDefaultServices(sessionIdSubject, 'sessionId-1');
|
||||
|
||||
async function mountWith({
|
||||
props = makeDefaultProps(),
|
||||
services = makeDefaultServices(sessionIdSubject),
|
||||
services = makeDefaultServicesForApp(),
|
||||
preloadedState,
|
||||
}: {
|
||||
props?: jest.Mocked<LensAppProps>;
|
||||
|
@ -110,11 +118,11 @@ describe('Lens App', () => {
|
|||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const storeDeps = mockStoreDeps({ lensServices: services });
|
||||
const { instance, lensStore } = await mountWithProvider(
|
||||
<App {...props} />,
|
||||
{
|
||||
data: services.data,
|
||||
storeDeps,
|
||||
preloadedState,
|
||||
},
|
||||
{ wrappingComponent }
|
||||
|
@ -144,7 +152,7 @@ describe('Lens App', () => {
|
|||
});
|
||||
|
||||
it('updates global filters with store state', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
const indexPattern = { id: 'index1' } as unknown as IndexPattern;
|
||||
const pinnedField = { name: 'pinnedField' } as unknown as FieldSpec;
|
||||
const pinnedFilter = esFilters.buildExistsFilter(pinnedField, indexPattern);
|
||||
|
@ -216,7 +224,7 @@ describe('Lens App', () => {
|
|||
|
||||
it('sets originatingApp breadcrumb when the document title changes', async () => {
|
||||
const props = makeDefaultProps();
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
props.incomingState = { originatingApp: 'coolContainer' };
|
||||
services.getOriginatingAppName = jest.fn(() => 'The Coolest Container Ever Made');
|
||||
|
||||
|
@ -262,7 +270,7 @@ describe('Lens App', () => {
|
|||
|
||||
describe('TopNavMenu#showDatePicker', () => {
|
||||
it('shows date picker if any used index pattern isTimeBased', async () => {
|
||||
const customServices = makeDefaultServices(sessionIdSubject);
|
||||
const customServices = makeDefaultServicesForApp();
|
||||
customServices.data.indexPatterns.get = jest
|
||||
.fn()
|
||||
.mockImplementation((id) =>
|
||||
|
@ -275,7 +283,7 @@ describe('Lens App', () => {
|
|||
);
|
||||
});
|
||||
it('shows date picker if active datasource isTimeBased', async () => {
|
||||
const customServices = makeDefaultServices(sessionIdSubject);
|
||||
const customServices = makeDefaultServicesForApp();
|
||||
customServices.data.indexPatterns.get = jest
|
||||
.fn()
|
||||
.mockImplementation((id) =>
|
||||
|
@ -290,7 +298,7 @@ describe('Lens App', () => {
|
|||
);
|
||||
});
|
||||
it('does not show date picker if index pattern nor active datasource is not time based', async () => {
|
||||
const customServices = makeDefaultServices(sessionIdSubject);
|
||||
const customServices = makeDefaultServicesForApp();
|
||||
customServices.data.indexPatterns.get = jest
|
||||
.fn()
|
||||
.mockImplementation((id) =>
|
||||
|
@ -337,7 +345,7 @@ describe('Lens App', () => {
|
|||
);
|
||||
});
|
||||
it('handles rejected index pattern', async () => {
|
||||
const customServices = makeDefaultServices(sessionIdSubject);
|
||||
const customServices = makeDefaultServicesForApp();
|
||||
customServices.data.indexPatterns.get = jest
|
||||
.fn()
|
||||
.mockImplementation((id) => Promise.reject({ reason: 'Could not locate that data view' }));
|
||||
|
@ -385,7 +393,7 @@ describe('Lens App', () => {
|
|||
: undefined,
|
||||
};
|
||||
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.attributeService.wrapAttributes = jest
|
||||
.fn()
|
||||
.mockImplementation(async ({ savedObjectId }) => ({
|
||||
|
@ -419,7 +427,7 @@ describe('Lens App', () => {
|
|||
}
|
||||
|
||||
it('shows a disabled save button when the user does not have permissions', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.application = {
|
||||
...services.application,
|
||||
capabilities: {
|
||||
|
@ -469,7 +477,7 @@ describe('Lens App', () => {
|
|||
|
||||
it('Shows Save and Return and Save As buttons in create by value mode with originating app', async () => {
|
||||
const props = makeDefaultProps();
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.dashboardFeatureFlag = { allowByValueEmbeddables: true };
|
||||
props.incomingState = {
|
||||
originatingApp: 'ultraDashboard',
|
||||
|
@ -618,7 +626,7 @@ describe('Lens App', () => {
|
|||
const mockedConsoleDir = jest.spyOn(console, 'dir'); // mocked console.dir to avoid messages in the console when running tests
|
||||
mockedConsoleDir.mockImplementation(() => {});
|
||||
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.attributeService.wrapAttributes = jest
|
||||
.fn()
|
||||
.mockRejectedValue({ message: 'failed' });
|
||||
|
@ -692,7 +700,7 @@ describe('Lens App', () => {
|
|||
});
|
||||
|
||||
it('checks for duplicate title before saving', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.attributeService.wrapAttributes = jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve({ savedObjectId: '123' }));
|
||||
|
@ -759,7 +767,7 @@ describe('Lens App', () => {
|
|||
});
|
||||
|
||||
it('should still be enabled even if the user is missing save permissions', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.application = {
|
||||
...services.application,
|
||||
capabilities: {
|
||||
|
@ -799,7 +807,7 @@ describe('Lens App', () => {
|
|||
});
|
||||
|
||||
it('should open inspect panel', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
const { instance } = await mountWith({ services, preloadedState: { isSaveable: true } });
|
||||
|
||||
await runInspect(instance);
|
||||
|
@ -943,7 +951,7 @@ describe('Lens App', () => {
|
|||
|
||||
describe('saved query handling', () => {
|
||||
it('does not allow saving when the user is missing the saveQuery permission', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.application = {
|
||||
...services.application,
|
||||
capabilities: {
|
||||
|
@ -1136,7 +1144,7 @@ describe('Lens App', () => {
|
|||
|
||||
it('updates the state if session id changes from the outside', async () => {
|
||||
const sessionIdS = new Subject<string>();
|
||||
const services = makeDefaultServices(sessionIdS);
|
||||
const services = makeDefaultServices(sessionIdS, 'sessionId-1');
|
||||
const { lensStore } = await mountWith({ props: undefined, services });
|
||||
|
||||
act(() => {
|
||||
|
@ -1180,7 +1188,7 @@ describe('Lens App', () => {
|
|||
});
|
||||
|
||||
it('does not confirm if the user is missing save permissions', async () => {
|
||||
const services = makeDefaultServices(sessionIdSubject);
|
||||
const services = makeDefaultServicesForApp();
|
||||
services.application = {
|
||||
...services.application,
|
||||
capabilities: {
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
createMockVisualization,
|
||||
createMockFramePublicAPI,
|
||||
createMockDatasource,
|
||||
DatasourceMock,
|
||||
} from '../../../mocks';
|
||||
import { createMockFramePublicAPI, visualizationMap, datasourceMap } from '../../../mocks';
|
||||
import { Visualization } from '../../../types';
|
||||
import { LayerPanels } from './config_panel';
|
||||
import { LayerPanel } from './layer_panel';
|
||||
|
@ -43,32 +38,23 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
describe('ConfigPanel', () => {
|
||||
let mockVisualization: jest.Mocked<Visualization>;
|
||||
let mockVisualization2: jest.Mocked<Visualization>;
|
||||
let mockDatasource: DatasourceMock;
|
||||
const frame = createMockFramePublicAPI();
|
||||
|
||||
function getDefaultProps() {
|
||||
frame.datasourceLayers = {
|
||||
first: mockDatasource.publicAPIMock,
|
||||
first: datasourceMap.testDatasource.publicAPIMock,
|
||||
};
|
||||
return {
|
||||
activeVisualizationId: 'vis1',
|
||||
visualizationMap: {
|
||||
vis1: mockVisualization,
|
||||
vis2: mockVisualization2,
|
||||
},
|
||||
activeDatasourceId: 'mockindexpattern',
|
||||
datasourceMap: {
|
||||
mockindexpattern: mockDatasource,
|
||||
},
|
||||
activeVisualizationId: 'testVis',
|
||||
visualizationMap,
|
||||
activeDatasourceId: 'testDatasource',
|
||||
datasourceMap,
|
||||
activeVisualization: {
|
||||
...mockVisualization,
|
||||
...visualizationMap.testVis,
|
||||
getLayerIds: () => Object.keys(frame.datasourceLayers),
|
||||
appendLayer: jest.fn(),
|
||||
} as unknown as Visualization,
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
|
@ -85,38 +71,6 @@ describe('ConfigPanel', () => {
|
|||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockVisualization = {
|
||||
...createMockVisualization(),
|
||||
id: 'testVis',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis',
|
||||
label: 'TEST1',
|
||||
groupLabel: 'testVisGroup',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockVisualization2 = {
|
||||
...createMockVisualization(),
|
||||
|
||||
id: 'testVis2',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis2',
|
||||
label: 'TEST2',
|
||||
groupLabel: 'testVis2Group',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockVisualization.getLayerIds.mockReturnValue(Object.keys(frame.datasourceLayers));
|
||||
mockDatasource = createMockDatasource('mockindexpattern');
|
||||
});
|
||||
|
||||
// in what case is this test needed?
|
||||
it('should fail to render layerPanels if the public API is out of date', async () => {
|
||||
const props = getDefaultProps();
|
||||
|
@ -130,7 +84,7 @@ describe('ConfigPanel', () => {
|
|||
const { instance, lensStore } = await mountWithProvider(<LayerPanels {...props} />, {
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
|
@ -140,22 +94,22 @@ describe('ConfigPanel', () => {
|
|||
const { updateDatasource, updateAll } = instance.find(LayerPanel).props();
|
||||
|
||||
const updater = () => 'updated';
|
||||
updateDatasource('mockindexpattern', updater);
|
||||
updateDatasource('testDatasource', updater);
|
||||
await waitMs(0);
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
(lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater(
|
||||
props.datasourceStates.mockindexpattern.state
|
||||
props.datasourceStates.testDatasource.state
|
||||
)
|
||||
).toEqual('updated');
|
||||
|
||||
updateAll('mockindexpattern', updater, props.visualizationState);
|
||||
updateAll('testDatasource', updater, props.visualizationState);
|
||||
// wait for one tick so async updater has a chance to trigger
|
||||
await waitMs(0);
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(
|
||||
(lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater(
|
||||
props.datasourceStates.mockindexpattern.state
|
||||
props.datasourceStates.testDatasource.state
|
||||
)
|
||||
).toEqual('updated');
|
||||
});
|
||||
|
@ -167,7 +121,7 @@ describe('ConfigPanel', () => {
|
|||
{
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
|
@ -195,15 +149,15 @@ describe('ConfigPanel', () => {
|
|||
const defaultProps = getDefaultProps();
|
||||
// overwriting datasourceLayers to test two layers
|
||||
frame.datasourceLayers = {
|
||||
first: mockDatasource.publicAPIMock,
|
||||
second: mockDatasource.publicAPIMock,
|
||||
first: datasourceMap.testDatasource.publicAPIMock,
|
||||
second: datasourceMap.testDatasource.publicAPIMock,
|
||||
};
|
||||
const { instance } = await mountWithProvider(
|
||||
<LayerPanels {...defaultProps} />,
|
||||
{
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
|
@ -232,15 +186,15 @@ describe('ConfigPanel', () => {
|
|||
const defaultProps = getDefaultProps();
|
||||
// overwriting datasourceLayers to test two layers
|
||||
frame.datasourceLayers = {
|
||||
first: mockDatasource.publicAPIMock,
|
||||
second: mockDatasource.publicAPIMock,
|
||||
first: datasourceMap.testDatasource.publicAPIMock,
|
||||
second: datasourceMap.testDatasource.publicAPIMock,
|
||||
};
|
||||
const { instance } = await mountWithProvider(
|
||||
<LayerPanels {...defaultProps} />,
|
||||
{
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
|
@ -273,16 +227,16 @@ describe('ConfigPanel', () => {
|
|||
{
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
},
|
||||
activeDatasourceId: 'mockindexpattern',
|
||||
activeDatasourceId: 'testDatasource',
|
||||
},
|
||||
dispatch: jest.fn((x) => {
|
||||
if (x.payload.subType === 'ADD_LAYER') {
|
||||
frame.datasourceLayers.second = mockDatasource.publicAPIMock;
|
||||
frame.datasourceLayers.second = datasourceMap.testDatasource.publicAPIMock;
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
@ -303,16 +257,15 @@ describe('ConfigPanel', () => {
|
|||
(generateId as jest.Mock).mockReturnValue(`newId`);
|
||||
return mountWithProvider(
|
||||
<LayerPanels {...props} />,
|
||||
|
||||
{
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
mockindexpattern: {
|
||||
testDatasource: {
|
||||
isLoading: false,
|
||||
state: 'state',
|
||||
},
|
||||
},
|
||||
activeDatasourceId: 'mockindexpattern',
|
||||
activeDatasourceId: 'testDatasource',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -352,13 +305,13 @@ describe('ConfigPanel', () => {
|
|||
label: 'Threshold layer',
|
||||
},
|
||||
]);
|
||||
mockDatasource.initializeDimension = jest.fn();
|
||||
datasourceMap.testDatasource.initializeDimension = jest.fn();
|
||||
|
||||
const { instance, lensStore } = await prepareAndMountComponent(props);
|
||||
await clickToAddLayer(instance);
|
||||
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.initializeDimension).not.toHaveBeenCalled();
|
||||
expect(datasourceMap.testDatasource.initializeDimension).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not add an initial dimension when initialDimensions are not available for the given layer type', async () => {
|
||||
|
@ -382,13 +335,13 @@ describe('ConfigPanel', () => {
|
|||
label: 'Threshold layer',
|
||||
},
|
||||
]);
|
||||
mockDatasource.initializeDimension = jest.fn();
|
||||
datasourceMap.testDatasource.initializeDimension = jest.fn();
|
||||
|
||||
const { instance, lensStore } = await prepareAndMountComponent(props);
|
||||
await clickToAddLayer(instance);
|
||||
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.initializeDimension).not.toHaveBeenCalled();
|
||||
expect(datasourceMap.testDatasource.initializeDimension).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use group initial dimension value when adding a new layer if available', async () => {
|
||||
|
@ -409,13 +362,13 @@ describe('ConfigPanel', () => {
|
|||
],
|
||||
},
|
||||
]);
|
||||
mockDatasource.initializeDimension = jest.fn();
|
||||
datasourceMap.testDatasource.initializeDimension = jest.fn();
|
||||
|
||||
const { instance, lensStore } = await prepareAndMountComponent(props);
|
||||
await clickToAddLayer(instance);
|
||||
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatasource.initializeDimension).toHaveBeenCalledWith(undefined, 'newId', {
|
||||
expect(datasourceMap.testDatasource.initializeDimension).toHaveBeenCalledWith({}, 'newId', {
|
||||
columnId: 'myColumn',
|
||||
dataType: 'number',
|
||||
groupId: 'testGroup',
|
||||
|
@ -441,20 +394,24 @@ describe('ConfigPanel', () => {
|
|||
],
|
||||
},
|
||||
]);
|
||||
mockDatasource.initializeDimension = jest.fn();
|
||||
datasourceMap.testDatasource.initializeDimension = jest.fn();
|
||||
|
||||
const { instance, lensStore } = await prepareAndMountComponent(props);
|
||||
|
||||
await clickToAddDimension(instance);
|
||||
expect(lensStore.dispatch).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(mockDatasource.initializeDimension).toHaveBeenCalledWith('state', 'first', {
|
||||
groupId: 'a',
|
||||
columnId: 'newId',
|
||||
dataType: 'number',
|
||||
label: 'Initial value',
|
||||
staticValue: 100,
|
||||
});
|
||||
expect(datasourceMap.testDatasource.initializeDimension).toHaveBeenCalledWith(
|
||||
'state',
|
||||
'first',
|
||||
{
|
||||
groupId: 'a',
|
||||
columnId: 'newId',
|
||||
dataType: 'number',
|
||||
label: 'Initial value',
|
||||
staticValue: 100,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { layerTypes } from '../../../../common';
|
||||
import { initialState } from '../../../state_management/lens_slice';
|
||||
import { LensAppState } from '../../../state_management/types';
|
||||
import { removeLayer, appendLayer } from './layer_actions';
|
||||
|
||||
function createTestArgs(initialLayerIds: string[]) {
|
||||
|
@ -44,15 +43,14 @@ function createTestArgs(initialLayerIds: string[]) {
|
|||
|
||||
return {
|
||||
state: {
|
||||
...initialState,
|
||||
activeDatasourceId: 'ds1',
|
||||
datasourceStates,
|
||||
title: 'foo',
|
||||
visualization: {
|
||||
activeId: 'vis1',
|
||||
activeId: 'testVis',
|
||||
state: initialLayerIds,
|
||||
},
|
||||
},
|
||||
} as unknown as LensAppState,
|
||||
activeVisualization,
|
||||
datasourceMap: {
|
||||
ds1: testDatasource('ds1'),
|
||||
|
@ -61,7 +59,7 @@ function createTestArgs(initialLayerIds: string[]) {
|
|||
trackUiEvent,
|
||||
stagedPreview: {
|
||||
visualization: {
|
||||
activeId: 'vis1',
|
||||
activeId: 'testVis',
|
||||
state: initialLayerIds,
|
||||
},
|
||||
datasourceStates,
|
||||
|
|
|
@ -146,7 +146,6 @@ describe('editor_frame', () => {
|
|||
};
|
||||
const lensStore = (
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
activeDatasourceId: 'testDatasource',
|
||||
datasourceStates: {
|
||||
|
@ -196,7 +195,6 @@ describe('editor_frame', () => {
|
|||
};
|
||||
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
visualization: { activeId: 'testVis', state: initialState },
|
||||
},
|
||||
|
@ -228,7 +226,6 @@ describe('editor_frame', () => {
|
|||
};
|
||||
instance = (
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
visualization: { activeId: 'testVis', state: {} },
|
||||
datasourceStates: {
|
||||
|
@ -283,7 +280,6 @@ describe('editor_frame', () => {
|
|||
|
||||
instance = (
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
visualization: { activeId: 'testVis', state: {} },
|
||||
datasourceStates: {
|
||||
|
@ -395,7 +391,6 @@ describe('editor_frame', () => {
|
|||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
activeDatasourceId: 'testDatasource',
|
||||
visualization: { activeId: mockVisualization.id, state: {} },
|
||||
|
@ -437,7 +432,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data });
|
||||
await mountWithProvider(<EditorFrame {...props} />);
|
||||
|
||||
const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1]
|
||||
.setState;
|
||||
|
@ -474,7 +469,6 @@ describe('editor_frame', () => {
|
|||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: { visualization: { activeId: mockVisualization.id, state: {} } },
|
||||
});
|
||||
|
||||
|
@ -523,7 +517,6 @@ describe('editor_frame', () => {
|
|||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -587,8 +580,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data }))
|
||||
.instance;
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
// necessary to flush elements to dom synchronously
|
||||
instance.update();
|
||||
|
@ -692,7 +684,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data });
|
||||
await mountWithProvider(<EditorFrame {...props} />);
|
||||
|
||||
expect(mockDatasource.getDatasourceSuggestionsFromCurrentState).toHaveBeenCalled();
|
||||
expect(mockDatasource2.getDatasourceSuggestionsFromCurrentState).not.toHaveBeenCalled();
|
||||
|
@ -725,7 +717,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data });
|
||||
await mountWithProvider(<EditorFrame {...props} />);
|
||||
|
||||
expect(mockVisualization.getSuggestions).toHaveBeenCalled();
|
||||
expect(mockVisualization2.getSuggestions).toHaveBeenCalled();
|
||||
|
@ -793,8 +785,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data }))
|
||||
.instance;
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
expect(
|
||||
instance
|
||||
|
@ -840,8 +831,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data }))
|
||||
.instance;
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
act(() => {
|
||||
instance.find('[data-test-subj="lnsSuggestion"]').at(2).simulate('click');
|
||||
|
@ -898,8 +888,7 @@ describe('editor_frame', () => {
|
|||
|
||||
ExpressionRenderer: expressionRendererMock,
|
||||
};
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />, { data: props.plugins.data }))
|
||||
.instance;
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
act(() => {
|
||||
instance.find('[data-test-subj="lnsWorkspace"]').last().simulate('drop');
|
||||
|
@ -968,7 +957,6 @@ describe('editor_frame', () => {
|
|||
} as EditorFrameProps;
|
||||
instance = (
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -1080,11 +1068,7 @@ describe('editor_frame', () => {
|
|||
ExpressionRenderer: expressionRendererMock,
|
||||
} as EditorFrameProps;
|
||||
|
||||
instance = (
|
||||
await mountWithProvider(<EditorFrame {...props} />, {
|
||||
data: props.plugins.data,
|
||||
})
|
||||
).instance;
|
||||
instance = (await mountWithProvider(<EditorFrame {...props} />)).instance;
|
||||
|
||||
act(() => {
|
||||
instance.find(DragDrop).filter('[dataTestSubj="lnsWorkspace"]').prop('onDrop')!(
|
||||
|
|
|
@ -76,7 +76,7 @@ export function EditorFrame(props: EditorFrameProps) {
|
|||
const suggestion = getSuggestionForField.current!(field);
|
||||
if (suggestion) {
|
||||
trackUiEvent('drop_onto_workspace');
|
||||
switchToSuggestion(dispatchLens, suggestion, 'SWITCH_VISUALIZATION');
|
||||
switchToSuggestion(dispatchLens, suggestion, true);
|
||||
}
|
||||
},
|
||||
[getSuggestionForField, dispatchLens]
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('suggestion helpers', () => {
|
|||
]);
|
||||
const suggestedState = {};
|
||||
const visualizationMap = {
|
||||
vis1: {
|
||||
testVis: {
|
||||
...mockVisualization,
|
||||
getSuggestions: () => [
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ describe('suggestion helpers', () => {
|
|||
};
|
||||
const suggestions = getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -76,7 +76,7 @@ describe('suggestion helpers', () => {
|
|||
generateSuggestion(),
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: {
|
||||
testVis: {
|
||||
...mockVisualization1,
|
||||
getSuggestions: () => [
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ describe('suggestion helpers', () => {
|
|||
};
|
||||
const suggestions = getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -119,11 +119,11 @@ describe('suggestion helpers', () => {
|
|||
datasourceMap.mock.getDatasourceSuggestionsForField.mockReturnValue([generateSuggestion()]);
|
||||
const droppedField = {};
|
||||
const visualizationMap = {
|
||||
vis1: createMockVisualization(),
|
||||
testVis: createMockVisualization(),
|
||||
};
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -153,12 +153,12 @@ describe('suggestion helpers', () => {
|
|||
mock3: createMockDatasource('a'),
|
||||
};
|
||||
const visualizationMap = {
|
||||
vis1: createMockVisualization(),
|
||||
testVis: createMockVisualization(),
|
||||
};
|
||||
const droppedField = {};
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap: multiDatasourceMap,
|
||||
datasourceStates: multiDatasourceStates,
|
||||
|
@ -183,12 +183,12 @@ describe('suggestion helpers', () => {
|
|||
]);
|
||||
|
||||
const visualizationMap = {
|
||||
vis1: createMockVisualization(),
|
||||
testVis: createMockVisualization(),
|
||||
};
|
||||
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -226,11 +226,11 @@ describe('suggestion helpers', () => {
|
|||
};
|
||||
|
||||
const visualizationMap = {
|
||||
vis1: createMockVisualization(),
|
||||
testVis: createMockVisualization(),
|
||||
};
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap: multiDatasourceMap,
|
||||
datasourceStates: multiDatasourceStates,
|
||||
|
@ -258,7 +258,7 @@ describe('suggestion helpers', () => {
|
|||
generateSuggestion(),
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: {
|
||||
testVis: {
|
||||
...mockVisualization1,
|
||||
getSuggestions: () => [
|
||||
{
|
||||
|
@ -289,7 +289,7 @@ describe('suggestion helpers', () => {
|
|||
};
|
||||
const suggestions = getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -319,12 +319,12 @@ describe('suggestion helpers', () => {
|
|||
{ state: {}, table: table2, keptLayerIds: ['first'] },
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: mockVisualization1,
|
||||
testVis: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -372,7 +372,7 @@ describe('suggestion helpers', () => {
|
|||
},
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: {
|
||||
testVis: {
|
||||
...mockVisualization1,
|
||||
getSuggestions: vis1Suggestions,
|
||||
},
|
||||
|
@ -384,7 +384,7 @@ describe('suggestion helpers', () => {
|
|||
|
||||
const suggestions = getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -407,13 +407,13 @@ describe('suggestion helpers', () => {
|
|||
]);
|
||||
|
||||
const visualizationMap = {
|
||||
vis1: mockVisualization1,
|
||||
testVis: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -439,12 +439,12 @@ describe('suggestion helpers', () => {
|
|||
generateSuggestion(1),
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: mockVisualization1,
|
||||
testVis: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -472,13 +472,13 @@ describe('suggestion helpers', () => {
|
|||
generateSuggestion(1),
|
||||
]);
|
||||
const visualizationMap = {
|
||||
vis1: mockVisualization1,
|
||||
testVis: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
|
||||
getSuggestions({
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap.vis1,
|
||||
activeVisualization: visualizationMap.testVis,
|
||||
visualizationState: {},
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
|
@ -542,9 +542,9 @@ describe('suggestion helpers', () => {
|
|||
getOperationForColumnId: jest.fn(),
|
||||
},
|
||||
},
|
||||
{ activeId: 'vis1', state: {} },
|
||||
{ mockindexpattern: { state: mockDatasourceState, isLoading: false } },
|
||||
{ vis1: mockVisualization1 },
|
||||
{ activeId: 'testVis', state: {} },
|
||||
{ testDatasource: { state: mockDatasourceState, isLoading: false } },
|
||||
{ testVis: mockVisualization1 },
|
||||
datasourceMap.mock,
|
||||
{ id: 'myfield', humanData: { label: 'myfieldLabel' } },
|
||||
];
|
||||
|
@ -574,7 +574,7 @@ describe('suggestion helpers', () => {
|
|||
it('should return nothing if datasource does not produce suggestions', () => {
|
||||
datasourceMap.mock.getDatasourceSuggestionsForField.mockReturnValue([]);
|
||||
defaultParams[3] = {
|
||||
vis1: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
testVis: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
|
@ -583,7 +583,7 @@ describe('suggestion helpers', () => {
|
|||
|
||||
it('should not consider suggestion from other visualization if there is data', () => {
|
||||
defaultParams[3] = {
|
||||
vis1: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
testVis: { ...mockVisualization1, getSuggestions: () => [] },
|
||||
vis2: mockVisualization2,
|
||||
};
|
||||
const result = getTopSuggestionForField(...defaultParams);
|
||||
|
@ -609,7 +609,7 @@ describe('suggestion helpers', () => {
|
|||
},
|
||||
]);
|
||||
defaultParams[3] = {
|
||||
vis1: mockVisualization1,
|
||||
testVis: mockVisualization1,
|
||||
vis2: mockVisualization2,
|
||||
vis3: mockVisualization3,
|
||||
};
|
||||
|
|
|
@ -25,7 +25,6 @@ import { LayerType, layerTypes } from '../../../common';
|
|||
import { getLayerType } from './config_panel/add_layer';
|
||||
import {
|
||||
LensDispatch,
|
||||
selectSuggestion,
|
||||
switchVisualization,
|
||||
DatasourceStates,
|
||||
VisualizationState,
|
||||
|
@ -164,24 +163,21 @@ export function getVisualizeFieldSuggestions({
|
|||
datasourceMap,
|
||||
datasourceStates,
|
||||
visualizationMap,
|
||||
activeVisualization,
|
||||
visualizationState,
|
||||
visualizeTriggerFieldContext,
|
||||
}: {
|
||||
datasourceMap: DatasourceMap;
|
||||
datasourceStates: DatasourceStates;
|
||||
visualizationMap: VisualizationMap;
|
||||
activeVisualization: Visualization;
|
||||
subVisualizationId?: string;
|
||||
visualizationState: unknown;
|
||||
visualizeTriggerFieldContext?: VisualizeFieldContext;
|
||||
}): Suggestion | undefined {
|
||||
const activeVisualization = visualizationMap?.[Object.keys(visualizationMap)[0]] || null;
|
||||
const suggestions = getSuggestions({
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
visualizationMap,
|
||||
activeVisualization,
|
||||
visualizationState,
|
||||
visualizationState: undefined,
|
||||
visualizeTriggerFieldContext,
|
||||
});
|
||||
if (suggestions.length) {
|
||||
|
@ -230,19 +226,18 @@ export function switchToSuggestion(
|
|||
Suggestion,
|
||||
'visualizationId' | 'visualizationState' | 'datasourceState' | 'datasourceId'
|
||||
>,
|
||||
type: 'SWITCH_VISUALIZATION' | 'SELECT_SUGGESTION' = 'SELECT_SUGGESTION'
|
||||
clearStagedPreview?: boolean
|
||||
) {
|
||||
const pickedSuggestion = {
|
||||
newVisualizationId: suggestion.visualizationId,
|
||||
initialState: suggestion.visualizationState,
|
||||
datasourceState: suggestion.datasourceState,
|
||||
datasourceId: suggestion.datasourceId!,
|
||||
};
|
||||
|
||||
dispatchLens(
|
||||
type === 'SELECT_SUGGESTION'
|
||||
? selectSuggestion(pickedSuggestion)
|
||||
: switchVisualization(pickedSuggestion)
|
||||
switchVisualization({
|
||||
suggestion: {
|
||||
newVisualizationId: suggestion.visualizationId,
|
||||
visualizationState: suggestion.visualizationState,
|
||||
datasourceState: suggestion.datasourceState,
|
||||
datasourceId: suggestion.datasourceId!,
|
||||
},
|
||||
clearStagedPreview,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -214,16 +214,17 @@ describe('suggestion_panel', () => {
|
|||
act(() => {
|
||||
instance.find('button[data-test-subj="lnsSuggestion"]').at(1).simulate('click');
|
||||
});
|
||||
// instance.update();
|
||||
|
||||
expect(lensStore.dispatch).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'lens/selectSuggestion',
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
datasourceId: undefined,
|
||||
datasourceState: {},
|
||||
initialState: { suggestion1: true },
|
||||
newVisualizationId: 'testVis',
|
||||
suggestion: {
|
||||
datasourceId: undefined,
|
||||
datasourceState: {},
|
||||
visualizationState: { suggestion1: true },
|
||||
newVisualizationId: 'testVis',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -200,10 +200,13 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
initialState: 'suggestion visB',
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
suggestion: {
|
||||
visualizationState: 'suggestion visB',
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -238,8 +241,11 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
initialState: 'visB initial state',
|
||||
newVisualizationId: 'visB',
|
||||
suggestion: {
|
||||
visualizationState: 'visB initial state',
|
||||
newVisualizationId: 'visB',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
|
@ -522,10 +528,13 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
datasourceId: undefined,
|
||||
datasourceState: undefined,
|
||||
initialState: 'visB initial state',
|
||||
newVisualizationId: 'visB',
|
||||
suggestion: {
|
||||
datasourceId: undefined,
|
||||
datasourceState: undefined,
|
||||
visualizationState: 'visB initial state',
|
||||
newVisualizationId: 'visB',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -598,10 +607,13 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
initialState: 'switched',
|
||||
newVisualizationId: 'visC',
|
||||
suggestion: {
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
visualizationState: 'switched',
|
||||
newVisualizationId: 'visC',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
expect(datasourceMap.testDatasource.removeLayer).not.toHaveBeenCalled();
|
||||
|
@ -694,10 +706,13 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: 'testDatasource suggestion',
|
||||
initialState: 'suggestion visB',
|
||||
suggestion: {
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: 'testDatasource suggestion',
|
||||
visualizationState: 'suggestion visB',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -731,10 +746,13 @@ describe('chart_switch', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
initialState: 'suggestion visB visB',
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
suggestion: {
|
||||
visualizationState: 'suggestion visB visB',
|
||||
newVisualizationId: 'visB',
|
||||
datasourceId: 'testDatasource',
|
||||
datasourceState: {},
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -166,7 +166,7 @@ export const ChartSwitch = memo(function ChartSwitch(props: Props) {
|
|||
...selection,
|
||||
visualizationState: selection.getVisualizationState(),
|
||||
},
|
||||
'SWITCH_VISUALIZATION'
|
||||
true
|
||||
);
|
||||
|
||||
if (
|
||||
|
|
|
@ -103,7 +103,6 @@ describe('workspace_panel', () => {
|
|||
/>,
|
||||
|
||||
{
|
||||
data: defaultProps.plugins.data,
|
||||
preloadedState: { visualization: { activeId: null, state: {} }, datasourceStates: {} },
|
||||
}
|
||||
);
|
||||
|
@ -121,7 +120,7 @@ describe('workspace_panel', () => {
|
|||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data, preloadedState: { datasourceStates: {} } }
|
||||
{ preloadedState: { datasourceStates: {} } }
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -138,7 +137,7 @@ describe('workspace_panel', () => {
|
|||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data, preloadedState: { datasourceStates: {} } }
|
||||
{ preloadedState: { datasourceStates: {} } }
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -165,8 +164,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
|
||||
instance = mounted.instance;
|
||||
|
@ -199,9 +197,7 @@ describe('workspace_panel', () => {
|
|||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
plugins={{ ...props.plugins, uiActions: uiActionsMock }}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -233,9 +229,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
|
||||
instance = mounted.instance;
|
||||
|
@ -279,7 +273,6 @@ describe('workspace_panel', () => {
|
|||
/>,
|
||||
|
||||
{
|
||||
data: defaultProps.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -360,9 +353,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
});
|
||||
|
@ -408,9 +399,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
});
|
||||
|
@ -456,7 +445,6 @@ describe('workspace_panel', () => {
|
|||
/>,
|
||||
|
||||
{
|
||||
data: defaultProps.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -499,7 +487,6 @@ describe('workspace_panel', () => {
|
|||
/>,
|
||||
|
||||
{
|
||||
data: defaultProps.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -543,7 +530,6 @@ describe('workspace_panel', () => {
|
|||
/>,
|
||||
|
||||
{
|
||||
data: defaultProps.plugins.data,
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
@ -582,9 +568,7 @@ describe('workspace_panel', () => {
|
|||
visualizationMap={{
|
||||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -614,9 +598,7 @@ describe('workspace_panel', () => {
|
|||
visualizationMap={{
|
||||
testVis: mockVisualization,
|
||||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -648,9 +630,7 @@ describe('workspace_panel', () => {
|
|||
visualizationMap={{
|
||||
testVis: mockVisualization,
|
||||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -679,9 +659,7 @@ describe('workspace_panel', () => {
|
|||
visualizationMap={{
|
||||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
|
||||
|
@ -709,9 +687,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
});
|
||||
|
@ -745,9 +721,7 @@ describe('workspace_panel', () => {
|
|||
testVis: { ...mockVisualization, toExpression: () => 'testVis' },
|
||||
}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
/>,
|
||||
|
||||
{ data: defaultProps.plugins.data }
|
||||
/>
|
||||
);
|
||||
instance = mounted.instance;
|
||||
lensStore = mounted.lensStore;
|
||||
|
@ -832,10 +806,13 @@ describe('workspace_panel', () => {
|
|||
expect(lensStore.dispatch).toHaveBeenCalledWith({
|
||||
type: 'lens/switchVisualization',
|
||||
payload: {
|
||||
newVisualizationId: 'testVis',
|
||||
initialState: {},
|
||||
datasourceState: {},
|
||||
datasourceId: 'testDatasource',
|
||||
suggestion: {
|
||||
newVisualizationId: 'testVis',
|
||||
visualizationState: {},
|
||||
datasourceState: {},
|
||||
datasourceId: 'testDatasource',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -275,7 +275,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
if (suggestionForDraggedField) {
|
||||
trackUiEvent('drop_onto_workspace');
|
||||
trackUiEvent(expressionExists ? 'drop_non_empty' : 'drop_empty');
|
||||
switchToSuggestion(dispatchLens, suggestionForDraggedField, 'SWITCH_VISUALIZATION');
|
||||
switchToSuggestion(dispatchLens, suggestionForDraggedField, true);
|
||||
}
|
||||
}, [suggestionForDraggedField, expressionExists, dispatchLens]);
|
||||
|
||||
|
|
|
@ -39,7 +39,12 @@ import { fieldFormatsServiceMock } from '../../../../src/plugins/field_formats/p
|
|||
import type { LensAttributeService } from './lens_attribute_service';
|
||||
import type { EmbeddableStateTransfer } from '../../../../src/plugins/embeddable/public';
|
||||
|
||||
import { makeConfigureStore, LensAppState, LensState } from './state_management/index';
|
||||
import {
|
||||
makeConfigureStore,
|
||||
LensAppState,
|
||||
LensState,
|
||||
LensStoreDeps,
|
||||
} from './state_management/index';
|
||||
import { getResolvedDateRange } from './utils';
|
||||
import { presentationUtilPluginMock } from '../../../../src/plugins/presentation_util/public/mocks';
|
||||
import {
|
||||
|
@ -48,6 +53,8 @@ import {
|
|||
Visualization,
|
||||
FramePublicAPI,
|
||||
FrameDatasourceAPI,
|
||||
DatasourceMap,
|
||||
VisualizationMap,
|
||||
} from './types';
|
||||
|
||||
export function mockDatasourceStates() {
|
||||
|
@ -59,7 +66,7 @@ export function mockDatasourceStates() {
|
|||
};
|
||||
}
|
||||
|
||||
export function createMockVisualization(id = 'vis1'): jest.Mocked<Visualization> {
|
||||
export function createMockVisualization(id = 'testVis'): jest.Mocked<Visualization> {
|
||||
return {
|
||||
id,
|
||||
clearLayer: jest.fn((state, _layerId) => state),
|
||||
|
@ -75,11 +82,12 @@ export function createMockVisualization(id = 'vis1'): jest.Mocked<Visualization>
|
|||
groupLabel: `${id}Group`,
|
||||
},
|
||||
],
|
||||
appendLayer: jest.fn(),
|
||||
getVisualizationTypeId: jest.fn((_state) => 'empty'),
|
||||
getDescription: jest.fn((_state) => ({ label: '' })),
|
||||
switchVisualizationType: jest.fn((_, x) => x),
|
||||
getSuggestions: jest.fn((_options) => []),
|
||||
initialize: jest.fn((_frame, _state?) => ({})),
|
||||
initialize: jest.fn((_frame, _state?) => ({ newState: 'newState' })),
|
||||
getConfiguration: jest.fn((props) => ({
|
||||
groups: [
|
||||
{
|
||||
|
@ -120,7 +128,7 @@ export function createMockDatasource(id: string): DatasourceMock {
|
|||
};
|
||||
|
||||
return {
|
||||
id: 'mockindexpattern',
|
||||
id: 'testDatasource',
|
||||
clearLayer: jest.fn((state, _layerId) => state),
|
||||
getDatasourceSuggestionsForField: jest.fn((_state, _item, filterFn) => []),
|
||||
getDatasourceSuggestionsForVisualizeField: jest.fn((_state, _indexpatternId, _fieldName) => []),
|
||||
|
@ -134,7 +142,7 @@ export function createMockDatasource(id: string): DatasourceMock {
|
|||
renderDataPanel: jest.fn(),
|
||||
renderLayerPanel: jest.fn(),
|
||||
toExpression: jest.fn((_frame, _state) => null),
|
||||
insertLayer: jest.fn((_state, _newLayerId) => {}),
|
||||
insertLayer: jest.fn((_state, _newLayerId) => ({})),
|
||||
removeLayer: jest.fn((_state, _layerId) => {}),
|
||||
removeColumn: jest.fn((props) => {}),
|
||||
getLayers: jest.fn((_state) => []),
|
||||
|
@ -153,8 +161,9 @@ export function createMockDatasource(id: string): DatasourceMock {
|
|||
};
|
||||
}
|
||||
|
||||
const mockDatasource: DatasourceMock = createMockDatasource('testDatasource');
|
||||
const mockDatasource2: DatasourceMock = createMockDatasource('testDatasource2');
|
||||
export const mockDatasource: DatasourceMock = createMockDatasource('testDatasource');
|
||||
export const mockDatasource2: DatasourceMock = createMockDatasource('testDatasource2');
|
||||
|
||||
export const datasourceMap = {
|
||||
testDatasource2: mockDatasource2,
|
||||
testDatasource: mockDatasource,
|
||||
|
@ -251,14 +260,41 @@ export function createMockTimefilter() {
|
|||
};
|
||||
}
|
||||
|
||||
export function mockDataPlugin(sessionIdSubject = new Subject<string>()) {
|
||||
export const exactMatchDoc = {
|
||||
...defaultDoc,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'exactMatch',
|
||||
},
|
||||
};
|
||||
|
||||
export const mockStoreDeps = (deps?: {
|
||||
lensServices?: LensAppServices;
|
||||
datasourceMap?: DatasourceMap;
|
||||
visualizationMap?: VisualizationMap;
|
||||
}) => {
|
||||
return {
|
||||
datasourceMap: deps?.datasourceMap || datasourceMap,
|
||||
visualizationMap: deps?.visualizationMap || visualizationMap,
|
||||
lensServices: deps?.lensServices || makeDefaultServices(),
|
||||
};
|
||||
};
|
||||
|
||||
export function mockDataPlugin(
|
||||
sessionIdSubject = new Subject<string>(),
|
||||
initialSessionId?: string
|
||||
) {
|
||||
function createMockSearchService() {
|
||||
let sessionIdCounter = 1;
|
||||
let sessionIdCounter = initialSessionId ? 1 : 0;
|
||||
let currentSessionId: string | undefined = initialSessionId;
|
||||
const start = () => {
|
||||
currentSessionId = `sessionId-${++sessionIdCounter}`;
|
||||
return currentSessionId;
|
||||
};
|
||||
return {
|
||||
session: {
|
||||
start: jest.fn(() => `sessionId-${sessionIdCounter++}`),
|
||||
start: jest.fn(start),
|
||||
clear: jest.fn(),
|
||||
getSessionId: jest.fn(() => `sessionId-${sessionIdCounter}`),
|
||||
getSessionId: jest.fn(() => currentSessionId),
|
||||
getSession$: jest.fn(() => sessionIdSubject.asObservable()),
|
||||
},
|
||||
};
|
||||
|
@ -296,7 +332,6 @@ export function mockDataPlugin(sessionIdSubject = new Subject<string>()) {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createMockQueryString() {
|
||||
return {
|
||||
getQuery: jest.fn(() => ({ query: '', language: 'lucene' })),
|
||||
|
@ -328,6 +363,7 @@ export function mockDataPlugin(sessionIdSubject = new Subject<string>()) {
|
|||
|
||||
export function makeDefaultServices(
|
||||
sessionIdSubject = new Subject<string>(),
|
||||
sessionId: string | undefined = undefined,
|
||||
doc = defaultDoc
|
||||
): jest.Mocked<LensAppServices> {
|
||||
const core = coreMock.createStart({ basePath: '/testbasepath' });
|
||||
|
@ -365,13 +401,7 @@ export function makeDefaultServices(
|
|||
},
|
||||
core
|
||||
);
|
||||
|
||||
attributeServiceMock.unwrapAttributes = jest.fn().mockResolvedValue({
|
||||
...doc,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'exactMatch',
|
||||
},
|
||||
});
|
||||
attributeServiceMock.unwrapAttributes = jest.fn().mockResolvedValue(exactMatchDoc);
|
||||
attributeServiceMock.wrapAttributes = jest.fn().mockResolvedValue({
|
||||
savedObjectId: (doc as unknown as LensByReferenceInput).savedObjectId,
|
||||
});
|
||||
|
@ -402,7 +432,7 @@ export function makeDefaultServices(
|
|||
},
|
||||
getUrlForApp: jest.fn((appId: string) => `/testbasepath/app/${appId}#/`),
|
||||
},
|
||||
data: mockDataPlugin(sessionIdSubject),
|
||||
data: mockDataPlugin(sessionIdSubject, sessionId),
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
storage: {
|
||||
get: jest.fn(),
|
||||
|
@ -432,44 +462,34 @@ export const defaultState = {
|
|||
};
|
||||
|
||||
export function makeLensStore({
|
||||
data,
|
||||
preloadedState,
|
||||
dispatch,
|
||||
storeDeps = mockStoreDeps(),
|
||||
}: {
|
||||
data?: DataPublicPluginStart;
|
||||
storeDeps?: LensStoreDeps;
|
||||
preloadedState?: Partial<LensAppState>;
|
||||
dispatch?: jest.Mock;
|
||||
}) {
|
||||
if (!data) {
|
||||
data = mockDataPlugin();
|
||||
}
|
||||
const lensStore = makeConfigureStore(
|
||||
{
|
||||
lensServices: { ...makeDefaultServices(), data },
|
||||
datasourceMap,
|
||||
visualizationMap,
|
||||
const data = storeDeps.lensServices.data;
|
||||
const store = makeConfigureStore(storeDeps, {
|
||||
lens: {
|
||||
...defaultState,
|
||||
query: data.query.queryString.getQuery(),
|
||||
filters: data.query.filterManager.getGlobalFilters(),
|
||||
resolvedDateRange: getResolvedDateRange(data.query.timefilter.timefilter),
|
||||
...preloadedState,
|
||||
},
|
||||
{
|
||||
lens: {
|
||||
...defaultState,
|
||||
searchSessionId: data.search.session.start(),
|
||||
query: data.query.queryString.getQuery(),
|
||||
filters: data.query.filterManager.getGlobalFilters(),
|
||||
resolvedDateRange: getResolvedDateRange(data.query.timefilter.timefilter),
|
||||
...preloadedState,
|
||||
},
|
||||
} as DeepPartial<LensState>
|
||||
);
|
||||
} as DeepPartial<LensState>);
|
||||
|
||||
const origDispatch = lensStore.dispatch;
|
||||
lensStore.dispatch = jest.fn(dispatch || origDispatch);
|
||||
return lensStore;
|
||||
const origDispatch = store.dispatch;
|
||||
store.dispatch = jest.fn(dispatch || origDispatch);
|
||||
return { store, deps: storeDeps };
|
||||
}
|
||||
|
||||
export const mountWithProvider = async (
|
||||
component: React.ReactElement,
|
||||
store?: {
|
||||
data?: DataPublicPluginStart;
|
||||
storeDeps?: LensStoreDeps;
|
||||
preloadedState?: Partial<LensAppState>;
|
||||
dispatch?: jest.Mock;
|
||||
},
|
||||
|
@ -480,7 +500,7 @@ export const mountWithProvider = async (
|
|||
attachTo?: HTMLElement;
|
||||
}
|
||||
) => {
|
||||
const lensStore = makeLensStore(store || {});
|
||||
const { store: lensStore, deps } = makeLensStore(store || {});
|
||||
|
||||
let wrappingComponent: React.FC<{
|
||||
children: React.ReactNode;
|
||||
|
@ -510,5 +530,5 @@ export const mountWithProvider = async (
|
|||
...restOptions,
|
||||
} as unknown as ReactWrapper);
|
||||
});
|
||||
return { instance, lensStore };
|
||||
return { instance, lensStore, deps };
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`init_middleware should initialize all datasources with state from doc 1`] = `
|
||||
exports[`Initializing the store should initialize all datasources with state from doc 1`] = `
|
||||
Object {
|
||||
"lens": Object {
|
||||
"activeDatasourceId": "testDatasource",
|
||||
|
@ -82,7 +82,7 @@ Object {
|
|||
"fromDate": "2021-01-10T04:00:00.000Z",
|
||||
"toDate": "2021-01-10T08:00:00.000Z",
|
||||
},
|
||||
"searchSessionId": "sessionId-2",
|
||||
"searchSessionId": "sessionId-1",
|
||||
"sharingSavedObjectProps": Object {
|
||||
"aliasTargetId": undefined,
|
||||
"outcome": undefined,
|
|
@ -8,7 +8,7 @@
|
|||
import { configureStore, getDefaultMiddleware, DeepPartial } from '@reduxjs/toolkit';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
|
||||
import { lensSlice } from './lens_slice';
|
||||
import { makeLensReducer, lensActions } from './lens_slice';
|
||||
import { timeRangeMiddleware } from './time_range_middleware';
|
||||
import { optimizingMiddleware } from './optimizing_middleware';
|
||||
import { LensState, LensStoreDeps } from './types';
|
||||
|
@ -16,10 +16,6 @@ import { initMiddleware } from './init_middleware';
|
|||
export * from './types';
|
||||
export * from './selectors';
|
||||
|
||||
export const reducer = {
|
||||
lens: lensSlice.reducer,
|
||||
};
|
||||
|
||||
export const {
|
||||
loadInitial,
|
||||
navigateAway,
|
||||
|
@ -31,12 +27,12 @@ export const {
|
|||
updateVisualizationState,
|
||||
updateLayer,
|
||||
switchVisualization,
|
||||
selectSuggestion,
|
||||
rollbackSuggestion,
|
||||
submitSuggestion,
|
||||
switchDatasource,
|
||||
setToggleFullscreen,
|
||||
} = lensSlice.actions;
|
||||
initEmpty,
|
||||
} = lensActions;
|
||||
|
||||
export const makeConfigureStore = (
|
||||
storeDeps: LensStoreDeps,
|
||||
|
@ -60,7 +56,9 @@ export const makeConfigureStore = (
|
|||
}
|
||||
|
||||
return configureStore({
|
||||
reducer,
|
||||
reducer: {
|
||||
lens: makeLensReducer(storeDeps),
|
||||
},
|
||||
middleware,
|
||||
preloadedState,
|
||||
});
|
||||
|
|
|
@ -1,410 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
makeDefaultServices,
|
||||
makeLensStore,
|
||||
defaultDoc,
|
||||
createMockVisualization,
|
||||
createMockDatasource,
|
||||
} from '../../mocks';
|
||||
import { Location, History } from 'history';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { loadInitial } from './load_initial';
|
||||
import { LensEmbeddableInput } from '../../embeddable';
|
||||
import { getPreloadedState } from '../lens_slice';
|
||||
import { LensAppState } from '..';
|
||||
import { LensAppServices } from '../../app_plugin/types';
|
||||
import { DatasourceMap, VisualizationMap } from '../../types';
|
||||
|
||||
const defaultSavedObjectId = '1234';
|
||||
const preloadedState = {
|
||||
isLoading: true,
|
||||
visualization: {
|
||||
state: null,
|
||||
activeId: 'testVis',
|
||||
},
|
||||
};
|
||||
|
||||
const exactMatchDoc = {
|
||||
...defaultDoc,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'exactMatch',
|
||||
},
|
||||
};
|
||||
|
||||
const getDefaultLensServices = () => {
|
||||
const lensServices = makeDefaultServices();
|
||||
lensServices.attributeService.unwrapAttributes = jest.fn().mockResolvedValue(exactMatchDoc);
|
||||
return lensServices;
|
||||
};
|
||||
|
||||
const getStoreDeps = (deps?: {
|
||||
lensServices?: LensAppServices;
|
||||
datasourceMap?: DatasourceMap;
|
||||
visualizationMap?: VisualizationMap;
|
||||
}) => {
|
||||
const lensServices = deps?.lensServices || getDefaultLensServices();
|
||||
const datasourceMap = deps?.datasourceMap || {
|
||||
testDatasource2: createMockDatasource('testDatasource2'),
|
||||
testDatasource: createMockDatasource('testDatasource'),
|
||||
};
|
||||
const visualizationMap = deps?.visualizationMap || {
|
||||
testVis: {
|
||||
...createMockVisualization(),
|
||||
id: 'testVis',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis',
|
||||
label: 'TEST1',
|
||||
groupLabel: 'testVisGroup',
|
||||
},
|
||||
],
|
||||
},
|
||||
testVis2: {
|
||||
...createMockVisualization(),
|
||||
id: 'testVis2',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis2',
|
||||
label: 'TEST2',
|
||||
groupLabel: 'testVis2Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
return {
|
||||
datasourceMap,
|
||||
visualizationMap,
|
||||
lensServices,
|
||||
};
|
||||
};
|
||||
|
||||
describe('init_middleware', () => {
|
||||
it('should initialize initial datasource', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices, datasourceMap } = storeDeps;
|
||||
|
||||
const lensStore = await makeLensStore({
|
||||
data: lensServices.data,
|
||||
preloadedState,
|
||||
});
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: { savedObjectId: defaultSavedObjectId } as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have initialized the initial datasource and visualization', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices, datasourceMap, visualizationMap } = storeDeps;
|
||||
|
||||
const lensStore = await makeLensStore({ data: lensServices.data, preloadedState });
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, { redirectCallback: jest.fn() });
|
||||
});
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
expect(datasourceMap.testDatasource2.initialize).not.toHaveBeenCalled();
|
||||
expect(visualizationMap.testVis.initialize).toHaveBeenCalled();
|
||||
expect(visualizationMap.testVis2.initialize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize all datasources with state from doc', async () => {
|
||||
const datasource1State = { datasource1: '' };
|
||||
const datasource2State = { datasource2: '' };
|
||||
const services = makeDefaultServices();
|
||||
services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({
|
||||
exactMatchDoc,
|
||||
visualizationType: 'testVis',
|
||||
title: '',
|
||||
state: {
|
||||
datasourceStates: {
|
||||
testDatasource: datasource1State,
|
||||
testDatasource2: datasource2State,
|
||||
},
|
||||
visualization: {},
|
||||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
},
|
||||
references: [],
|
||||
});
|
||||
|
||||
const storeDeps = getStoreDeps({
|
||||
lensServices: services,
|
||||
visualizationMap: {
|
||||
testVis: {
|
||||
...createMockVisualization(),
|
||||
id: 'testVis',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis',
|
||||
label: 'TEST1',
|
||||
groupLabel: 'testVisGroup',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
datasourceMap: {
|
||||
testDatasource: createMockDatasource('testDatasource'),
|
||||
testDatasource2: createMockDatasource('testDatasource2'),
|
||||
testDatasource3: createMockDatasource('testDatasource3'),
|
||||
},
|
||||
});
|
||||
const { datasourceMap } = storeDeps;
|
||||
|
||||
const lensStore = await makeLensStore({
|
||||
data: services.data,
|
||||
preloadedState,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: { savedObjectId: defaultSavedObjectId } as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalledWith(
|
||||
datasource1State,
|
||||
[],
|
||||
undefined,
|
||||
{
|
||||
isFullEditor: true,
|
||||
}
|
||||
);
|
||||
expect(datasourceMap.testDatasource2.initialize).toHaveBeenCalledWith(
|
||||
datasource2State,
|
||||
[],
|
||||
undefined,
|
||||
{
|
||||
isFullEditor: true,
|
||||
}
|
||||
);
|
||||
expect(datasourceMap.testDatasource3.initialize).not.toHaveBeenCalled();
|
||||
expect(lensStore.getState()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('loadInitial', () => {
|
||||
it('does not load a document if there is no initial input', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices } = storeDeps;
|
||||
|
||||
const lensStore = makeLensStore({ data: lensServices.data, preloadedState });
|
||||
await loadInitial(lensStore, storeDeps, { redirectCallback: jest.fn() });
|
||||
expect(lensServices.attributeService.unwrapAttributes).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cleans datasource and visualization state properly when reloading', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const lensStore = await makeLensStore({
|
||||
data: storeDeps.lensServices.data,
|
||||
preloadedState: {
|
||||
...preloadedState,
|
||||
visualization: {
|
||||
activeId: 'testVis',
|
||||
state: {},
|
||||
},
|
||||
datasourceStates: { testDatasource: { isLoading: false, state: {} } },
|
||||
},
|
||||
});
|
||||
|
||||
expect(lensStore.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
visualization: {
|
||||
activeId: 'testVis',
|
||||
state: {},
|
||||
},
|
||||
activeDatasourceId: 'testDatasource',
|
||||
datasourceStates: {
|
||||
testDatasource: { isLoading: false, state: {} },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const emptyState = getPreloadedState(storeDeps) as LensAppState;
|
||||
storeDeps.lensServices.attributeService.unwrapAttributes = jest.fn();
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: undefined,
|
||||
emptyState,
|
||||
});
|
||||
});
|
||||
|
||||
expect(lensStore.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
visualization: {
|
||||
activeId: 'testVis',
|
||||
state: null, // resets to null
|
||||
},
|
||||
activeDatasourceId: 'testDatasource2', // resets to first on the list
|
||||
datasourceStates: {
|
||||
testDatasource: { isLoading: false, state: undefined }, // state resets to undefined
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('loads a document and uses query and filters if initial input is provided', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices } = storeDeps;
|
||||
const emptyState = getPreloadedState(storeDeps) as LensAppState;
|
||||
|
||||
const lensStore = await makeLensStore({ data: lensServices.data, preloadedState });
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
emptyState,
|
||||
});
|
||||
});
|
||||
|
||||
expect(lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
|
||||
expect(lensServices.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([
|
||||
{ query: { match_phrase: { src: 'test' } } },
|
||||
]);
|
||||
|
||||
expect(lensStore.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
persistedDoc: { ...defaultDoc, type: 'lens' },
|
||||
query: 'kuery',
|
||||
isLoading: false,
|
||||
activeDatasourceId: 'testDatasource',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('does not load documents on sequential renders unless the id changes', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices } = storeDeps;
|
||||
|
||||
const lensStore = makeLensStore({ data: lensServices.data, preloadedState });
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
|
||||
expect(lensServices.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1);
|
||||
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: { savedObjectId: '5678' } as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
|
||||
expect(lensServices.attributeService.unwrapAttributes).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('handles document load errors', async () => {
|
||||
const services = makeDefaultServices();
|
||||
services.attributeService.unwrapAttributes = jest.fn().mockRejectedValue('failed to load');
|
||||
|
||||
const storeDeps = getStoreDeps({ lensServices: services });
|
||||
const { lensServices } = storeDeps;
|
||||
|
||||
const redirectCallback = jest.fn();
|
||||
|
||||
const lensStore = makeLensStore({ data: lensServices.data, preloadedState });
|
||||
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback,
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
expect(lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
expect(lensServices.notifications.toasts.addDanger).toHaveBeenCalled();
|
||||
expect(redirectCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('redirects if saved object is an aliasMatch', async () => {
|
||||
const services = makeDefaultServices();
|
||||
services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({
|
||||
...defaultDoc,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'aliasMatch',
|
||||
aliasTargetId: 'id2',
|
||||
},
|
||||
});
|
||||
|
||||
const storeDeps = getStoreDeps({ lensServices: services });
|
||||
const lensStore = makeLensStore({ data: storeDeps.lensServices.data, preloadedState });
|
||||
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
history: {
|
||||
location: {
|
||||
search: '?search',
|
||||
} as Location,
|
||||
} as History,
|
||||
});
|
||||
});
|
||||
expect(storeDeps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
|
||||
expect(storeDeps.lensServices.spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith(
|
||||
'#/edit/id2?search',
|
||||
'Lens visualization'
|
||||
);
|
||||
});
|
||||
|
||||
it('adds to the recently accessed list on load', async () => {
|
||||
const storeDeps = getStoreDeps();
|
||||
const { lensServices } = storeDeps;
|
||||
|
||||
const lensStore = makeLensStore({ data: lensServices.data, preloadedState });
|
||||
await act(async () => {
|
||||
await loadInitial(lensStore, storeDeps, {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: {
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
} as unknown as LensEmbeddableInput,
|
||||
});
|
||||
});
|
||||
|
||||
expect(lensServices.chrome.recentlyAccessed.add).toHaveBeenCalledWith(
|
||||
'/app/lens#/edit/1234',
|
||||
'An extremely cool default document!',
|
||||
'1234'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,17 +9,11 @@ import { MiddlewareAPI } from '@reduxjs/toolkit';
|
|||
import { isEqual } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { History } from 'history';
|
||||
import { LensAppState, setState } from '..';
|
||||
import { updateLayer, updateVisualizationState, LensStoreDeps } from '..';
|
||||
import { LensAppState, setState, initEmpty, LensStoreDeps } from '..';
|
||||
import { SharingSavedObjectProps } from '../../types';
|
||||
import { LensEmbeddableInput, LensByReferenceInput } from '../../embeddable/embeddable';
|
||||
import { getInitialDatasourceId } from '../../utils';
|
||||
import { initializeDatasources } from '../../editor_frame_service/editor_frame';
|
||||
import { generateId } from '../../id_generator';
|
||||
import {
|
||||
getVisualizeFieldSuggestions,
|
||||
switchToSuggestion,
|
||||
} from '../../editor_frame_service/editor_frame/suggestion_helpers';
|
||||
import { LensAppServices } from '../../app_plugin/types';
|
||||
import { getEditPath, getFullPath, LENS_EMBEDDABLE_TYPE } from '../../../common/constants';
|
||||
import { Document, injectFilterReferences } from '../../persistence';
|
||||
|
@ -89,13 +83,7 @@ export const getPersisted = async ({
|
|||
|
||||
export function loadInitial(
|
||||
store: MiddlewareAPI,
|
||||
{
|
||||
lensServices,
|
||||
datasourceMap,
|
||||
visualizationMap,
|
||||
embeddableEditorIncomingState,
|
||||
initialContext,
|
||||
}: LensStoreDeps,
|
||||
{ lensServices, datasourceMap, embeddableEditorIncomingState, initialContext }: LensStoreDeps,
|
||||
{
|
||||
redirectCallback,
|
||||
initialInput,
|
||||
|
@ -108,78 +96,39 @@ export function loadInitial(
|
|||
history?: History<unknown>;
|
||||
}
|
||||
) {
|
||||
const { getState, dispatch } = store;
|
||||
const { attributeService, notifications, data, dashboardFeatureFlag } = lensServices;
|
||||
const { persistedDoc } = getState().lens;
|
||||
const currentSessionId = data.search.session.getSessionId();
|
||||
|
||||
const { lens } = store.getState();
|
||||
if (
|
||||
!initialInput ||
|
||||
(attributeService.inputIsRefType(initialInput) &&
|
||||
initialInput.savedObjectId === persistedDoc?.savedObjectId)
|
||||
initialInput.savedObjectId === lens.persistedDoc?.savedObjectId)
|
||||
) {
|
||||
return initializeDatasources(
|
||||
datasourceMap,
|
||||
getState().lens.datasourceStates,
|
||||
undefined,
|
||||
initialContext,
|
||||
{
|
||||
isFullEditor: true,
|
||||
}
|
||||
)
|
||||
return initializeDatasources(datasourceMap, lens.datasourceStates, undefined, initialContext, {
|
||||
isFullEditor: true,
|
||||
})
|
||||
.then((result) => {
|
||||
const datasourceStates = Object.entries(result).reduce(
|
||||
(state, [datasourceId, datasourceState]) => ({
|
||||
...state,
|
||||
[datasourceId]: {
|
||||
...datasourceState,
|
||||
store.dispatch(
|
||||
initEmpty({
|
||||
newState: {
|
||||
...emptyState,
|
||||
searchSessionId: currentSessionId || data.search.session.start(),
|
||||
datasourceStates: Object.entries(result).reduce(
|
||||
(state, [datasourceId, datasourceState]) => ({
|
||||
...state,
|
||||
[datasourceId]: {
|
||||
...datasourceState,
|
||||
isLoading: false,
|
||||
},
|
||||
}),
|
||||
{}
|
||||
),
|
||||
isLoading: false,
|
||||
},
|
||||
}),
|
||||
{}
|
||||
);
|
||||
dispatch(
|
||||
setState({
|
||||
...emptyState,
|
||||
datasourceStates,
|
||||
isLoading: false,
|
||||
initialContext,
|
||||
})
|
||||
);
|
||||
if (initialContext) {
|
||||
const selectedSuggestion = getVisualizeFieldSuggestions({
|
||||
datasourceMap,
|
||||
datasourceStates,
|
||||
visualizationMap,
|
||||
activeVisualization: visualizationMap?.[Object.keys(visualizationMap)[0]] || null,
|
||||
visualizationState: null,
|
||||
visualizeTriggerFieldContext: initialContext,
|
||||
});
|
||||
if (selectedSuggestion) {
|
||||
switchToSuggestion(dispatch, selectedSuggestion, 'SWITCH_VISUALIZATION');
|
||||
}
|
||||
}
|
||||
const activeDatasourceId = getInitialDatasourceId(datasourceMap);
|
||||
const visualization = getState().lens.visualization;
|
||||
const activeVisualization =
|
||||
visualization.activeId && visualizationMap[visualization.activeId];
|
||||
|
||||
if (visualization.state === null && activeVisualization) {
|
||||
const newLayerId = generateId();
|
||||
|
||||
const initialVisualizationState = activeVisualization.initialize(() => newLayerId);
|
||||
dispatch(
|
||||
updateLayer({
|
||||
datasourceId: activeDatasourceId!,
|
||||
layerId: newLayerId,
|
||||
updater: datasourceMap[activeDatasourceId!].insertLayer,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
updateVisualizationState({
|
||||
visualizationId: activeVisualization.id,
|
||||
updater: initialVisualizationState,
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((e: { message: string }) => {
|
||||
notifications.toasts.addDanger({
|
||||
|
@ -188,6 +137,7 @@ export function loadInitial(
|
|||
redirectCallback();
|
||||
});
|
||||
}
|
||||
|
||||
getPersisted({ initialInput, lensServices, history })
|
||||
.then(
|
||||
(persisted) => {
|
||||
|
@ -226,11 +176,7 @@ export function loadInitial(
|
|||
}
|
||||
)
|
||||
.then((result) => {
|
||||
const activeDatasourceId = getInitialDatasourceId(datasourceMap, doc);
|
||||
|
||||
const currentSessionId = data.search.session.getSessionId();
|
||||
|
||||
dispatch(
|
||||
store.dispatch(
|
||||
setState({
|
||||
sharingSavedObjectProps,
|
||||
query: doc.state.query,
|
||||
|
@ -241,8 +187,8 @@ export function loadInitial(
|
|||
currentSessionId
|
||||
? currentSessionId
|
||||
: data.search.session.start(),
|
||||
...(!isEqual(persistedDoc, doc) ? { persistedDoc: doc } : null),
|
||||
activeDatasourceId,
|
||||
...(!isEqual(lens.persistedDoc, doc) ? { persistedDoc: doc } : null),
|
||||
activeDatasourceId: getInitialDatasourceId(datasourceMap, doc),
|
||||
visualization: {
|
||||
activeId: doc.visualizationType,
|
||||
state: doc.state.visualization,
|
||||
|
@ -271,7 +217,7 @@ export function loadInitial(
|
|||
}
|
||||
},
|
||||
() => {
|
||||
dispatch(
|
||||
store.dispatch(
|
||||
setState({
|
||||
isLoading: false,
|
||||
})
|
||||
|
@ -279,9 +225,10 @@ export function loadInitial(
|
|||
redirectCallback();
|
||||
}
|
||||
)
|
||||
.catch((e: { message: string }) =>
|
||||
.catch((e: { message: string }) => {
|
||||
notifications.toasts.addDanger({
|
||||
title: e.message,
|
||||
})
|
||||
);
|
||||
});
|
||||
redirectCallback();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,13 +17,9 @@ import {
|
|||
import { makeLensStore, defaultState } from '../mocks';
|
||||
|
||||
describe('lensSlice', () => {
|
||||
const store = makeLensStore({});
|
||||
const { store } = makeLensStore({});
|
||||
const customQuery = { query: 'custom' } as Query;
|
||||
|
||||
// TODO: need to move some initialization logic from mounter
|
||||
// describe('initialization', () => {
|
||||
// })
|
||||
|
||||
describe('state update', () => {
|
||||
it('setState: updates state ', () => {
|
||||
const lensState = store.getState().lens;
|
||||
|
@ -79,8 +75,11 @@ describe('lensSlice', () => {
|
|||
const newVisState = {};
|
||||
store.dispatch(
|
||||
switchVisualization({
|
||||
newVisualizationId: 'testVis2',
|
||||
initialState: newVisState,
|
||||
suggestion: {
|
||||
newVisualizationId: 'testVis2',
|
||||
visualizationState: newVisState,
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -93,10 +92,13 @@ describe('lensSlice', () => {
|
|||
|
||||
store.dispatch(
|
||||
switchVisualization({
|
||||
newVisualizationId: 'testVis2',
|
||||
initialState: newVisState,
|
||||
datasourceState: newDatasourceState,
|
||||
datasourceId: 'testDatasource',
|
||||
suggestion: {
|
||||
newVisualizationId: 'testVis2',
|
||||
visualizationState: newVisState,
|
||||
datasourceState: newDatasourceState,
|
||||
datasourceId: 'testDatasource',
|
||||
},
|
||||
clearStagedPreview: true,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -117,7 +119,7 @@ describe('lensSlice', () => {
|
|||
|
||||
it('not initialize already initialized datasource on switch', () => {
|
||||
const datasource2State = {};
|
||||
const customStore = makeLensStore({
|
||||
const { store: customStore } = makeLensStore({
|
||||
preloadedState: {
|
||||
datasourceStates: {
|
||||
testDatasource: {
|
||||
|
|
|
@ -5,12 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createSlice, current, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createAction, createReducer, current, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { VisualizeFieldContext } from 'src/plugins/ui_actions/public';
|
||||
import { History } from 'history';
|
||||
import { LensEmbeddableInput } from '..';
|
||||
import { TableInspectorAdapter } from '../editor_frame_service/types';
|
||||
import { getInitialDatasourceId, getResolvedDateRange } from '../utils';
|
||||
import { LensAppState, LensStoreDeps } from './types';
|
||||
import { generateId } from '../id_generator';
|
||||
import {
|
||||
getVisualizeFieldSuggestions,
|
||||
Suggestion,
|
||||
} from '../editor_frame_service/editor_frame/suggestion_helpers';
|
||||
|
||||
export const initialState: LensAppState = {
|
||||
persistedDoc: undefined,
|
||||
|
@ -68,29 +74,105 @@ export const getPreloadedState = ({
|
|||
return state;
|
||||
};
|
||||
|
||||
export const lensSlice = createSlice({
|
||||
name: 'lens',
|
||||
initialState,
|
||||
reducers: {
|
||||
setState: (state, { payload }: PayloadAction<Partial<LensAppState>>) => {
|
||||
export const setState = createAction<Partial<LensAppState>>('lens/setState');
|
||||
export const onActiveDataChange = createAction<TableInspectorAdapter>('lens/onActiveDataChange');
|
||||
export const setSaveable = createAction<boolean>('lens/setSaveable');
|
||||
export const updateState = createAction<{
|
||||
subType: string;
|
||||
updater: (prevState: LensAppState) => LensAppState;
|
||||
}>('lens/updateState');
|
||||
export const updateDatasourceState = createAction<{
|
||||
updater: unknown | ((prevState: unknown) => unknown);
|
||||
datasourceId: string;
|
||||
clearStagedPreview?: boolean;
|
||||
}>('lens/updateDatasourceState');
|
||||
export const updateVisualizationState = createAction<{
|
||||
visualizationId: string;
|
||||
updater: unknown;
|
||||
clearStagedPreview?: boolean;
|
||||
}>('lens/updateVisualizationState');
|
||||
|
||||
export const updateLayer = createAction<{
|
||||
layerId: string;
|
||||
datasourceId: string;
|
||||
updater: (state: unknown, layerId: string) => unknown;
|
||||
}>('lens/updateLayer');
|
||||
|
||||
export const switchVisualization = createAction<{
|
||||
suggestion: {
|
||||
newVisualizationId: string;
|
||||
visualizationState: unknown;
|
||||
datasourceState?: unknown;
|
||||
datasourceId?: string;
|
||||
};
|
||||
clearStagedPreview?: boolean;
|
||||
}>('lens/switchVisualization');
|
||||
export const rollbackSuggestion = createAction<void>('lens/rollbackSuggestion');
|
||||
export const setToggleFullscreen = createAction<void>('lens/setToggleFullscreen');
|
||||
export const submitSuggestion = createAction<void>('lens/submitSuggestion');
|
||||
export const switchDatasource = createAction<{
|
||||
newDatasourceId: string;
|
||||
}>('lens/switchDatasource');
|
||||
export const navigateAway = createAction<void>('lens/navigateAway');
|
||||
export const loadInitial = createAction<{
|
||||
initialInput?: LensEmbeddableInput;
|
||||
redirectCallback: (savedObjectId?: string) => void;
|
||||
emptyState: LensAppState;
|
||||
history: History<unknown>;
|
||||
}>('lens/loadInitial');
|
||||
export const initEmpty = createAction(
|
||||
'initEmpty',
|
||||
function prepare({
|
||||
newState,
|
||||
initialContext,
|
||||
}: {
|
||||
newState: Partial<LensAppState>;
|
||||
initialContext?: VisualizeFieldContext;
|
||||
}) {
|
||||
return { payload: { layerId: generateId(), newState, initialContext } };
|
||||
}
|
||||
);
|
||||
|
||||
export const lensActions = {
|
||||
setState,
|
||||
onActiveDataChange,
|
||||
setSaveable,
|
||||
updateState,
|
||||
updateDatasourceState,
|
||||
updateVisualizationState,
|
||||
updateLayer,
|
||||
switchVisualization,
|
||||
rollbackSuggestion,
|
||||
setToggleFullscreen,
|
||||
submitSuggestion,
|
||||
switchDatasource,
|
||||
navigateAway,
|
||||
loadInitial,
|
||||
initEmpty,
|
||||
};
|
||||
|
||||
export const makeLensReducer = (storeDeps: LensStoreDeps) => {
|
||||
const { datasourceMap, visualizationMap } = storeDeps;
|
||||
return createReducer<LensAppState>(initialState, {
|
||||
[setState.type]: (state, { payload }: PayloadAction<Partial<LensAppState>>) => {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
onActiveDataChange: (state, { payload }: PayloadAction<TableInspectorAdapter>) => {
|
||||
[onActiveDataChange.type]: (state, { payload }: PayloadAction<TableInspectorAdapter>) => {
|
||||
return {
|
||||
...state,
|
||||
activeData: payload,
|
||||
};
|
||||
},
|
||||
setSaveable: (state, { payload }: PayloadAction<boolean>) => {
|
||||
[setSaveable.type]: (state, { payload }: PayloadAction<boolean>) => {
|
||||
return {
|
||||
...state,
|
||||
isSaveable: payload,
|
||||
};
|
||||
},
|
||||
updateState: (
|
||||
[updateState.type]: (
|
||||
state,
|
||||
action: {
|
||||
payload: {
|
||||
|
@ -101,7 +183,7 @@ export const lensSlice = createSlice({
|
|||
) => {
|
||||
return action.payload.updater(current(state) as LensAppState);
|
||||
},
|
||||
updateDatasourceState: (
|
||||
[updateDatasourceState.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
|
@ -128,7 +210,7 @@ export const lensSlice = createSlice({
|
|||
stagedPreview: payload.clearStagedPreview ? undefined : state.stagedPreview,
|
||||
};
|
||||
},
|
||||
updateVisualizationState: (
|
||||
[updateVisualizationState.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
|
@ -161,7 +243,7 @@ export const lensSlice = createSlice({
|
|||
stagedPreview: payload.clearStagedPreview ? undefined : state.stagedPreview,
|
||||
};
|
||||
},
|
||||
updateLayer: (
|
||||
[updateLayer.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
|
@ -188,92 +270,65 @@ export const lensSlice = createSlice({
|
|||
};
|
||||
},
|
||||
|
||||
switchVisualization: (
|
||||
[switchVisualization.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
}: {
|
||||
payload: {
|
||||
newVisualizationId: string;
|
||||
initialState: unknown;
|
||||
datasourceState?: unknown;
|
||||
datasourceId?: string;
|
||||
suggestion: {
|
||||
newVisualizationId: string;
|
||||
visualizationState: unknown;
|
||||
datasourceState?: unknown;
|
||||
datasourceId?: string;
|
||||
};
|
||||
clearStagedPreview?: boolean;
|
||||
};
|
||||
}
|
||||
) => {
|
||||
const { newVisualizationId, visualizationState, datasourceState, datasourceId } =
|
||||
payload.suggestion;
|
||||
return {
|
||||
...state,
|
||||
datasourceStates:
|
||||
'datasourceId' in payload && payload.datasourceId
|
||||
? {
|
||||
...state.datasourceStates,
|
||||
[payload.datasourceId]: {
|
||||
...state.datasourceStates[payload.datasourceId],
|
||||
state: payload.datasourceState,
|
||||
},
|
||||
}
|
||||
: state.datasourceStates,
|
||||
datasourceStates: datasourceId
|
||||
? {
|
||||
...state.datasourceStates,
|
||||
[datasourceId]: {
|
||||
...state.datasourceStates[datasourceId],
|
||||
state: datasourceState,
|
||||
},
|
||||
}
|
||||
: state.datasourceStates,
|
||||
visualization: {
|
||||
...state.visualization,
|
||||
activeId: payload.newVisualizationId,
|
||||
state: payload.initialState,
|
||||
activeId: newVisualizationId,
|
||||
state: visualizationState,
|
||||
},
|
||||
stagedPreview: undefined,
|
||||
stagedPreview: payload.clearStagedPreview
|
||||
? undefined
|
||||
: state.stagedPreview || {
|
||||
datasourceStates: state.datasourceStates,
|
||||
visualization: state.visualization,
|
||||
},
|
||||
};
|
||||
},
|
||||
selectSuggestion: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
}: {
|
||||
payload: {
|
||||
newVisualizationId: string;
|
||||
initialState: unknown;
|
||||
datasourceState: unknown;
|
||||
datasourceId: string;
|
||||
};
|
||||
}
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
datasourceStates:
|
||||
'datasourceId' in payload && payload.datasourceId
|
||||
? {
|
||||
...state.datasourceStates,
|
||||
[payload.datasourceId]: {
|
||||
...state.datasourceStates[payload.datasourceId],
|
||||
state: payload.datasourceState,
|
||||
},
|
||||
}
|
||||
: state.datasourceStates,
|
||||
visualization: {
|
||||
...state.visualization,
|
||||
activeId: payload.newVisualizationId,
|
||||
state: payload.initialState,
|
||||
},
|
||||
stagedPreview: state.stagedPreview || {
|
||||
datasourceStates: state.datasourceStates,
|
||||
visualization: state.visualization,
|
||||
},
|
||||
};
|
||||
},
|
||||
rollbackSuggestion: (state) => {
|
||||
[rollbackSuggestion.type]: (state) => {
|
||||
return {
|
||||
...state,
|
||||
...(state.stagedPreview || {}),
|
||||
stagedPreview: undefined,
|
||||
};
|
||||
},
|
||||
setToggleFullscreen: (state) => {
|
||||
[setToggleFullscreen.type]: (state) => {
|
||||
return { ...state, isFullscreenDatasource: !state.isFullscreenDatasource };
|
||||
},
|
||||
submitSuggestion: (state) => {
|
||||
[submitSuggestion.type]: (state) => {
|
||||
return {
|
||||
...state,
|
||||
stagedPreview: undefined,
|
||||
};
|
||||
},
|
||||
switchDatasource: (
|
||||
[switchDatasource.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
|
@ -295,8 +350,8 @@ export const lensSlice = createSlice({
|
|||
activeDatasourceId: payload.newDatasourceId,
|
||||
};
|
||||
},
|
||||
navigateAway: (state) => state,
|
||||
loadInitial: (
|
||||
[navigateAway.type]: (state) => state,
|
||||
[loadInitial.type]: (
|
||||
state,
|
||||
payload: PayloadAction<{
|
||||
initialInput?: LensEmbeddableInput;
|
||||
|
@ -305,9 +360,78 @@ export const lensSlice = createSlice({
|
|||
history: History<unknown>;
|
||||
}>
|
||||
) => state,
|
||||
},
|
||||
});
|
||||
[initEmpty.type]: (
|
||||
state,
|
||||
{
|
||||
payload,
|
||||
}: {
|
||||
payload: {
|
||||
newState: Partial<LensAppState>;
|
||||
initialContext: VisualizeFieldContext | undefined;
|
||||
layerId: string;
|
||||
};
|
||||
}
|
||||
) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload.newState,
|
||||
};
|
||||
const suggestion: Suggestion | undefined = getVisualizeFieldSuggestions({
|
||||
datasourceMap,
|
||||
datasourceStates: newState.datasourceStates,
|
||||
visualizationMap,
|
||||
visualizeTriggerFieldContext: payload.initialContext,
|
||||
});
|
||||
if (suggestion) {
|
||||
return {
|
||||
...newState,
|
||||
datasourceStates: {
|
||||
...newState.datasourceStates,
|
||||
[suggestion.datasourceId!]: {
|
||||
...newState.datasourceStates[suggestion.datasourceId!],
|
||||
state: suggestion.datasourceState,
|
||||
},
|
||||
},
|
||||
visualization: {
|
||||
...newState.visualization,
|
||||
activeId: suggestion.visualizationId,
|
||||
state: suggestion.visualizationState,
|
||||
},
|
||||
stagedPreview: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export const reducer = {
|
||||
lens: lensSlice.reducer,
|
||||
const visualization = newState.visualization;
|
||||
|
||||
if (!visualization.activeId) {
|
||||
throw new Error('Invariant: visualization state got updated without active visualization');
|
||||
}
|
||||
|
||||
const activeVisualization = visualizationMap[visualization.activeId];
|
||||
if (visualization.state === null && activeVisualization) {
|
||||
const activeDatasourceId = getInitialDatasourceId(datasourceMap)!;
|
||||
const newVisState = activeVisualization.initialize(() => payload.layerId);
|
||||
const activeDatasource = datasourceMap[activeDatasourceId];
|
||||
return {
|
||||
...newState,
|
||||
activeDatasourceId,
|
||||
datasourceStates: {
|
||||
...newState.datasourceStates,
|
||||
[activeDatasourceId]: {
|
||||
...newState.datasourceStates[activeDatasourceId],
|
||||
state: activeDatasource.insertLayer(
|
||||
newState.datasourceStates[activeDatasourceId]?.state,
|
||||
payload.layerId
|
||||
),
|
||||
},
|
||||
},
|
||||
visualization: {
|
||||
...visualization,
|
||||
state: newVisState,
|
||||
},
|
||||
};
|
||||
}
|
||||
return newState;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
makeDefaultServices,
|
||||
makeLensStore,
|
||||
defaultDoc,
|
||||
createMockVisualization,
|
||||
createMockDatasource,
|
||||
mockStoreDeps,
|
||||
exactMatchDoc,
|
||||
} from '../mocks';
|
||||
import { Location, History } from 'history';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { LensEmbeddableInput } from '../embeddable';
|
||||
import { getPreloadedState, initialState, loadInitial } from './lens_slice';
|
||||
import { LensAppState } from '.';
|
||||
|
||||
const history = {
|
||||
location: {
|
||||
search: '?search',
|
||||
} as Location,
|
||||
} as History;
|
||||
|
||||
const defaultSavedObjectId = '1234';
|
||||
const preloadedState = {
|
||||
isLoading: true,
|
||||
visualization: {
|
||||
state: null,
|
||||
activeId: 'testVis',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
redirectCallback: jest.fn(),
|
||||
initialInput: { savedObjectId: defaultSavedObjectId } as unknown as LensEmbeddableInput,
|
||||
history,
|
||||
emptyState: initialState,
|
||||
};
|
||||
|
||||
describe('Initializing the store', () => {
|
||||
it('should initialize initial datasource', async () => {
|
||||
const { store, deps } = await makeLensStore({ preloadedState });
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
expect(deps.datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have initialized the initial datasource and visualization', async () => {
|
||||
const { store, deps } = await makeLensStore({ preloadedState });
|
||||
const emptyState = getPreloadedState(deps) as LensAppState;
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial({ ...defaultProps, initialInput: undefined, emptyState }));
|
||||
});
|
||||
expect(deps.datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
expect(deps.datasourceMap.testDatasource2.initialize).not.toHaveBeenCalled();
|
||||
expect(deps.visualizationMap.testVis.initialize).toHaveBeenCalled();
|
||||
expect(deps.visualizationMap.testVis2.initialize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize all datasources with state from doc', async () => {
|
||||
const datasource1State = { datasource1: '' };
|
||||
const datasource2State = { datasource2: '' };
|
||||
const services = makeDefaultServices();
|
||||
services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({
|
||||
exactMatchDoc,
|
||||
visualizationType: 'testVis',
|
||||
title: '',
|
||||
state: {
|
||||
datasourceStates: {
|
||||
testDatasource: datasource1State,
|
||||
testDatasource2: datasource2State,
|
||||
},
|
||||
visualization: {},
|
||||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
},
|
||||
references: [],
|
||||
});
|
||||
|
||||
const storeDeps = mockStoreDeps({
|
||||
lensServices: services,
|
||||
visualizationMap: {
|
||||
testVis: {
|
||||
...createMockVisualization(),
|
||||
id: 'testVis',
|
||||
visualizationTypes: [
|
||||
{
|
||||
icon: 'empty',
|
||||
id: 'testVis',
|
||||
label: 'TEST1',
|
||||
groupLabel: 'testVisGroup',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
datasourceMap: {
|
||||
testDatasource: createMockDatasource('testDatasource'),
|
||||
testDatasource2: createMockDatasource('testDatasource2'),
|
||||
testDatasource3: createMockDatasource('testDatasource3'),
|
||||
},
|
||||
});
|
||||
|
||||
const { store, deps } = await makeLensStore({
|
||||
storeDeps,
|
||||
preloadedState,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
const { datasourceMap } = deps;
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalled();
|
||||
|
||||
expect(datasourceMap.testDatasource.initialize).toHaveBeenCalledWith(
|
||||
datasource1State,
|
||||
[],
|
||||
undefined,
|
||||
{
|
||||
isFullEditor: true,
|
||||
}
|
||||
);
|
||||
expect(datasourceMap.testDatasource2.initialize).toHaveBeenCalledWith(
|
||||
datasource2State,
|
||||
[],
|
||||
undefined,
|
||||
{
|
||||
isFullEditor: true,
|
||||
}
|
||||
);
|
||||
expect(datasourceMap.testDatasource3.initialize).not.toHaveBeenCalled();
|
||||
expect(store.getState()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('loadInitial', () => {
|
||||
it('does not load a document if there is no initial input', async () => {
|
||||
const { deps, store } = makeLensStore({ preloadedState });
|
||||
await act(async () => {
|
||||
await store.dispatch(
|
||||
loadInitial({
|
||||
...defaultProps,
|
||||
initialInput: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('starts new searchSessionId', async () => {
|
||||
const { store } = await makeLensStore({ preloadedState });
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
expect(store.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
searchSessionId: 'sessionId-1',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('cleans datasource and visualization state properly when reloading', async () => {
|
||||
const { store, deps } = await makeLensStore({
|
||||
preloadedState: {
|
||||
...preloadedState,
|
||||
visualization: {
|
||||
activeId: 'testVis',
|
||||
state: {},
|
||||
},
|
||||
datasourceStates: { testDatasource: { isLoading: false, state: {} } },
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
visualization: {
|
||||
activeId: 'testVis',
|
||||
state: {},
|
||||
},
|
||||
activeDatasourceId: 'testDatasource',
|
||||
datasourceStates: {
|
||||
testDatasource: { isLoading: false, state: {} },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const emptyState = getPreloadedState(deps) as LensAppState;
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(
|
||||
loadInitial({
|
||||
...defaultProps,
|
||||
emptyState,
|
||||
initialInput: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
expect(deps.visualizationMap.testVis.initialize).toHaveBeenCalled();
|
||||
expect(store.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
visualization: {
|
||||
state: { newState: 'newState' }, // new vis gets initialized
|
||||
activeId: 'testVis',
|
||||
},
|
||||
activeDatasourceId: 'testDatasource2', // resets to first on the list
|
||||
datasourceStates: {
|
||||
testDatasource: { isLoading: false, state: undefined }, // state resets to undefined
|
||||
testDatasource2: {
|
||||
state: {}, // initializes first in the map
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('loads a document and uses query and filters if initial input is provided', async () => {
|
||||
const { store, deps } = await makeLensStore({ preloadedState });
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
|
||||
expect(deps.lensServices.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([
|
||||
{ query: { match_phrase: { src: 'test' } } },
|
||||
]);
|
||||
|
||||
expect(store.getState()).toEqual({
|
||||
lens: expect.objectContaining({
|
||||
persistedDoc: { ...defaultDoc, type: 'lens' },
|
||||
query: 'kuery',
|
||||
isLoading: false,
|
||||
activeDatasourceId: 'testDatasource',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('does not load documents on sequential renders unless the id changes', async () => {
|
||||
const { store, deps } = makeLensStore({ preloadedState });
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1);
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(
|
||||
loadInitial({
|
||||
...defaultProps,
|
||||
initialInput: { savedObjectId: '5678' } as unknown as LensEmbeddableInput,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('handles document load errors', async () => {
|
||||
const { store, deps } = makeLensStore({ preloadedState });
|
||||
|
||||
deps.lensServices.attributeService.unwrapAttributes = jest
|
||||
.fn()
|
||||
.mockRejectedValue('failed to load');
|
||||
const redirectCallback = jest.fn();
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial({ ...defaultProps, redirectCallback }));
|
||||
});
|
||||
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
expect(deps.lensServices.notifications.toasts.addDanger).toHaveBeenCalled();
|
||||
expect(redirectCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('redirects if saved object is an aliasMatch', async () => {
|
||||
const { store, deps } = makeLensStore({ preloadedState });
|
||||
deps.lensServices.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({
|
||||
...defaultDoc,
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'aliasMatch',
|
||||
aliasTargetId: 'id2',
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
|
||||
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
|
||||
savedObjectId: defaultSavedObjectId,
|
||||
});
|
||||
expect(deps.lensServices.spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith(
|
||||
'#/edit/id2?search',
|
||||
'Lens visualization'
|
||||
);
|
||||
});
|
||||
|
||||
it('adds to the recently accessed list on load', async () => {
|
||||
const { store, deps } = makeLensStore({ preloadedState });
|
||||
await act(async () => {
|
||||
await store.dispatch(loadInitial(defaultProps));
|
||||
});
|
||||
|
||||
expect(deps.lensServices.chrome.recentlyAccessed.add).toHaveBeenCalledWith(
|
||||
'/app/lens#/edit/1234',
|
||||
'An extremely cool default document!',
|
||||
'1234'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,117 +14,12 @@
|
|||
|
||||
import { timeRangeMiddleware } from './time_range_middleware';
|
||||
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { DataPublicPluginStart, esFilters } from '../../../../../src/plugins/data/public';
|
||||
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
import moment from 'moment';
|
||||
import { initialState } from './lens_slice';
|
||||
import { LensAppState } from './types';
|
||||
import { PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
const sessionIdSubject = new Subject<string>();
|
||||
|
||||
function createMockSearchService() {
|
||||
let sessionIdCounter = 1;
|
||||
return {
|
||||
session: {
|
||||
start: jest.fn(() => `sessionId-${sessionIdCounter++}`),
|
||||
clear: jest.fn(),
|
||||
getSessionId: jest.fn(() => `sessionId-${sessionIdCounter}`),
|
||||
getSession$: jest.fn(() => sessionIdSubject.asObservable()),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createMockFilterManager() {
|
||||
const unsubscribe = jest.fn();
|
||||
|
||||
let subscriber: () => void;
|
||||
let filters: unknown = [];
|
||||
|
||||
return {
|
||||
getUpdates$: () => ({
|
||||
subscribe: ({ next }: { next: () => void }) => {
|
||||
subscriber = next;
|
||||
return unsubscribe;
|
||||
},
|
||||
}),
|
||||
setFilters: jest.fn((newFilters: unknown[]) => {
|
||||
filters = newFilters;
|
||||
if (subscriber) subscriber();
|
||||
}),
|
||||
setAppFilters: jest.fn((newFilters: unknown[]) => {
|
||||
filters = newFilters;
|
||||
if (subscriber) subscriber();
|
||||
}),
|
||||
getFilters: () => filters,
|
||||
getGlobalFilters: () => {
|
||||
// @ts-ignore
|
||||
return filters.filter(esFilters.isFilterPinned);
|
||||
},
|
||||
removeAll: () => {
|
||||
filters = [];
|
||||
subscriber();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createMockQueryString() {
|
||||
return {
|
||||
getQuery: jest.fn(() => ({ query: '', language: 'kuery' })),
|
||||
setQuery: jest.fn(),
|
||||
getDefaultQuery: jest.fn(() => ({ query: '', language: 'kuery' })),
|
||||
};
|
||||
}
|
||||
|
||||
function createMockTimefilter() {
|
||||
const unsubscribe = jest.fn();
|
||||
|
||||
let timeFilter = { from: 'now-7d', to: 'now' };
|
||||
let subscriber: () => void;
|
||||
return {
|
||||
getTime: jest.fn(() => timeFilter),
|
||||
setTime: jest.fn((newTimeFilter) => {
|
||||
timeFilter = newTimeFilter;
|
||||
if (subscriber) {
|
||||
subscriber();
|
||||
}
|
||||
}),
|
||||
getTimeUpdate$: () => ({
|
||||
subscribe: ({ next }: { next: () => void }) => {
|
||||
subscriber = next;
|
||||
return unsubscribe;
|
||||
},
|
||||
}),
|
||||
calculateBounds: jest.fn(() => ({
|
||||
min: moment('2021-01-10T04:00:00.000Z'),
|
||||
max: moment('2021-01-10T08:00:00.000Z'),
|
||||
})),
|
||||
getBounds: jest.fn(() => timeFilter),
|
||||
getRefreshInterval: () => {},
|
||||
getRefreshIntervalDefaults: () => {},
|
||||
getAutoRefreshFetch$: () => new Observable(),
|
||||
};
|
||||
}
|
||||
|
||||
function makeDefaultData(): jest.Mocked<DataPublicPluginStart> {
|
||||
return {
|
||||
query: {
|
||||
filterManager: createMockFilterManager(),
|
||||
timefilter: {
|
||||
timefilter: createMockTimefilter(),
|
||||
},
|
||||
queryString: createMockQueryString(),
|
||||
state$: new Observable(),
|
||||
},
|
||||
indexPatterns: {
|
||||
get: jest.fn().mockImplementation((id) => Promise.resolve({ id, isTimeBased: () => true })),
|
||||
},
|
||||
search: createMockSearchService(),
|
||||
nowProvider: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
} as unknown as DataPublicPluginStart;
|
||||
}
|
||||
import { mockDataPlugin } from '../mocks';
|
||||
|
||||
const createMiddleware = (data: DataPublicPluginStart) => {
|
||||
const middleware = timeRangeMiddleware(data);
|
||||
|
@ -142,7 +37,7 @@ const createMiddleware = (data: DataPublicPluginStart) => {
|
|||
describe('timeRangeMiddleware', () => {
|
||||
describe('time update', () => {
|
||||
it('does update the searchSessionId when the state changes and too much time passed', () => {
|
||||
const data = makeDefaultData();
|
||||
const data = mockDataPlugin();
|
||||
(data.nowProvider.get as jest.Mock).mockReturnValue(new Date(Date.now() - 30000));
|
||||
(data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({
|
||||
from: 'now-2m',
|
||||
|
@ -176,7 +71,7 @@ describe('timeRangeMiddleware', () => {
|
|||
expect(next).toHaveBeenCalledWith(action);
|
||||
});
|
||||
it('does not update the searchSessionId when the state changes and too little time has passed', () => {
|
||||
const data = makeDefaultData();
|
||||
const data = mockDataPlugin();
|
||||
// time range is 100,000ms ago to 300ms ago (that's a lag of .3 percent, not enough to trigger a session update)
|
||||
(data.nowProvider.get as jest.Mock).mockReturnValue(new Date(Date.now() - 300));
|
||||
(data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({
|
||||
|
@ -202,7 +97,7 @@ describe('timeRangeMiddleware', () => {
|
|||
expect(next).toHaveBeenCalledWith(action);
|
||||
});
|
||||
it('does not trigger another update when the update already contains searchSessionId', () => {
|
||||
const data = makeDefaultData();
|
||||
const data = mockDataPlugin();
|
||||
(data.nowProvider.get as jest.Mock).mockReturnValue(new Date(Date.now() - 30000));
|
||||
(data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({
|
||||
from: 'now-2m',
|
||||
|
|
Loading…
Reference in a new issue