Commit 645812f6 by Johannes Schill

Let VizTypePicker use the keyboard navigation hoc

parent 5d4034be
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import withKeyboardNavigation from './withKeyboardNavigation'; import withKeyboardNavigation, { KeyboardNavigationProps } from './withKeyboardNavigation';
import { DataSourceSelectItem } from 'app/types'; import { DataSourceSelectItem } from 'app/types';
export interface Props { export interface Props {
onChangeDataSource: (ds: any) => void; onChangeDataSource: (ds: DataSourceSelectItem) => void;
datasources: DataSourceSelectItem[]; datasources: DataSourceSelectItem[];
selected?: number;
onKeyDown?: (evt: any, maxSelectedIndex: number, onEnterAction: () => void) => void;
onMouseEnter?: (select: number) => void;
} }
interface State { interface State {
...@@ -17,7 +14,7 @@ interface State { ...@@ -17,7 +14,7 @@ interface State {
} }
export const DataSourcePicker = withKeyboardNavigation( export const DataSourcePicker = withKeyboardNavigation(
class DataSourcePicker extends PureComponent<Props, State> { class DataSourcePicker extends PureComponent<Props & KeyboardNavigationProps, State> {
searchInput: HTMLElement; searchInput: HTMLElement;
constructor(props) { constructor(props) {
......
...@@ -4,153 +4,129 @@ import _ from 'lodash'; ...@@ -4,153 +4,129 @@ import _ from 'lodash';
import config from 'app/core/config'; import config from 'app/core/config';
import { PanelPlugin } from 'app/types/plugins'; import { PanelPlugin } from 'app/types/plugins';
import VizTypePickerPlugin from './VizTypePickerPlugin'; import VizTypePickerPlugin from './VizTypePickerPlugin';
import withKeyboardNavigation, { KeyboardNavigationProps } from './withKeyboardNavigation';
interface Props { export interface Props {
current: PanelPlugin; current: PanelPlugin;
onTypeChanged: (newType: PanelPlugin) => void; onTypeChanged: (newType: PanelPlugin) => void;
} }
interface State { interface State {
searchQuery: string; searchQuery: string;
selected: number;
} }
export class VizTypePicker extends PureComponent<Props, State> { export const VizTypePicker = withKeyboardNavigation(
searchInput: HTMLElement; class VizTypePicker extends PureComponent<Props & KeyboardNavigationProps, State> {
pluginList = this.getPanelPlugins(''); searchInput: HTMLElement;
pluginList = this.getPanelPlugins('');
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
searchQuery: '', searchQuery: '',
selected: 0, };
}; }
}
get maxSelectedIndex() {
const filteredPluginList = this.getFilteredPluginList();
return filteredPluginList.length - 1;
}
goRight = () => { get maxSelectedIndex() {
const nextIndex = this.state.selected >= this.maxSelectedIndex ? 0 : this.state.selected + 1; const filteredPluginList = this.getFilteredPluginList();
this.setState({ return filteredPluginList.length - 1;
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(); componentDidMount() {
this.goLeft(); setTimeout(() => {
this.searchInput.focus();
}, 300);
} }
if (evt.key === 'Enter') {
const filteredPluginList = this.getFilteredPluginList(); getPanelPlugins(filter): PanelPlugin[] {
this.props.onTypeChanged(filteredPluginList[this.state.selected]); const panels = _.chain(config.panels)
.filter({ hideFromList: false })
.map(item => item)
.value();
// add sort by sort property
return _.sortBy(panels, 'sort');
} }
};
componentDidMount() { renderVizPlugin = (plugin: PanelPlugin, index: number) => {
setTimeout(() => { const { onTypeChanged, selected, onMouseEnter } = this.props;
this.searchInput.focus(); const isSelected = selected === index;
}, 300); const isCurrent = plugin.id === this.props.current.id;
} return (
<VizTypePickerPlugin
key={plugin.id}
isSelected={isSelected}
isCurrent={isCurrent}
plugin={plugin}
onMouseEnter={() => {
onMouseEnter(index);
}}
onClick={() => onTypeChanged(plugin)}
/>
);
};
getPanelPlugins(filter): PanelPlugin[] { getFilteredPluginList = (): PanelPlugin[] => {
const panels = _.chain(config.panels) const { searchQuery } = this.state;
.filter({ hideFromList: false }) const regex = new RegExp(searchQuery, 'i');
.map(item => item) const pluginList = this.pluginList;
.value();
// add sort by sort property const filtered = pluginList.filter(item => {
return _.sortBy(panels, 'sort'); return regex.test(item.name);
} });
return filtered;
};
onMouseEnter = (mouseEnterIndex: number) => { onSearchQueryChange = evt => {
this.setState({ const value = evt.target.value;
selected: mouseEnterIndex, this.setState(prevState => ({
}); ...prevState,
}; searchQuery: value,
}));
renderVizPlugin = (plugin: PanelPlugin, index: number) => { };
const isSelected = this.state.selected === index;
const isCurrent = plugin.id === this.props.current.id; renderFilters = () => {
return ( const { searchQuery } = this.state;
<VizTypePickerPlugin const { onKeyDown } = this.props;
key={plugin.id} return (
isSelected={isSelected} <>
isCurrent={isCurrent} <label className="gf-form--has-input-icon">
plugin={plugin} <input
onMouseEnter={() => { type="text"
this.onMouseEnter(index); className="gf-form-input width-13"
}} placeholder=""
onClick={() => this.props.onTypeChanged(plugin)} ref={elem => (this.searchInput = elem)}
/> onChange={this.onSearchQueryChange}
); value={searchQuery}
}; // onKeyDown={this.props.onKeyDown}
onKeyDown={evt => {
getFilteredPluginList = (): PanelPlugin[] => { onKeyDown(evt, this.maxSelectedIndex, () => {
const { searchQuery } = this.state; const { onTypeChanged, selected } = this.props;
const regex = new RegExp(searchQuery, 'i'); const vizType = this.getFilteredPluginList()[selected];
const pluginList = this.pluginList; onTypeChanged(vizType);
});
const filtered = pluginList.filter(item => { }}
return regex.test(item.name); />
}); <i className="gf-form-input-icon fa fa-search" />
</label>
return filtered; </>
}; );
};
onSearchQueryChange = evt => {
const value = evt.target.value; render() {
this.setState(prevState => ({ const filteredPluginList = this.getFilteredPluginList();
...prevState,
searchQuery: value, return (
selected: 0, <>
})); <div className="cta-form__bar">
}; {this.renderFilters()}
<div className="gf-form--grow" />
renderFilters = () => { </div>
return ( <div className="viz-picker">{filteredPluginList.map(this.renderVizPlugin)}</div>
<> </>
<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}
onKeyDown={this.onKeyDown}
/>
<i className="gf-form-input-icon fa fa-search" />
</label>
</>
);
};
render() {
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>
</>
);
} }
} );
import React from 'react'; import React from 'react';
import { Props } from './DataSourcePicker'; import { Props as DataSourceProps } from './DataSourcePicker';
import { Props as VizTypeProps } from './VizTypePicker';
interface State { interface State {
selected: number; selected: number;
} }
export interface KeyboardNavigationProps {
selected?: number;
onKeyDown?: (evt: React.KeyboardEvent<EventTarget>, maxSelectedIndex: number, onEnterAction: () => void) => void;
onMouseEnter?: (select: number) => void;
}
const withKeyboardNavigation = WrappedComponent => { const withKeyboardNavigation = WrappedComponent => {
return class extends React.Component<Props, State> { return class extends React.Component<DataSourceProps | VizTypeProps, State> {
constructor(props) { constructor(props) {
super(props); super(props);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment