Plugins Catalog: fix overflowing text in plugin cards (#39862)

* refactor(Plugins/Admin): add a "badge" for displaying available updates

* refactor(Plugins/Admin): rename component

* refactor(Plugins/Admin): use the PluginListItemBadges component
This commit is contained in:
Levente Balogh 2021-09-30 17:23:40 +02:00 committed by GitHub
parent fffbdf4c82
commit 3ad5ee87a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 31 deletions

View File

@ -0,0 +1,33 @@
import React from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Tooltip, useStyles2 } from '@grafana/ui';
import { CatalogPlugin } from '../../types';
type Props = {
plugin: CatalogPlugin;
};
export function PluginUpdateAvailableBadge({ plugin }: Props): React.ReactElement | null {
const styles = useStyles2(getStyles);
if (plugin.hasUpdate && !plugin.isCore) {
return (
<Tooltip content={plugin.version}>
<p className={styles.hasUpdate}>Update available!</p>
</Tooltip>
);
}
return null;
}
export const getStyles = (theme: GrafanaTheme2) => {
return {
hasUpdate: css`
color: ${theme.colors.text.secondary};
font-size: ${theme.typography.bodySmall.fontSize};
margin-bottom: 0;
`,
};
};

View File

@ -1,3 +1,4 @@
export { PluginDisabledBadge } from './PluginDisabledBadge';
export { PluginInstalledBadge } from './PluginInstallBadge';
export { PluginEnterpriseBadge } from './PluginEnterpriseBadge';
export { PluginUpdateAvailableBadge } from './PluginUpdateAvailableBadge';

View File

