All files / src/form/fields EnumSelect.tsx

80.76% Statements 21/26
47.61% Branches 10/21
80% Functions 4/5
80.76% Lines 21/26

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                  66x                               66x       51x   48x   48x 48x   48x 48x     48x 48x 48x 48x     48x   48x 24x 24x 8x 16x 12x                 48x                                                                 64x              
import React, { CSSProperties, ReactElement, useEffect, useState } from 'react';
import { Select, Spin } from 'antd';
import { useTranslation } from 'react-i18next';
 
import { EnumMetaProps } from "@props/RecordProps";
import { getReadOnlyClass } from '@utils/ComponentUtils';
import { SuffixIcon, ErrorBoundary } from '../../components';
import { getEnumLabelFromCache } from "@utils/EnumUtils";
 
const { Option } = Select;
 
export interface EnumSelectProps {
  type: string;
  mode?: "tags" | "multiple";
  placeholder: string;
  notFoundContent: string;
  value?: string | { enumType: string; name: string };
  style?: CSSProperties | undefined;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  onSelect?: any;
  updatable?: boolean;
  onChange?: (e: string) => void;
  options?: Array<EnumMetaProps>;
}
 
const EnumSelect = (props: EnumSelectProps): ReactElement => {
  const {
    type, mode, placeholder, value,
    notFoundContent, updatable, options
  } = props;
 
  const { t } = useTranslation();
 
  const [data, setData] = useState<Array<EnumMetaProps>>([] as Array<EnumMetaProps>);
  const [fetching, setFetching] = useState<boolean>(false);
 
  const disabled = (updatable == null) ? false : !updatable;
  const enumName = (typeof value === 'string') ? value : value?.name;
 
  function removeUpdatableAttribute(): EnumSelectProps {
    const propsToSelect = {} as EnumSelectProps;
    Object.assign(propsToSelect, props);
    delete propsToSelect.updatable;
    return propsToSelect;
  }
 
  const propsToSelect = removeUpdatableAttribute();
 
  useEffect(() => {
    setFetching(true);
    if (options != null) {
      setData(options);
    } else if (data?.length === 0) {
      getEnumLabelFromCache(type).then(setData);
    }
  }, [type, options, data]);
 
  // **** Attention ****
  // zIndex of the select dropdown should higher than the zIndex of modal(by
  // default 1051) because the dropdown might be displayed on a modal
  // https://stackoverflow.com/questions/53926911/antd-select-not-working-inside-a-full-screen-dialog
  // Set in App.less by .ant-select-dropdown
  return (
    <ErrorBoundary>
      <Select
        {...propsToSelect}
        mode={mode}
        showSearch
        disabled={disabled}
        value={enumName}
        placeholder={t(placeholder)}
        defaultActiveFirstOption={true}
        showArrow={true}
        notFoundContent={fetching ? <Spin size="small" /> : notFoundContent}
        dropdownMatchSelectWidth={true}
        optionFilterProp="children"
        dropdownStyle={{ zIndex: 2000 }}
        filterOption={(input, option) => {
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          if (option == null || option.value == null || input == null) {
            return false;
          }
          const il = input.toLowerCase();
          const validOption = ('children' in option) ?
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            (option.children.toLowerCase().indexOf(il) >= 0 || option.label?.toLowerCase()?.indexOf(il) >= 0) :
            (option.value.toLowerCase().indexOf(il) >= 0 || option.label?.toLowerCase()?.indexOf(il) >= 0);
          return validOption;
        }}
        className={`enum ${getReadOnlyClass(updatable)}`}
        suffixIcon={<SuffixIcon updatable={updatable} />}
        allowClear={true}
      >
        {data?.map(d => <Option key={d.value} value={d.value}>{d.label}</Option>)}
      </Select>
    </ErrorBoundary>
  );
};
 
export default EnumSelect;