diff --git a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx index 88bb90929fa..cd0170429f1 100644 --- a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx +++ b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx @@ -1,4 +1,4 @@ -import { render, act } from '@testing-library/react'; +import { render, act, waitFor } from '@testing-library/react'; import React from 'react'; import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; @@ -9,6 +9,8 @@ import { locationService, setDataSourceSrv } from '@grafana/runtime'; import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; +import { toggleOption } from 'app/features/variables/pickers/OptionsPicker/reducer'; +import { toKeyedAction } from 'app/features/variables/state/keyedVariablesReducer'; import { PrometheusDatasource } from 'app/plugins/datasource/prometheus/datasource'; import { PromOptions } from 'app/plugins/datasource/prometheus/types'; import { configureStore } from 'app/store/configureStore'; @@ -28,6 +30,7 @@ import { import { getAllDataSources } from './utils/config'; import { Annotation } from './utils/constants'; import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; +import * as ruleFormUtils from './utils/rule-form'; jest.mock('./api/prometheus'); jest.mock('./api/ruler'); @@ -56,8 +59,12 @@ const mocks = { }, }; -const renderAlertTabContent = (dashboard: DashboardModel, panel: PanelModel) => { - const store = configureStore(); +const renderAlertTabContent = ( + dashboard: DashboardModel, + panel: PanelModel, + initialStore?: ReturnType +) => { + const store = initialStore ?? configureStore(); return act(async () => { render( @@ -349,4 +356,24 @@ describe('PanelAlertTabContent', () => { panelId: panel.id, }); }); + + it('Update NewRuleFromPanel button url when template changes', async () => { + const panelToRuleValuesSpy = jest.spyOn(ruleFormUtils, 'panelToRuleFormValues'); + + const store = configureStore(); + await renderAlertTabContent(dashboard, panel, store); + + store.dispatch( + toKeyedAction( + 'optionKey', + toggleOption({ + option: { value: 'optionValue', selected: true, text: 'Option' }, + clearOthers: false, + forceSelect: false, + }) + ) + ); + + await waitFor(() => expect(panelToRuleValuesSpy).toHaveBeenCalledTimes(2)); + }); }); diff --git a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx index 6b500e8ab46..608f830987d 100644 --- a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx +++ b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx @@ -1,10 +1,12 @@ import React, { FC } from 'react'; +import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { useAsync } from 'react-use'; import { urlUtil } from '@grafana/data'; -import { Alert, LinkButton, Button } from '@grafana/ui'; +import { Alert, Button, LinkButton } from '@grafana/ui'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; +import { StoreState } from 'app/types'; import { panelToRuleFormValues } from '../../utils/rule-form'; @@ -15,8 +17,18 @@ interface Props { } export const NewRuleFromPanelButton: FC = ({ dashboard, panel, className }) => { - const { loading, value: formValues } = useAsync(() => panelToRuleFormValues(panel, dashboard), [panel, dashboard]); + const templating = useSelector((state: StoreState) => { + return state.templating; + }); + const location = useLocation(); + + const { loading, value: formValues } = useAsync( + () => panelToRuleFormValues(panel, dashboard), + // Templating variables are required to update formValues on each variable's change. It's used implicitly by the templating engine + [panel, dashboard, templating] + ); + if (loading) { return ; } diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx index e9c4ed1a4dc..3d8be41e1c4 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx @@ -31,7 +31,7 @@ export const PanelEditorTabs: FC = React.memo(({ panel, da eventSubs.add(panel.events.subscribe(PanelQueriesChangedEvent, forceUpdate)); eventSubs.add(panel.events.subscribe(PanelTransformationsChangedEvent, forceUpdate)); return () => eventSubs.unsubscribe(); - }, [panel, forceUpdate]); + }, [panel, dashboard, forceUpdate]); const activeTab = tabs.find((item) => item.active)!;