mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Let VizTypePicker use the keyboard navigation hoc
This commit is contained in:
parent
5d4034be5a
commit
645812f643
@ -1,15 +1,12 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import withKeyboardNavigation from './withKeyboardNavigation';
|
||||
import withKeyboardNavigation, { KeyboardNavigationProps } from './withKeyboardNavigation';
|
||||
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 {
|
||||
@ -17,7 +14,7 @@ interface State {
|
||||
}
|
||||
|
||||
export const DataSourcePicker = withKeyboardNavigation(
|
||||
class DataSourcePicker extends PureComponent<Props, State> {
|
||||
class DataSourcePicker extends PureComponent<Props & KeyboardNavigationProps, State> {
|
||||
searchInput: HTMLElement;
|
||||
|
||||
constructor(props) {
|
||||
|
@ -4,153 +4,129 @@ import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
import { PanelPlugin } from 'app/types/plugins';
|
||||
import VizTypePickerPlugin from './VizTypePickerPlugin';
|
||||
import withKeyboardNavigation, { KeyboardNavigationProps } from './withKeyboardNavigation';
|
||||
|
||||
interface Props {
|
||||
export interface Props {
|
||||
current: PanelPlugin;
|
||||
onTypeChanged: (newType: PanelPlugin) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchQuery: string;
|
||||
selected: number;
|
||||
}
|
||||
|
||||
export class VizTypePicker extends PureComponent<Props, State> {
|
||||
searchInput: HTMLElement;
|
||||
pluginList = this.getPanelPlugins('');
|
||||
export const VizTypePicker = withKeyboardNavigation(
|
||||
class VizTypePicker extends PureComponent<Props & KeyboardNavigationProps, State> {
|
||||
searchInput: HTMLElement;
|
||||
pluginList = this.getPanelPlugins('');
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
selected: 0,
|
||||
};
|
||||
}
|
||||
|
||||
get maxSelectedIndex() {
|
||||
const filteredPluginList = this.getFilteredPluginList();
|
||||
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();
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
};
|
||||
}
|
||||
if (evt.key === 'ArrowUp') {
|
||||
evt.preventDefault();
|
||||
this.goLeft();
|
||||
}
|
||||
if (evt.key === 'Enter') {
|
||||
|
||||
get maxSelectedIndex() {
|
||||
const filteredPluginList = this.getFilteredPluginList();
|
||||
this.props.onTypeChanged(filteredPluginList[this.state.selected]);
|
||||
return filteredPluginList.length - 1;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 300);
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
getPanelPlugins(filter): PanelPlugin[] {
|
||||
const panels = _.chain(config.panels)
|
||||
.filter({ hideFromList: false })
|
||||
.map(item => item)
|
||||
.value();
|
||||
|
||||
// add sort by sort property
|
||||
return _.sortBy(panels, 'sort');
|
||||
}
|
||||
|
||||
renderVizPlugin = (plugin: PanelPlugin, index: number) => {
|
||||
const { onTypeChanged, selected, onMouseEnter } = this.props;
|
||||
const isSelected = selected === index;
|
||||
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)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
getFilteredPluginList = (): PanelPlugin[] => {
|
||||
const { searchQuery } = this.state;
|
||||
const regex = new RegExp(searchQuery, 'i');
|
||||
const pluginList = this.pluginList;
|
||||
|
||||
const filtered = pluginList.filter(item => {
|
||||
return regex.test(item.name);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
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={this.props.onKeyDown}
|
||||
onKeyDown={evt => {
|
||||
onKeyDown(evt, this.maxSelectedIndex, () => {
|
||||
const { onTypeChanged, selected } = this.props;
|
||||
const vizType = this.getFilteredPluginList()[selected];
|
||||
onTypeChanged(vizType);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getPanelPlugins(filter): PanelPlugin[] {
|
||||
const panels = _.chain(config.panels)
|
||||
.filter({ hideFromList: false })
|
||||
.map(item => item)
|
||||
.value();
|
||||
|
||||
// add sort by sort property
|
||||
return _.sortBy(panels, 'sort');
|
||||
}
|
||||
|
||||
onMouseEnter = (mouseEnterIndex: number) => {
|
||||
this.setState({
|
||||
selected: mouseEnterIndex,
|
||||
});
|
||||
};
|
||||
|
||||
renderVizPlugin = (plugin: PanelPlugin, index: number) => {
|
||||
const isSelected = this.state.selected === index;
|
||||
const isCurrent = plugin.id === this.props.current.id;
|
||||
return (
|
||||
<VizTypePickerPlugin
|
||||
key={plugin.id}
|
||||
isSelected={isSelected}
|
||||
isCurrent={isCurrent}
|
||||
plugin={plugin}
|
||||
onMouseEnter={() => {
|
||||
this.onMouseEnter(index);
|
||||
}}
|
||||
onClick={() => this.props.onTypeChanged(plugin)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
getFilteredPluginList = (): PanelPlugin[] => {
|
||||
const { searchQuery } = this.state;
|
||||
const regex = new RegExp(searchQuery, 'i');
|
||||
const pluginList = this.pluginList;
|
||||
|
||||
const filtered = pluginList.filter(item => {
|
||||
return regex.test(item.name);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
onSearchQueryChange = evt => {
|
||||
const value = evt.target.value;
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
searchQuery: value,
|
||||
selected: 0,
|
||||
}));
|
||||
};
|
||||
|
||||
renderFilters = () => {
|
||||
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}
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -1,12 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Props } from './DataSourcePicker';
|
||||
import { Props as DataSourceProps } from './DataSourcePicker';
|
||||
import { Props as VizTypeProps } from './VizTypePicker';
|
||||
|
||||
interface State {
|
||||
selected: number;
|
||||
}
|
||||
|
||||
export interface KeyboardNavigationProps {
|
||||
selected?: number;
|
||||
onKeyDown?: (evt: React.KeyboardEvent<EventTarget>, maxSelectedIndex: number, onEnterAction: () => void) => void;
|
||||
onMouseEnter?: (select: number) => void;
|
||||
}
|
||||
|
||||
const withKeyboardNavigation = WrappedComponent => {
|
||||
return class extends React.Component<Props, State> {
|
||||
return class extends React.Component<DataSourceProps | VizTypeProps, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user