Bladeren bron

data source picker demo state

Torkel Ödegaard 7 jaren geleden
bovenliggende
commit
1bad8db94c

+ 89 - 0
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<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>
+      </>
+    );
+  }
+}

+ 37 - 23
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,22 +32,32 @@ export class EditorTabBody extends PureComponent<Props, State> {
     this.setState({
       openView: item === this.state.openView ? null : item,
     });
-  }
+  };
 
   onCloseOpenView = () => {
     this.setState({ openView: null });
-  }
+  };
 
-  renderToolBarViewToggle(item: EditorToolBarView) {
+  renderMainSelection(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>
+      <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>
     );
   }
 
+  renderButton(view: EditorToolBarView) {
+    return (
+      <div className="nav-buttons" key={view.title}>
+        <button className="btn navbar-button">
+          <i className={view.icon} /> {view.title}
+        </button>
+      </div>
+    );
+  }
+
   renderOpenView(view: EditorToolBarView) {
     return (
       <div className="editor-toolbar-view">
@@ -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>
       </>
-      );
+    );
   }
 }

+ 15 - 9
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<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>
     );

+ 2 - 3
public/app/features/dashboard/dashgrid/VisualizationTab.tsx

@@ -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>
     );

+ 14 - 2
public/app/features/dashboard/dashgrid/VizTypePicker.tsx

@@ -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 (

+ 3 - 6
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) {

+ 7 - 0
public/app/types/datasources.ts

@@ -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;

+ 2 - 1
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,

+ 1 - 0
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;
   }

+ 44 - 8
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;
+}