mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 11:44:26 -06:00
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:
parent
fffbdf4c82
commit
3ad5ee87a3
@ -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;
|
||||
`,
|
||||
};
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
export { PluginDisabledBadge } from './PluginDisabledBadge';
|
||||
export { PluginInstalledBadge } from './PluginInstallBadge';
|
||||
export { PluginEnterpriseBadge } from './PluginEnterpriseBadge';
|
||||
export { PluginUpdateAvailableBadge } from './PluginUpdateAvailableBadge';
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
|
@ -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`} />
|
||||
|
Loading…
Reference in New Issue
Block a user