diff --git a/src/plugins/input_control_vis/public/vis_controller.tsx b/src/plugins/input_control_vis/public/vis_controller.tsx index e4310960851ca..faea98b792291 100644 --- a/src/plugins/input_control_vis/public/vis_controller.tsx +++ b/src/plugins/input_control_vis/public/vis_controller.tsx @@ -18,8 +18,10 @@ */ import React from 'react'; +import { isEqual } from 'lodash'; import { render, unmountComponentAtNode } from 'react-dom'; +import { Subscription } from 'rxjs'; import { I18nStart } from 'kibana/public'; import { InputControlVis } from './components/vis/input_control_vis'; import { getControlFactory } from './control/control_factory'; @@ -34,11 +36,13 @@ import { VisParams, Vis } from '../../visualizations/public'; export const createInputControlVisController = (deps: InputControlVisDependencies) => { return class InputControlVisController { private I18nContext?: I18nStart['Context']; + private isLoaded = false; controls: Array; queryBarUpdateHandler: () => void; filterManager: FilterManager; updateSubsciption: any; + timeFilterSubscription: Subscription; visParams?: VisParams; constructor(public el: Element, public vis: Vis) { @@ -50,19 +54,32 @@ export const createInputControlVisController = (deps: InputControlVisDependencie this.updateSubsciption = this.filterManager .getUpdates$() .subscribe(this.queryBarUpdateHandler); + this.timeFilterSubscription = deps.data.query.timefilter.timefilter + .getTimeUpdate$() + .subscribe(() => { + if (this.visParams?.useTimeFilter) { + this.isLoaded = false; + } + }); } async render(visData: any, visParams: VisParams) { - this.visParams = visParams; - this.controls = []; - this.controls = await this.initControls(); - const [{ i18n }] = await deps.core.getStartServices(); - this.I18nContext = i18n.Context; + if (!this.I18nContext) { + const [{ i18n }] = await deps.core.getStartServices(); + this.I18nContext = i18n.Context; + } + if (!this.isLoaded || !isEqual(visParams, this.visParams)) { + this.visParams = visParams; + this.controls = []; + this.controls = await this.initControls(); + this.isLoaded = true; + } this.drawVis(); } destroy() { this.updateSubsciption.unsubscribe(); + this.timeFilterSubscription.unsubscribe(); unmountComponentAtNode(this.el); this.controls.forEach((control) => control.destroy()); } diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index 16397d340d951..80d433873d2a0 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -16,3 +16,7 @@ export function getBasePath() { export function getEditPath(id: string) { return `#/edit/${encodeURIComponent(id)}`; } + +export function getFullPath(id: string) { + return `/app/${PLUGIN_ID}#/edit/${encodeURIComponent(id)}`; +} diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 2b979f064b8eb..b70e0a4fff02e 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -300,6 +300,29 @@ describe('Lens App', () => { ]); }); + it('adds to the recently viewed list on load', async () => { + const defaultArgs = makeDefaultArgs(); + instance = mount(); + + (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ + id: '1234', + title: 'Daaaaaaadaumching!', + state: { + query: 'fake query', + filters: [], + }, + references: [], + }); + await act(async () => { + instance.setProps({ docId: '1234' }); + }); + expect(defaultArgs.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + '/app/lens#/edit/1234', + 'Daaaaaaadaumching!', + '1234' + ); + }); + it('sets originatingApp breadcrumb when the document title changes', async () => { const defaultArgs = makeDefaultArgs(); defaultArgs.originatingApp = 'ultraCoolDashboard'; @@ -591,6 +614,19 @@ describe('Lens App', () => { expect(args.docStorage.load).not.toHaveBeenCalled(); }); + it('adds to the recently viewed list on save', async () => { + const { args } = await save({ + initialDocId: undefined, + newCopyOnSave: false, + newTitle: 'hello there', + }); + expect(args.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + '/app/lens#/edit/aaa', + 'hello there', + 'aaa' + ); + }); + it('saves the latest doc as a copy', async () => { const { args, instance: inst } = await save({ initialDocId: '1234', diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 021ca8b182b2b..3f1f6d0e5509d 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -39,6 +39,7 @@ import { IndexPatternsContract, SavedQuery, } from '../../../../../src/plugins/data/public'; +import { getFullPath } from '../../common'; interface State { indicateNoData: boolean; @@ -271,6 +272,7 @@ export function App({ docStorage .load(docId) .then((doc) => { + core.chrome.recentlyAccessed.add(getFullPath(docId), doc.title, docId); getAllIndexPatterns( _.uniq( doc.references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id) @@ -365,6 +367,7 @@ export function App({ docStorage .save(doc) .then(({ id }) => { + core.chrome.recentlyAccessed.add(getFullPath(id), doc.title, id); // Prevents unnecessary network request and disables save button const newDoc = { ...doc, id }; const currentOriginatingApp = state.originatingApp;