ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/chronology/ui/DateEditor/DateSelectionInput.tsx

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import React, { useState } from 'react'
import _ from 'lodash'
import { InputGroup, Form } from 'react-bootstrap'
import Select from 'react-select'
import {
  EponymDateField,
  KingDateField,
  Ur3Calendar,
} from 'chronology/domain/DateParameters'
import { KingField } from 'chronology/ui/Kings/Kings'
import { EponymField } from 'chronology/ui/DateEditor/Eponyms'
import getDateConfigs from 'chronology/application/DateSelectionInputConfig'
import {
  InputGroupProps,
  RadioButton,
} from 'chronology/ui/DateEditor/DateSelectionInputBase'
import { BrokenAndUncertainSwitches } from 'common/BrokenAndUncertain'

type InputGroupsProps = {
  yearValue: string
  yearBroken: boolean
  yearUncertain: boolean
  monthValue: string
  monthBroken: boolean
  monthUncertain: boolean
  isIntercalary?: boolean
  isAssyrianDate?: boolean
  dayValue: string
  dayBroken: boolean
  dayUncertain: boolean
  setYearValue: React.Dispatch<React.SetStateAction<string>>
  setYearBroken: React.Dispatch<React.SetStateAction<boolean>>
  setYearUncertain: React.Dispatch<React.SetStateAction<boolean>>
  setMonthValue: React.Dispatch<React.SetStateAction<string>>
  setMonthBroken: React.Dispatch<React.SetStateAction<boolean>>
  setMonthUncertain: React.Dispatch<React.SetStateAction<boolean>>
  setIntercalary?: React.Dispatch<React.SetStateAction<boolean>>
  setDayValue: React.Dispatch<React.SetStateAction<string>>
  setDayBroken: React.Dispatch<React.SetStateAction<boolean>>
  setDayUncertain: React.Dispatch<React.SetStateAction<boolean>>
}

export interface DateOptionsProps {
  king?: KingDateField
  kingBroken?: boolean
  kingUncertain?: boolean
  eponym?: EponymDateField
  eponymBroken?: boolean
  eponymUncertain?: boolean
  ur3Calendar?: Ur3Calendar
  isSeleucidEra: boolean
  isAssyrianDate: boolean
  isCalendarFieldDisplayed: boolean
  setKing: React.Dispatch<React.SetStateAction<KingDateField | undefined>>
  setKingBroken: React.Dispatch<React.SetStateAction<boolean>>
  setKingUncertain: React.Dispatch<React.SetStateAction<boolean>>
  setEponym: React.Dispatch<React.SetStateAction<EponymDateField | undefined>>
  setEponymBroken: React.Dispatch<React.SetStateAction<boolean>>
  setEponymUncertain: React.Dispatch<React.SetStateAction<boolean>>
  setIsSeleucidEra: React.Dispatch<React.SetStateAction<boolean>>
  setIsAssyrianDate: React.Dispatch<React.SetStateAction<boolean>>
  setIsCalenderFieldDisplayed: React.Dispatch<React.SetStateAction<boolean>>
  setUr3Calendar: React.Dispatch<React.SetStateAction<Ur3Calendar | undefined>>
}

export function DateOptionsInput(props: DateOptionsProps): JSX.Element {
  const [assyrianPhase, setAssyrianPhase] = useState(
    props?.eponym?.phase ?? 'NA'
  )
  return (
    <>
      {getDateTypeSwitch(props)}
      {props.isAssyrianDate &&
        getAssyrianDateSwitch({ assyrianPhase, setAssyrianPhase })}
      {!props.isSeleucidEra &&
        !props.isAssyrianDate &&
        getKingEponymSelect(props, 'king')}
      {props.isAssyrianDate &&
        getKingEponymSelect(props, 'eponym', assyrianPhase)}
      {props.isCalendarFieldDisplayed && getUr3CalendarField(props)}
    </>
  )
}

function getKingEponymSelect(
  props: DateOptionsProps,
  name: 'king' | 'eponym',
  assyrianPhase?: 'NA' | 'MA' | 'OA'
): JSX.Element {
  return (
    <>
      {name === 'eponym' && assyrianPhase
        ? EponymField({ ...props, assyrianPhase })
        : KingField(props)}
      <InputGroup size="sm">
        <BrokenAndUncertainSwitches
          {...{
            name,
            isBroken: props[`${name}isBroken`] ?? false,
            isUncertain: props[`${name}Uncertain`] ?? false,
            setBroken: props[`set${_.capitalize(name)}Broken`],
            setUncertain: props[`set${_.capitalize(name)}Uncertain`],
          }}
        />
      </InputGroup>
      <br />
    </>
  )
}

function getDateTypeSwitch(props: DateOptionsProps): JSX.Element {
  const dateConfigs = getDateConfigs(props)
  return (
    <div key="inline-radio-date-type" className="mb-3">
      {dateConfigs.map((config) => (
        <RadioButton key={config.id} {...config} name="date-type" />
      ))}
    </div>
  )
}

function getAssyrianDateSwitch(props: {
  assyrianPhase: 'NA' | 'MA' | 'OA'
  setAssyrianPhase: React.Dispatch<React.SetStateAction<'NA' | 'MA' | 'OA'>>
}): JSX.Element {
  const phases: ('NA' | 'MA' | 'OA')[] = ['NA', 'MA', 'OA']

  const assyrianConfigs = phases.map((phase) => ({
    id: `${phase.toLowerCase()}-assyrian-date`,
    label: `${
      phase === 'NA' ? 'Neo' : phase === 'MA' ? 'Middle' : 'Old'
    }-Assyrian`,
    checked: props.assyrianPhase === phase,
    onChange: () => props.setAssyrianPhase(phase),
  }))

  return (
    <div key="inline-radio-assyrian-phase" className="mb-3">
      {assyrianConfigs.map((config) => (
        <RadioButton key={config.id} {...config} name="assyrian-date" />
      ))}
    </div>
  )
}

function getUr3CalendarField({
  ur3Calendar,
  setUr3Calendar,
}: DateOptionsProps): JSX.Element {
  const options = Object.keys(Ur3Calendar).map((key) => {
    return { label: Ur3Calendar[key], value: key }
  })
  const value = options.find(
    (option) => option.label === ur3Calendar?.toString()
  )
  return (
    <Select
      aria-label="select-calendar"
      options={options}
      onChange={(option): void => {
        setUr3Calendar(Ur3Calendar[option?.value as keyof typeof Ur3Calendar])
      }}
      isSearchable={true}
      autoFocus={true}
      placeholder="Calendar"
      value={value}
    />
  )
}

function getDateInputGroup({
  name,
  value,
  isBroken,
  isUncertain,
  setValue,
  setBroken,
  setUncertain,
  isIntercalary = false,
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  setIntercalary = (): void => {},
}: InputGroupProps): JSX.Element {
  return (
    <InputGroup size="sm">
      <Form.Control
        placeholder={_.startCase(name)}
        aria-label={_.startCase(name)}
        onChange={(event) => setValue(event.target.value)}
        value={value}
      />
      {name === 'month' && (
        <Form.Check
          label="Intercalary"
          id={`${name}_intercalary`}
          style={{ marginLeft: '10px' }}
          onChange={(event) => setIntercalary(event.target.checked)}
          checked={isIntercalary}
        />
      )}
      <BrokenAndUncertainSwitches
        {...{
          name,
          isBroken,
          isUncertain,
          setBroken,
          setUncertain,
        }}
      />
    </InputGroup>
  )
}

function getYearInputGroup(props: InputGroupsProps): JSX.Element {
  return getDateInputGroup({
    name: 'year',
    value: props.yearValue,
    isBroken: props.yearBroken,
    isUncertain: props.yearUncertain,
    setValue: props.setYearValue,
    setBroken: props.setYearBroken,
    setUncertain: props.setYearUncertain,
  })
}

function getMonthInputGroup(props: InputGroupsProps): JSX.Element {
  return getDateInputGroup({
    name: 'month',
    value: props.monthValue,
    isBroken: props.monthBroken,
    isUncertain: props.monthUncertain,
    isIntercalary: props.isIntercalary,
    setValue: props.setMonthValue,
    setBroken: props.setMonthBroken,
    setUncertain: props.setMonthUncertain,
    setIntercalary: props.setIntercalary,
  })
}

function getDayInputGroup(props: InputGroupsProps): JSX.Element {
  return getDateInputGroup({
    name: 'day',
    value: props.dayValue,
    isBroken: props.dayBroken,
    isUncertain: props.dayUncertain,
    setValue: props.setDayValue,
    setBroken: props.setDayBroken,
    setUncertain: props.setDayUncertain,
  })
}

export function DateInputGroups(props: InputGroupsProps): JSX.Element {
  return (
    <>
      {!props.isAssyrianDate && getYearInputGroup(props)}
      {getMonthInputGroup(props)}
      {getDayInputGroup(props)}
    </>
  )
}

export const exportedForTesting = { getUr3CalendarField, getAssyrianDateSwitch }