Browse Source

User picker using common select componnet

Torkel Ödegaard 7 years ago
parent
commit
58cc2e34d6

+ 7 - 5
public/app/core/components/Picker/NoOptionsMessage.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React from 'react';
 import { components } from 'react-select';
 import { OptionProps } from 'react-select/lib/components/Option';
 
@@ -6,13 +6,15 @@ export interface Props {
   children: Element;
 }
 
-export const PickerOption = (props: OptionProps<any>) => {
-  const { children, className } = props;
+export const NoOptionsMessage = (props: OptionProps<any>) => {
+  const { children } = props;
   return (
     <components.Option {...props}>
-      <div className={`description-picker-option__button btn btn-link ${className}`}>{children}</div>
+      <div className="gf-form-select-box__desc-option">
+        <div className="gf-form-select-box__desc-option__body">{children}</div>
+      </div>
     </components.Option>
   );
 };
 
-export default PickerOption;
+export default NoOptionsMessage;

+ 81 - 6
public/app/core/components/Picker/Select.tsx

@@ -2,10 +2,12 @@
 import classNames from 'classnames';
 import React, { PureComponent } from 'react';
 import { default as ReactSelect } from 'react-select';
+import { default as ReactAsyncSelect } from 'react-select/lib/Async';
 
 // Components
-import DescriptionOption from './DescriptionOption';
+import { Option, SingleValue } from './PickerOption';
 import IndicatorsContainer from './IndicatorsContainer';
+import NoOptionsMessage from './NoOptionsMessage';
 import ResetStyles from './ResetStyles';
 
 export interface SelectOptionItem {
@@ -16,20 +18,31 @@ export interface SelectOptionItem {
   [key: string]: any;
 }
 
-interface Props {
+interface CommonProps {
   defaultValue?: any;
   getOptionLabel?: (item: SelectOptionItem) => string;
   getOptionValue?: (item: SelectOptionItem) => string;
   onChange: (item: SelectOptionItem) => {} | void;
-  options: SelectOptionItem[];
   placeholder?: string;
   width?: number;
-  value: SelectOptionItem;
+  value?: SelectOptionItem;
   className?: string;
   components: object;
 }
 
-export class Select extends PureComponent<Props> {
+interface SelectProps {
+  options: SelectOptionItem[];
+}
+
+interface AsyncProps {
+  defaultOptions: boolean;
+  loadOptions: (query: string) => Promise<SelectOptionItem[]>;
+  isLoading: boolean;
+  loadingMessage?: () => string;
+  noOptionsMessage?: () => string;
+}
+
+export class Select extends PureComponent<CommonProps & SelectProps> {
   static defaultProps = {
     width: null,
     className: '',
@@ -61,7 +74,8 @@ export class Select extends PureComponent<Props> {
         classNamePrefix="gf-form-select-box"
         className={selectClassNames}
         components={{
-          Option: DescriptionOption,
+          Option,
+          SingleValue,
           IndicatorsContainer,
         }}
         defaultValue={defaultValue}
@@ -79,4 +93,65 @@ export class Select extends PureComponent<Props> {
   }
 }
 
+export class AsyncSelect extends PureComponent<CommonProps & AsyncProps> {
+  static defaultProps = {
+    width: null,
+    className: '',
+    components: {},
+    loadingMessage: () => 'Loading...',
+  };
+
+  render() {
+    const {
+      defaultValue,
+      getOptionLabel,
+      getOptionValue,
+      onChange,
+      placeholder,
+      width,
+      value,
+      className,
+      loadOptions,
+      defaultOptions,
+      isLoading,
+      loadingMessage,
+      noOptionsMessage,
+    } = this.props;
+
+    let widthClass = '';
+    if (width) {
+      widthClass = 'width-' + width;
+    }
+
+    const selectClassNames = classNames('gf-form-input', 'gf-form-input--form-dropdown', widthClass, className);
+
+    return (
+      <ReactAsyncSelect
+        classNamePrefix="gf-form-select-box"
+        className={selectClassNames}
+        components={{
+          Option,
+          SingleValue,
+          IndicatorsContainer,
+          NoOptionsMessage,
+        }}
+        defaultValue={defaultValue}
+        value={value}
+        getOptionLabel={getOptionLabel}
+        getOptionValue={getOptionValue}
+        menuShouldScrollIntoView={false}
+        isSearchable={false}
+        onChange={onChange}
+        loadOptions={loadOptions}
+        isLoading={isLoading}
+        defaultOptions={defaultOptions}
+        placeholder={placeholder || 'Choose'}
+        styles={ResetStyles}
+        loadingMessage={loadingMessage}
+        noOptionsMessage={noOptionsMessage}
+      />
+    );
+  }
+}
+
 export default Select;

+ 10 - 17
public/app/core/components/Picker/UserPicker.tsx

@@ -1,12 +1,15 @@
+// Libraries
 import React, { Component } from 'react';
-import AsyncSelect from 'react-select/lib/Async';
-import PickerOption from './PickerOption';
+
+// Components
+import { AsyncSelect } from 'app/core/components/Picker/Select';
+
+// Utils & Services
 import { debounce } from 'lodash';
 import { getBackendSrv } from 'app/core/services/backend_srv';
+
+// Types
 import { User } from 'app/types';
-import ResetStyles from './ResetStyles';
-import IndicatorsContainer from './IndicatorsContainer';
-import NoOptionsMessage from './NoOptionsMessage';
 
 export interface Props {
   onSelected: (user: User) => void;
@@ -40,6 +43,7 @@ export class UserPicker extends Component<Props, State> {
       .then(result => {
         return result.map(user => ({
           id: user.userId,
+          value: user.userId,
           label: user.login === user.email ? user.login : `${user.login} - ${user.email}`,
           imgUrl: user.avatarUrl,
           login: user.login,
@@ -57,24 +61,13 @@ export class UserPicker extends Component<Props, State> {
     return (
       <div className="user-picker">
         <AsyncSelect
-          classNamePrefix={`gf-form-select-box`}
-          isMulti={false}
+          className={className}
           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"
-          loadingMessage={() => 'Loading...'}
           noOptionsMessage={() => 'No users found'}
-          getOptionValue={i => i.id}
-          getOptionLabel={i => i.label}
         />
       </div>
     );

+ 2 - 2
public/app/features/panel/panel_header.ts

@@ -111,11 +111,11 @@ function panelHeader($compile) {
        */
       function togglePanelStackPosition() {
         const menuOpenClass = 'dropdown-menu-open';
-        const panelGridClass = '.react-grid-item.panel';
+        const panelGridClass = '.react-grid-item';
 
         let panelElem = elem
           .find('[data-toggle=dropdown]')
-          .parentsUntil('.panel')
+          .parentsUntil(panelGridClass)
           .parent();
         const menuElem = elem.find('[data-toggle=dropdown]').parent();
         panelElem = panelElem && panelElem.length ? panelElem[0] : undefined;