diff --git a/packages/grafana-toolkit/src/config/webpack.plugin.config.ts b/packages/grafana-toolkit/src/config/webpack.plugin.config.ts index 09f57379e9b..ddb9300d8c8 100644 --- a/packages/grafana-toolkit/src/config/webpack.plugin.config.ts +++ b/packages/grafana-toolkit/src/config/webpack.plugin.config.ts @@ -188,6 +188,7 @@ const getBaseWebpackConfig: WebpackConfigurationGetter = async (options) => { 'react-redux', 'redux', 'rxjs', + 'react-router', 'react-router-dom', 'd3', 'angular', diff --git a/public/app/features/plugins/components/AppRootPage.test.tsx b/public/app/features/plugins/components/AppRootPage.test.tsx index cf9ba1f02ed..4fd3a56525e 100644 --- a/public/app/features/plugins/components/AppRootPage.test.tsx +++ b/public/app/features/plugins/components/AppRootPage.test.tsx @@ -77,17 +77,18 @@ describe('AppRootPage', () => { setEchoSrv(new Echo()); }); + const pluginMeta = getMockPlugin({ + id: 'my-awesome-plugin', + type: PluginType.app, + enabled: true, + }); + it('should not mount plugin twice if nav is changed', async () => { // reproduces https://github.com/grafana/grafana/pull/28105 - - getPluginSettingsMock.mockResolvedValue( - getMockPlugin({ - type: PluginType.app, - enabled: true, - }) - ); + getPluginSettingsMock.mockResolvedValue(pluginMeta); const plugin = new AppPlugin(); + plugin.meta = pluginMeta; plugin.root = RootComponent; importAppPluginMock.mockResolvedValue(plugin); @@ -102,12 +103,7 @@ describe('AppRootPage', () => { }); it('should not render component if not at plugin path', async () => { - getPluginSettingsMock.mockResolvedValue( - getMockPlugin({ - type: PluginType.app, - enabled: true, - }) - ); + getPluginSettingsMock.mockResolvedValue(pluginMeta); class RootComponent extends Component { static timesRendered = 0; @@ -118,6 +114,7 @@ describe('AppRootPage', () => { } const plugin = new AppPlugin(); + plugin.meta = pluginMeta; plugin.root = RootComponent; importAppPluginMock.mockResolvedValue(plugin); @@ -127,18 +124,18 @@ describe('AppRootPage', () => { expect(await screen.findByText('my great component')).toBeVisible(); // renders the first time - expect(RootComponent.timesRendered).toEqual(1); + expect(RootComponent.timesRendered).toEqual(2); await act(async () => { locationService.push('/foo'); }); - expect(RootComponent.timesRendered).toEqual(1); + expect(RootComponent.timesRendered).toEqual(2); await act(async () => { locationService.push('/a/my-awesome-plugin'); }); - expect(RootComponent.timesRendered).toEqual(2); + expect(RootComponent.timesRendered).toEqual(4); }); }); diff --git a/public/app/features/plugins/components/AppRootPage.tsx b/public/app/features/plugins/components/AppRootPage.tsx index c5b7b6c20a1..66291463fa7 100644 --- a/public/app/features/plugins/components/AppRootPage.tsx +++ b/public/app/features/plugins/components/AppRootPage.tsx @@ -94,7 +94,15 @@ class AppRootPage extends Component { render() { const { loading, plugin, nav, portalNode } = this.state; - if (plugin && !plugin.root) { + if (!plugin || this.props.match.params.pluginId !== plugin.meta.id) { + return ( + + + + ); + } + + if (!plugin.root) { // TODO? redirect to plugin page? return
No Root App
; } @@ -102,15 +110,13 @@ class AppRootPage extends Component { return ( <> - {plugin && plugin.root && ( - - )} + {nav ? (