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

Code before:
  /** True if the text box is a read-only field. */
  readonly?: B
  /** True if the text box should allow multi-line text entry. */
  multiline?: B
  /** True if the text box should hide text content. */
  password?: B
  /** True if the form should be submitted when the text value changes. */
  trigger?: B
  /** The height of the text box, e.g. '100px'. Percentage values not supported. Applicable only if `multiline` is true. */
  height?: S
  /** The width of the text box, e.g. '100px'. Defaults to '100%'. */
  width?: S
  /** True if the component should be visible. Defaults to True. */
  visible?: B
  /** An optional tooltip message displayed when a user clicks the help icon to the right of the component. */
  tooltip?: S
  /** True if the text may be checked for spelling errors. Defaults to True. */
  spellcheck?: B
}

const DEBOUNCE_TIMEOUT = 500
export const
  XTextbox = ({ model: m }: { model: Textbox }) => {
    const
      [value, setValue] = React.useState(m.value ?? ''),
      debounceRef = React.useRef(debounce(DEBOUNCE_TIMEOUT, () => wave.push())),
      onChange = ({ target }: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, v?: S) => {
        v = v || (target as HTMLInputElement).value

        wave.args[m.name] = v ?? (m.value || '')
        if (m.trigger) debounceRef.current()
        setValue(v)
        m.value = v
      },
      textFieldProps: Fluent.ITextFieldProps & { 'data-test': S } = {
        'data-test': m.name,
        label: m.label,
        value,
        errorMessage: m.error,
        required: m.required,
        disabled: m.disabled,
        readOnly: m.readonly,
        onChange,
        iconProps: m.icon ? { iconName: m.icon } : undefined,
        placeholder: m.placeholder,
        prefix: m.prefix,
        suffix: m.suffix,
        multiline: m.multiline,
        spellCheck: m.spellcheck,
        type: m.password ? 'password' : undefined,
      }

Review comment (suggestion):
Wondering whether this shouldn't be just a module-scoped let instead of ref. Does every instance need its own debounced wave.push or can it be reused across instances?

```suggestion
      debounceRef = React.useRef(debounce(DEBOUNCE_TIMEOUT, wave.push)),
```

Code after:
  /** True if the text box is a read-only field. */
  readonly?: B
  /** True if the text box should allow multi-line text entry. */
  multiline?: B
  /** True if the text box should hide text content. */
  password?: B
  /** True if the form should be submitted when the text value changes. */
  trigger?: B
  /** The height of the text box, e.g. '100px'. Percentage values not supported. Applicable only if `multiline` is true. */
  height?: S
  /** The width of the text box, e.g. '100px'. Defaults to '100%'. */
  width?: S
  /** True if the component should be visible. Defaults to True. */
  visible?: B
  /** An optional tooltip message displayed when a user clicks the help icon to the right of the component. */
  tooltip?: S
  /** True if the text may be checked for spelling errors. Defaults to True. */
  spellcheck?: B
}

const DEBOUNCE_TIMEOUT = 500
export const
  XTextbox = ({ model: m }: { model: Textbox }) => {
    const
      [value, setValue] = React.useState(m.value ?? ''),
      debounceRef = React.useRef(debounce(DEBOUNCE_TIMEOUT, wave.push)),
      onChange = ({ target }: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, v?: S) => {
        v = v || (target as HTMLInputElement).value

        wave.args[m.name] = v ?? (m.value || '')
        if (m.trigger) debounceRef.current()
        setValue(v)
        m.value = v
      },
      textFieldProps: Fluent.ITextFieldProps & { 'data-test': S } = {
        'data-test': m.name,
        label: m.label,
        value,
        errorMessage: m.error,
        required: m.required,
        disabled: m.disabled,
        readOnly: m.readonly,
        onChange,
        iconProps: m.icon ? { iconName: m.icon } : undefined,
        placeholder: m.placeholder,
        prefix: m.prefix,
        suffix: m.suffix,
        multiline: m.multiline,
        spellCheck: m.spellcheck,
        type: m.password ? 'password' : undefined,
      }