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

Code before:
      <Fluent.Spinner styles={{ root: { height: '100%' } }} size={Fluent.SpinnerSize.small} />
    </div>,
  Toolbar = ({ setOpenView, time, label, switchAmPm }: ToolbarProps) =>
    <div className={css.toolbar}>
      {label && <Fluent.Label className={css.toolbarLabel}>{label}</Fluent.Label>}
      <Fluent.Text className={css.toolbarText}>
        <Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('hours')}>{time?.substring(0, 2) || '--'}</Fluent.Text>:
        <Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('minutes')}>{time?.substring(3, 5) || '--'}</Fluent.Text>{' '}
        <Fluent.Text className={css.toolbarTime} onClick={switchAmPm}>{time?.substring(6, 8) || ''}</Fluent.Text>
      </Fluent.Text>
    </div>


export const
  XTimePicker = ({ model: m }: { model: TimePicker }) => {
    const
      { hour_format = '12', label, disabled, min, max, placeholder, required, minutes_step = 1 } = m,
      [value, setValue] = React.useState(m.value ? parseTimeStringToDate(m.value) : null),
      [isDialogOpen, setIsDialogOpen] = React.useState(false),
      textInputRef = React.useRef<HTMLDivElement | null>(null),
      popperRef = React.useRef<HTMLDivElement | null>(null),
      isOutOfBounds = (date: Date) => {
        if (min && date < parseTimeStringToDate(min)) return true
        if (max && date > parseTimeStringToDate(max)) return true
        return false
      },
      switchAmPm = () => {
        setValue((prevValue) => {
          const date = new Date(prevValue!)
          date.setTime(date.getTime() + 12 * 60 * 60 * 1000)
          const newValue = formatDateToTimeString(date, '24')
          wave.args[m.name] = newValue
          if (!isOutOfBounds(date)) m.value = newValue
          return date
        })
      },
      onChangeTime = (time: unknown) => {
        if (time instanceof Date) {
          const newValue = formatDateToTimeString(time, '24')
          wave.args[m.name] = newValue
          if (!isOutOfBounds(time)) m.value = newValue
          setValue(time)
        }
      },
      onSelectTime = () => { if (m.trigger) wave.push() },
      // HACK: https://stackoverflow.com/questions/70106353/material-ui-date-time-picker-safari-browser-issue
      onOpen = () => setTimeout(() => (document.activeElement as HTMLElement)?.blur()),
      onBlur = (ev: React.FocusEvent) => {
        // Close the popover if the side panel or dialog opens.
        // TODO: Investigate why MUI dismiss on outer click handling doesn't work in this case.
        if (!ev.relatedTarget || !Fluent.elementContains(popperRef.current, ev.relatedTarget as HTMLElement)) setIsDialogOpen(false)

Review comment (suggestion):
Would this work?

```suggestion
      isOutOfBounds = (date: Date) => (min && date < parseTimeStringToDate(min)) || (max && date > parseTimeStringToDate(max)),
```

Code after:
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    return { theme, AdapterDateFns, format: formatF }
  },
  LazyLoadPlaceholder = () =>
    <div data-test='lazyload' style={{ height: 59 }}>
      <Fluent.Spinner styles={{ root: { height: '100%' } }} size={Fluent.SpinnerSize.small} />
    </div>,
  Toolbar = ({ setOpenView, time, label }: ToolbarProps) =>
    <div className={css.toolbar}>
      {label && <Fluent.Label className={css.toolbarLabel}>{label}</Fluent.Label>}
      <Fluent.Text className={css.toolbarText}>
        <Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('hours')}>{time?.substring(0, 2) || '--'}</Fluent.Text>:
        <Fluent.Text className={css.toolbarTime} onClick={() => setOpenView('minutes')}>{time?.substring(3, 5) || '--'}</Fluent.Text>{' '}
        <Fluent.Text className={css.toolbarAmPm}>{time?.substring(6, 8) || ''}</Fluent.Text>
      </Fluent.Text>
    </div>


export const
  XTimePicker = ({ model: m }: { model: TimePicker }) => {
    const
      { hour_format = '12', label, disabled, min, max, placeholder, required, minutes_step = 1 } = m,
      [value, setValue] = React.useState(m.value ? parseTimeStringToDate(m.value) : null),
      [isDialogOpen, setIsDialogOpen] = React.useState(false),
      textInputRef = React.useRef<HTMLDivElement | null>(null),
      popperRef = React.useRef<HTMLDivElement | null>(null),
      minTime = React.useMemo(() => parseTimeStringToDate(min || '00:00'), [min]),
      maxTime = React.useMemo(() => parseTimeStringToDate(max || '24:00'), [max]),
      onChangeTime = (time: unknown) => {
        if (!(time instanceof Date)) return
        const newValue = formatDateToTimeString(time, '24')
        const date = new Date(normalize(newValue))
        if (isOutOfBounds(date, minTime, maxTime)) return
        m.value = newValue
        wave.args[m.name] = newValue
        setValue(time)
      },
      onSelectTime = () => { if (m.trigger) wave.push() },
      // HACK: https://stackoverflow.com/questions/70106353/material-ui-date-time-picker-safari-browser-issue
      onOpen = () => setTimeout(() => (document.activeElement as HTMLElement)?.blur()),
      onBlur = (ev: React.FocusEvent) => {
        // Close the popover if the side panel or dialog opens.
        // TODO: Investigate why MUI dismiss on outer click handling doesn't work in this case.
        if (!ev.relatedTarget || !Fluent.elementContains(popperRef.current, ev.relatedTarget as HTMLElement)) setIsDialogOpen(false)
      },
      { palette: fluentPalette } = Fluent.useTheme(),
      themeObj = {
        palette: {
          background: {