Skip to content

Commit

Permalink
Add tests, refactor & extend options fields (WiP)
Browse files Browse the repository at this point in the history
  • Loading branch information
khoidt committed Jan 2, 2024
1 parent 43d7c1e commit 4bc979d
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ const mesopotamianDateInformationFields = [
name: 'mesopotamianDay',
type: 'number',
placeholder: 'Mesopotamian Day',
help: 'Mesopotamian day of the month as a number from 1 to 30.',
help:
'Mesopotamian day of the month as a number from 1 to 29-30 (depending on the month).',
},
{
name: 'mesopotamianMonthLength',
Expand Down
30 changes: 20 additions & 10 deletions src/chronology/ui/BrinkmanKings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'chronology/ui/BrinkmanKings.sass'
import BrinkmanKings from 'chronology/domain/BrinkmanKings.json'
import { Popover } from 'react-bootstrap'
import HelpTrigger from 'common/HelpTrigger'
import Select from 'react-select'
import Select, { ValueType } from 'react-select'

export interface King {
orderGlobal: number
Expand Down Expand Up @@ -101,20 +101,15 @@ export function KingField({
}: {
king?: King
setKing: React.Dispatch<React.SetStateAction<King | undefined>>
setIsCalenderFieldDisplayed: React.Dispatch<React.SetStateAction<boolean>>
setIsCalenderFieldDisplayed?: React.Dispatch<React.SetStateAction<boolean>>
}): JSX.Element {
return (
<Select
aria-label="select-king"
options={kingOptions}
onChange={(option): void => {
setKing(option?.value)
if (option?.value?.dynastyNumber === '2') {
setIsCalenderFieldDisplayed(true)
} else {
setIsCalenderFieldDisplayed(false)
}
}}
onChange={(option) =>
onKingFieldChange(option, setKing, setIsCalenderFieldDisplayed)
}
isSearchable={true}
autoFocus={true}
placeholder="King"
Expand All @@ -123,6 +118,21 @@ export function KingField({
)
}

const onKingFieldChange = (
option: ValueType<{ label: string; value: King }, false>,
setKing: React.Dispatch<React.SetStateAction<King | undefined>>,
setIsCalenderFieldDisplayed?: React.Dispatch<React.SetStateAction<boolean>>
): void => {
setKing(option?.value)
if (setIsCalenderFieldDisplayed) {
if (option?.value?.dynastyNumber === '2') {
setIsCalenderFieldDisplayed(true)
} else {
setIsCalenderFieldDisplayed(false)
}
}
}

function getKingSelectLabel(king: King): string {
const kingYears = king.date ? ` (${king.date})` : ''
return `${king.name}${kingYears}, ${king.dynastyName}`
Expand Down
44 changes: 44 additions & 0 deletions src/chronology/ui/DateConverterForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ const optionToArray = (select: HTMLElement): number => {
}

describe('DateConverterForm', () => {
beforeEach(() => {
Object.defineProperty(window.navigator, 'clipboard', {
value: {
writeText: jest.fn().mockResolvedValue(true),
},
writable: true,
})
})

afterEach(() => {
jest.resetAllMocks()
})

it('renders form, options, and scenario panel correctly', () => {
render(<DateConverterForm />)
expect(screen.getAllByLabelText(/date/i)).toHaveLength(4)
Expand Down Expand Up @@ -143,4 +156,35 @@ describe('DateConverterForm', () => {
expect(screen.getByLabelText('CJDN')).toHaveValue('1607923')
expect(screen.getByLabelText('Lunation Nabonassar')).toHaveValue('5395')
})

it("copies the form's JSON to clipboard", async () => {
render(<DateConverterForm />)
await act(async () => {
fireEvent.click(screen.getByText('Copy JSON'))
})
const expected = {
gregorianYear: -309,
gregorianMonth: 3,
gregorianDay: 29,
julianYear: -310,
julianMonth: 4,
julianDay: 3,
cjdn: 1607923,
weekDay: 4,
mesopotamianMonth: 1,
seBabylonianYear: 1,
lunationNabonassar: 5395,
bcJulianYear: 311,
bcGregorianYear: 310,
mesopotamianDay: 1,
mesopotamianMonthLength: 29,
ruler: 'Seleucus I Nicator',
regnalYear: 1,
seMacedonianYear: 1,
}
expect(navigator.clipboard.writeText).toHaveBeenCalled()
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
JSON.stringify(expected)
)
})
})
2 changes: 1 addition & 1 deletion src/chronology/ui/DateConverterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { Form, Row, Col } from 'react-bootstrap'
import DateConverter from 'chronology/domain/DateConverter'
import { sections } from 'chronology/ui/DateConverterFormFieldData'
import { sections } from 'chronology/application/DateConverterFormFieldData'
import './DateConverterForm.sass'
import { Markdown } from 'common/Markdown'
import MarkupService from 'markup/application/MarkupService'
Expand Down
236 changes: 236 additions & 0 deletions src/chronology/ui/DateConverterFormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import React from 'react'
import { Form, FormGroup, FormLabel, Popover, Col } from 'react-bootstrap'
import DateConverter from 'chronology/domain/DateConverter'
import HelpTrigger from 'common/HelpTrigger'
import { Field } from 'chronology/application/DateConverterFormFieldData'
import { weekDayNames, monthNames } from 'chronology/domain/DateConverter'
import { CalendarProps } from 'chronology/domain/DateConverterBase'
import { romanize } from 'romans'

const selectFields = [
'weekDay',
'gregorianYear',
'gregorianMonth',
'gregorianDay',
'julianYear',
'julianMonth',
'julianDay',
'regnalYear',
'mesopotamianMonth',
'mesopotamianDay',
]

export function DateConverterFormField({
field,
index,
dateConverter,
formData,
handleChange,
scenario,
}: {
field: Field
index: number
dateConverter: DateConverter
formData: CalendarProps
handleChange: (event) => void
scenario: string
}): JSX.Element {
const isSelect = selectFields.includes(field.name)
return (
<Col xs={12} sm={12} md={6} lg={6} key={index}>
<FormGroup>
<FormLabel htmlFor={field.name}>{field.placeholder}</FormLabel>{' '}
<DateConverterFormHelpTrigger field={field} />
<Form.Control
{...(isSelect
? { as: 'select', children: getOptions({ field, dateConverter }) }
: {})}
id={field.name}
name={field.name}
value={formData[field.name] || ''}
onChange={handleChange}
disabled={!fieldIsActive(field.name, scenario)}
required={field.required && fieldIsActive(field.name, scenario)}
className={fieldIsActive(field.name, scenario) ? 'active-field' : ''}
/>
</FormGroup>
</Col>
)
}

function getOptions({
field,
dateConverter,
}: {
field: Field
dateConverter: DateConverter
}): JSX.Element[] {
if (field.name.includes('Year')) {
return getYearOptions(field)
}
if (field.name.includes('Month')) {
return getMonthOptions(field)
}
if (field.name.includes('Day')) {
return getDayOptions(field, dateConverter)
}
return []
}

const getYearOptions = (field: Field): JSX.Element[] => {
if (field.name === 'gregorianYear') {
return getGregorianYearOptions()
}
if (field.name === 'julianYear') {
return getJulianYearOptions()
}
if (field.name === 'regnalYear') {
return getRegnalYearOptions()
}
return []
}

const getMonthOptions = (field: Field): JSX.Element[] => {
if (['gregorianMonth', 'julianMonth'].includes(field.name)) {
return getGregorianJulianMonthOptions()
}
if (field.name === 'mesopotamianMonth') {
return getMesopotamianMonthOptions()
}
return []
}

const getDayOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
if (['gregorianDay', 'julianDay'].includes(field.name)) {
return getGregorianJulianDayOptions(field, dateConverter)
}
if (field.name === 'weekDay') {
return getGregorianJulianweekDayOptions()
}
if (field.name === 'mesopotamianDay') {
return getMesopotamianDayOptions(dateConverter)
}
return []
}

const getGregorianYearOptions = (): JSX.Element[] => {
// ToDo: Update to Gregorian dates
return getNumberRangeOptions(-625, 76, getYearOptionLabel)
}

const getJulianYearOptions = (): JSX.Element[] => {
return getNumberRangeOptions(-625, 76, getYearOptionLabel)
}

const getRegnalYearOptions = (): JSX.Element[] => {
// ToDo: Implement restrictions
return getNumberRangeOptions(1, 40)
}

const getGregorianJulianMonthOptions = (): JSX.Element[] => {
return getStringOptions(monthNames)
}

const getMesopotamianMonthOptions = (): JSX.Element[] => {
// ToDo: Implement restrictions
return getNumberRangeOptions(1, 13, romanize)
}

// ToDo: Implement for SE Babylonian Year

const getGregorianJulianDayOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
return getNumberRangeOptions(
1,
dateConverter.getMonthLength(field.name.includes('julian'))
)
}

const getGregorianJulianweekDayOptions = (): JSX.Element[] => {
return getStringOptions(weekDayNames)
}

const getMesopotamianDayOptions = (
dateConverter: DateConverter
): JSX.Element[] => {
return getNumberRangeOptions(
1,
dateConverter.calendar?.mesopotamianMonthLength ?? 30
)
}

function getNumberRangeOptions(
from: number,
to: number,
labelFormatter?: (number) => string
): JSX.Element[] {
const numbersArray = Array.from(
{ length: to - from + 1 },
(_, index) => index + from
)

return numbersArray.map((number) => (
<option key={number} value={number}>
{labelFormatter ? labelFormatter(number) : number}
</option>
))
}

function getStringOptions(options: string[]): JSX.Element[] {
return options.map((name, index) => (
<option key={index} value={index + 1}>
{name}
</option>
))
}

function getYearOptionLabel(year: number): string {
return year < 1 ? `${Math.abs(year) + 1} BCE` : `${year} CE`
}

const fieldIsActive = (fieldName: string, scenario: string) => {
switch (scenario) {
case 'setToGregorianDate':
return ['gregorianYear', 'gregorianMonth', 'gregorianDay'].includes(
fieldName
)
case 'setToJulianDate':
return ['julianYear', 'julianMonth', 'julianDay'].includes(fieldName)
case 'setToSeBabylonianDate':
return [
'seBabylonianYear',
'mesopotamianMonth',
'mesopotamianDay',
].includes(fieldName)
case 'setToMesopotamianDate':
return [
'ruler',
'regnalYear',
'mesopotamianMonth',
'mesopotamianDay',
].includes(fieldName)
default:
return false
}
}

function DateConverterFormHelpTrigger({
field,
}: {
field: Field
}): JSX.Element {
return (
<HelpTrigger
overlay={
<Popover id={field.name} title={field.name}>
<Popover.Content>{field.help}</Popover.Content>
</Popover>
}
/>
)
}
Loading

0 comments on commit 4bc979d

Please sign in to comment.