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

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

Johannes Schill 7 лет назад
Родитель
Сommit
4d2be024f4

+ 2 - 7
public/app/core/components/TagFilter/TagBadge.tsx

@@ -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>

+ 59 - 11
public/app/core/components/TagFilter/TagFilter.tsx

@@ -1,8 +1,13 @@
 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>

+ 70 - 47
public/app/core/components/TagFilter/TagOption.tsx

@@ -1,52 +1,75 @@
 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>
+//     );
+//   }
+// }

+ 1 - 1
public/app/core/components/TagFilter/TagValue.tsx

@@ -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} />;
   }
 }

+ 15 - 0
public/sass/components/_form_select_box.scss

@@ -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;
+  }
+}