@ -1,11 +1,11 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { PluginErrorCode, PluginSignatureStatus } from '@grafana/data';
import { PluginListBadges } from './PluginListBadges';
import { PluginListItemBadges } from './PluginListItemBadges';
import { CatalogPlugin } from '../types';
import { config } from '@grafana/runtime';
describe('PluginBadges', () => {
describe('PluginListItemBadges', () => {
const plugin: CatalogPlugin = {
description: 'The test plugin',
downloads: 5,
@ -36,13 +36,13 @@ describe('PluginBadges', () => {
});
it('renders a plugin signature badge', () => {
render(<PluginListBadges plugin={plugin} />);
render(<PluginListItemBadges plugin={plugin} />);
expect(screen.getByText(/signed/i)).toBeVisible();
});
it('renders an installed badge', () => {
render(<PluginListBadges plugin={{ ...plugin, isInstalled: true }} />);
render(<PluginListItemBadges plugin={{ ...plugin, isInstalled: true }} />);
expect(screen.getByText(/signed/i)).toBeVisible();
expect(screen.getByText(/installed/i)).toBeVisible();
@ -50,21 +50,21 @@ describe('PluginBadges', () => {
it('renders an enterprise badge (when a license is valid)', () => {
config.licenseInfo.hasValidLicense = true;
render(<PluginListBadges plugin={{ ...plugin, isEnterprise: true }} />);
render(<PluginListItemBadges plugin={{ ...plugin, isEnterprise: true }} />);
expect(screen.getByText(/enterprise/i)).toBeVisible();
expect(screen.queryByRole('button', { name: /learn more/i })).not.toBeInTheDocument();
});
it('renders an enterprise badge with icon and link (when a license is invalid)', () => {
config.licenseInfo.hasValidLicense = false;
render(<PluginListBadges plugin={{ ...plugin, isEnterprise: true }} />);
render(<PluginListItemBadges plugin={{ ...plugin, isEnterprise: true }} />);
expect(screen.getByText(/enterprise/i)).toBeVisible();
expect(screen.getByLabelText(/lock icon/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /learn more/i })).toBeInTheDocument();
});
it('renders a error badge (when plugin has an error', () => {
render(<PluginListBadges plugin={{ ...plugin, isDisabled: true, error: PluginErrorCode.modifiedSignature }} />);
render(<PluginListItemBadges plugin={{ ...plugin, isDisabled: true, error: PluginErrorCode.modifiedSignature }} />);
expect(screen.getByText(/disabled/i)).toBeVisible();
});
});

View File

@ -1,27 +1,29 @@
import React from 'react';
import { HorizontalGroup, PluginSignatureBadge } from '@grafana/ui';
import { CatalogPlugin } from '../types';
import { PluginEnterpriseBadge, PluginDisabledBadge, PluginInstalledBadge } from './Badges';
import { PluginEnterpriseBadge, PluginDisabledBadge, PluginInstalledBadge, PluginUpdateAvailableBadge } from './Badges';
type PluginBadgeType = {
plugin: CatalogPlugin;
};
export function PluginListBadges({ plugin }: PluginBadgeType) {
export function PluginListItemBadges({ plugin }: PluginBadgeType) {
if (plugin.isEnterprise) {
return (
<HorizontalGroup>
<HorizontalGroup height="auto" wrap>
<PluginEnterpriseBadge plugin={plugin} />
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
<PluginUpdateAvailableBadge plugin={plugin} />
</HorizontalGroup>
);
}
return (
<HorizontalGroup>
<HorizontalGroup height="auto" wrap>
<PluginSignatureBadge status={plugin.signature} />
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
{plugin.isInstalled && <PluginInstalledBadge />}
<PluginUpdateAvailableBadge plugin={plugin} />
</HorizontalGroup>
);
}

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Icon, useStyles2, HorizontalGroup, Tooltip, CardContainer, VerticalGroup } from '@grafana/ui';
import { Icon, useStyles2, CardContainer, VerticalGroup } from '@grafana/ui';
import { CatalogPlugin, PluginIconName, PluginListDisplayMode, PluginTabIds } from '../types';
import { PluginLogo } from './PluginLogo';
import { PluginListBadges } from './PluginListBadges';
import { PluginListItemBadges } from './PluginListItemBadges';
import { getStyles, LOGO_SIZE } from './PluginListItem';
type Props = {
@ -17,28 +17,30 @@ export function PluginListItemCard({ plugin, pathName }: Props) {
<CardContainer href={`${pathName}/${plugin.id}?page=${PluginTabIds.OVERVIEW}`} className={styles.cardContainer}>
<VerticalGroup spacing="md">
<div className={styles.headerWrap}>
{/* Logo */}
<PluginLogo
src={plugin.info.logos.small}
alt={`${plugin.name} logo`}
className={styles.image}
height={LOGO_SIZE}
/>
{/* Name */}
<h2 className={styles.name}>{plugin.name}</h2>
{/* Type Icon */}
{plugin.type && (
<div className={styles.icon} data-testid={`${plugin.type} plugin icon`}>
<Icon name={PluginIconName[plugin.type]} />
</div>
)}
</div>
{/* Org */}
<p className={styles.orgName}>By {plugin.orgName}</p>
<HorizontalGroup align="center">
<PluginListBadges plugin={plugin} />
{plugin.hasUpdate && !plugin.isCore ? (
<Tooltip content={plugin.version}>
<p className={styles.hasUpdate}>Update available!</p>
</Tooltip>
) : null}
</HorizontalGroup>
{/* Badges */}
<PluginListItemBadges plugin={plugin} />
</VerticalGroup>
</CardContainer>
);

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Icon, useStyles2, HorizontalGroup, Tooltip, CardContainer, VerticalGroup } from '@grafana/ui';
import { Icon, useStyles2, CardContainer, VerticalGroup } from '@grafana/ui';
import { CatalogPlugin, PluginIconName, PluginListDisplayMode, PluginTabIds } from '../types';
import { PluginLogo } from './PluginLogo';
import { PluginListBadges } from './PluginListBadges';
import { PluginListItemBadges } from './PluginListItemBadges';
import { getStyles, LOGO_SIZE } from './PluginListItem';
type Props = {
@ -17,24 +17,26 @@ export function PluginListItemRow({ plugin, pathName }: Props) {
<CardContainer href={`${pathName}/${plugin.id}?page=${PluginTabIds.OVERVIEW}`} className={styles.cardContainer}>
<VerticalGroup spacing="md">
<div className={styles.headerWrap}>
{/* Logo */}
<PluginLogo
src={plugin.info.logos.small}
alt={`${plugin.name} logo`}
className={styles.image}
height={LOGO_SIZE}
/>
<div>
{/* Name */}
<h3 className={styles.name}>{plugin.name}</h3>
{/* Org */}
<p className={styles.orgName}>By {plugin.orgName}</p>
<HorizontalGroup height="auto">
<PluginListBadges plugin={plugin} />
{plugin.hasUpdate && !plugin.isCore && (
<Tooltip content={plugin.version}>
<p className={styles.hasUpdate}>Update available!</p>
</Tooltip>
)}
</HorizontalGroup>
{/* Badges */}
<PluginListItemBadges plugin={plugin} />
</div>
{/* Type Icon */}
{plugin.type && (
<div className={styles.icon}>
<Icon name={PluginIconName[plugin.type]} aria-label={`${plugin.type} plugin icon`} />