Plugins: Fix version tab breaking for non semantic version (#101225)

This commit is contained in:
Hugo Kiyodi Oshiro
2025-02-27 09:16:00 +01:00
committed by GitHub
parent 22a6dc6b52
commit e8e79e9c79
2 changed files with 77 additions and 24 deletions
@@ -177,6 +177,46 @@ describe('VersionInstallButton', () => {
);
expect(screen.getByText('Downgrade')).not.toBeVisible();
});
it('should show the installation button if invalid semver version is provided', () => {
const version: Version = {
version: '1.0.a',
createdAt: '',
isCompatible: false,
grafanaDependency: null,
};
const installedVersion = '1.0.1';
renderWithStore(
<VersionInstallButton
installedVersion={installedVersion}
pluginId={'test'}
version={version}
disabled={false}
onConfirmInstallation={() => {}}
/>
);
expect(screen.getByText('Install')).toBeInTheDocument();
});
it('should show the installation button if invalid semver installed version is provided', () => {
const version: Version = {
version: '1.0.0',
createdAt: '',
isCompatible: false,
grafanaDependency: null,
};
const installedVersion = '1.0.a';
renderWithStore(
<VersionInstallButton
installedVersion={installedVersion}
pluginId={'test'}
version={version}
disabled={false}
onConfirmInstallation={() => {}}
/>
);
expect(screen.getByText('Install')).toBeInTheDocument();
});
});
function renderWithStore(component: JSX.Element) {
@@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { useEffect, useState } from 'react';
import { gt } from 'semver';
import { gt, valid } from 'semver';
import { GrafanaTheme2 } from '@grafana/data';
import { config, reportInteraction } from '@grafana/runtime';
@@ -14,6 +14,12 @@ import { Version } from '../types';
const PLUGINS_VERSION_PAGE_UPGRADE_INTERACTION_EVENT_NAME = 'plugins_upgrade_clicked';
const PLUGINS_VERSION_PAGE_CHANGE_INTERACTION_EVENT_NAME = 'plugins_downgrade_clicked';
enum InstallState {
INSTALL = 'Install',
UPGRADE = 'Upgrade',
DOWNGRADE = 'Downgrade',
}
interface Props {
pluginId: string;
version: Version;
@@ -38,7 +44,7 @@ export const VersionInstallButton = ({
const [isModalOpen, setIsModalOpen] = useState(false);
const styles = useStyles2(getStyles);
const isDowngrade = installedVersion && gt(installedVersion, version.version);
const installState = getInstallState(installedVersion, version.version);
useEffect(() => {
if (installedVersion === version.version) {
@@ -61,7 +67,7 @@ export const VersionInstallButton = ({
schema_version: '1.0.0',
};
if (!installedVersion || gt(version.version, installedVersion)) {
if (installState === InstallState.UPGRADE) {
reportInteraction(PLUGINS_VERSION_PAGE_UPGRADE_INTERACTION_EVENT_NAME, trackProps);
} else {
reportInteraction(PLUGINS_VERSION_PAGE_CHANGE_INTERACTION_EVENT_NAME, {
@@ -76,7 +82,7 @@ export const VersionInstallButton = ({
};
const onInstallClick = () => {
if (isDowngrade) {
if (installState === InstallState.DOWNGRADE) {
setIsModalOpen(true);
} else {
performInstallation();
@@ -91,24 +97,9 @@ export const VersionInstallButton = ({
setIsModalOpen(false);
};
let label = 'Downgrade';
let hidden = false;
const isPreinstalled = isPreinstalledPlugin(pluginId);
if (!installedVersion) {
label = 'Install';
} else if (gt(version.version, installedVersion)) {
label = 'Upgrade';
if (isPreinstalled.withVersion) {
// Hide button if the plugin is preinstalled with a specific version
hidden = true;
}
} else {
if (isPreinstalled.found && Boolean(config.featureToggles.preinstallAutoUpdate)) {
// Hide the downgrade button if the plugin is preinstalled since it will be auto-updated
hidden = true;
}
}
const hidden = getButtonHiddenState(installState, isPreinstalled);
return (
<>
@@ -124,7 +115,7 @@ export const VersionInstallButton = ({
tooltip={tooltip}
tooltipPlacement="bottom-start"
>
{label} {isInstalling ? <Spinner className={styles.spinner} inline size="sm" /> : getIcon(label)}
{installState} {isInstalling ? <Spinner className={styles.spinner} inline size="sm" /> : getIcon(installState)}
</Button>
<ConfirmModal
isOpen={isModalOpen}
@@ -140,16 +131,38 @@ export const VersionInstallButton = ({
);
};
function getIcon(label: string) {
if (label === 'Downgrade') {
function getIcon(installState: InstallState) {
if (installState === InstallState.DOWNGRADE) {
return <Icon name="arrow-down" />;
}
if (label === 'Upgrade') {
if (installState === InstallState.UPGRADE) {
return <Icon name="arrow-up" />;
}
return '';
}
function getInstallState(installedVersion?: string, version?: string): InstallState {
if (!installedVersion || !version || !valid(installedVersion) || !valid(version)) {
return InstallState.INSTALL;
}
return gt(installedVersion, version) ? InstallState.DOWNGRADE : InstallState.UPGRADE;
}
function getButtonHiddenState(installState: InstallState, isPreinstalled: { found: boolean; withVersion: boolean }) {
// Default state for initial install
if (installState === InstallState.INSTALL) {
return false;
}
// Handle downgrade case
if (installState === InstallState.DOWNGRADE) {
return isPreinstalled.found && Boolean(config.featureToggles.preinstallAutoUpdate);
}
// Handle upgrade case
return isPreinstalled.withVersion;
}
const getStyles = (theme: GrafanaTheme2) => ({
spinner: css({
marginLeft: theme.spacing(1),