@material-ui/lab#Value TypeScript Examples

The following examples show how to use @material-ui/lab#Value. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Autocomplete.tsx    From abacus with GNU General Public License v2.0 4 votes vote down vote up
export default function AbacusAutocomplete<Multiple extends boolean>(
  props: AutocompleteProps<AutocompleteItem, Multiple, false, false>,
): JSX.Element {
  const {
    form: { setFieldValue },
    field: { name },
    multiple,
  } = props

  // ## Translating OuterValues (value | value[]) <-> InnerValues (AutocompleteItem | AutocompleteItem[])
  //
  // Typescript and eslint make this look much more complicated than it is, we are just keeping the form value type
  // in the form (string | number, and array version for multiple), and inside here we are using Autocomplete.
  //
  // We transform on the way in and on the way out.

  type InnerValue = Multiple extends true ? InnerValueSingle[] : InnerValueSingle
  type OuterValue = Multiple extends true ? OuterValueSingle[] : OuterValueSingle

  const outerSingleToInnerSingle = (value: OuterValueSingle): InnerValueSingle => {
    // istanbul ignore next; the ?? shouldn't occur
    return props.options.find((autocompleteItem) => autocompleteItem.value === value) ?? emptyInnerValue
  }
  const innerSingleToOuterSingle = useCallback(
    (autocompleteItem: InnerValueSingle): OuterValueSingle => autocompleteItem.value,
    // eslint-disable-next-line react-hooks/exhaustive-deps -- React strangely thinks the types should be dependecies, smh...
    [],
  )

  const outerValue = props.field.value as OuterValue
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore; Typescript can't quite work this:
  const innerValue: InnerValue = multiple
    ? outerValue
      ? (outerValue as OuterValueSingle[]).map(outerSingleToInnerSingle)
      : []
    : outerValue
    ? outerSingleToInnerSingle(outerValue as OuterValueSingle)
    : emptyInnerValue
  const onChange = useCallback(
    (_event, newInnerValue: AutocompleteItem | AutocompleteItem[] | null) => {
      if (newInnerValue === null) {
        // This happens on clear, it should never occur on multiple as we receive a []
        setFieldValue(name, '')
      } else if (_.isArray(newInnerValue)) {
        setFieldValue(name, newInnerValue.map(innerSingleToOuterSingle))
      } else {
        setFieldValue(name, innerSingleToOuterSingle(newInnerValue))
      }
    },
    [setFieldValue, name, innerSingleToOuterSingle],
  )

  // Autocomplete doesn't like it when it is set to a value which isn't available as a option.
  // So we add it here like this and it works well enough.
  const options =
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore; Typescript can't quite work this:
    !multiple && innerValue.value === emptyInnerValue.value ? [emptyInnerValue, ...props.options] : props.options

  const error = _.get(props.form.touched, props.field.name) && _.get(props.form.errors, props.field.name)

  return (
    <Autocomplete
      {...fieldToAutocomplete({
        ...props,
        options,
        field: {
          ...props.field,
          // This just disables a warning, we actually handle it below
          value: multiple ? [] : undefined,
        },
      })}
      getOptionLabel={(option) => option.name}
      getOptionSelected={(optionA, optionB) => optionA.value === optionB.value}
      renderInput={(params) => {
        // istanbul ignore else; Doesn't occur at all, it's just here for completeness
        if (props.renderInput) {
          return props.renderInput({
            ...params,
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore - It does exist
            error: !!error,
            helperText: _.isString(error) ? error : undefined,
          })
        } else {
          return <TextField {...params} error={!!error} helperText={_.isString(error) ? error : undefined} />
        }
      }}
      value={innerValue as unknown as Value<AutocompleteItem, Multiple, false, false>}
      onChange={onChange}
    />
  )
}