Jelajahi Sumber

wip: making things work again

Torkel Ödegaard 7 tahun lalu
induk
melakukan
3452ee5a9c

+ 2 - 2
public/app/core/components/Picker/PickerOption.test.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React from 'react';
 import renderer from 'react-test-renderer';
 import PickerOption from './PickerOption';
 
@@ -24,7 +24,7 @@ const model = {
   children: 'Model title',
   data: {
     title: 'Model title',
-    avatarUrl: 'url/to/avatar',
+    imgUrl: 'url/to/avatar',
     label: 'User picker label',
   },
   className: 'class-for-user-picker',

+ 2 - 1
public/app/core/components/Picker/PickerOption.tsx

@@ -27,7 +27,8 @@ export const Option = (props: ExtendedOptionProps) => {
   );
 };
 
-export const SingleValue = (props: ExtendedOptionProps) => {
+// was not able to type this without typescript error
+export const SingleValue = props => {
   const { children, data } = props;
 
   return (

+ 79 - 40
public/app/features/dashboard/dashgrid/EditorTabBody.tsx

@@ -5,19 +5,37 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn';
 interface Props {
   children: JSX.Element;
   heading: string;
-  renderToolbar: () => JSX.Element;
+  renderToolbar?: () => JSX.Element;
+  toolbarItems?: EditorToolBarView[];
+}
+
+export interface EditorToolBarView {
+  title: string;
+  imgSrc?: string;
+  icon?: string;
+  disabled?: boolean;
+  onClick?: () => void;
+  render: (closeFunction: any) => JSX.Element | JSX.Element[];
 }
 
 interface State {
+  openView?: EditorToolBarView;
+  isOpen: boolean;
   fadeIn: boolean;
 }
 
 export class EditorTabBody extends PureComponent<Props, State> {
+  static defaultProps = {
+    toolbarItems: [],
+  };
+
   constructor(props) {
     super(props);
 
     this.state = {
+      openView: null,
       fadeIn: false,
+      isOpen: false,
     };
   }
 
@@ -25,56 +43,77 @@ export class EditorTabBody extends PureComponent<Props, State> {
     this.setState({ fadeIn: true });
   }
 
-  // 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>
-  //   );
-  // }
+  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;
+  }
+
+  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, renderToolbar, heading } = this.props;
-    const { fadeIn } = this.state;
+    const { children, renderToolbar, heading, toolbarItems } = this.props;
+    const { openView, fadeIn, isOpen } = this.state;
 
     return (
       <>
         <div className="toolbar">
           <div className="toolbar__heading">{heading}</div>
           {renderToolbar && renderToolbar()}
+          <div className="gf-form--grow" />
+          {toolbarItems.map(item => this.renderButton(item))}
         </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}

+ 129 - 137
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -30,8 +30,8 @@ interface Props {
 
 interface State {
   currentDS: DataSourceSelectItem;
-  helpContent: JSX.Element;
-  isLoadingHelp: string;
+  helpContent: JSX.Element | string;
+  isLoadingHelp: boolean;
   isPickerOpen: boolean;
 }
 
@@ -126,113 +126,113 @@ export class QueriesTab extends PureComponent<Props, State> {
     });
   };
 
-  // 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}
-  //     </>
-  //   );
-  // };
+  loadHelp = () => {
+    const { currentDS } = this.state;
+    const hasHelp = currentDS.meta.hasQueryHelp;
+
+    if (hasHelp) {
+      this.setState({
+        helpContent: <h2>Loading help...</h2>,
+        isLoadingHelp: true,
+      });
+
+      this.backendSrv
+        .get(`/api/plugins/${currentDS.meta.id}/markdown/query_help`)
+        .then(res => {
+          const md = new Remarkable();
+          const helpHtml = 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 { currentDS } = this.state;
+    const { queryOptions } = currentDS.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 { helpContent, isLoadingHelp } = this.state;
+    return isLoadingHelp ? <LoadingPlaceholder text="Loading help..." /> : helpContent;
+  };
 
   onAddQuery = (query?: DataQuery) => {
     this.props.panel.addQuery(query);
@@ -278,43 +278,35 @@ export class QueriesTab extends PureComponent<Props, State> {
 
   render() {
     const { panel } = this.props;
+    const { currentDS } = this.state;
+    const { hasQueryHelp } = currentDS.meta;
+
+    const queryInspector = {
+      title: 'Query Inspector',
+      render: this.renderQueryInspector,
+    };
 
-    // 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,
-    // };
+    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" renderToolbar={this.renderToolbar}>
+      <EditorTabBody
+        heading="Queries"
+        renderToolbar={this.renderToolbar}
+        toolbarItems={[options, queryInspector, dsHelp]}
+      >
         <div className="query-editor-rows gf-form-group">
           <div ref={element => (this.element = element)} />