diff --git a/public/app/features/dashboard/state/actions.ts b/public/app/features/dashboard/state/actions.ts index 8fcc935604e..d4f87ea1876 100644 --- a/public/app/features/dashboard/state/actions.ts +++ b/public/app/features/dashboard/state/actions.ts @@ -16,6 +16,7 @@ import { PermissionLevel, ThunkResult, } from 'app/types'; +import { DataQuery } from '@grafana/data'; export const loadDashboardPermissions = createAction('dashboard/loadDashboardPermissions'); @@ -53,6 +54,21 @@ function toUpdateItem(item: DashboardAcl): DashboardAclUpdateDTO { }; } +interface SetDashboardQueriesToUpdatePayload { + panelId: number; + queries: DataQuery[]; +} + +export const clearDashboardQueriesToUpdate = createAction('dashboard/clearDashboardQueriesToUpdate'); +export const setDashboardQueriesToUpdate = createAction( + 'dashboard/setDashboardQueriesToUpdate' +); +export const setDashboardQueriesToUpdateOnLoad = (panelId: number, queries: DataQuery[]): ThunkResult => { + return async dispatch => { + await dispatch(setDashboardQueriesToUpdate({ panelId, queries })); + }; +}; + export function updateDashboardPermission( dashboardId: number, itemToUpdate: DashboardAcl, diff --git a/public/app/features/dashboard/state/initDashboard.test.ts b/public/app/features/dashboard/state/initDashboard.test.ts index c09c2b9c5fb..1a6f31fb809 100644 --- a/public/app/features/dashboard/state/initDashboard.test.ts +++ b/public/app/features/dashboard/state/initDashboard.test.ts @@ -4,7 +4,6 @@ import { initDashboard, InitDashboardArgs } from './initDashboard'; import { DashboardRouteInfo } from 'app/types'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { dashboardInitCompleted, dashboardInitFetching, dashboardInitServices } from './actions'; -import { resetExploreAction } from 'app/features/explore/state/actionTypes'; import { updateLocation } from '../../../core/actions'; jest.mock('app/core/services/backend_srv'); @@ -109,6 +108,12 @@ function describeInitScenario(description: string, scenarioFn: ScenarioFn) { location: { query: {}, }, + dashboard: { + modifiedQueries: { + panelId: undefined, + queries: undefined, + }, + }, user: {}, explore: { left: { @@ -201,8 +206,6 @@ describeInitScenario('Initializing existing dashboard', ctx => { }, ]; - const expectedQueries = mockQueries.map(query => ({ refId: query.refId, expr: query.expr })); - ctx.setup(() => { ctx.storeState.user.orgId = 12; ctx.storeState.explore.left.originPanelId = 2; @@ -222,23 +225,9 @@ describeInitScenario('Initializing existing dashboard', ctx => { expect(ctx.actions[2].payload.query.orgId).toBe(12); }); - it('Should send resetExploreAction when coming from explore', () => { - expect(ctx.actions[3].type).toBe(resetExploreAction.type); - expect(ctx.actions[3].payload.force).toBe(true); - expect(ctx.dashboardSrv.setCurrent).lastCalledWith( - expect.objectContaining({ - panels: expect.arrayContaining([ - expect.objectContaining({ - targets: expectedQueries, - }), - ]), - }) - ); - }); - it('Should send action dashboardInitCompleted', () => { - expect(ctx.actions[4].type).toBe(dashboardInitCompleted.type); - expect(ctx.actions[4].payload.title).toBe('My cool dashboard'); + expect(ctx.actions[3].type).toBe(dashboardInitCompleted.type); + expect(ctx.actions[3].payload.title).toBe('My cool dashboard'); }); it('Should initialize services', () => { diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts index baa3342e27f..672d42d23fa 100644 --- a/public/app/features/dashboard/state/initDashboard.ts +++ b/public/app/features/dashboard/state/initDashboard.ts @@ -18,12 +18,12 @@ import { dashboardInitFailed, dashboardInitSlow, dashboardInitServices, + clearDashboardQueriesToUpdate, } from './actions'; // Types -import { DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResult, DashboardDTO, ExploreItemState } from 'app/types'; +import { DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResult, DashboardDTO } from 'app/types'; import { DashboardModel } from './DashboardModel'; -import { resetExploreAction } from 'app/features/explore/state/actionTypes'; import { DataQuery } from '@grafana/data'; export interface InitDashboardArgs { @@ -173,8 +173,8 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult { timeSrv.init(dashboard); annotationsSrv.init(dashboard); - const left = storeState.explore && storeState.explore.left; - dashboard.meta.fromExplore = !!(left && left.originPanelId); + const { panelId, queries } = storeState.dashboard.modifiedQueries; + dashboard.meta.fromExplore = !!(panelId && queries); // template values service needs to initialize completely before // the rest of the dashboard can load @@ -204,7 +204,7 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult { } if (dashboard.meta.fromExplore) { - updateQueriesWhenComingFromExplore(dispatch, dashboard, left); + updateQueriesWhenComingFromExplore(dispatch, dashboard, panelId, queries); } // legacy srv state @@ -245,24 +245,15 @@ function getNewDashboardModelData(urlFolderId?: string): any { function updateQueriesWhenComingFromExplore( dispatch: ThunkDispatch, dashboard: DashboardModel, - left: ExploreItemState + originPanelId: number, + queries: DataQuery[] ) { - // When returning to the origin panel from explore, if we're doing - // so with changes all the explore state is reset _except_ the queries - // and the origin panel ID. - const panelArrId = dashboard.panels.findIndex(panel => panel.id === left.originPanelId); + const panelArrId = dashboard.panels.findIndex(panel => panel.id === originPanelId); if (panelArrId > -1) { - dashboard.panels[panelArrId].targets = left.queries.map((query: DataQuery & { context?: string }) => { - delete query.context; - delete query.key; - return query; - }); + dashboard.panels[panelArrId].targets = queries; } - dashboard.startRefresh(); - - // Force-reset explore so that on subsequent dashboard loads we aren't - // taking the modified queries from explore again. - dispatch(resetExploreAction({ force: true })); + // Clear update state now that we're done + dispatch(clearDashboardQueriesToUpdate()); } diff --git a/public/app/features/dashboard/state/reducers.ts b/public/app/features/dashboard/state/reducers.ts index 813eb2f6a6a..78fd4127a1c 100644 --- a/public/app/features/dashboard/state/reducers.ts +++ b/public/app/features/dashboard/state/reducers.ts @@ -8,6 +8,8 @@ import { dashboardInitServices, dashboardInitSlow, loadDashboardPermissions, + setDashboardQueriesToUpdate, + clearDashboardQueriesToUpdate, } from './actions'; import { processAclItems } from 'app/core/utils/acl'; import { panelEditorReducer } from '../panel_editor/state/reducers'; @@ -18,6 +20,10 @@ export const initialState: DashboardState = { isInitSlow: false, model: null, permissions: [], + modifiedQueries: { + panelId: undefined, + queries: undefined, + }, }; // Redux Toolkit uses ImmerJs as part of their solution to ensure that state objects are not mutated. @@ -87,6 +93,28 @@ export const dashboardReducer = (state: DashboardState = initialState, action: A }; } + if (setDashboardQueriesToUpdate.match(action)) { + const { panelId, queries } = action.payload; + + return { + ...state, + modifiedQueries: { + panelId, + queries, + }, + }; + } + + if (clearDashboardQueriesToUpdate.match(action)) { + return { + ...state, + modifiedQueries: { + panelId: undefined, + queries: undefined, + }, + }; + } + return state; }; diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 522d36aa03c..f055bf7a7b0 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -20,7 +20,6 @@ import { syncTimes, changeRefreshInterval, changeMode, - clearOrigin, } from './state/actions'; import { updateLocation } from 'app/core/actions'; import { getTimeZone } from '../profile/state/selectors'; @@ -32,6 +31,7 @@ import { ResponsiveButton } from './ResponsiveButton'; import { RunButton } from './RunButton'; import { LiveTailControls } from './useLiveTailControls'; import { getExploreDatasources } from './state/selectors'; +import { setDashboardQueriesToUpdateOnLoad } from '../dashboard/state/actions'; const getStyles = memoizeOne(() => { return { @@ -78,8 +78,8 @@ interface DispatchProps { syncTimes: typeof syncTimes; changeRefreshInterval: typeof changeRefreshInterval; changeMode: typeof changeMode; - clearOrigin: typeof clearOrigin; updateLocation: typeof updateLocation; + setDashboardQueriesToUpdateOnLoad: typeof setDashboardQueriesToUpdateOnLoad; } type Props = StateProps & DispatchProps & OwnProps; @@ -113,14 +113,14 @@ export class UnConnectedExploreToolbar extends PureComponent { }; returnToPanel = async ({ withChanges = false } = {}) => { - const { originPanelId } = this.props; + const { originPanelId, queries } = this.props; const dashboardSrv = getDashboardSrv(); const dash = dashboardSrv.getCurrent(); const titleSlug = kbn.slugifyForUrl(dash.title); - if (!withChanges) { - this.props.clearOrigin(); + if (withChanges) { + this.props.setDashboardQueriesToUpdateOnLoad(originPanelId, this.cleanQueries(queries)); } const dashViewOptions = { @@ -137,6 +137,15 @@ export class UnConnectedExploreToolbar extends PureComponent { }); }; + // Remove explore specific parameters from queries + private cleanQueries(queries: DataQuery[]) { + return queries.map((query: DataQuery & { context?: string }) => { + delete query.context; + delete query.key; + return query; + }); + } + getSelectedDatasource = () => { const { datasourceName } = this.props; const exploreDatasources = getExploreDatasources(); @@ -381,7 +390,7 @@ const mapDispatchToProps: DispatchProps = { split: splitOpen, syncTimes, changeMode: changeMode, - clearOrigin, + setDashboardQueriesToUpdateOnLoad, }; export const ExploreToolbar = hot(module)(connect(mapStateToProps, mapDispatchToProps)(UnConnectedExploreToolbar)); diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts index eab71357c8d..adecd2786d9 100644 --- a/public/app/features/explore/state/actionTypes.ts +++ b/public/app/features/explore/state/actionTypes.ts @@ -49,10 +49,6 @@ export interface ClearQueriesPayload { exploreId: ExploreId; } -export interface ClearOriginPayload { - exploreId: ExploreId; -} - export interface HighlightLogsExpressionPayload { exploreId: ExploreId; expressions: string[]; @@ -220,11 +216,6 @@ export const changeRefreshIntervalAction = createAction('explore/clearQueries'); -/** - * Clear origin panel id. - */ -export const clearOriginAction = createAction('explore/clearOrigin'); - /** * Highlight expressions in the log results */ diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 2120cd78ae5..8a94715c10e 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -50,7 +50,6 @@ import { ChangeRefreshIntervalPayload, changeSizeAction, ChangeSizePayload, - clearOriginAction, clearQueriesAction, historyUpdatedAction, initializeExploreAction, @@ -232,12 +231,6 @@ export function clearQueries(exploreId: ExploreId): ThunkResult { }; } -export function clearOrigin(): ThunkResult { - return dispatch => { - dispatch(clearOriginAction({ exploreId: ExploreId.left })); - }; -} - /** * Loads all explore data sources and sets the chosen datasource. * If there are no datasources a missing datasource action is dispatched. diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 1a02657f37f..def6f66cab5 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -34,7 +34,6 @@ import { changeRangeAction, changeRefreshIntervalAction, changeSizeAction, - clearOriginAction, clearQueriesAction, highlightLogsExpressionAction, historyUpdatedAction, @@ -234,13 +233,6 @@ export const itemReducer = (state: ExploreItemState = makeExploreItemState(), ac }; } - if (clearOriginAction.match(action)) { - return { - ...state, - originPanelId: undefined, - }; - } - if (highlightLogsExpressionAction.match(action)) { const { expressions } = action.payload; return { ...state, logsHighlighterExpressions: expressions }; diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index d5070321bda..b8bb74d4ef2 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -1,4 +1,5 @@ import { DashboardAcl } from './acl'; +import { DataQuery } from '@grafana/data'; export interface MutableDashboard { title: string; @@ -71,4 +72,8 @@ export interface DashboardState { isInitSlow: boolean; initError?: DashboardInitError; permissions: DashboardAcl[] | null; + modifiedQueries?: { + panelId: number; + queries: DataQuery[]; + }; }