Alerting: Re-render panel's tabs on variables change (#49893)

This commit is contained in:
Konrad Lalik 2022-06-01 13:03:33 +02:00 committed by GitHub
parent 39096208ed
commit 63303eb4ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 6 deletions

View File

@ -1,4 +1,4 @@
import { render, act } from '@testing-library/react'; import { render, act, waitFor } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { Router } from 'react-router-dom'; 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 { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; 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 { PrometheusDatasource } from 'app/plugins/datasource/prometheus/datasource';
import { PromOptions } from 'app/plugins/datasource/prometheus/types'; import { PromOptions } from 'app/plugins/datasource/prometheus/types';
import { configureStore } from 'app/store/configureStore'; import { configureStore } from 'app/store/configureStore';
@ -28,6 +30,7 @@ import {
import { getAllDataSources } from './utils/config'; import { getAllDataSources } from './utils/config';
import { Annotation } from './utils/constants'; import { Annotation } from './utils/constants';
import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
import * as ruleFormUtils from './utils/rule-form';
jest.mock('./api/prometheus'); jest.mock('./api/prometheus');
jest.mock('./api/ruler'); jest.mock('./api/ruler');
@ -56,8 +59,12 @@ const mocks = {
}, },
}; };
const renderAlertTabContent = (dashboard: DashboardModel, panel: PanelModel) => { const renderAlertTabContent = (
const store = configureStore(); dashboard: DashboardModel,
panel: PanelModel,
initialStore?: ReturnType<typeof configureStore>
) => {
const store = initialStore ?? configureStore();
return act(async () => { return act(async () => {
render( render(
@ -349,4 +356,24 @@ describe('PanelAlertTabContent', () => {
panelId: panel.id, 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));
});
}); });

View File

@ -1,10 +1,12 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { useAsync } from 'react-use'; import { useAsync } from 'react-use';
import { urlUtil } from '@grafana/data'; 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 { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { StoreState } from 'app/types';
import { panelToRuleFormValues } from '../../utils/rule-form'; import { panelToRuleFormValues } from '../../utils/rule-form';
@ -15,8 +17,18 @@ interface Props {
} }
export const NewRuleFromPanelButton: FC<Props> = ({ dashboard, panel, className }) => { export const NewRuleFromPanelButton: FC<Props> = ({ 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 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) { if (loading) {
return <Button disabled={true}>Create alert rule from this panel</Button>; return <Button disabled={true}>Create alert rule from this panel</Button>;
} }

View File

@ -31,7 +31,7 @@ export const PanelEditorTabs: FC<PanelEditorTabsProps> = React.memo(({ panel, da
eventSubs.add(panel.events.subscribe(PanelQueriesChangedEvent, forceUpdate)); eventSubs.add(panel.events.subscribe(PanelQueriesChangedEvent, forceUpdate));
eventSubs.add(panel.events.subscribe(PanelTransformationsChangedEvent, forceUpdate)); eventSubs.add(panel.events.subscribe(PanelTransformationsChangedEvent, forceUpdate));
return () => eventSubs.unsubscribe(); return () => eventSubs.unsubscribe();
}, [panel, forceUpdate]); }, [panel, dashboard, forceUpdate]);
const activeTab = tabs.find((item) => item.active)!; const activeTab = tabs.find((item) => item.active)!;