mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
data source picker demo state
This commit is contained in:
89
public/app/features/dashboard/dashgrid/DataSourcePicker.tsx
Normal file
89
public/app/features/dashboard/dashgrid/DataSourcePicker.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { DataSourceSelectItem } from 'app/types';
|
||||
|
||||
interface Props {}
|
||||
|
||||
interface State {
|
||||
datasources: DataSourceSelectItem[];
|
||||
searchQuery: string;
|
||||
}
|
||||
|
||||
export class DataSourcePicker extends PureComponent<Props, State> {
|
||||
searchInput: HTMLElement;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
datasources: getDatasourceSrv().getMetricSources(),
|
||||
searchQuery: '',
|
||||
};
|
||||
}
|
||||
|
||||
getDataSources() {
|
||||
const { datasources, searchQuery } = this.state;
|
||||
const regex = new RegExp(searchQuery, 'i');
|
||||
|
||||
const filtered = datasources.filter(item => {
|
||||
return regex.test(item.name) || regex.test(item.meta.name);
|
||||
});
|
||||
|
||||
return _.sortBy(filtered, 'sort');
|
||||
}
|
||||
|
||||
renderDataSource = (ds: DataSourceSelectItem, index) => {
|
||||
const cssClass = classNames({
|
||||
'ds-picker-list__item': true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div key={index} className={cssClass} title={ds.name}>
|
||||
<img className="ds-picker-list__img" src={ds.meta.info.logos.small} />
|
||||
<div className="ds-picker-list__name">{ds.name}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
renderFilters() {
|
||||
return (
|
||||
<>
|
||||
<label className="gf-form--has-input-icon">
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input width-13"
|
||||
placeholder=""
|
||||
ref={elem => (this.searchInput = elem)}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
</label>
|
||||
<div className="p-l-1">
|
||||
<button className="btn toggle-btn gf-form-btn active">All</button>
|
||||
<button className="btn toggle-btn gf-form-btn">Favorites</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="cta-form__bar">
|
||||
<div className="cta-form__bar-header">Select data source</div>
|
||||
{this.renderFilters()}
|
||||
<div className="gf-form--grow" />
|
||||
</div>
|
||||
<div className="ds-picker-list">{this.getDataSources().map(this.renderDataSource)}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,15 @@ import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar
|
||||
import { FadeIn } from 'app/core/components/Animations/FadeIn';
|
||||
|
||||
interface Props {
|
||||
selectedText?: string;
|
||||
selectedImage?: string;
|
||||
children: JSX.Element;
|
||||
main: EditorToolBarView;
|
||||
toolbarItems: EditorToolBarView[];
|
||||
}
|
||||
|
||||
export interface EditorToolBarView {
|
||||
title: string;
|
||||
imgSrc: string;
|
||||
imgSrc?: string;
|
||||
icon?: string;
|
||||
render: () => JSX.Element;
|
||||
}
|
||||
|
||||
@@ -32,18 +32,28 @@ export class EditorTabBody extends PureComponent<Props, State> {
|
||||
this.setState({
|
||||
openView: item === this.state.openView ? null : item,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onCloseOpenView = () => {
|
||||
this.setState({ openView: null });
|
||||
};
|
||||
|
||||
renderMainSelection(view: EditorToolBarView) {
|
||||
return (
|
||||
<div className="edit-section__selected" onClick={() => this.onToggleToolBarView(view)} key={view.title}>
|
||||
<img className="edit-section__selected-image" src={view.imgSrc} />
|
||||
<div className="edit-section__selected-name">{view.title}</div>
|
||||
<i className="fa fa-caret-down" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderToolBarViewToggle(item: EditorToolBarView) {
|
||||
renderButton(view: EditorToolBarView) {
|
||||
return (
|
||||
<div className="edit-section__selected" onClick={() => this.onToggleToolBarView(item)} key={item.title}>
|
||||
<img className="edit-section__selected-image" src={item.imgSrc} />
|
||||
<div className="edit-section__selected-name">{item.title}</div>
|
||||
<i className="fa fa-caret-down" />
|
||||
<div className="nav-buttons" key={view.title}>
|
||||
<button className="btn navbar-button">
|
||||
<i className={view.icon} /> {view.title}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -60,25 +70,29 @@ export class EditorTabBody extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, toolbarItems} = this.props;
|
||||
const { children, toolbarItems, main } = this.props;
|
||||
const { openView } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="edit-section">
|
||||
<div className="edit-section__header">{toolbarItems.map(item => this.renderToolBarViewToggle(item))}</div>
|
||||
</div>
|
||||
<div className="panel-editor__scroll">
|
||||
<CustomScrollbar>
|
||||
<div className="panel-editor__content">
|
||||
<FadeIn in={openView !== null} duration={300}>
|
||||
{openView && this.renderOpenView(openView)}
|
||||
</FadeIn>
|
||||
{children}
|
||||
<div className="edit-section">
|
||||
<div className="edit-section__header">
|
||||
{this.renderMainSelection(main)}
|
||||
<div className="gf-form--grow" />
|
||||
{toolbarItems.map(item => this.renderButton(item))}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-editor__scroll">
|
||||
<CustomScrollbar>
|
||||
<div className="panel-editor__content">
|
||||
<FadeIn in={openView !== null} duration={200}>
|
||||
{openView && this.renderOpenView(openView)}
|
||||
</FadeIn>
|
||||
{children}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Services & utils
|
||||
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
|
||||
import { EditorTabBody } from './EditorTabBody';
|
||||
import { DataSourcePicker } from './DataSourcePicker';
|
||||
|
||||
// Types
|
||||
import { PanelModel } from '../panel_model';
|
||||
import { DashboardModel } from '../dashboard_model';
|
||||
|
||||
@@ -52,15 +50,23 @@ export class QueriesTab extends PureComponent<Props> {
|
||||
const currentDataSource = {
|
||||
title: 'ProductionDB',
|
||||
imgSrc: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
|
||||
render: () => {
|
||||
return (
|
||||
<h2>Hello</h2>
|
||||
);
|
||||
},
|
||||
render: () => <DataSourcePicker />,
|
||||
};
|
||||
|
||||
const queryInspector = {
|
||||
title: 'Query Inspector',
|
||||
icon: 'fa fa-lightbulb-o',
|
||||
render: () => <h2>hello</h2>,
|
||||
};
|
||||
|
||||
const dsHelp = {
|
||||
title: 'Help',
|
||||
icon: 'fa fa-question',
|
||||
render: () => <h2>hello</h2>,
|
||||
};
|
||||
|
||||
return (
|
||||
<EditorTabBody toolbarItems={[currentDataSource]}>
|
||||
<EditorTabBody main={currentDataSource} toolbarItems={[dsHelp, queryInspector]}>
|
||||
<div ref={element => (this.element = element)} style={{ width: '100%' }} />
|
||||
</EditorTabBody>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@ interface Props {
|
||||
}
|
||||
|
||||
export class VisualizationTab extends PureComponent<Props> {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
@@ -37,7 +36,7 @@ export class VisualizationTab extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {plugin, onTypeChanged} = this.props;
|
||||
const { plugin, onTypeChanged } = this.props;
|
||||
|
||||
const panelSelection = {
|
||||
title: plugin.name,
|
||||
@@ -48,7 +47,7 @@ export class VisualizationTab extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
return (
|
||||
<EditorTabBody toolbarItems={[panelSelection]}>
|
||||
<EditorTabBody main={panelSelection} toolbarItems={[]}>
|
||||
{this.renderPanelOptions()}
|
||||
</EditorTabBody>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,8 @@ interface State {
|
||||
}
|
||||
|
||||
export class VizTypePicker extends PureComponent<Props, State> {
|
||||
searchInput: HTMLElement;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -47,11 +49,22 @@ export class VizTypePicker extends PureComponent<Props, State> {
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
renderFilters() {
|
||||
return (
|
||||
<>
|
||||
<label className="gf-form--has-input-icon">
|
||||
<input type="text" className="gf-form-input width-13" placeholder="" />
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input width-13"
|
||||
placeholder=""
|
||||
ref={elem => (this.searchInput = elem)}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
</label>
|
||||
<div className="p-l-1">
|
||||
@@ -63,7 +76,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { current } = this.props;
|
||||
const { pluginList } = this.state;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// Libraries
|
||||
import _ from 'lodash';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
// Utils
|
||||
import config from 'app/core/config';
|
||||
import { importPluginModule } from './plugin_loader';
|
||||
|
||||
// Types
|
||||
import { DataSourceApi } from 'app/types/series';
|
||||
import { DataSource } from 'app/types';
|
||||
import { DataSource, DataSourceSelectItem } from 'app/types';
|
||||
|
||||
export class DatasourceSrv {
|
||||
datasources: { [name: string]: DataSource };
|
||||
@@ -102,8 +99,8 @@ export class DatasourceSrv {
|
||||
return _.sortBy(es, ['name']);
|
||||
}
|
||||
|
||||
getMetricSources(options) {
|
||||
const metricSources = [];
|
||||
getMetricSources(options?) {
|
||||
const metricSources: DataSourceSelectItem[] = [];
|
||||
|
||||
_.each(config.datasources, (value, key) => {
|
||||
if (value.meta && value.meta.metrics) {
|
||||
|
||||
@@ -22,6 +22,13 @@ export interface DataSource {
|
||||
testDatasource?: () => Promise<any>;
|
||||
}
|
||||
|
||||
export interface DataSourceSelectItem {
|
||||
name: string;
|
||||
value: string | null;
|
||||
meta: PluginMeta;
|
||||
sort: string;
|
||||
}
|
||||
|
||||
export interface DataSourcesState {
|
||||
dataSources: DataSource[];
|
||||
searchQuery: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DashboardState } from './dashboard';
|
||||
import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
|
||||
import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys';
|
||||
import { Invitee, OrgUser, User, UsersState, UserState } from './user';
|
||||
import { DataSource, DataSourcesState } from './datasources';
|
||||
import { DataSource, DataSourceSelectItem, DataSourcesState } from './datasources';
|
||||
import {
|
||||
TimeRange,
|
||||
LoadingState,
|
||||
@@ -55,6 +55,7 @@ export {
|
||||
OrgRole,
|
||||
PermissionLevel,
|
||||
DataSource,
|
||||
DataSourceSelectItem,
|
||||
PluginMeta,
|
||||
ApiKey,
|
||||
ApiKeysState,
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
background-image: linear-gradient(to right, #ffd500 0%, #ff4400 99%, #ff4400 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.active--panel {
|
||||
background: $panel-bg !important;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.viz-picker__search {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.viz-picker {
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
@@ -64,7 +60,6 @@
|
||||
.viz-picker__items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
// for scrollbar
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
|
||||
@@ -192,7 +187,7 @@
|
||||
border-radius: $input-border-radius;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.fa {
|
||||
.fa {
|
||||
margin-left: 20px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
@@ -247,6 +242,7 @@
|
||||
background-color: $empty-list-cta-bg;
|
||||
border-top: 3px solid $orange;
|
||||
margin: 0 100px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.editor-toolbar-view__close {
|
||||
@@ -254,8 +250,8 @@
|
||||
padding: 4px 8px 4px 9px;
|
||||
border: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -2px;
|
||||
right: 9px;
|
||||
top: 7px;
|
||||
font-size: $font-size-md;
|
||||
|
||||
&:hover {
|
||||
@@ -263,3 +259,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ds-picker-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 13px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ds-picker-list__item {
|
||||
background: $card-background;
|
||||
box-shadow: $card-shadow;
|
||||
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
margin-bottom: 3px;
|
||||
padding: 5px 15px;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: $card-background-hover;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
.ds-picker-list__name {
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ds-picker-list__name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-size: $font-size-md;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.ds-picker-list__img {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user