Просмотр исходного кода

Merge branch 'develop' into metrics-tab-changes

Torkel Ödegaard 7 лет назад
Родитель
Сommit
45fde57ef9

+ 91 - 98
public/app/features/dashboard/dashgrid/DataSourcePicker.tsx

@@ -1,124 +1,117 @@
 import React, { PureComponent } from 'react';
 import classNames from 'classnames';
 import _ from 'lodash';
-import withKeyboardNavigation from './withKeyboardNavigation';
+import KeyboardNavigation, { KeyboardNavigationProps } from './KeyboardNavigation';
 import { DataSourceSelectItem } from 'app/types';
 
 export interface Props {
-  onChangeDataSource: (ds: any) => void;
+  onChangeDataSource: (ds: DataSourceSelectItem) => void;
   datasources: DataSourceSelectItem[];
-  selected?: number;
-  onKeyDown?: (evt: any, maxSelectedIndex: number, onEnterAction: () => void) => void;
-  onMouseEnter?: (select: number) => void;
 }
 
 interface State {
   searchQuery: string;
 }
 
-export const DataSourcePicker = withKeyboardNavigation(
-  class DataSourcePicker extends PureComponent<Props, State> {
-    searchInput: HTMLElement;
+export class DataSourcePicker extends PureComponent<Props, State> {
+  searchInput: HTMLElement;
 
-    constructor(props) {
-      super(props);
-      this.state = {
-        searchQuery: '',
-      };
-    }
+  constructor(props) {
+    super(props);
+    this.state = {
+      searchQuery: '',
+    };
+  }
 
-    getDataSources() {
-      const { searchQuery } = this.state;
-      const regex = new RegExp(searchQuery, 'i');
-      const { datasources } = this.props;
+  getDataSources() {
+    const { searchQuery } = this.state;
+    const regex = new RegExp(searchQuery, 'i');
+    const { datasources } = this.props;
 
-      const filtered = datasources.filter(item => {
-        return regex.test(item.name) || regex.test(item.meta.name);
-      });
+    const filtered = datasources.filter(item => {
+      return regex.test(item.name) || regex.test(item.meta.name);
+    });
 
-      return filtered;
-    }
+    return filtered;
+  }
 
-    get maxSelectedIndex() {
-      const filtered = this.getDataSources();
-      return filtered.length - 1;
-    }
+  get maxSelectedIndex() {
+    const filtered = this.getDataSources();
+    return filtered.length - 1;
+  }
 
-    renderDataSource = (ds: DataSourceSelectItem, index: number) => {
-      const { onChangeDataSource, selected, onMouseEnter } = this.props;
-      const onClick = () => onChangeDataSource(ds);
-      const isSelected = selected === index;
-      const cssClass = classNames({
-        'ds-picker-list__item': true,
-        'ds-picker-list__item--selected': isSelected,
-      });
-      return (
-        <div
-          key={index}
-          className={cssClass}
-          title={ds.name}
-          onClick={onClick}
-          onMouseEnter={() => onMouseEnter(index)}
-        >
-          <img className="ds-picker-list__img" src={ds.meta.info.logos.small} />
-          <div className="ds-picker-list__name">{ds.name}</div>
-        </div>
-      );
-    };
+  renderDataSource = (ds: DataSourceSelectItem, index: number, keyNavProps: KeyboardNavigationProps) => {
+    const { onChangeDataSource } = this.props;
+    const { selected, onMouseEnter } = keyNavProps;
+    const onClick = () => onChangeDataSource(ds);
+    const isSelected = selected === index;
+    const cssClass = classNames({
+      'ds-picker-list__item': true,
+      'ds-picker-list__item--selected': isSelected,
+    });
+    return (
+      <div key={index} className={cssClass} title={ds.name} onClick={onClick} onMouseEnter={() => onMouseEnter(index)}>
+        <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);
-    }
+  componentDidMount() {
+    setTimeout(() => {
+      this.searchInput.focus();
+    }, 300);
+  }
 
-    onSearchQueryChange = evt => {
-      const value = evt.target.value;
-      this.setState(prevState => ({
-        ...prevState,
-        searchQuery: value,
-      }));
-    };
+  onSearchQueryChange = evt => {
+    const value = evt.target.value;
+    this.setState(prevState => ({
+      ...prevState,
+      searchQuery: value,
+    }));
+  };
 
-    renderFilters() {
-      const { searchQuery } = this.state;
-      const { onKeyDown } = this.props;
-      return (
-        <>
-          <label className="gf-form--has-input-icon">
-            <input
-              type="text"
-              className="gf-form-input width-13"
-              placeholder=""
-              ref={elem => (this.searchInput = elem)}
-              onChange={this.onSearchQueryChange}
-              value={searchQuery}
-              onKeyDown={evt => {
-                onKeyDown(evt, this.maxSelectedIndex, () => {
-                  const { onChangeDataSource, selected } = this.props;
-                  const ds = this.getDataSources()[selected];
-                  onChangeDataSource(ds);
-                });
-              }}
-            />
-            <i className="gf-form-input-icon fa fa-search" />
-          </label>
-        </>
-      );
-    }
+  renderFilters({ onKeyDown, selected }: KeyboardNavigationProps) {
+    const { searchQuery } = this.state;
+    return (
+      <label className="gf-form--has-input-icon">
+        <input
+          type="text"
+          className="gf-form-input width-13"
+          placeholder=""
+          ref={elem => (this.searchInput = elem)}
+          onChange={this.onSearchQueryChange}
+          value={searchQuery}
+          onKeyDown={evt => {
+            onKeyDown(evt, this.maxSelectedIndex, () => {
+              const { onChangeDataSource } = this.props;
+              const ds = this.getDataSources()[selected];
+              onChangeDataSource(ds);
+            });
+          }}
+        />
+        <i className="gf-form-input-icon fa fa-search" />
+      </label>
+    );
+  }
 
-    render() {
-      return (
-        <>
-          <div className="cta-form__bar">
-            {this.renderFilters()}
-            <div className="gf-form--grow" />
-          </div>
-          <div className="ds-picker-list">{this.getDataSources().map(this.renderDataSource)}</div>
-        </>
-      );
-    }
+  render() {
+    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>
+          </>
+        )}
+      />
+    );
   }
-);
+}
 
 export default DataSourcePicker;

+ 71 - 0
public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx

@@ -0,0 +1,71 @@
+import React, { KeyboardEvent, Component } from 'react';
+
+interface State {
+  selected: number;
+}
+
+export interface KeyboardNavigationProps {
+  onKeyDown: (evt: KeyboardEvent<EventTarget>, maxSelectedIndex: number, onEnterAction: () => void) => void;
+  onMouseEnter: (select: number) => void;
+  selected: number;
+}
+
+interface Props {
+  render: (injectProps: any) => void;
+}
+
+class KeyboardNavigation extends Component<Props, State> {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      selected: 0,
+    };
+  }
+
+  goToNext = (maxSelectedIndex: number) => {
+    const nextIndex = this.state.selected >= maxSelectedIndex ? 0 : this.state.selected + 1;
+    this.setState({
+      selected: nextIndex,
+    });
+  };
+
+  goToPrev = (maxSelectedIndex: number) => {
+    const nextIndex = this.state.selected <= 0 ? maxSelectedIndex : this.state.selected - 1;
+    this.setState({
+      selected: nextIndex,
+    });
+  };
+
+  onKeyDown = (evt: KeyboardEvent, maxSelectedIndex: number, onEnterAction: any) => {
+    if (evt.key === 'ArrowDown') {
+      evt.preventDefault();
+      this.goToNext(maxSelectedIndex);
+    }
+    if (evt.key === 'ArrowUp') {
+      evt.preventDefault();
+      this.goToPrev(maxSelectedIndex);
+    }
+    if (evt.key === 'Enter' && onEnterAction) {
+      onEnterAction();
+    }
+  };
+
+  onMouseEnter = (mouseEnterIndex: number) => {
+    this.setState({
+      selected: mouseEnterIndex,
+    });
+  };
+
+  render() {
+    const injectProps = {
+      onKeyDown: this.onKeyDown,
+      onMouseEnter: this.onMouseEnter,
+      selected: this.state.selected,
+    };
+
+    return <>{this.props.render({ ...injectProps })}</>;
+  }
+}
+
+export default KeyboardNavigation;

+ 31 - 52
public/app/features/dashboard/dashgrid/VizTypePicker.tsx

@@ -4,15 +4,15 @@ import _ from 'lodash';
 import config from 'app/core/config';
 import { PanelPlugin } from 'app/types/plugins';
 import VizTypePickerPlugin from './VizTypePickerPlugin';
+import KeyboardNavigation, { KeyboardNavigationProps } from './KeyboardNavigation';
 
-interface Props {
+export interface Props {
   current: PanelPlugin;
   onTypeChanged: (newType: PanelPlugin) => void;
 }
 
 interface State {
   searchQuery: string;
-  selected: number;
 }
 
 export class VizTypePicker extends PureComponent<Props, State> {
@@ -24,7 +24,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
 
     this.state = {
       searchQuery: '',
-      selected: 0,
     };
   }
 
@@ -33,35 +32,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
     return filteredPluginList.length - 1;
   }
 
-  goRight = () => {
-    const nextIndex = this.state.selected >= this.maxSelectedIndex ? 0 : this.state.selected + 1;
-    this.setState({
-      selected: nextIndex,
-    });
-  };
-
-  goLeft = () => {
-    const nextIndex = this.state.selected <= 0 ? this.maxSelectedIndex : this.state.selected - 1;
-    this.setState({
-      selected: nextIndex,
-    });
-  };
-
-  onKeyDown = evt => {
-    if (evt.key === 'ArrowDown') {
-      evt.preventDefault();
-      this.goRight();
-    }
-    if (evt.key === 'ArrowUp') {
-      evt.preventDefault();
-      this.goLeft();
-    }
-    if (evt.key === 'Enter') {
-      const filteredPluginList = this.getFilteredPluginList();
-      this.props.onTypeChanged(filteredPluginList[this.state.selected]);
-    }
-  };
-
   componentDidMount() {
     setTimeout(() => {
       this.searchInput.focus();
@@ -78,14 +48,10 @@ export class VizTypePicker extends PureComponent<Props, State> {
     return _.sortBy(panels, 'sort');
   }
 
-  onMouseEnter = (mouseEnterIndex: number) => {
-    this.setState({
-      selected: mouseEnterIndex,
-    });
-  };
-
-  renderVizPlugin = (plugin: PanelPlugin, index: number) => {
-    const isSelected = this.state.selected === index;
+  renderVizPlugin = (plugin: PanelPlugin, index: number, keyNavProps: KeyboardNavigationProps) => {
+    const { onTypeChanged } = this.props;
+    const { selected, onMouseEnter } = keyNavProps;
+    const isSelected = selected === index;
     const isCurrent = plugin.id === this.props.current.id;
     return (
       <VizTypePickerPlugin
@@ -94,9 +60,9 @@ export class VizTypePicker extends PureComponent<Props, State> {
         isCurrent={isCurrent}
         plugin={plugin}
         onMouseEnter={() => {
-          this.onMouseEnter(index);
+          onMouseEnter(index);
         }}
-        onClick={() => this.props.onTypeChanged(plugin)}
+        onClick={() => onTypeChanged(plugin)}
       />
     );
   };
@@ -118,11 +84,11 @@ export class VizTypePicker extends PureComponent<Props, State> {
     this.setState(prevState => ({
       ...prevState,
       searchQuery: value,
-      selected: 0,
     }));
   };
 
-  renderFilters = () => {
+  renderFilters = ({ onKeyDown, selected }: KeyboardNavigationProps) => {
+    const { searchQuery } = this.state;
     return (
       <>
         <label className="gf-form--has-input-icon">
@@ -132,7 +98,14 @@ export class VizTypePicker extends PureComponent<Props, State> {
             placeholder=""
             ref={elem => (this.searchInput = elem)}
             onChange={this.onSearchQueryChange}
-            onKeyDown={this.onKeyDown}
+            value={searchQuery}
+            onKeyDown={evt => {
+              onKeyDown(evt, this.maxSelectedIndex, () => {
+                const { onTypeChanged } = this.props;
+                const vizType = this.getFilteredPluginList()[selected];
+                onTypeChanged(vizType);
+              });
+            }}
           />
           <i className="gf-form-input-icon fa fa-search" />
         </label>
@@ -144,13 +117,19 @@ export class VizTypePicker extends PureComponent<Props, State> {
     const filteredPluginList = this.getFilteredPluginList();
 
     return (
-      <>
-        <div className="cta-form__bar">
-          {this.renderFilters()}
-          <div className="gf-form--grow" />
-        </div>
-        <div className="viz-picker">{filteredPluginList.map(this.renderVizPlugin)}</div>
-      </>
+      <KeyboardNavigation
+        render={(keyNavProps: KeyboardNavigationProps) => (
+          <>
+            <div className="cta-form__bar">
+              {this.renderFilters(keyNavProps)}
+              <div className="gf-form--grow" />
+            </div>
+            <div className="viz-picker">
+              {filteredPluginList.map((plugin, index) => this.renderVizPlugin(plugin, index, keyNavProps))}
+            </div>
+          </>
+        )}
+      />
     );
   }
 }

+ 0 - 65
public/app/features/dashboard/dashgrid/withKeyboardNavigation.tsx

@@ -1,65 +0,0 @@
-import React from 'react';
-import { Props } from './DataSourcePicker';
-
-interface State {
-  selected: number;
-}
-
-const withKeyboardNavigation = WrappedComponent => {
-  return class extends React.Component<Props, State> {
-    constructor(props) {
-      super(props);
-
-      this.state = {
-        selected: 0,
-      };
-    }
-
-    goToNext = (maxSelectedIndex: number) => {
-      const nextIndex = this.state.selected >= maxSelectedIndex ? 0 : this.state.selected + 1;
-      this.setState({
-        selected: nextIndex,
-      });
-    };
-
-    goToPrev = (maxSelectedIndex: number) => {
-      const nextIndex = this.state.selected <= 0 ? maxSelectedIndex : this.state.selected - 1;
-      this.setState({
-        selected: nextIndex,
-      });
-    };
-
-    onKeyDown = (evt: KeyboardEvent, maxSelectedIndex: number, onEnterAction: any) => {
-      if (evt.key === 'ArrowDown') {
-        evt.preventDefault();
-        this.goToNext(maxSelectedIndex);
-      }
-      if (evt.key === 'ArrowUp') {
-        evt.preventDefault();
-        this.goToPrev(maxSelectedIndex);
-      }
-      if (evt.key === 'Enter' && onEnterAction) {
-        onEnterAction();
-      }
-    };
-
-    onMouseEnter = (mouseEnterIndex: number) => {
-      this.setState({
-        selected: mouseEnterIndex,
-      });
-    };
-
-    render() {
-      return (
-        <WrappedComponent
-          selected={this.state.selected}
-          onKeyDown={this.onKeyDown}
-          onMouseEnter={this.onMouseEnter}
-          {...this.props}
-        />
-      );
-    }
-  };
-};
-
-export default withKeyboardNavigation;