diff --git a/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.test.tsx b/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.test.tsx index 74ee6e929e5..be65618a497 100644 --- a/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.test.tsx +++ b/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.test.tsx @@ -69,4 +69,24 @@ describe('InstallControlsButton', () => { }); } ); + + it("should allow to uninstall a plugin even if it's unpublished", () => { + render( + + + + ); + const el = screen.getByRole('button'); + expect(el).toHaveTextContent(/uninstall/i); + expect(el).toBeVisible(); + }); + + it('should not render install or upgrade buttons if the plugin is unpublished', () => { + render( + + + + ); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); }); diff --git a/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx b/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx index 86778817a8d..f7ef5e6ede7 100644 --- a/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx +++ b/public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx @@ -17,6 +17,7 @@ type InstallControlsButtonProps = { plugin: CatalogPlugin; pluginStatus: PluginStatus; latestCompatibleVersion?: Version; + hasInstallWarning?: boolean; setNeedReload?: (needReload: boolean) => void; }; @@ -24,6 +25,7 @@ export function InstallControlsButton({ plugin, pluginStatus, latestCompatibleVersion, + hasInstallWarning, setNeedReload, }: InstallControlsButtonProps) { const dispatch = useDispatch(); @@ -110,6 +112,11 @@ export function InstallControlsButton({ ); } + if (!plugin.isPublished || hasInstallWarning) { + // Cannot be updated or installed + return null; + } + if (pluginStatus === PluginStatus.UPDATE) { return ( diff --git a/public/app/features/plugins/admin/components/PluginActions.tsx b/public/app/features/plugins/admin/components/PluginActions.tsx index ae46d10a774..4ee30e3f0b6 100644 --- a/public/app/features/plugins/admin/components/PluginActions.tsx +++ b/public/app/features/plugins/admin/components/PluginActions.tsx @@ -33,15 +33,14 @@ export const PluginActions = ({ plugin }: Props) => { ? PluginStatus.UPDATE : PluginStatus.UNINSTALL : PluginStatus.INSTALL; - const isInstallControlsDisabled = - plugin.isCore || plugin.isDisabled || !isInstallControlsEnabled() || hasInstallWarning; + const isInstallControlsDisabled = plugin.isCore || plugin.isDisabled || !isInstallControlsEnabled(); return ( {!isInstallControlsDisabled && ( <> - {isExternallyManaged ? ( + {isExternallyManaged && !hasInstallWarning ? ( { latestCompatibleVersion={latestCompatibleVersion} pluginStatus={pluginStatus} setNeedReload={setNeedReload} + hasInstallWarning={hasInstallWarning} /> )} diff --git a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx index ca67562c169..4f7f12e0236 100644 --- a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx +++ b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx @@ -325,12 +325,13 @@ describe('Plugin details page', () => { expect(await queryByRole('button', { name: /install/i })).toBeInTheDocument(); }); - it('should not display install button for enterprise plugins if license is invalid', async () => { + it('should not display install button for enterprise plugins if license is invalid (but allow uninstall)', async () => { config.licenseInfo.enabledFeatures = {}; const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: true, isEnterprise: true }); - expect(await queryByRole('button', { name: /install/i })).not.toBeInTheDocument(); + expect(await queryByRole('button', { name: /Install/ })).not.toBeInTheDocument(); + expect(await queryByRole('button', { name: /Uninstall/ })).toBeInTheDocument(); expect(queryByText(/no valid Grafana Enterprise license detected/i)).toBeInTheDocument(); expect(queryByRole('link', { name: /learn more/i })).toBeInTheDocument(); });