All files / inputDropdown index.tsx

0% Statements 0/13
0% Branches 0/45
0% Functions 0/2
0% Lines 0/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134                                                                                                                                                                                                                                                                           
import React from 'react';
import cx from 'classnames';
 
import { FieldRenderProps, IOption } from '../types';
import { IGlyph, Icon } from '../icon';
 
/**
 * Dropdown component.
 */
interface IInputDropdown extends FieldRenderProps<HTMLElement> {
  /** Passed down classname. */
  className?: string;
  /** Empty Option Title. Pass formatMessage to have this translated. If not, it's an empty string*/
  emptyOptionTitle?: string;
  /** Function for translation of options */
  formatMessage?: (id: string) => string;
  /** Glyph for svg */
  icon?: IGlyph;
  /** Optional size for icon */
  iconSize?: { height?: number; width?: number };
  /** Optional id indicator */
  id?: string;
  /** True if we want to include the empty first option */
  includeEmptyOption?: boolean;
  /** Is disabled */
  isDisabled?: boolean;
  /** If background has a solid color */
  isReverse?: boolean;
  /** Is small */
  isSmall?: boolean;
  /** Mandatory options list. If formatted message is passed, then it will translate */
  options: IOption[];
  /** Optional placeholder */
  placeholder?: string;
  /** Optional title */
  title?: string;
}
 
export const InputDropdown: React.FC<IInputDropdown> = ({
  formatMessage,
  emptyOptionTitle,
  id,
  input: { name, onBlur, onChange, onFocus, value },
  icon,
  iconSize,
  includeEmptyOption,
  isSmall = false,
  isDisabled = false,
  isReverse = false,
  options = [],
  meta: { active, error, submitError, submitting, touched, invalid, dirtySinceLastSubmit },
  placeholder,
  title,
}) => {
  const errorMsg = error || submitError;
  const isValidationError = invalid && touched && !!error;
  const isSubmitError = !!invalid && !dirtySinceLastSubmit && !!submitError;
  const isError = (isValidationError || isSubmitError) && !submitting;
  const combinedContainerClass = cx({
    'panda-input-container': true,
  });
  // Most styles are copied from inputText/index.css
  const combinedInputClass = cx({
    'panda-input-dropdown': true,
    'panda-input--small': isSmall,
    'panda-input--large': !isSmall,
    'panda-typography--P2': isSmall,
    'panda-typography--P1': !isSmall,
    'panda-input-text--focused': !!active,
    'panda-input-text--error': isError,
    'panda-input-text--disabled': isDisabled,
  });
  const combinedLabelClass =
    !!title &&
    cx({
      'panda-input-title--large': !isSmall,
      'panda-input-title--small': isSmall,
      'panda-input-title--white': isReverse,
    });
  const combinedIconClass =
    !!icon &&
    cx({
      'panda-input-icon--large': !isSmall,
      'panda-input-icon--small': isSmall,
    });
  const combinedErrorClass =
    !!isError &&
    cx({
      'panda-input-error': true,
      'panda-input-error--small': isSmall,
    });
  const emptyOption = {
    name: !!emptyOptionTitle
      ? formatMessage
        ? formatMessage(emptyOptionTitle)
        : emptyOptionTitle
      : '',
    value: '',
  };
 
  return (
    <div className={combinedContainerClass}>
      {!!title && <label className={combinedLabelClass || ''}>{title}</label>}
      {!!icon && (
        <Icon
          className={combinedIconClass as string}
          icon={icon}
          size={iconSize || { height: isSmall ? 8 : 10 }}
        />
      )}
      <select
        className={combinedInputClass}
        disabled={isDisabled || submitting}
        id={id}
        name={name}
        onBlur={onBlur}
        onChange={onChange}
        onFocus={onFocus}
        placeholder={placeholder}
        value={value}
      >
        {(includeEmptyOption ? [emptyOption].concat(options) : options).map(
          (option: IOption, index: number) => (
            <option key={index} value={option.value}>
              {formatMessage ? formatMessage(option.name) : option.name}
            </option>
          ),
        )}
      </select>
      {!!isError && <p className={combinedErrorClass || ''}>{errorMsg}</p>}
    </div>
  );
};