diff --git a/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx b/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx index 2c33474ee73..21eaac3e027 100644 --- a/public/app/features/dashboard/dashgrid/DataSourcePicker.tsx +++ b/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 { - searchInput: HTMLElement; +export class DataSourcePicker extends PureComponent { + searchInput: HTMLElement; - constructor(props) { - super(props); - this.state = { - searchQuery: '', - }; - } - - 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); - }); - - return filtered; - } - - 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 ( -
onMouseEnter(index)} - > - -
{ds.name}
-
- ); + constructor(props) { + super(props); + this.state = { + searchQuery: '', }; - - componentDidMount() { - setTimeout(() => { - this.searchInput.focus(); - }, 300); - } - - onSearchQueryChange = evt => { - const value = evt.target.value; - this.setState(prevState => ({ - ...prevState, - searchQuery: value, - })); - }; - - renderFilters() { - const { searchQuery } = this.state; - const { onKeyDown } = this.props; - return ( - <> - - - ); - } - - render() { - return ( - <> -
- {this.renderFilters()} -
-
-
{this.getDataSources().map(this.renderDataSource)}
- - ); - } } -); + + 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); + }); + + return filtered; + } + + get maxSelectedIndex() { + const filtered = this.getDataSources(); + return filtered.length - 1; + } + + 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 ( +
onMouseEnter(index)}> + +
{ds.name}
+
+ ); + }; + + componentDidMount() { + setTimeout(() => { + this.searchInput.focus(); + }, 300); + } + + onSearchQueryChange = evt => { + const value = evt.target.value; + this.setState(prevState => ({ + ...prevState, + searchQuery: value, + })); + }; + + renderFilters({ onKeyDown, selected }: KeyboardNavigationProps) { + const { searchQuery } = this.state; + return ( + + ); + } + + render() { + return ( + ( + <> +
+ {this.renderFilters(keyNavProps)} +
+
+
+ {this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))} +
+ + )} + /> + ); + } +} export default DataSourcePicker; diff --git a/public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx b/public/app/features/dashboard/dashgrid/KeyboardNavigation.tsx new file mode 100644 index 00000000000..dab8371c925 --- /dev/null +++ b/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, maxSelectedIndex: number, onEnterAction: () => void) => void; + onMouseEnter: (select: number) => void; + selected: number; +} + +interface Props { + render: (injectProps: any) => void; +} + +class KeyboardNavigation extends Component { + 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; diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx index eff51ada020..939fa79c289 100644 --- a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx +++ b/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 { @@ -24,7 +24,6 @@ export class VizTypePicker extends PureComponent { this.state = { searchQuery: '', - selected: 0, }; } @@ -33,35 +32,6 @@ export class VizTypePicker extends PureComponent { 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 { 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 ( { 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 { this.setState(prevState => ({ ...prevState, searchQuery: value, - selected: 0, })); }; - renderFilters = () => { + renderFilters = ({ onKeyDown, selected }: KeyboardNavigationProps) => { + const { searchQuery } = this.state; return ( <> @@ -144,13 +117,19 @@ export class VizTypePicker extends PureComponent { const filteredPluginList = this.getFilteredPluginList(); return ( - <> -
- {this.renderFilters()} -
-
-
{filteredPluginList.map(this.renderVizPlugin)}
- + ( + <> +
+ {this.renderFilters(keyNavProps)} +
+
+
+ {filteredPluginList.map((plugin, index) => this.renderVizPlugin(plugin, index, keyNavProps))} +
+ + )} + /> ); } } diff --git a/public/app/features/dashboard/dashgrid/withKeyboardNavigation.tsx b/public/app/features/dashboard/dashgrid/withKeyboardNavigation.tsx deleted file mode 100644 index 58affdf0471..00000000000 --- a/public/app/features/dashboard/dashgrid/withKeyboardNavigation.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { Props } from './DataSourcePicker'; - -interface State { - selected: number; -} - -const withKeyboardNavigation = WrappedComponent => { - return class extends React.Component { - 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 ( - - ); - } - }; -}; - -export default withKeyboardNavigation;