Commit 4d2be024 by Johannes Schill

Start implementing the upgraded react-select in the tag filter box #13425

parent c0b7ca39
......@@ -5,17 +5,12 @@ export interface Props {
label: string;
removeIcon: boolean;
count: number;
onClick: any;
onClick?: any;
}
export class TagBadge extends React.Component<Props, any> {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick(event) {
this.props.onClick(event);
}
render() {
......@@ -28,7 +23,7 @@ export class TagBadge extends React.Component<Props, any> {
const countLabel = count !== 0 && <span className="tag-count-label">{`(${count})`}</span>;
return (
<span className={`label label-tag`} onClick={this.onClick} style={tagStyle}>
<span className={`label label-tag`} style={tagStyle}>
{removeIcon && <i className="fa fa-remove" />}
{label} {countLabel}
</span>
......
import _ from 'lodash';
import React from 'react';
import { Async } from 'react-select';
import AsyncSelect from 'react-select/lib/Async';
import { TagValue } from './TagValue';
import { TagOption } from './TagOption';
import { TagBadge } from './TagBadge';
import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
import { components } from 'react-select';
import ResetStyles from 'app/core/components/Picker/ResetStyles';
export interface Props {
tags: string[];
......@@ -23,10 +28,11 @@ export class TagFilter extends React.Component<Props, any> {
searchTags(query) {
return this.props.tagOptions().then(options => {
const tags = _.map(options, tagOption => {
return { value: tagOption.term, label: tagOption.term, count: tagOption.count };
});
return { options: tags };
return options.map(option => ({
value: option.term,
label: option.term,
count: option.count,
}));
});
}
......@@ -44,23 +50,65 @@ export class TagFilter extends React.Component<Props, any> {
render() {
const selectOptions = {
classNamePrefix: 'gf-form-select2',
isMulti: true,
defaultOptions: true,
loadOptions: this.searchTags,
onChange: this.onChange,
value: this.props.tags,
multi: true,
className: 'gf-form-input gf-form-input--form-dropdown',
placeholder: 'Tags',
loadingPlaceholder: 'Loading...',
noResultsText: 'No tags found',
optionComponent: TagOption,
loadingMessage: () => 'Loading...',
noOptionsMessage: () => 'No tags found',
getOptionValue: i => i.value,
getOptionLabel: i => i.label,
styles: ResetStyles,
components: {
Option: TagOption,
IndicatorsContainer,
NoOptionsMessage,
MultiValueContainer: props => {
const { data } = props;
return (
<components.MultiValueContainer {...props}>
<TagBadge label={data.label} removeIcon={true} count={data.count} />
</components.MultiValueContainer>
);
},
MultiValueRemove: props => {
return <components.MultiValueRemove {...props}>X</components.MultiValueRemove>;
},
},
};
// <AsyncSelect
// classNamePrefix={`gf-form-select2`}
// isMulti={false}
// isLoading={isLoading}
// defaultOptions={true}
// loadOptions={this.debouncedSearch}
// onChange={onSelected}
// className={`gf-form-input gf-form-input--form-dropdown ${className || ''}`}
// styles={ResetStyles}
// components={{
// Option: PickerOption,
// IndicatorsContainer,
// NoOptionsMessage,
// }}
// placeholder="Select user"
// filterOption={(option: { label: string }, searchText?: string) => {
// return option.label.includes(searchText);
// }}
// loadingMessage={() => 'Loading...'}
// noOptionsMessage={() => 'No users found'}
// getOptionValue={i => i.id}
// getOptionLabel={i => i.label}
selectOptions['valueComponent'] = TagValue;
return (
<div className="gf-form gf-form--has-input-icon gf-form--grow">
<div className="tag-filter">
<Async {...selectOptions} />
<AsyncSelect {...selectOptions} />
</div>
<i className="gf-form-input-icon fa fa-tag" />
</div>
......
import React from 'react';
import { components } from 'react-select';
import { OptionProps } from 'react-select/lib/components/Option';
import { TagBadge } from './TagBadge';
export interface Props {
onSelect: any;
onFocus: any;
option: any;
isFocused: any;
className: any;
// https://github.com/JedWatson/react-select/issues/3038
interface ExtendedOptionProps extends OptionProps<any> {
data: any;
}
export class TagOption extends React.Component<Props, any> {
constructor(props) {
super(props);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseDown(event) {
event.preventDefault();
event.stopPropagation();
this.props.onSelect(this.props.option, event);
}
handleMouseEnter(event) {
this.props.onFocus(this.props.option, event);
}
handleMouseMove(event) {
if (this.props.isFocused) {
return;
}
this.props.onFocus(this.props.option, event);
}
render() {
const { option, className } = this.props;
return (
<button
onMouseDown={this.handleMouseDown}
onMouseEnter={this.handleMouseEnter}
onMouseMove={this.handleMouseMove}
title={option.title}
className={`tag-filter-option btn btn-link ${className || ''}`}
>
<TagBadge label={option.label} removeIcon={false} count={option.count} onClick={this.handleMouseDown} />
</button>
);
}
}
export const TagOption = (props: ExtendedOptionProps) => {
const { data, className, label } = props;
return (
<components.Option {...props}>
<div className={`tag-filter-option btn btn-link ${className || ''}`}>
<TagBadge label={label} removeIcon={true} count={data.count} />
</div>
</components.Option>
);
};
export default TagOption;
// import React from 'react';
// import { TagBadge } from './TagBadge';
// export interface Props {
// onSelect: any;
// onFocus: any;
// option: any;
// isFocused: any;
// className: any;
// }
// export class TagOption extends React.Component<Props, any> {
// constructor(props) {
// super(props);
// this.handleMouseDown = this.handleMouseDown.bind(this);
// this.handleMouseEnter = this.handleMouseEnter.bind(this);
// this.handleMouseMove = this.handleMouseMove.bind(this);
// }
// handleMouseDown(event) {
// event.preventDefault();
// event.stopPropagation();
// this.props.onSelect(this.props.option, event);
// }
// handleMouseEnter(event) {
// this.props.onFocus(this.props.option, event);
// }
// handleMouseMove(event) {
// if (this.props.isFocused) {
// return;
// }
// this.props.onFocus(this.props.option, event);
// }
// render() {
// const { option, className } = this.props;
// return (
// <button
// onMouseDown={this.handleMouseDown}
// onMouseEnter={this.handleMouseEnter}
// onMouseMove={this.handleMouseMove}
// title={option.title}
// className={`tag-filter-option btn btn-link ${className || ''}`}
// >
// <TagBadge label={option.label} removeIcon={false} count={option.count} onClick={this.handleMouseDown} />
// </button>
// );
// }
// }
......@@ -21,6 +21,6 @@ export class TagValue extends React.Component<Props, any> {
render() {
const { value } = this.props;
return <TagBadge label={value.label} removeIcon={true} count={0} onClick={this.onClick} />;
return <TagBadge label={value.label} removeIcon={false} count={0} onClick={this.onClick} />;
}
}
......@@ -58,6 +58,15 @@ $select-input-bg-disabled: $input-bg-disabled;
background: $select-input-bg-disabled;
position: absolute;
z-index: 2;
width: 100%;
}
.tag-filter .gf-form-select2__menu {
width: 100%;
}
.gf-form-select2__multi-value {
display: inline;
}
.gf-form-select2__option {
......@@ -106,3 +115,9 @@ $select-input-bg-disabled: $input-bg-disabled;
border: 0;
overflow: visible;
}
.gf-form--has-input-icon {
.gf-form-select2__value-container {
padding-left: 30px;
}
}
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