mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AddDataSource: Updated page design & categories (#16971)
* minor refactoring * Added category * Minor progress * Progres * Updated descriptions * Added custom sort * NewDataSource: progress * Updated design * NewDataSource: Updated design * Updated link * Feedback changes
This commit is contained in:
parent
99c754c4a5
commit
e1d408a66f
@ -33,6 +33,7 @@ export { UnitPicker } from './UnitPicker/UnitPicker';
|
||||
export { StatsPicker } from './StatsPicker/StatsPicker';
|
||||
export { Input, InputStatus } from './Input/Input';
|
||||
export { RefreshPicker } from './RefreshPicker/RefreshPicker';
|
||||
export { List } from './List/List';
|
||||
|
||||
// Renderless
|
||||
export { SetInterval } from './SetInterval/SetInterval';
|
||||
|
@ -79,7 +79,9 @@ export interface DataSourcePluginMeta extends PluginMeta {
|
||||
annotations?: boolean;
|
||||
mixed?: boolean;
|
||||
hasQueryHelp?: boolean;
|
||||
category?: string;
|
||||
queryOptions?: PluginMetaQueryOptions;
|
||||
sort?: number;
|
||||
}
|
||||
|
||||
interface PluginMetaQueryOptions {
|
||||
|
@ -34,6 +34,7 @@ type PluginListItem struct {
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
HasUpdate bool `json:"hasUpdate"`
|
||||
DefaultNavUrl string `json:"defaultNavUrl"`
|
||||
Category string `json:"category"`
|
||||
State plugins.PluginState `json:"state"`
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ func (hs *HTTPServer) GetPluginList(c *m.ReqContext) Response {
|
||||
Id: pluginDef.Id,
|
||||
Name: pluginDef.Name,
|
||||
Type: pluginDef.Type,
|
||||
Category: pluginDef.Category,
|
||||
Info: &pluginDef.Info,
|
||||
LatestVersion: pluginDef.GrafanaNetVersion,
|
||||
HasUpdate: pluginDef.GrafanaNetHasUpdate,
|
||||
|
@ -45,6 +45,7 @@ type PluginBase struct {
|
||||
Includes []*PluginInclude `json:"includes"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Category string `json:"category"`
|
||||
HideFromList bool `json:"hideFromList,omitempty"`
|
||||
Preload bool `json:"preload"`
|
||||
State PluginState `json:"state,omitempty"`
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, FC } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { StoreState } from 'app/types';
|
||||
import { addDataSource, loadDataSourceTypes, setDataSourceTypeSearchQuery } from './state/actions';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getDataSourceTypes } from './state/selectors';
|
||||
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
|
||||
import { NavModel, DataSourcePluginMeta } from '@grafana/ui';
|
||||
import { NavModel, DataSourcePluginMeta, List } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
@ -15,13 +14,40 @@ export interface Props {
|
||||
isLoading: boolean;
|
||||
addDataSource: typeof addDataSource;
|
||||
loadDataSourceTypes: typeof loadDataSourceTypes;
|
||||
dataSourceTypeSearchQuery: string;
|
||||
searchQuery: string;
|
||||
setDataSourceTypeSearchQuery: typeof setDataSourceTypeSearchQuery;
|
||||
}
|
||||
|
||||
interface DataSourceCategories {
|
||||
[key: string]: DataSourcePluginMeta[];
|
||||
}
|
||||
|
||||
interface DataSourceCategoryInfo {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
class NewDataSourcePage extends PureComponent<Props> {
|
||||
searchInput: HTMLElement;
|
||||
categoryInfoList: DataSourceCategoryInfo[] = [
|
||||
{ id: 'tsdb', title: 'Time series databases' },
|
||||
{ id: 'logging', title: 'Logging & document databases' },
|
||||
{ id: 'sql', title: 'SQL' },
|
||||
{ id: 'cloud', title: 'Cloud' },
|
||||
{ id: 'other', title: 'Others' },
|
||||
];
|
||||
|
||||
sortingRules: { [id: string]: number } = {
|
||||
prometheus: 100,
|
||||
graphite: 95,
|
||||
loki: 90,
|
||||
mysql: 80,
|
||||
postgres: 79,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadDataSourceTypes();
|
||||
this.searchInput.focus();
|
||||
}
|
||||
|
||||
onDataSourceTypeClicked = (plugin: DataSourcePluginMeta) => {
|
||||
@ -32,35 +58,108 @@ class NewDataSourcePage extends PureComponent<Props> {
|
||||
this.props.setDataSourceTypeSearchQuery(value);
|
||||
};
|
||||
|
||||
renderTypes(types: DataSourcePluginMeta[]) {
|
||||
if (!types) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// apply custom sort ranking
|
||||
types.sort((a, b) => {
|
||||
const aSort = this.sortingRules[a.id] || 0;
|
||||
const bSort = this.sortingRules[b.id] || 0;
|
||||
if (aSort > bSort) {
|
||||
return -1;
|
||||
}
|
||||
if (aSort < bSort) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.name > b.name ? -1 : 1;
|
||||
});
|
||||
|
||||
return (
|
||||
<List
|
||||
items={types}
|
||||
getItemKey={item => item.id.toString()}
|
||||
renderItem={item => (
|
||||
<DataSourceTypeCard
|
||||
plugin={item}
|
||||
onClick={() => this.onDataSourceTypeClicked(item)}
|
||||
onLearnMoreClick={this.onLearnMoreClick}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onLearnMoreClick = (evt: React.SyntheticEvent<HTMLElement>) => {
|
||||
evt.stopPropagation();
|
||||
};
|
||||
|
||||
renderGroupedList() {
|
||||
const { dataSourceTypes } = this.props;
|
||||
|
||||
if (dataSourceTypes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const categories = dataSourceTypes.reduce(
|
||||
(accumulator, item) => {
|
||||
const category = item.category || 'other';
|
||||
const list = accumulator[category] || [];
|
||||
list.push(item);
|
||||
accumulator[category] = list;
|
||||
return accumulator;
|
||||
},
|
||||
{} as DataSourceCategories
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.categoryInfoList.map(category => (
|
||||
<div className="add-data-source-category" key={category.id}>
|
||||
<div className="add-data-source-category__header">{category.title}</div>
|
||||
{this.renderTypes(categories[category.id])}
|
||||
</div>
|
||||
))}
|
||||
<div className="add-data-source-more">
|
||||
<a
|
||||
className="btn btn-inverse"
|
||||
href="https://grafana.com/plugins?type=datasource&utm_source=new-data-source"
|
||||
target="_blank"
|
||||
>
|
||||
Find more data source plugins on grafana.com
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navModel, dataSourceTypes, dataSourceTypeSearchQuery, isLoading } = this.props;
|
||||
const { navModel, isLoading, searchQuery, dataSourceTypes } = this.props;
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents isLoading={isLoading}>
|
||||
<h2 className="add-data-source-header">Choose data source type</h2>
|
||||
<div className="add-data-source-search">
|
||||
<FilterInput
|
||||
labelClassName="gf-form--has-input-icon"
|
||||
inputClassName="gf-form-input width-20"
|
||||
value={dataSourceTypeSearchQuery}
|
||||
onChange={this.onSearchQueryChange}
|
||||
placeholder="Filter by name or type"
|
||||
/>
|
||||
<div className="page-action-bar">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<FilterInput
|
||||
ref={elem => (this.searchInput = elem)}
|
||||
labelClassName="gf-form--has-input-icon"
|
||||
inputClassName="gf-form-input width-30"
|
||||
value={searchQuery}
|
||||
onChange={this.onSearchQueryChange}
|
||||
placeholder="Filter by name or type"
|
||||
/>
|
||||
</div>
|
||||
<div className="page-action-bar__spacer" />
|
||||
<a className="btn btn-secondary" href="datasources">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
<div className="add-data-source-grid">
|
||||
{dataSourceTypes.map((plugin, index) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => this.onDataSourceTypeClicked(plugin)}
|
||||
className="add-data-source-grid-item"
|
||||
key={`${plugin.id}-${index}`}
|
||||
aria-label={`${plugin.name} datasource plugin`}
|
||||
>
|
||||
<img className="add-data-source-grid-item-logo" src={plugin.info.logos.small} />
|
||||
<span className="add-data-source-grid-item-text">{plugin.name}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div>
|
||||
{searchQuery && this.renderTypes(dataSourceTypes)}
|
||||
{!searchQuery && this.renderGroupedList()}
|
||||
</div>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
@ -68,11 +167,57 @@ class NewDataSourcePage extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
interface DataSourceTypeCardProps {
|
||||
plugin: DataSourcePluginMeta;
|
||||
onClick: () => void;
|
||||
onLearnMoreClick: (evt: React.SyntheticEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
|
||||
const { plugin, onClick, onLearnMoreClick } = props;
|
||||
|
||||
// find first plugin info link
|
||||
const learnMoreLink = plugin.info.links && plugin.info.links.length > 0 ? plugin.info.links[0].url : null;
|
||||
|
||||
return (
|
||||
<div className="add-data-source-item" onClick={onClick} aria-label={`${plugin.name} datasource plugin`}>
|
||||
<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>}
|
||||
</div>
|
||||
<div className="add-data-source-item-actions">
|
||||
{learnMoreLink && (
|
||||
<a className="btn btn-inverse" href={learnMoreLink} target="_blank" onClick={onLearnMoreClick}>
|
||||
Learn more
|
||||
</a>
|
||||
)}
|
||||
<button className="btn btn-primary">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function getNavModel(): NavModel {
|
||||
const main = {
|
||||
icon: 'gicon gicon-add-datasources',
|
||||
id: 'datasource-new',
|
||||
text: 'New data source',
|
||||
href: 'datasources/new',
|
||||
subTitle: 'Choose a data source type',
|
||||
};
|
||||
|
||||
return {
|
||||
main: main,
|
||||
node: main,
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: StoreState) {
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, 'datasources'),
|
||||
navModel: getNavModel(),
|
||||
dataSourceTypes: getDataSourceTypes(state.dataSources),
|
||||
dataSourceTypeSearchQuery: state.dataSources.dataSourceTypeSearchQuery,
|
||||
searchQuery: state.dataSources.dataSourceTypeSearchQuery,
|
||||
isLoading: state.dataSources.isLoadingDataSources,
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import angular from 'angular';
|
||||
const template = `
|
||||
<input type="file" id="dashupload" name="dashupload" class="hide" onchange="angular.element(this).scope().file_selected"/>
|
||||
<label class="btn btn-primary" for="dashupload">
|
||||
<i class="fa fa-upload"></i>
|
||||
{{btnText}}
|
||||
</label>
|
||||
`;
|
||||
@ -20,7 +19,7 @@ export function uploadDashboardDirective(timer, $location) {
|
||||
btnText: '@?',
|
||||
},
|
||||
link: (scope, elem) => {
|
||||
scope.btnText = angular.isDefined(scope.btnText) ? scope.btnText : 'Upload .json File';
|
||||
scope.btnText = angular.isDefined(scope.btnText) ? scope.btnText : 'Upload .json file';
|
||||
|
||||
function file_selected(evt) {
|
||||
const files = evt.target.files; // FileList object
|
||||
|
@ -2,13 +2,14 @@
|
||||
"type": "datasource",
|
||||
"name": "CloudWatch",
|
||||
"id": "cloudwatch",
|
||||
"category": "cloud",
|
||||
|
||||
"metrics": true,
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
|
||||
"info": {
|
||||
"description": "Cloudwatch Data Source for Grafana",
|
||||
"description": "Data source for Amazon AWS monitoring service",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,9 +2,10 @@
|
||||
"type": "datasource",
|
||||
"name": "Elasticsearch",
|
||||
"id": "elasticsearch",
|
||||
"category": "logging",
|
||||
|
||||
"info": {
|
||||
"description": "Elasticsearch Data Source for Grafana",
|
||||
"description": "Open source logging & analytics database",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@ -14,7 +15,7 @@
|
||||
"small": "img/elasticsearch.svg",
|
||||
"large": "img/elasticsearch.svg"
|
||||
},
|
||||
"links": [{ "name": "elastic.co", "url": "https://www.elastic.co/products/elasticsearch" }]
|
||||
"links": [{ "name": "Learn more", "url": "https://grafana.com/docs/features/datasources/elasticsearch/" }]
|
||||
},
|
||||
|
||||
"alerting": true,
|
||||
|
@ -2,9 +2,10 @@
|
||||
"type": "datasource",
|
||||
"name": "Azure Monitor",
|
||||
"id": "grafana-azure-monitor-datasource",
|
||||
"category": "cloud",
|
||||
|
||||
"info": {
|
||||
"description": "Grafana data source for Azure Monitor/Application Insights",
|
||||
"description": "Data source for Microsoft Azure Monitor & Application Insights",
|
||||
"author": {
|
||||
"name": "Grafana Labs",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"name": "Graphite",
|
||||
"type": "datasource",
|
||||
"id": "graphite",
|
||||
"category": "tsdb",
|
||||
|
||||
"includes": [{ "type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json" }],
|
||||
|
||||
@ -16,7 +17,7 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Graphite Data Source for Grafana",
|
||||
"description": "Open source time series database",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"type": "datasource",
|
||||
"name": "InfluxDB",
|
||||
"id": "influxdb",
|
||||
"category": "tsdb",
|
||||
|
||||
"defaultMatchFormat": "regex values",
|
||||
"metrics": true,
|
||||
@ -14,7 +15,7 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "InfluxDB Data Source for Grafana",
|
||||
"description": "Open source time series database",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -11,7 +11,7 @@
|
||||
"explore": false,
|
||||
|
||||
"info": {
|
||||
"description": "User Input Data Source for Grafana",
|
||||
"description": "Data source that supports manual table & CSV input",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"type": "datasource",
|
||||
"name": "Loki",
|
||||
"id": "loki",
|
||||
"category": "logging",
|
||||
|
||||
"metrics": true,
|
||||
"alerting": false,
|
||||
@ -11,7 +12,7 @@
|
||||
"tables": false,
|
||||
|
||||
"info": {
|
||||
"description": "Loki Logging Data Source for Grafana",
|
||||
"description": "Like Prometheus but for logs. OSS logging solution from Grafana Labs",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
@ -22,7 +23,11 @@
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"name": "Loki",
|
||||
"name": "Learn more",
|
||||
"url": "https://grafana.com/loki"
|
||||
},
|
||||
{
|
||||
"name": "GitHub Project",
|
||||
"url": "https://github.com/grafana/loki"
|
||||
}
|
||||
],
|
||||
|
@ -2,9 +2,10 @@
|
||||
"type": "datasource",
|
||||
"name": "Microsoft SQL Server",
|
||||
"id": "mssql",
|
||||
"category": "sql",
|
||||
|
||||
"info": {
|
||||
"description": "Microsoft SQL Server Data Source for Grafana",
|
||||
"description": "Data source for Microsoft SQL Server compatible databases",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,9 +2,10 @@
|
||||
"type": "datasource",
|
||||
"name": "MySQL",
|
||||
"id": "mysql",
|
||||
"category": "sql",
|
||||
|
||||
"info": {
|
||||
"description": "MySQL Data Source for Grafana",
|
||||
"description": "Data source for MySQL databases",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"type": "datasource",
|
||||
"name": "OpenTSDB",
|
||||
"id": "opentsdb",
|
||||
"category": "tsdb",
|
||||
|
||||
"metrics": true,
|
||||
"defaultMatchFormat": "pipe",
|
||||
@ -10,7 +11,7 @@
|
||||
"tables": false,
|
||||
|
||||
"info": {
|
||||
"description": "OpenTSDB Data Source for Grafana",
|
||||
"description": "Open source time series database",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,9 +2,10 @@
|
||||
"type": "datasource",
|
||||
"name": "PostgreSQL",
|
||||
"id": "postgres",
|
||||
"category": "sql",
|
||||
|
||||
"info": {
|
||||
"description": "PostgreSQL Data Source for Grafana",
|
||||
"description": "Data source for PostgreSQL and compatible databases",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,8 @@
|
||||
"type": "datasource",
|
||||
"name": "Prometheus",
|
||||
"id": "prometheus",
|
||||
"category": "tsdb",
|
||||
|
||||
"includes": [
|
||||
{
|
||||
"type": "dashboard",
|
||||
@ -28,7 +30,7 @@
|
||||
"minInterval": true
|
||||
},
|
||||
"info": {
|
||||
"description": "Prometheus Data Source for Grafana",
|
||||
"description": "Open source time series database & alerting",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
|
@ -2,6 +2,8 @@
|
||||
"name": "Stackdriver",
|
||||
"type": "datasource",
|
||||
"id": "stackdriver",
|
||||
"category": "cloud",
|
||||
|
||||
"metrics": true,
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
@ -10,8 +12,9 @@
|
||||
"maxDataPoints": true,
|
||||
"cacheTimeout": true
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Google Stackdriver Datasource for Grafana",
|
||||
"description": "Data source for Google's monitoring service",
|
||||
"version": "1.0.0",
|
||||
"logos": {
|
||||
"small": "img/stackdriver_logo.svg",
|
||||
|
1
public/app/plugins/datasource/testdata/img/testdata.svg
vendored
Normal file
1
public/app/plugins/datasource/testdata/img/testdata.svg
vendored
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="100%" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;" version="1.1" viewBox="0 0 64 64" width="100%" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:serif="http://www.serif.com/" xmlns:xlink="http://www.w3.org/1999/xlink"><rect height="64" id="comparison--data--analytics" style="fill:none;" width="64" x="0" y="0"/><path d="M22,11c0,-0.552 -0.448,-1 -1,-1c-1.916,0 -6.084,0 -8,0c-0.552,0 -1,0.448 -1,1c0,4.3 0,21 0,21l10,0c0,0 0,-16.7 0,-21Z" style="fill:#ccff90;"/><path d="M42,43c0,0.552 0.448,1 1,1c1.916,0 6.084,0 8,0c0.552,0 1,-0.448 1,-1c0,-2.882 0,-11 0,-11l-10,0c0,0 0,8.118 0,11Z" style="fill:#6ae573;"/><path d="M37,18c0,-0.552 -0.448,-1 -1,-1c-1.916,0 -6.084,0 -8,0c-0.552,0 -1,0.448 -1,1c0,3.355 0,14 0,14l10,0c0,0 0,-10.645 0,-14Z" style="fill:#ccff90;"/><path d="M27,39c0,0.552 0.448,1 1,1c1.916,0 6.084,0 8,0c0.552,0 1,-0.448 1,-1c0,-2.146 0,-7 0,-7l-10,0c0,0 0,4.854 0,7Z" style="fill:#6ae573;"/><path d="M52,23c0,-0.552 -0.448,-1 -1,-1c-1.916,0 -6.084,0 -8,0c-0.552,0 -1,0.448 -1,1c0,2.533 0,9 0,9l10,0c0,0 0,-6.467 0,-9Z" style="fill:#ccff90;"/><path d="M12,47c0,0.552 0.448,1 1,1c1.916,0 6.084,0 8,0c0.552,0 1,-0.448 1,-1c0,-3.502 0,-15 0,-15l-10,0c0,0 0,11.498 0,15Z" style="fill:#6ae573;"/><rect height="2" style="fill:#00c853;" width="48" x="8" y="31"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -12,13 +12,14 @@
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Generates test data in different forms",
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
"logos": {
|
||||
"small": "../../../../img/grafana_icon.svg",
|
||||
"large": "../../../../img/grafana_icon.svg"
|
||||
"small": "img/testdata.svg",
|
||||
"large": "img/testdata.svg"
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -10,37 +10,74 @@
|
||||
margin-bottom: $space-lg;
|
||||
}
|
||||
|
||||
.add-data-source-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-row-gap: 10px;
|
||||
grid-column-gap: 10px;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.add-data-source-category {
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.add-data-source-grid-item {
|
||||
padding: 15px;
|
||||
.add-data-source-category__header {
|
||||
font-size: $font-size-h5;
|
||||
margin-bottom: $space-sm;
|
||||
}
|
||||
|
||||
.add-data-source-item {
|
||||
padding: $space-md;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
background: $card-background;
|
||||
box-shadow: $card-shadow;
|
||||
color: $text-color;
|
||||
background: $panel-editor-viz-item-bg;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
margin-bottom: $space-xxs;
|
||||
|
||||
&:hover {
|
||||
background: $card-background-hover;
|
||||
box-shadow: $panel-editor-viz-item-shadow-hover;
|
||||
background: $panel-editor-viz-item-bg-hover;
|
||||
border: $panel-editor-viz-item-border-hover;
|
||||
color: $text-color-strong;
|
||||
|
||||
.add-data-source-item-actions {
|
||||
opacity: 1;
|
||||
transition: 0.15s opacity ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-data-source-grid-item-text {
|
||||
.add-data-source-item-text-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.add-data-source-item-desc {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-color-weak;
|
||||
}
|
||||
|
||||
.add-data-source-item-text {
|
||||
font-size: $font-size-h5;
|
||||
}
|
||||
|
||||
.add-data-source-grid-item-logo {
|
||||
margin: 0 $space-md;
|
||||
.add-data-source-item-logo {
|
||||
margin-right: $space-lg;
|
||||
margin-left: $space-sm;
|
||||
width: 55px;
|
||||
max-height: 55px;
|
||||
}
|
||||
|
||||
.add-data-source-item-actions {
|
||||
opacity: 0;
|
||||
padding-left: $space-md;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> button {
|
||||
margin-left: $space-md;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.add-data-source-more {
|
||||
text-align: center;
|
||||
margin: $space-xl;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user