mirror of https://github.com/grafana/grafana.git
				
				
				
			User picker using common select componnet
This commit is contained in:
		
							parent
							
								
									104292df63
								
							
						
					
					
						commit
						58cc2e34d6
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue