diff --git a/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx b/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx new file mode 100644 index 00000000000..d8fb1b9d48e --- /dev/null +++ b/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx @@ -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 { + 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 ( +
+ +
{ds.name}
+
+ ); + }; + + componentDidMount() { + setTimeout(() => { + this.searchInput.focus(); + }, 300); + } + + renderFilters() { + return ( + <> + +
+ + +
+ + ); + } + + render() { + return ( + <> +
+
Select data source
+ {this.renderFilters()} +
+
+
{this.getDataSources().map(this.renderDataSource)}
+ + ); + } +} diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index 2460514eda1..f7ba511f086 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -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 { this.setState({ openView: item === this.state.openView ? null : item, }); - } + }; onCloseOpenView = () => { this.setState({ openView: null }); + }; + + renderMainSelection(view: EditorToolBarView) { + return ( +
this.onToggleToolBarView(view)} key={view.title}> + +
{view.title}
+ +
+ ); } - renderToolBarViewToggle(item: EditorToolBarView) { + renderButton(view: EditorToolBarView) { return ( -
this.onToggleToolBarView(item)} key={item.title}> - -
{item.title}
- +
+
); } @@ -60,25 +70,29 @@ export class EditorTabBody extends PureComponent { } render() { - const { children, toolbarItems} = this.props; + const { children, toolbarItems, main } = this.props; const { openView } = this.state; return ( <> -
-
{toolbarItems.map(item => this.renderToolBarViewToggle(item))}
-
-
- -
- - {openView && this.renderOpenView(openView)} - - {children} +
+
+ {this.renderMainSelection(main)} +
+ {toolbarItems.map(item => this.renderButton(item))}
- -
+
+
+ +
+ + {openView && this.renderOpenView(openView)} + + {children} +
+
+
- ); + ); } } diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index e1884992901..873924737e7 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -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 { const currentDataSource = { title: 'ProductionDB', imgSrc: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg', - render: () => { - return ( -

Hello

- ); - }, + render: () => , + }; + + const queryInspector = { + title: 'Query Inspector', + icon: 'fa fa-lightbulb-o', + render: () =>

hello

, + }; + + const dsHelp = { + title: 'Help', + icon: 'fa fa-question', + render: () =>

hello

, }; return ( - +
(this.element = element)} style={{ width: '100%' }} /> ); diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx index 34d37c0e61b..ad20ed514af 100644 --- a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx +++ b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx @@ -15,7 +15,6 @@ interface Props { } export class VisualizationTab extends PureComponent { - constructor(props) { super(props); } @@ -37,7 +36,7 @@ export class VisualizationTab extends PureComponent { }; 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 { }; return ( - + {this.renderPanelOptions()} ); diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx index 7e87670955b..bd4895514ad 100644 --- a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx +++ b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx @@ -15,6 +15,8 @@ interface State { } export class VizTypePicker extends PureComponent { + searchInput: HTMLElement; + constructor(props) { super(props); @@ -47,11 +49,22 @@ export class VizTypePicker extends PureComponent { ); }; + componentDidMount() { + setTimeout(() => { + this.searchInput.focus(); + }, 300); + } + renderFilters() { return ( <>
@@ -63,7 +76,6 @@ export class VizTypePicker extends PureComponent { } render() { - const { current } = this.props; const { pluginList } = this.state; return ( diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index fed455472c9..d3c90c2ae43 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -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) { diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts index 5522f3a11ce..f247d13bf5a 100644 --- a/public/app/types/datasources.ts +++ b/public/app/types/datasources.ts @@ -22,6 +22,13 @@ export interface DataSource { testDatasource?: () => Promise; } +export interface DataSourceSelectItem { + name: string; + value: string | null; + meta: PluginMeta; + sort: string; +} + export interface DataSourcesState { dataSources: DataSource[]; searchQuery: string; diff --git a/public/app/types/index.ts b/public/app/types/index.ts index fc176fed7e2..d6a6e73f06d 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -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, diff --git a/public/sass/components/_tabs.scss b/public/sass/components/_tabs.scss index eb3c8ce13f5..ea557b985a7 100644 --- a/public/sass/components/_tabs.scss +++ b/public/sass/components/_tabs.scss @@ -53,6 +53,7 @@ background-image: linear-gradient(to right, #ffd500 0%, #ff4400 99%, #ff4400 100%); } } + &.active--panel { background: $panel-bg !important; } diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index b51f6d335a3..371fcd49cb7 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -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; +}