diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index 99926c646da22..374e3270b3d45 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -38,7 +38,10 @@ jest const { TopNavMenu } = npStart.plugins.navigation.ui; -const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); +const waitForPromises = async () => + act(async () => { + await new Promise(resolve => setTimeout(resolve)); + }); function createMockFrame(): jest.Mocked { return { @@ -220,6 +223,7 @@ describe('Lens App', () => { }); instance.setProps({ docId: '1234' }); + await waitForPromises(); expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ @@ -373,8 +377,10 @@ describe('Lens App', () => { async function save({ initialDocId, addToDashboardMode, + lastKnownDoc = { expression: 'kibana 3' }, ...saveProps }: SaveProps & { + lastKnownDoc?: object; initialDocId?: string; addToDashboardMode?: boolean; }) { @@ -392,6 +398,7 @@ describe('Lens App', () => { state: { query: 'fake query', datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] }, + filters: [], }, }); (args.docStorage.save as jest.Mock).mockImplementation(async ({ id }) => ({ @@ -410,10 +417,12 @@ describe('Lens App', () => { } const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ - filterableIndexPatterns: [], - doc: ({ id: initialDocId, expression: 'kibana 3' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [], + doc: { id: initialDocId, ...lastKnownDoc } as Document, + }) + ); instance.update(); @@ -441,10 +450,12 @@ describe('Lens App', () => { expect(getButton(instance).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ - filterableIndexPatterns: [], - doc: ({ id: 'will save this', expression: 'valid expression' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: 'will save this', expression: 'valid expression' } as unknown) as Document, + }) + ); instance.update(); expect(getButton(instance).disableButton).toEqual(true); }); @@ -482,10 +493,12 @@ describe('Lens App', () => { expect(getButton(instance).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ - filterableIndexPatterns: [], - doc: ({ id: 'will save this', expression: 'valid expression' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: 'will save this', expression: 'valid expression' } as unknown) as Document, + }) + ); instance.update(); expect(getButton(instance).disableButton).toEqual(false); @@ -559,10 +572,12 @@ describe('Lens App', () => { const instance = mount(); const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ - filterableIndexPatterns: [], - doc: ({ id: undefined, expression: 'new expression' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [], + doc: ({ id: undefined, expression: 'new expression' } as unknown) as Document, + }) + ); instance.update(); @@ -593,6 +608,38 @@ describe('Lens App', () => { expect(args.redirectTo).toHaveBeenCalledWith('aaa'); }); + + it('saves app filters and does not save pinned filters', async () => { + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; + + const unpinned = esFilters.buildExistsFilter(field, indexPattern); + const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); + FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); + await waitForPromises(); + + const { args } = await save({ + initialDocId: '1234', + newCopyOnSave: false, + newTitle: 'hello there2', + lastKnownDoc: { + expression: 'kibana 3', + state: { + filters: [pinned, unpinned], + }, + }, + }); + + expect(args.docStorage.save).toHaveBeenCalledWith({ + id: '1234', + title: 'hello there2', + expression: 'kibana 3', + state: { + filters: [unpinned], + }, + }); + }); }); }); @@ -658,10 +705,12 @@ describe('Lens App', () => { ); const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ - filterableIndexPatterns: [{ id: '1', title: 'newIndex' }], - doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [{ id: '1', title: 'newIndex' }], + doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, + }) + ); await waitForPromises(); instance.update(); @@ -674,12 +723,15 @@ describe('Lens App', () => { ); // Do it again to verify that the dirty checking is done right - onChange({ - filterableIndexPatterns: [{ id: '2', title: 'second index' }], - doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, - }); + act(() => + onChange({ + filterableIndexPatterns: [{ id: '2', title: 'second index' }], + doc: ({ id: undefined, expression: 'valid expression' } as unknown) as Document, + }) + ); await waitForPromises(); + instance.update(); expect(TopNavMenu).toHaveBeenLastCalledWith( @@ -689,17 +741,18 @@ describe('Lens App', () => { {} ); }); - it('updates the editor frame when the user changes query or time in the search bar', () => { const args = defaultArgs; args.editorFrame = frame; const instance = mount(); - instance.find(TopNavMenu).prop('onQuerySubmit')!({ - dateRange: { from: 'now-14d', to: 'now-7d' }, - query: { query: 'new', language: 'lucene' }, - }); + act(() => + instance.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: 'new', language: 'lucene' }, + }) + ); instance.update(); @@ -728,7 +781,9 @@ describe('Lens App', () => { const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; - args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]); + act(() => + args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]) + ); instance.update(); @@ -852,10 +907,12 @@ describe('Lens App', () => { const instance = mount(); - instance.find(TopNavMenu).prop('onQuerySubmit')!({ - dateRange: { from: 'now-14d', to: 'now-7d' }, - query: { query: 'new', language: 'lucene' }, - }); + act(() => + instance.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: 'new', language: 'lucene' }, + }) + ); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; @@ -865,10 +922,10 @@ describe('Lens App', () => { const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); - args.data.query.filterManager.setFilters([pinned, unpinned]); + act(() => args.data.query.filterManager.setFilters([pinned, unpinned])); instance.update(); - instance.find(TopNavMenu).prop('onClearSavedQuery')!(); + act(() => instance.find(TopNavMenu).prop('onClearSavedQuery')!()); instance.update(); expect(frame.mount).toHaveBeenLastCalledWith( diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index c901d4c0c1497..a212cb0a1a879 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -20,6 +20,7 @@ import { EditorFrameInstance } from '../types'; import { NativeRenderer } from '../native_renderer'; import { trackUiEvent } from '../lens_ui_telemetry'; import { + esFilters, Filter, IndexPattern as IndexPatternInstance, IndexPatternsContract, @@ -320,8 +321,22 @@ export function App({ {lastKnownDoc && state.isSaveModalVisible && ( { + const [pinnedFilters, appFilters] = _.partition( + lastKnownDoc.state?.filters, + esFilters.isFilterPinned + ); + const lastDocWithoutPinned = pinnedFilters?.length + ? { + ...lastKnownDoc, + state: { + ...lastKnownDoc.state, + filters: appFilters, + }, + } + : lastKnownDoc; + const doc = { - ...lastKnownDoc, + ...lastDocWithoutPinned, id: props.newCopyOnSave ? undefined : lastKnownDoc.id, title: props.newTitle, };