Torkel Ödegaard há 7 anos atrás
pai
commit
d998527128

+ 34 - 12
public/app/features/dashboard/dashgrid/DataSourcePicker.tsx

@@ -7,10 +7,12 @@ import { DataSourceSelectItem } from 'app/types';
 export interface Props {
   onChangeDataSource: (ds: DataSourceSelectItem) => void;
   datasources: DataSourceSelectItem[];
+  current: DataSourceSelectItem;
 }
 
 interface State {
   searchQuery: string;
+  isOpen: boolean;
 }
 
 export class DataSourcePicker extends PureComponent<Props, State> {
@@ -18,8 +20,10 @@ export class DataSourcePicker extends PureComponent<Props, State> {
 
   constructor(props) {
     super(props);
+
     this.state = {
       searchQuery: '',
+      isOpen: false,
     };
   }
 
@@ -95,21 +99,39 @@ export class DataSourcePicker extends PureComponent<Props, State> {
     );
   }
 
+  onOpen = () => {
+    this.setState({ isOpen: true });
+  };
+
   render() {
+    const { current } = this.props;
+    const { isOpen } = this.state;
+
     return (
-      <KeyboardNavigation
-        render={(keyNavProps: KeyboardNavigationProps) => (
-          <>
-            <div className="cta-form__bar">
-              {this.renderFilters(keyNavProps)}
-              <div className="gf-form--grow" />
-            </div>
-            <div className="ds-picker-list">
-              {this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
-            </div>
-          </>
+      <div className="ds-picker">
+        {!isOpen && (
+          <div className="toolbar__main" onClick={this.onOpen}>
+            <img className="toolbar__main-image" src={current.meta.info.logos.small} />
+            <div className="toolbar__main-name">{current.name}</div>
+            <i className="fa fa-caret-down" />
+          </div>
+        )}
+        {isOpen && (
+          <KeyboardNavigation
+            render={(keyNavProps: KeyboardNavigationProps) => (
+              <div className="ds-picker-menu">
+                <div className="cta-form__bar">
+                  {this.renderFilters(keyNavProps)}
+                  <div className="gf-form--grow" />
+                </div>
+                <div className="ds-picker-list">
+                  {this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
+                </div>
+              </div>
+            )}
+          />
         )}
-      />
+      </div>
     );
   }
 }

+ 41 - 86
public/app/features/dashboard/dashgrid/EditorTabBody.tsx

@@ -5,22 +5,10 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn';
 interface Props {
   children: JSX.Element;
   heading: string;
-  main?: EditorToolBarView;
-  toolbarItems: EditorToolBarView[];
-}
-
-export interface EditorToolBarView {
-  title: string;
-  imgSrc?: string;
-  icon?: string;
-  disabled?: boolean;
-  onClick?: () => void;
-  render: (closeFunction: any) => JSX.Element | JSX.Element[];
+  renderToolbar: () => JSX.Element;
 }
 
 interface State {
-  openView?: EditorToolBarView;
-  isOpen: boolean;
   fadeIn: boolean;
 }
 
@@ -29,9 +17,7 @@ export class EditorTabBody extends PureComponent<Props, State> {
     super(props);
 
     this.state = {
-      openView: null,
       fadeIn: false,
-      isOpen: false,
     };
   }
 
@@ -39,87 +25,56 @@ export class EditorTabBody extends PureComponent<Props, State> {
     this.setState({ fadeIn: true });
   }
 
-  onToggleToolBarView = (item: EditorToolBarView) => {
-    this.setState({
-      openView: item,
-      isOpen: !this.state.isOpen,
-    });
-  };
-
-  onCloseOpenView = () => {
-    this.setState({ isOpen: false });
-  };
-
-  static getDerivedStateFromProps(props, state) {
-    if (state.openView) {
-      const activeToolbarItem = props.toolbarItems.find(
-        item => item.title === state.openView.title && item.icon === state.openView.icon
-      );
-      if (activeToolbarItem) {
-        return {
-          ...state,
-          openView: activeToolbarItem,
-        };
-      }
-    }
-    return state;
-  }
-
-  renderMainSelection(view: EditorToolBarView) {
-    return (
-      <div className="toolbar__main" onClick={() => this.onToggleToolBarView(view)} key={view.title + view.icon}>
-        <img className="toolbar__main-image" src={view.imgSrc} />
-        <div className="toolbar__main-name">{view.title}</div>
-        <i className="fa fa-caret-down" />
-      </div>
-    );
-  }
-
-  renderButton(view: EditorToolBarView) {
-    const onClick = () => {
-      if (view.onClick) {
-        view.onClick();
-      }
-      this.onToggleToolBarView(view);
-    };
-
-    return (
-      <div className="nav-buttons" key={view.title + view.icon}>
-        <button className="btn navbar-button" onClick={onClick} disabled={view.disabled}>
-          {view.icon && <i className={view.icon} />} {view.title}
-        </button>
-      </div>
-    );
-  }
-
-  renderOpenView(view: EditorToolBarView) {
-    return (
-      <div className="toolbar-subview">
-        <button className="toolbar-subview__close" onClick={this.onCloseOpenView}>
-          <i className="fa fa-chevron-up" />
-        </button>
-        {view.render(this.onCloseOpenView)}
-      </div>
-    );
-  }
+  // renderMainSelection(view: EditorToolBarView) {
+  //   return (
+  //     <div className="toolbar__main" onClick={() => this.onToggleToolBarView(view)} key={view.title + view.icon}>
+  //       <img className="toolbar__main-image" src={view.imgSrc} />
+  //       <div className="toolbar__main-name">{view.title}</div>
+  //       <i className="fa fa-caret-down" />
+  //     </div>
+  //   );
+  // }
+  //
+  // renderButton(view: EditorToolBarView) {
+  //   const onClick = () => {
+  //     if (view.onClick) {
+  //       view.onClick();
+  //     }
+  //     this.onToggleToolBarView(view);
+  //   };
+  //
+  //   return (
+  //     <div className="nav-buttons" key={view.title + view.icon}>
+  //       <button className="btn navbar-button" onClick={onClick} disabled={view.disabled}>
+  //         {view.icon && <i className={view.icon} />} {view.title}
+  //       </button>
+  //     </div>
+  //   );
+  // }
+  //
+  // renderOpenView(view: EditorToolBarView) {
+  //   return (
+  //     <div className="toolbar-subview">
+  //       <button className="toolbar-subview__close" onClick={this.onCloseOpenView}>
+  //         <i className="fa fa-chevron-up" />
+  //       </button>
+  //       {view.render(this.onCloseOpenView)}
+  //     </div>
+  //   );
+  // }
 
   render() {
-    const { children, toolbarItems, main, heading } = this.props;
-    const { openView, fadeIn, isOpen } = this.state;
+    const { children, renderToolbar, heading } = this.props;
+    const { fadeIn } = this.state;
 
     return (
       <>
         <div className="toolbar">
           <div className="toolbar__heading">{heading}</div>
-          {main && this.renderMainSelection(main)}
-          <div className="gf-form--grow" />
-          {toolbarItems.map(item => this.renderButton(item))}
+          {renderToolbar && renderToolbar()}
         </div>
         <div className="panel-editor__scroll">
           <CustomScrollbar autoHide={false}>
-            <FadeIn in={isOpen} duration={200} unmountOnExit={true}>
-              <div className="panel-editor__toolbar-view">{openView && this.renderOpenView(openView)}</div>
-            </FadeIn>
             <div className="panel-editor__content">
               <FadeIn in={fadeIn} duration={50}>
                 {children}

+ 162 - 168
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -28,15 +28,11 @@ interface Props {
   dashboard: DashboardModel;
 }
 
-interface Help {
-  isLoading: boolean;
-  helpHtml: any;
-}
-
 interface State {
-  currentDatasource: DataSourceSelectItem;
-  help: Help;
-  hideTimeOverride: boolean;
+  currentDS: DataSourceSelectItem;
+  helpContent: JSX.Element;
+  isLoadingHelp: string;
+  isPickerOpen: boolean;
 }
 
 interface LoadingPlaceholderProps {
@@ -56,12 +52,10 @@ export class QueriesTab extends PureComponent<Props, State> {
     const { panel } = props;
 
     this.state = {
-      currentDatasource: this.datasources.find(datasource => datasource.value === panel.datasource),
-      help: {
-        isLoading: false,
-        helpHtml: null,
-      },
-      hideTimeOverride: false,
+      currentDS: this.datasources.find(datasource => datasource.value === panel.datasource),
+      isLoadingHelp: false,
+      helpContent: null,
+      isPickerOpen: false,
     };
   }
 
@@ -102,7 +96,7 @@ export class QueriesTab extends PureComponent<Props, State> {
 
   onChangeDataSource = datasource => {
     const { panel } = this.props;
-    const { currentDatasource } = this.state;
+    const { currentDS } = this.state;
 
     // switching to mixed
     if (datasource.meta.mixed) {
@@ -112,13 +106,13 @@ export class QueriesTab extends PureComponent<Props, State> {
           target.datasource = config.defaultDatasource;
         }
       });
-    } else if (currentDatasource) {
+    } else if (currentDS) {
       // if switching from mixed
-      if (currentDatasource.meta.mixed) {
+      if (currentDS.meta.mixed) {
         for (const target of panel.targets) {
           delete target.datasource;
         }
-      } else if (currentDatasource.meta.id !== datasource.meta.id) {
+      } else if (currentDS.meta.id !== datasource.meta.id) {
         // we are changing data source type, clear queries
         panel.targets = [{ refId: 'A' }];
       }
@@ -127,128 +121,118 @@ export class QueriesTab extends PureComponent<Props, State> {
     panel.datasource = datasource.value;
     panel.refresh();
 
-    this.setState(prevState => ({
-      ...prevState,
-      currentDatasource: datasource,
-    }));
+    this.setState({
+      currentDS: datasource,
+    });
   };
 
-  loadHelp = () => {
-    const { currentDatasource } = this.state;
-    const hasHelp = currentDatasource.meta.hasQueryHelp;
-
-    if (hasHelp) {
-      this.setState(prevState => ({
-        ...prevState,
-        help: {
-          helpHtml: <h2>Loading help...</h2>,
-          isLoading: true,
-        },
-      }));
-
-      this.backendSrv
-        .get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`)
-        .then(res => {
-          const md = new Remarkable();
-          const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res));
-          this.setState(prevState => ({
-            ...prevState,
-            help: {
-              helpHtml: <div className="markdown-html" dangerouslySetInnerHTML={{ __html: helpHtml }} />,
-              isLoading: false,
-            },
-          }));
-        })
-        .catch(() => {
-          this.setState(prevState => ({
-            ...prevState,
-            help: {
-              helpHtml: 'Error occured when loading help',
-              isLoading: false,
-            },
-          }));
-        });
-    }
-  };
-
-  renderOptions = close => {
-    const { currentDatasource } = this.state;
-    const { queryOptions } = currentDatasource.meta;
-    const { panel } = this.props;
-
-    const onChangeFn = (panelKey: string) => {
-      return (value: string | number) => {
-        panel[panelKey] = value;
-        panel.refresh();
-      };
-    };
-
-    const allOptions = {
-      cacheTimeout: {
-        label: 'Cache timeout',
-        placeholder: '60',
-        name: 'cacheTimeout',
-        value: panel.cacheTimeout,
-        tooltipInfo: (
-          <>
-            If your time series store has a query cache this option can override the default cache timeout. Specify a
-            numeric value in seconds.
-          </>
-        ),
-      },
-      maxDataPoints: {
-        label: 'Max data points',
-        placeholder: 'auto',
-        name: 'maxDataPoints',
-        value: panel.maxDataPoints,
-        tooltipInfo: (
-          <>
-            The maximum data points the query should return. For graphs this is automatically set to one data point per
-            pixel.
-          </>
-        ),
-      },
-      minInterval: {
-        label: 'Min time interval',
-        placeholder: '0',
-        name: 'minInterval',
-        value: panel.interval,
-        panelKey: 'interval',
-        tooltipInfo: (
-          <>
-            A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
-            <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
-            <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
-            be used in math expressions.
-          </>
-        ),
-      },
-    };
-
-    const dsOptions = queryOptions
-      ? Object.keys(queryOptions).map(key => {
-          const options = allOptions[key];
-          return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />;
-        })
-      : null;
-
-    return (
-      <>
-        <TimeRangeOptions panel={this.props.panel} />
-        {dsOptions}
-      </>
-    );
-  };
+  // loadHelp = () => {
+  //   const { currentDatasource } = this.state;
+  //   const hasHelp = currentDatasource.meta.hasQueryHelp;
+  //
+  //   if (hasHelp) {
+  //     this.setState({
+  //       helpContent: <h2>Loading help...</h2>,
+  //       isLoadingHelp: true
+  //     });
+  //
+  //     this.backendSrv
+  //       .get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`)
+  //       .then(res => {
+  //         const md = new Remarkable();
+  //         const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res));
+  //         this.setState({
+  //           helpContent: <div className="markdown-html" dangerouslySetInnerHTML={{ __html: helpHtml }} />,
+  //           isLoadingHelp: false
+  //         });
+  //       })
+  //       .catch(() => {
+  //         this.setState({
+  //           helpContent: 'Error occured when loading help',
+  //           isLoadingHelp: false,
+  //         });
+  //       });
+  //   }
+  // };
+
+  // renderOptions = close => {
+  //   const { currentDatasource } = this.state;
+  //   const { queryOptions } = currentDatasource.meta;
+  //   const { panel } = this.props;
+  //
+  //   const onChangeFn = (panelKey: string) => {
+  //     return (value: string | number) => {
+  //       panel[panelKey] = value;
+  //       panel.refresh();
+  //     };
+  //   };
+  //
+  //   const allOptions = {
+  //     cacheTimeout: {
+  //       label: 'Cache timeout',
+  //       placeholder: '60',
+  //       name: 'cacheTimeout',
+  //       value: panel.cacheTimeout,
+  //       tooltipInfo: (
+  //         <>
+  //           If your time series store has a query cache this option can override the default cache timeout. Specify a
+  //           numeric value in seconds.
+  //         </>
+  //       ),
+  //     },
+  //     maxDataPoints: {
+  //       label: 'Max data points',
+  //       placeholder: 'auto',
+  //       name: 'maxDataPoints',
+  //       value: panel.maxDataPoints,
+  //       tooltipInfo: (
+  //         <>
+  //           The maximum data points the query should return. For graphs this is automatically set to one data point per
+  //           pixel.
+  //         </>
+  //       ),
+  //     },
+  //     minInterval: {
+  //       label: 'Min time interval',
+  //       placeholder: '0',
+  //       name: 'minInterval',
+  //       value: panel.interval,
+  //       panelKey: 'interval',
+  //       tooltipInfo: (
+  //         <>
+  //           A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
+  //           <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
+  //           <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
+  //           be used in math expressions.
+  //         </>
+  //       ),
+  //     },
+  //   };
+  //
+  //   const dsOptions = queryOptions
+  //     ? Object.keys(queryOptions).map(key => {
+  //         const options = allOptions[key];
+  //         return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />;
+  //       })
+  //     : null;
+  //
+  //   return (
+  //     <>
+  //       <TimeRangeOptions panel={this.props.panel} />
+  //       {dsOptions}
+  //     </>
+  //   );
+  // };
 
   renderQueryInspector = () => {
     const { panel } = this.props;
     return <QueryInspector panel={panel} LoadingPlaceholder={LoadingPlaceholder} />;
   };
 
-  renderHelp = () => {
-    const { helpHtml, isLoading } = this.state.help;
-    return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
-  };
+  // renderHelp = () => {
+  //   const { helpHtml, isLoading } = this.state.help;
+  //   return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
+  // };
 
   onAddQuery = (query?: DataQuery) => {
     this.props.panel.addQuery(query);
@@ -280,47 +264,57 @@ export class QueriesTab extends PureComponent<Props, State> {
     this.forceUpdate();
   };
 
-  render() {
-    const { panel } = this.props;
-    const { currentDatasource } = this.state;
-    const { hasQueryHelp } = currentDatasource.meta;
-
-    const dsInformation = {
-      title: currentDatasource.name,
-      imgSrc: currentDatasource.meta.info.logos.small,
-      render: closeOpenView => (
-        <DataSourcePicker
-          datasources={this.datasources}
-          onChangeDataSource={ds => {
-            closeOpenView();
-            this.onChangeDataSource(ds);
-          }}
-        />
-      ),
-    };
+  renderToolbar = () => {
+    const { currentDS } = this.state;
 
-    const queryInspector = {
-      title: 'Query Inspector',
-      render: this.renderQueryInspector,
-    };
+    return (
+      <DataSourcePicker
+        datasources={this.datasources}
+        onChangeDataSource={this.onChangeDataSource}
+        current={currentDS}
+      />
+    );
+  };
 
-    const dsHelp = {
-      title: '',
-      icon: 'fa fa-question',
-      disabled: !hasQueryHelp,
-      onClick: this.loadHelp,
-      render: this.renderHelp,
-    };
+  render() {
+    const { panel } = this.props;
 
-    const options = {
-      title: 'Time Range',
-      icon: '',
-      disabled: false,
-      render: this.renderOptions,
-    };
+    // const dsInformation = {
+    //   title: currentDatasource.name,
+    //   imgSrc: currentDatasource.meta.info.logos.small,
+    //   render: closeOpenView => (
+    //     <DataSourcePicker
+    //       datasources={this.datasources}
+    //       onChangeDataSource={ds => {
+    //         closeOpenView();
+    //         this.onChangeDataSource(ds);
+    //       }}
+    //     />
+    //   ),
+    // };
+    //
+    // const queryInspector = {
+    //   title: 'Query Inspector',
+    //   render: this.renderQueryInspector,
+    // };
+    //
+    // const dsHelp = {
+    //   title: '',
+    //   icon: 'fa fa-question',
+    //   disabled: !hasQueryHelp,
+    //   onClick: this.loadHelp,
+    //   render: this.renderHelp,
+    // };
+    //
+    // const options = {
+    //   title: 'Time Range',
+    //   icon: '',
+    //   disabled: false,
+    //   render: this.renderOptions,
+    // };
 
     return (
-      <EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
+      <EditorTabBody heading="Queries" renderToolbar={this.renderToolbar}>
         <div className="query-editor-rows gf-form-group">
           <div ref={element => (this.element = element)} />
 

+ 13 - 0
public/sass/components/_panel_editor.scss

@@ -273,6 +273,19 @@
   }
 }
 
+.ds-picker {
+  position: relative;
+}
+
+.ds-picker-menu {
+  min-width: 400px;
+  max-width: 500px;
+  position: absolute;
+  background: $panel-editor-toolbar-view-bg;
+  padding: 5px;
+  overflow: auto;
+}
+
 .ds-picker-list__name {
   text-overflow: ellipsis;
   overflow: hidden;