mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Catalog: Add update text to PluginListCard (#39087)
* feat(catalog): add update info to PluginListCard * refactor(catalog): use IconName enum and minor styling changes to PluginHeaderDependencies * fix(catalog): add a semver range to grafanaVersion for dependency checks in InstallControls
This commit is contained in:
parent
cbf16bebc6
commit
c9620b2202
@ -21,9 +21,16 @@ export async function getPluginDetails(id: string): Promise<CatalogPluginDetails
|
|||||||
const isInstalled = Boolean(local);
|
const isInstalled = Boolean(local);
|
||||||
const [remote, versions] = await Promise.all([getRemotePlugin(id, isInstalled), getPluginVersions(id)]);
|
const [remote, versions] = await Promise.all([getRemotePlugin(id, isInstalled), getPluginVersions(id)]);
|
||||||
const dependencies = remote?.json?.dependencies;
|
const dependencies = remote?.json?.dependencies;
|
||||||
|
// Prepend semver range when we fallback to grafanaVersion (deprecated in favour of grafanaDependency)
|
||||||
|
// otherwise plugins cannot be installed.
|
||||||
|
const grafanaDependency = dependencies?.grafanaDependency
|
||||||
|
? dependencies?.grafanaDependency
|
||||||
|
: dependencies?.grafanaVersion
|
||||||
|
? `>=${dependencies?.grafanaVersion}`
|
||||||
|
: '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
grafanaDependency: dependencies?.grafanaDependency || dependencies?.grafanaVersion || '',
|
grafanaDependency,
|
||||||
pluginDependencies: dependencies?.plugins || [],
|
pluginDependencies: dependencies?.plugins || [],
|
||||||
links: remote?.json?.info.links || local?.info.links || [],
|
links: remote?.json?.info.links || local?.info.links || [],
|
||||||
readme: remote?.readme,
|
readme: remote?.readme,
|
||||||
|
@ -2,22 +2,13 @@ import React from 'react';
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { useStyles2, Icon } from '@grafana/ui';
|
import { useStyles2, Icon } from '@grafana/ui';
|
||||||
import { CatalogPlugin } from '../types';
|
import { CatalogPlugin, IconName } from '../types';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
plugin: CatalogPlugin;
|
plugin: CatalogPlugin;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PluginIconClassName: Record<string, string> = {
|
|
||||||
datasource: 'gicon gicon-datasources',
|
|
||||||
panel: 'icon-gf icon-gf-panel',
|
|
||||||
app: 'icon-gf icon-gf-apps',
|
|
||||||
page: 'icon-gf icon-gf-endpoint-tiny',
|
|
||||||
dashboard: 'gicon gicon-dashboard',
|
|
||||||
default: 'icon-gf icon-gf-apps',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PluginDetailsHeaderDependencies({ plugin, className }: Props): React.ReactElement | null {
|
export function PluginDetailsHeaderDependencies({ plugin, className }: Props): React.ReactElement | null {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const pluginDependencies = plugin.details?.pluginDependencies;
|
const pluginDependencies = plugin.details?.pluginDependencies;
|
||||||
@ -30,12 +21,12 @@ export function PluginDetailsHeaderDependencies({ plugin, className }: Props): R
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className={styles.textBold}>Dependencies:</div>
|
<div className={styles.dependencyTitle}>Dependencies:</div>
|
||||||
|
|
||||||
{/* Grafana dependency */}
|
{/* Grafana dependency */}
|
||||||
{Boolean(grafanaDependency) && (
|
{Boolean(grafanaDependency) && (
|
||||||
<div>
|
<div>
|
||||||
<Icon name="grafana" />
|
<Icon name="grafana" className={styles.icon} />
|
||||||
Grafana {grafanaDependency}
|
Grafana {grafanaDependency}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -46,7 +37,7 @@ export function PluginDetailsHeaderDependencies({ plugin, className }: Props): R
|
|||||||
{pluginDependencies.map((p) => {
|
{pluginDependencies.map((p) => {
|
||||||
return (
|
return (
|
||||||
<span key={p.name}>
|
<span key={p.name}>
|
||||||
<i className={PluginIconClassName[p.type] || PluginIconClassName.default} />
|
<Icon name={IconName[p.type]} className={styles.icon} />
|
||||||
{p.name} {p.version}
|
{p.name} {p.version}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -59,8 +50,18 @@ export function PluginDetailsHeaderDependencies({ plugin, className }: Props): R
|
|||||||
|
|
||||||
export const getStyles = (theme: GrafanaTheme2) => {
|
export const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
textBold: css`
|
dependencyTitle: css`
|
||||||
font-weight: ${theme.typography.fontWeightBold};
|
font-weight: ${theme.typography.fontWeightBold};
|
||||||
|
margin-right: ${theme.spacing(0.5)};
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
icon: css`
|
||||||
|
color: ${theme.colors.text.secondary};
|
||||||
|
margin-right: ${theme.spacing(0.5)};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,31 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Icon, useStyles2, CardContainer, VerticalGroup } from '@grafana/ui';
|
import { Icon, useStyles2, CardContainer, HorizontalGroup, VerticalGroup, Tooltip } from '@grafana/ui';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { CatalogPlugin } from '../types';
|
import { CatalogPlugin, IconName } from '../types';
|
||||||
import { PluginLogo } from './PluginLogo';
|
import { PluginLogo } from './PluginLogo';
|
||||||
import { PluginListBadges } from './PluginListBadges';
|
import { PluginListBadges } from './PluginListBadges';
|
||||||
|
|
||||||
const LOGO_SIZE = '48px';
|
const LOGO_SIZE = '48px';
|
||||||
|
|
||||||
enum IconName {
|
|
||||||
app = 'apps',
|
|
||||||
datasource = 'database',
|
|
||||||
panel = 'credit-card',
|
|
||||||
renderer = 'pen',
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginListCardProps = {
|
type PluginListCardProps = {
|
||||||
plugin: CatalogPlugin;
|
plugin: CatalogPlugin;
|
||||||
pathName: string;
|
pathName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PluginListCard({ plugin, pathName }: PluginListCardProps) {
|
export function PluginListCard({ plugin, pathName }: PluginListCardProps) {
|
||||||
const { name, id, orgName, type } = plugin;
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainer href={`${pathName}/${id}`} className={styles.cardContainer}>
|
<CardContainer href={`${pathName}/${plugin.id}`} className={styles.cardContainer}>
|
||||||
<VerticalGroup spacing="md">
|
<VerticalGroup spacing="md">
|
||||||
<div className={styles.headerWrap}>
|
<div className={styles.headerWrap}>
|
||||||
<PluginLogo
|
<PluginLogo
|
||||||
@ -34,15 +26,22 @@ export function PluginListCard({ plugin, pathName }: PluginListCardProps) {
|
|||||||
className={styles.image}
|
className={styles.image}
|
||||||
height={LOGO_SIZE}
|
height={LOGO_SIZE}
|
||||||
/>
|
/>
|
||||||
<h3 className={styles.name}>{name}</h3>
|
<h3 className={styles.name}>{plugin.name}</h3>
|
||||||
{type && (
|
{plugin.type && (
|
||||||
<div className={styles.icon}>
|
<div className={styles.icon}>
|
||||||
<Icon name={IconName[type]} aria-label={`${type} plugin icon`} />
|
<Icon name={IconName[plugin.type]} aria-label={`${plugin.type} plugin icon`} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.orgName}>By {orgName}</p>
|
<p className={styles.orgName}>By {plugin.orgName}</p>
|
||||||
|
<HorizontalGroup align="center">
|
||||||
<PluginListBadges plugin={plugin} />
|
<PluginListBadges plugin={plugin} />
|
||||||
|
{plugin.hasUpdate && !plugin.isCore ? (
|
||||||
|
<Tooltip content={plugin.version}>
|
||||||
|
<p className={styles.hasUpdate}>Update available!</p>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</HorizontalGroup>
|
||||||
</VerticalGroup>
|
</VerticalGroup>
|
||||||
</CardContainer>
|
</CardContainer>
|
||||||
);
|
);
|
||||||
@ -78,4 +77,9 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
color: ${theme.colors.text.secondary};
|
color: ${theme.colors.text.secondary};
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
`,
|
`,
|
||||||
|
hasUpdate: css`
|
||||||
|
color: ${theme.colors.text.secondary};
|
||||||
|
font-size: ${theme.typography.bodySmall.fontSize};
|
||||||
|
margin-bottom: 0;
|
||||||
|
`,
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,13 @@ export enum PluginAdminRoutes {
|
|||||||
DetailsAdmin = 'plugins-details-admin',
|
DetailsAdmin = 'plugins-details-admin',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum IconName {
|
||||||
|
app = 'apps',
|
||||||
|
datasource = 'database',
|
||||||
|
panel = 'credit-card',
|
||||||
|
renderer = 'pen',
|
||||||
|
}
|
||||||
|
|
||||||
export interface CatalogPlugin {
|
export interface CatalogPlugin {
|
||||||
description: string;
|
description: string;
|
||||||
downloads: number;
|
downloads: number;
|
||||||
|
Loading…
Reference in New Issue
Block a user