Commit 3d89f045 by Ryan McKinley Committed by GitHub

Plugins: show signing status on datasources and plugins (#23542)

* show signing status

* show signing status

* Progress on signed badge style

* Progress on signing status look and updated card background

* Updates

* Transforms card tweak

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent 68ba60ad
...@@ -14,6 +14,14 @@ export enum PluginType { ...@@ -14,6 +14,14 @@ export enum PluginType {
renderer = 'renderer', renderer = 'renderer',
} }
export enum PluginSignatureStatus {
internal = 'internal', // core plugin, no signature
valid = 'valid', // signed and accurate MANIFEST
invalid = 'invalid', // invalid signature
modified = 'modified', // valid signature, but content mismatch
unsigned = 'unsigned', // no MANIFEST file
}
export interface PluginMeta<T extends KeyValue = {}> { export interface PluginMeta<T extends KeyValue = {}> {
id: string; id: string;
name: string; name: string;
...@@ -38,6 +46,7 @@ export interface PluginMeta<T extends KeyValue = {}> { ...@@ -38,6 +46,7 @@ export interface PluginMeta<T extends KeyValue = {}> {
enterprise?: boolean; enterprise?: boolean;
latestVersion?: string; latestVersion?: string;
pinned?: boolean; pinned?: boolean;
signature?: PluginSignatureStatus;
} }
interface PluginDependencyInfo { interface PluginDependencyInfo {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { renderGeneratedFileBanner } from '../utils/generatedFileBanner'; import { renderGeneratedFileBanner } from '../utils/generatedFileBanner';
import { styleMixins } from '.';
export const darkThemeVarsTemplate = (theme: GrafanaTheme) => export const darkThemeVarsTemplate = (theme: GrafanaTheme) =>
`${renderGeneratedFileBanner('grafana-ui/src/themes/dark.ts', 'grafana-ui/src/themes/_variables.dark.scss.tmpl.ts')} `${renderGeneratedFileBanner('grafana-ui/src/themes/dark.ts', 'grafana-ui/src/themes/_variables.dark.scss.tmpl.ts')}
...@@ -140,9 +141,9 @@ $code-tag-bg: $dark-1; ...@@ -140,9 +141,9 @@ $code-tag-bg: $dark-1;
$code-tag-border: $dark-9; $code-tag-border: $dark-9;
// cards // cards
$card-background: linear-gradient(135deg, $dark-4, $dark-3); $card-background: ${theme.colors.bg2};
$card-background-hover: linear-gradient(135deg, $dark-5, $dark-6); $card-background-hover: ${styleMixins.hoverColor(theme.colors.bg2, theme)};
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3); $card-shadow: none;
// Lists // Lists
$list-item-bg: $card-background; $list-item-bg: $card-background;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { renderGeneratedFileBanner } from '../utils/generatedFileBanner'; import { renderGeneratedFileBanner } from '../utils/generatedFileBanner';
import { styleMixins } from '.';
export const lightThemeVarsTemplate = (theme: GrafanaTheme) => export const lightThemeVarsTemplate = (theme: GrafanaTheme) =>
`${renderGeneratedFileBanner('grafana-ui/src/themes/light.ts', 'grafana-ui/src/themes/_variable.light.scss.tmpl.ts')} `${renderGeneratedFileBanner('grafana-ui/src/themes/light.ts', 'grafana-ui/src/themes/_variable.light.scss.tmpl.ts')}
...@@ -133,9 +134,9 @@ $code-tag-bg: $gray-6; ...@@ -133,9 +134,9 @@ $code-tag-bg: $gray-6;
$code-tag-border: $gray-4; $code-tag-border: $gray-4;
// cards // cards
$card-background: linear-gradient(135deg, $gray-6, $gray-7); $card-background: ${theme.colors.bg2};
$card-background-hover: linear-gradient(135deg, $gray-6, $gray-5); $card-background-hover: ${styleMixins.hoverColor(theme.colors.bg2, theme)};
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.1); $card-shadow: none;
// Lists // Lists
$list-item-bg: $gray-7; $list-item-bg: $gray-7;
......
...@@ -129,7 +129,7 @@ const darkTheme: GrafanaTheme = { ...@@ -129,7 +129,7 @@ const darkTheme: GrafanaTheme = {
linkExternal: basicColors.blue85, linkExternal: basicColors.blue85,
}, },
shadows: { shadows: {
listItem: '-1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3)', listItem: 'none',
}, },
}; };
......
...@@ -130,7 +130,7 @@ const lightTheme: GrafanaTheme = { ...@@ -130,7 +130,7 @@ const lightTheme: GrafanaTheme = {
textHeading: basicColors.gray25, textHeading: basicColors.gray25,
}, },
shadows: { shadows: {
listItem: '-1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.1)', listItem: 'none',
}, },
}; };
......
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import LayoutSelector, { LayoutMode } from '../LayoutSelector/LayoutSelector';
import { FilterInput } from '../FilterInput/FilterInput'; import { FilterInput } from '../FilterInput/FilterInput';
import { LinkButton } from '@grafana/ui'; import { LinkButton } from '@grafana/ui';
export interface Props { export interface Props {
searchQuery: string; searchQuery: string;
layoutMode?: LayoutMode;
onSetLayoutMode?: (mode: LayoutMode) => {};
setSearchQuery: (value: string) => {}; setSearchQuery: (value: string) => {};
linkButton: { href: string; title: string }; linkButton: { href: string; title: string };
target?: string; target?: string;
...@@ -14,7 +11,7 @@ export interface Props { ...@@ -14,7 +11,7 @@ export interface Props {
export default class OrgActionBar extends PureComponent<Props> { export default class OrgActionBar extends PureComponent<Props> {
render() { render() {
const { searchQuery, layoutMode, onSetLayoutMode, linkButton, setSearchQuery, target } = this.props; const { searchQuery, linkButton, setSearchQuery, target } = this.props;
const linkProps = { href: linkButton.href }; const linkProps = { href: linkButton.href };
if (target) { if (target) {
...@@ -31,7 +28,6 @@ export default class OrgActionBar extends PureComponent<Props> { ...@@ -31,7 +28,6 @@ export default class OrgActionBar extends PureComponent<Props> {
onChange={setSearchQuery} onChange={setSearchQuery}
placeholder={'Search by name or type'} placeholder={'Search by name or type'}
/> />
<LayoutSelector mode={layoutMode} onLayoutModeChanged={(mode: LayoutMode) => onSetLayoutMode(mode)} />
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
<LinkButton {...linkProps}>{linkButton.title}</LinkButton> <LinkButton {...linkProps}>{linkButton.title}</LinkButton>
......
...@@ -14,9 +14,6 @@ exports[`Render should render component 1`] = ` ...@@ -14,9 +14,6 @@ exports[`Render should render component 1`] = `
placeholder="Search by name or type" placeholder="Search by name or type"
value="" value=""
/> />
<LayoutSelector
onLayoutModeChanged={[Function]}
/>
</div> </div>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
......
import React from 'react'; import React from 'react';
import { import { Container, CustomScrollbar, ValuePicker, Button, useTheme, VerticalGroup, stylesFactory } from '@grafana/ui';
Container,
CustomScrollbar,
InfoBox,
ValuePicker,
Button,
useTheme,
VerticalGroup,
stylesFactory,
} from '@grafana/ui';
import { import {
DataFrame, DataFrame,
DataTransformerConfig, DataTransformerConfig,
...@@ -120,33 +111,39 @@ export class TransformationsEditor extends React.PureComponent<Props> { ...@@ -120,33 +111,39 @@ export class TransformationsEditor extends React.PureComponent<Props> {
); );
}; };
renderNoAddedTransformsState() {
return (
<>
<p className="muted">
Transformations allow you to combine, re-order, hide and rename specific parts the the data set before being
visualized. <br />
Choose one of the transformations below to start with:
</p>
<VerticalGroup>
{standardTransformersRegistry.list().map(t => {
return (
<TransformationCard
title={t.name}
description={t.description}
actions={<Button>Select</Button>}
onClick={() => {
this.onTransformationAdd({ value: t.id });
}}
/>
);
})}
</VerticalGroup>
</>
);
}
render() { render() {
const hasTransformationsConfigured = this.props.transformations.length > 0; const hasTransformationsConfigured = this.props.transformations.length > 0;
return ( return (
<CustomScrollbar autoHeightMin="100%"> <CustomScrollbar autoHeightMin="100%">
<Container padding="md"> <Container padding="md">
{!hasTransformationsConfigured && ( {!hasTransformationsConfigured && this.renderNoAddedTransformsState()}
<InfoBox>
<p>
Transformations allow you to combine, re-order, hide and rename specific parts the the data set before
being visualized. Choose one of the transformations below to start with:
</p>
<VerticalGroup>
{standardTransformersRegistry.list().map(t => {
return (
<TransformationCard
title={t.name}
description={t.description}
actions={<Button>Select</Button>}
onClick={() => {
this.onTransformationAdd({ value: t.id });
}}
/>
);
})}
</VerticalGroup>
</InfoBox>
)}
{hasTransformationsConfigured && this.renderTransformationEditors()} {hasTransformationsConfigured && this.renderTransformationEditors()}
{hasTransformationsConfigured && this.renderTransformationSelector()} {hasTransformationsConfigured && this.renderTransformationSelector()}
</Container> </Container>
...@@ -166,8 +163,13 @@ const getTransformationCardStyles = stylesFactory((theme: GrafanaTheme) => { ...@@ -166,8 +163,13 @@ const getTransformationCardStyles = stylesFactory((theme: GrafanaTheme) => {
card: css` card: css`
background: ${theme.colors.bg2}; background: ${theme.colors.bg2};
width: 100%; width: 100%;
border: none;
padding: ${theme.spacing.sm};
&:hover { &:hover {
background: ${theme.colors.bg3}; background: ${theme.colors.bg3};
box-shadow: none;
border: none;
} }
`, `,
}; };
......
...@@ -64,7 +64,6 @@ export class DataSourcesListPage extends PureComponent<Props> { ...@@ -64,7 +64,6 @@ export class DataSourcesListPage extends PureComponent<Props> {
layoutMode, layoutMode,
searchQuery, searchQuery,
setDataSourcesSearchQuery, setDataSourcesSearchQuery,
setDataSourcesLayoutMode,
hasFetched, hasFetched,
} = this.props; } = this.props;
...@@ -81,9 +80,7 @@ export class DataSourcesListPage extends PureComponent<Props> { ...@@ -81,9 +80,7 @@ export class DataSourcesListPage extends PureComponent<Props> {
{hasFetched && {hasFetched &&
dataSourcesCount > 0 && [ dataSourcesCount > 0 && [
<OrgActionBar <OrgActionBar
layoutMode={layoutMode}
searchQuery={searchQuery} searchQuery={searchQuery}
onSetLayoutMode={mode => setDataSourcesLayoutMode(mode)}
setSearchQuery={query => setDataSourcesSearchQuery(query)} setSearchQuery={query => setDataSourcesSearchQuery(query)}
linkButton={linkButton} linkButton={linkButton}
key="action-bar" key="action-bar"
......
...@@ -11,6 +11,7 @@ import { addDataSource, loadDataSourcePlugins } from './state/actions'; ...@@ -11,6 +11,7 @@ import { addDataSource, loadDataSourcePlugins } from './state/actions';
import { getDataSourcePlugins } from './state/selectors'; import { getDataSourcePlugins } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { setDataSourceTypeSearchQuery } from './state/reducers'; import { setDataSourceTypeSearchQuery } from './state/reducers';
import { PluginSignatureBadge } from '../plugins/PluginSignatureBadge';
import { Card } from 'app/core/components/Card/Card'; import { Card } from 'app/core/components/Card/Card';
export interface Props { export interface Props {
...@@ -146,7 +147,34 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => { ...@@ -146,7 +147,34 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
} }
className={isPhantom && 'add-data-source-item--phantom'} className={isPhantom && 'add-data-source-item--phantom'}
onClick={onClick} onClick={onClick}
/> aria-label={e2e.pages.AddDataSource.selectors.dataSourcePlugins(plugin.name)}
>
<img className="add-data-source-item-logo" src={plugin.info.logos.small} />
<div className="add-data-source-item-text-wrapper">
<span className="add-data-source-item-text">{plugin.name}</span>
{plugin.info.description && <span className="add-data-source-item-desc">{plugin.info.description}</span>}
{!isPhantom && (
<div>
<PluginSignatureBadge status={plugin.signature} />
</div>
)}
</div>
<div className="add-data-source-item-actions">
{learnMoreLink && (
<LinkButton
variant="secondary"
href={`${learnMoreLink.url}?utm_source=grafana_add_ds`}
target="_blank"
rel="noopener"
onClick={onLearnMoreClick}
icon="external-link-alt"
>
{learnMoreLink.name}
</LinkButton>
)}
{!isPhantom && <Button>Select</Button>}
</div>
</Card>
); );
}; };
......
...@@ -18,14 +18,12 @@ exports[`Render should render action bar and datasources 1`] = ` ...@@ -18,14 +18,12 @@ exports[`Render should render action bar and datasources 1`] = `
> >
<OrgActionBar <OrgActionBar
key="action-bar" key="action-bar"
layoutMode="grid"
linkButton={ linkButton={
Object { Object {
"href": "datasources/new", "href": "datasources/new",
"title": "Add data source", "title": "Add data source",
} }
} }
onSetLayoutMode={[Function]}
searchQuery="" searchQuery=""
setSearchQuery={[Function]} setSearchQuery={[Function]}
/> />
......
import React, { FC } from 'react'; import React, { FC } from 'react';
import classNames from 'classnames';
import PluginListItem from './PluginListItem'; import PluginListItem from './PluginListItem';
import { PluginMeta } from '@grafana/data'; import { PluginMeta } from '@grafana/data';
import { LayoutMode, LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
interface Props { interface Props {
plugins: PluginMeta[]; plugins: PluginMeta[];
layoutMode: LayoutMode;
} }
const PluginList: FC<Props> = props => { const PluginList: FC<Props> = props => {
const { plugins, layoutMode } = props; const { plugins } = props;
const listStyle = classNames({
'card-section': true,
'card-list-layout-grid': layoutMode === LayoutModes.Grid,
'card-list-layout-list': layoutMode === LayoutModes.List,
});
return ( return (
<section className={listStyle}> <section className="card-section card-list-layout-list">
<ol className="card-list"> <ol className="card-list">
{plugins.map((plugin, index) => { {plugins.map((plugin, index) => {
return <PluginListItem plugin={plugin} key={`${plugin.name}-${index}`} />; return <PluginListItem plugin={plugin} key={`${plugin.name}-${index}`} />;
......
import React, { FC } from 'react'; import React, { FC } from 'react';
import { PluginMeta } from '@grafana/data'; import { PluginMeta } from '@grafana/data';
import { PluginSignatureBadge } from './PluginSignatureBadge';
interface Props { interface Props {
plugin: PluginMeta; plugin: PluginMeta;
...@@ -13,6 +14,7 @@ const PluginListItem: FC<Props> = props => { ...@@ -13,6 +14,7 @@ const PluginListItem: FC<Props> = props => {
<a className="card-item" href={`plugins/${plugin.id}/`}> <a className="card-item" href={`plugins/${plugin.id}/`}>
<div className="card-item-header"> <div className="card-item-header">
<div className="card-item-type">{plugin.type}</div> <div className="card-item-type">{plugin.type}</div>
<PluginSignatureBadge status={plugin.signature} />
{plugin.hasUpdate && ( {plugin.hasUpdate && (
<div className="card-item-notice"> <div className="card-item-notice">
<span bs-tooltip="plugin.latestVersion">Update available!</span> <span bs-tooltip="plugin.latestVersion">Update available!</span>
......
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { PluginListPage, Props } from './PluginListPage'; import { PluginListPage, Props } from './PluginListPage';
import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
import { NavModel, PluginMeta } from '@grafana/data'; import { NavModel, PluginMeta } from '@grafana/data';
import { mockToolkitActionCreator } from 'test/core/redux/mocks'; import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { setPluginsLayoutMode, setPluginsSearchQuery } from './state/reducers'; import { setPluginsSearchQuery } from './state/reducers';
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
...@@ -19,8 +18,6 @@ const setup = (propOverrides?: object) => { ...@@ -19,8 +18,6 @@ const setup = (propOverrides?: object) => {
plugins: [] as PluginMeta[], plugins: [] as PluginMeta[],
searchQuery: '', searchQuery: '',
setPluginsSearchQuery: mockToolkitActionCreator(setPluginsSearchQuery), setPluginsSearchQuery: mockToolkitActionCreator(setPluginsSearchQuery),
setPluginsLayoutMode: mockToolkitActionCreator(setPluginsLayoutMode),
layoutMode: LayoutModes.Grid,
loadPlugins: jest.fn(), loadPlugins: jest.fn(),
hasFetched: false, hasFetched: false,
}; };
......
...@@ -6,20 +6,17 @@ import OrgActionBar from 'app/core/components/OrgActionBar/OrgActionBar'; ...@@ -6,20 +6,17 @@ import OrgActionBar from 'app/core/components/OrgActionBar/OrgActionBar';
import PluginList from './PluginList'; import PluginList from './PluginList';
import { loadPlugins } from './state/actions'; import { loadPlugins } from './state/actions';
import { getNavModel } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { getLayoutMode, getPlugins, getPluginsSearchQuery } from './state/selectors'; import { getPlugins, getPluginsSearchQuery } from './state/selectors';
import { LayoutMode } from 'app/core/components/LayoutSelector/LayoutSelector';
import { NavModel, PluginMeta } from '@grafana/data'; import { NavModel, PluginMeta } from '@grafana/data';
import { StoreState } from 'app/types'; import { StoreState } from 'app/types';
import { setPluginsLayoutMode, setPluginsSearchQuery } from './state/reducers'; import { setPluginsSearchQuery } from './state/reducers';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
plugins: PluginMeta[]; plugins: PluginMeta[];
layoutMode: LayoutMode;
searchQuery: string; searchQuery: string;
hasFetched: boolean; hasFetched: boolean;
loadPlugins: typeof loadPlugins; loadPlugins: typeof loadPlugins;
setPluginsLayoutMode: typeof setPluginsLayoutMode;
setPluginsSearchQuery: typeof setPluginsSearchQuery; setPluginsSearchQuery: typeof setPluginsSearchQuery;
} }
...@@ -33,15 +30,7 @@ export class PluginListPage extends PureComponent<Props> { ...@@ -33,15 +30,7 @@ export class PluginListPage extends PureComponent<Props> {
} }
render() { render() {
const { const { hasFetched, navModel, plugins, setPluginsSearchQuery, searchQuery } = this.props;
hasFetched,
navModel,
plugins,
layoutMode,
setPluginsLayoutMode,
setPluginsSearchQuery,
searchQuery,
} = this.props;
const linkButton = { const linkButton = {
href: 'https://grafana.com/plugins?utm_source=grafana_plugin_list', href: 'https://grafana.com/plugins?utm_source=grafana_plugin_list',
...@@ -54,12 +43,10 @@ export class PluginListPage extends PureComponent<Props> { ...@@ -54,12 +43,10 @@ export class PluginListPage extends PureComponent<Props> {
<> <>
<OrgActionBar <OrgActionBar
searchQuery={searchQuery} searchQuery={searchQuery}
layoutMode={layoutMode}
onSetLayoutMode={mode => setPluginsLayoutMode(mode)}
setSearchQuery={query => setPluginsSearchQuery(query)} setSearchQuery={query => setPluginsSearchQuery(query)}
linkButton={linkButton} linkButton={linkButton}
/> />
{hasFetched && plugins && plugins && <PluginList plugins={plugins} layoutMode={layoutMode} />} {hasFetched && plugins && plugins && <PluginList plugins={plugins} />}
</> </>
</Page.Contents> </Page.Contents>
</Page> </Page>
...@@ -71,7 +58,6 @@ function mapStateToProps(state: StoreState) { ...@@ -71,7 +58,6 @@ function mapStateToProps(state: StoreState) {
return { return {
navModel: getNavModel(state.navIndex, 'plugins'), navModel: getNavModel(state.navIndex, 'plugins'),
plugins: getPlugins(state.plugins), plugins: getPlugins(state.plugins),
layoutMode: getLayoutMode(state.plugins),
searchQuery: getPluginsSearchQuery(state.plugins), searchQuery: getPluginsSearchQuery(state.plugins),
hasFetched: state.plugins.hasFetched, hasFetched: state.plugins.hasFetched,
}; };
...@@ -79,7 +65,6 @@ function mapStateToProps(state: StoreState) { ...@@ -79,7 +65,6 @@ function mapStateToProps(state: StoreState) {
const mapDispatchToProps = { const mapDispatchToProps = {
loadPlugins, loadPlugins,
setPluginsLayoutMode,
setPluginsSearchQuery, setPluginsSearchQuery,
}; };
......
import React from 'react';
import { Icon, stylesFactory, useTheme, IconName, Tooltip } from '@grafana/ui';
import { GrafanaTheme, PluginSignatureStatus, getColorFromHexRgbOrName } from '@grafana/data';
import { css } from 'emotion';
import tinycolor from 'tinycolor2';
interface Props {
status: PluginSignatureStatus;
}
export const PluginSignatureBadge: React.FC<Props> = ({ status }) => {
const theme = useTheme();
const display = getSignatureDisplayModel(status);
const styles = getStyles(theme, display);
return (
<Tooltip content={display.tooltip} placement="left">
<div className={styles.wrapper}>
<Icon name={display.icon} size="sm" />
<span>{display.text}</span>
</div>
</Tooltip>
);
};
interface DisplayModel {
text: string;
icon: IconName;
color: string;
tooltip: string;
}
function getSignatureDisplayModel(signature: PluginSignatureStatus): DisplayModel {
switch (signature) {
case PluginSignatureStatus.internal:
return { text: 'Core', icon: 'cube', color: 'blue', tooltip: 'Core plugin that is bundled with Grafana' };
case PluginSignatureStatus.valid:
return { text: 'Signed', icon: 'lock', color: 'green', tooltip: 'Signed and verified plugin' };
case PluginSignatureStatus.invalid:
return {
text: 'Invalid',
icon: 'exclamation-triangle',
color: 'red',
tooltip: 'Invalid plugin signature',
};
case PluginSignatureStatus.modified:
return {
text: 'Modified',
icon: 'exclamation-triangle',
color: 'red',
tooltip: 'Valid signature but content has been modified',
};
}
return { text: 'Unsigned', icon: 'exclamation-triangle', color: 'red', tooltip: 'Unsigned external plugin' };
}
const getStyles = stylesFactory((theme: GrafanaTheme, model: DisplayModel) => {
let sourceColor = getColorFromHexRgbOrName(model.color);
let borderColor = '';
let bgColor = '';
let textColor = '';
if (theme.isDark) {
bgColor = tinycolor(sourceColor)
.darken(38)
.toString();
borderColor = tinycolor(sourceColor)
.darken(25)
.toString();
textColor = tinycolor(sourceColor)
.lighten(45)
.toString();
} else {
bgColor = tinycolor(sourceColor)
.lighten(30)
.toString();
borderColor = tinycolor(sourceColor)
.lighten(15)
.toString();
textColor = tinycolor(sourceColor)
.darken(40)
.toString();
}
return {
wrapper: css`
font-size: ${theme.typography.size.sm};
display: inline-flex;
padding: 1px 4px;
border-radius: 3px;
margin-top: 6px;
background: ${bgColor};
border: 1px solid ${borderColor};
color: ${textColor};
> span {
position: relative;
top: 1px;
margin-left: 2px;
}
`,
};
});
PluginSignatureBadge.displayName = 'PluginSignatureBadge';
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
exports[`Render should render component 1`] = ` exports[`Render should render component 1`] = `
<section <section
className="card-section card-list-layout-grid" className="card-section card-list-layout-list"
> >
<ol <ol
className="card-list" className="card-list"
......
...@@ -16,6 +16,7 @@ exports[`Render should render component 1`] = ` ...@@ -16,6 +16,7 @@ exports[`Render should render component 1`] = `
> >
panel panel
</div> </div>
<PluginSignatureBadge />
</div> </div>
<div <div
className="card-item-body" className="card-item-body"
...@@ -62,6 +63,7 @@ exports[`Render should render has plugin section 1`] = ` ...@@ -62,6 +63,7 @@ exports[`Render should render has plugin section 1`] = `
> >
panel panel
</div> </div>
<PluginSignatureBadge />
<div <div
className="card-item-notice" className="card-item-notice"
> >
......
...@@ -17,14 +17,12 @@ exports[`Render should render component 1`] = ` ...@@ -17,14 +17,12 @@ exports[`Render should render component 1`] = `
isLoading={true} isLoading={true}
> >
<OrgActionBar <OrgActionBar
layoutMode="grid"
linkButton={ linkButton={
Object { Object {
"href": "https://grafana.com/plugins?utm_source=grafana_plugin_list", "href": "https://grafana.com/plugins?utm_source=grafana_plugin_list",
"title": "Find more plugins on Grafana.com", "title": "Find more plugins on Grafana.com",
} }
} }
onSetLayoutMode={[Function]}
searchQuery="" searchQuery=""
setSearchQuery={[Function]} setSearchQuery={[Function]}
/> />
...@@ -49,19 +47,16 @@ exports[`Render should render list 1`] = ` ...@@ -49,19 +47,16 @@ exports[`Render should render list 1`] = `
isLoading={false} isLoading={false}
> >
<OrgActionBar <OrgActionBar
layoutMode="grid"
linkButton={ linkButton={
Object { Object {
"href": "https://grafana.com/plugins?utm_source=grafana_plugin_list", "href": "https://grafana.com/plugins?utm_source=grafana_plugin_list",
"title": "Find more plugins on Grafana.com", "title": "Find more plugins on Grafana.com",
} }
} }
onSetLayoutMode={[Function]}
searchQuery="" searchQuery=""
setSearchQuery={[Function]} setSearchQuery={[Function]}
/> />
<PluginList <PluginList
layoutMode="grid"
plugins={Array []} plugins={Array []}
/> />
</PageContents> </PageContents>
......
...@@ -6,11 +6,9 @@ import { ...@@ -6,11 +6,9 @@ import {
pluginDashboardsLoaded, pluginDashboardsLoaded,
pluginsLoaded, pluginsLoaded,
pluginsReducer, pluginsReducer,
setPluginsLayoutMode,
setPluginsSearchQuery, setPluginsSearchQuery,
} from './reducers'; } from './reducers';
import { PluginMetaInfo, PluginType } from '@grafana/data'; import { PluginMetaInfo, PluginType } from '@grafana/data';
import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelector';
describe('pluginsReducer', () => { describe('pluginsReducer', () => {
describe('when pluginsLoaded is dispatched', () => { describe('when pluginsLoaded is dispatched', () => {
...@@ -58,18 +56,6 @@ describe('pluginsReducer', () => { ...@@ -58,18 +56,6 @@ describe('pluginsReducer', () => {
}); });
}); });
describe('when setPluginsLayoutMode is dispatched', () => {
it('then state should be correct', () => {
reducerTester<PluginsState>()
.givenReducer(pluginsReducer, { ...initialState })
.whenActionIsDispatched(setPluginsLayoutMode(LayoutModes.List))
.thenStateShouldEqual({
...initialState,
layoutMode: LayoutModes.List,
});
});
});
describe('when pluginDashboardsLoad is dispatched', () => { describe('when pluginDashboardsLoad is dispatched', () => {
it('then state should be correct', () => { it('then state should be correct', () => {
reducerTester<PluginsState>() reducerTester<PluginsState>()
......
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { PluginMeta, PanelPlugin } from '@grafana/data'; import { PluginMeta, PanelPlugin } from '@grafana/data';
import { PluginsState } from 'app/types'; import { PluginsState } from 'app/types';
import { LayoutMode, LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelector';
import { PluginDashboard } from '../../../types/plugins'; import { PluginDashboard } from '../../../types/plugins';
export const initialState: PluginsState = { export const initialState: PluginsState = {
plugins: [], plugins: [],
searchQuery: '', searchQuery: '',
layoutMode: LayoutModes.Grid,
hasFetched: false, hasFetched: false,
dashboards: [], dashboards: [],
isLoadingPluginDashboards: false, isLoadingPluginDashboards: false,
...@@ -25,9 +23,6 @@ const pluginsSlice = createSlice({ ...@@ -25,9 +23,6 @@ const pluginsSlice = createSlice({
setPluginsSearchQuery: (state, action: PayloadAction<string>) => { setPluginsSearchQuery: (state, action: PayloadAction<string>) => {
state.searchQuery = action.payload; state.searchQuery = action.payload;
}, },
setPluginsLayoutMode: (state, action: PayloadAction<LayoutMode>) => {
state.layoutMode = action.payload;
},
pluginDashboardsLoad: (state, action: PayloadAction<undefined>) => { pluginDashboardsLoad: (state, action: PayloadAction<undefined>) => {
state.isLoadingPluginDashboards = true; state.isLoadingPluginDashboards = true;
state.dashboards = []; state.dashboards = [];
...@@ -46,7 +41,6 @@ export const { ...@@ -46,7 +41,6 @@ export const {
pluginsLoaded, pluginsLoaded,
pluginDashboardsLoad, pluginDashboardsLoad,
pluginDashboardsLoaded, pluginDashboardsLoaded,
setPluginsLayoutMode,
setPluginsSearchQuery, setPluginsSearchQuery,
panelPluginLoaded, panelPluginLoaded,
} = pluginsSlice.actions; } = pluginsSlice.actions;
......
...@@ -9,4 +9,3 @@ export const getPlugins = (state: PluginsState) => { ...@@ -9,4 +9,3 @@ export const getPlugins = (state: PluginsState) => {
}; };
export const getPluginsSearchQuery = (state: PluginsState) => state.searchQuery; export const getPluginsSearchQuery = (state: PluginsState) => state.searchQuery;
export const getLayoutMode = (state: PluginsState) => state.layoutMode;
...@@ -24,7 +24,6 @@ export interface PanelPluginsIndex { ...@@ -24,7 +24,6 @@ export interface PanelPluginsIndex {
export interface PluginsState { export interface PluginsState {
plugins: PluginMeta[]; plugins: PluginMeta[];
searchQuery: string; searchQuery: string;
layoutMode: string;
hasFetched: boolean; hasFetched: boolean;
dashboards: PluginDashboard[]; dashboards: PluginDashboard[];
isLoadingPluginDashboards: boolean; isLoadingPluginDashboards: boolean;
......
...@@ -143,9 +143,9 @@ $code-tag-bg: $dark-1; ...@@ -143,9 +143,9 @@ $code-tag-bg: $dark-1;
$code-tag-border: $dark-9; $code-tag-border: $dark-9;
// cards // cards
$card-background: linear-gradient(135deg, $dark-4, $dark-3); $card-background: #202226;
$card-background-hover: linear-gradient(135deg, $dark-5, $dark-6); $card-background-hover: #25272b;
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3); $card-shadow: none;
// Lists // Lists
$list-item-bg: $card-background; $list-item-bg: $card-background;
......
...@@ -136,9 +136,9 @@ $code-tag-bg: $gray-6; ...@@ -136,9 +136,9 @@ $code-tag-bg: $gray-6;
$code-tag-border: $gray-4; $code-tag-border: $gray-4;
// cards // cards
$card-background: linear-gradient(135deg, $gray-6, $gray-7); $card-background: #f1f5f9;
$card-background-hover: linear-gradient(135deg, $gray-6, $gray-5); $card-background-hover: #eaf0f6;
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.1); $card-shadow: none;
// Lists // Lists
$list-item-bg: $gray-7; $list-item-bg: $gray-7;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
box-shadow: $card-shadow;
background: $panel-editor-viz-item-bg; background: $panel-editor-viz-item-bg;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 3px; border-radius: 3px;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment