Repository: h2oai/wave
File: ui/src/combobox.tsx (TypeScript)

Code before:
  }, [m.value])

  return (
    <Fluent.ComboBox
      data-test={m.name}
      label={m.label}
      placeholder={m.placeholder}
      options={options}
      required={m.required}
      selectedKey={selected}
      allowFreeform
      disabled={m.disabled}
      autoComplete="on"
      errorMessage={m.error}
      onChange={onChange}
    />
  )
}

const ComboboxMultiSelect = ({ model: m }: { model: Omit<Combobox, 'value'> }) => {
  const
    [options, setOptions] = useOptions(m.choices),
    [selected, setSelected] = React.useState<S[]>(m.values ?? []),
    selectOpt = (option: IComboBoxOption) => {
      setSelected(keys => {
        const result = option.selected ? [...keys, String(option.key)] : (keys as S[]).filter(key => key !== option.key)
        wave.args[m.name] = result
        return result
      })
    },
    onChange = (_e: React.FormEvent<IComboBox>, option?: IComboBoxOption, _index?: U, value?: S) => {
      if (!option && value) {
        const opt: IComboBoxOption = { key: value, text: value }
        setOptions((prevOptions = []) => [...prevOptions, opt])
        selectOpt({...opt, selected: true})
      }
      if (option) {
        selectOpt(option)
      }
      if (m.trigger) wave.push()
    }
  
  React.useEffect(() => {
      const intersection = options.map(o => String(o.key)).filter(choice => selected?.includes(choice))
      wave.args[m.name] = intersection ?? null
    }, [m.name, m.values, options, selected])
  
  React.useEffect(() => setSelected(m.values || []), [m.values])

  return (
    <Fluent.ComboBox

Review comment (suggestion):
Unnecessary type casting. The type is properly inferred automatically already.
```suggestion
        const result = option.selected ? [...keys, String(option.key)] : keys.filter(key => key !== option.key)
```

Code after:
      data-test={m.name}
      label={m.label}
      placeholder={m.placeholder}
      options={options}
      required={m.required}
      selectedKey={selected}
      allowFreeform
      disabled={m.disabled}
      autoComplete="on"
      errorMessage={m.error}
      onChange={onChange}
    />
  )
}

const ComboboxMultiSelect = ({ model: m }: { model: Omit<Combobox, 'value'> }) => {
  const
    [options, setOptions] = useOptions(m.choices),
    [selected, setSelected] = React.useState<S[]>(m.values ?? []),
    selectOpt = (option: IComboBoxOption) => {
      setSelected(keys => {
        const result = option.selected ? [...keys, String(option.key)] : keys.filter(key => key !== option.key)
        wave.args[m.name] = result
        return result
      })
    },
    onChange = (_e: React.FormEvent<IComboBox>, option?: IComboBoxOption, _index?: U, value?: S) => {
      if (!option && value) {
        const opt: IComboBoxOption = { key: value, text: value }
        setOptions((prevOptions = []) => [...prevOptions, opt])
        selectOpt({ ...opt, selected: true })
      }
      if (option) {
        selectOpt(option)
      }
      if (m.trigger) wave.push()
    }

  React.useEffect(() => {
    wave.args[m.name] = m.values?.length ? m.values : null
    setSelected(m.values || [])
    if (m.values?.length) {
      setOptions((prevOptions = []) =>
        [
          ...prevOptions,
          ...m.values!.filter(v => !prevOptions.some(o => o.key === v)).map(v => ({ key: v, text: v }))
        ]
      )
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [m.values])