Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #53 from ecmwf-projects/fix-date-range
Browse files Browse the repository at this point in the history
Fix date range
  • Loading branch information
pelusanchez authored Nov 20, 2023
2 parents 98c3c66 + 7b394a7 commit 6913216
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 31 deletions.
17 changes: 17 additions & 0 deletions __tests__/widgets/DateRangeWidget.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getAvailableYears,
getDateLimits,
getEndDateErrors,
getInitialSelection,
getStartDateErrors
} from '../../src'
import { getDateRangeWidgetConfiguration } from '../factories'
Expand Down Expand Up @@ -222,4 +223,20 @@ describe('<DateRangeWidget />', () => {
expect(months).toStrictEqual([1, 2, 3, 4, 5, 6])
})
})
describe('getInitialSelection', () => {
it('should return parsed persisted value', () => {
const persisted = {
date_range: ['2023-11-03/2023-12-24']
}
const { start, end } = getInitialSelection('date_range', persisted)

expect(start?.toString()).toStrictEqual('2023-11-03')
expect(end?.toString()).toStrictEqual('2023-12-24')
})
it('should return undefined values', () => {
const { start, end } = getInitialSelection('date_range')
expect(start).toBeUndefined()
expect(end).toBeUndefined()
})
})
})
3 changes: 1 addition & 2 deletions cypress/component/DateRangeWidget.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ describe('<DateRangeWidget />', () => {
JSON.stringify({
dataset: 'fake',
inputs: {
date_range_start: '2023-09-30',
date_range_end: '2023-10-10'
date_range: ['2023-09-30/2023-10-10']
}
})
)
Expand Down
3 changes: 3 additions & 0 deletions src/common/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ interface DateFieldProps {
label: string
value: CalendarDate
onChange(date: CalendarDate): void
onBlur?: VoidFunction
defaultValue: CalendarDate
minStart?: CalendarDate
maxEnd?: CalendarDate
Expand All @@ -149,6 +150,7 @@ const DateField = ({
label,
value,
onChange,
onBlur,
defaultValue,
minStart,
maxEnd,
Expand All @@ -171,6 +173,7 @@ const DateField = ({
granularity='day'
isRequired={required}
shouldForceLeadingZeros
onBlur={onBlur}
onChange={value => onChange(toCalendarDate(value))}
isDateUnavailable={isDateUnavailable}
>
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ export {
getAvailableYears,
getDateLimits,
getEndDateErrors,
getStartDateErrors
getStartDateErrors,
getInitialSelection
} from './widgets/DateRangeWidget'
90 changes: 62 additions & 28 deletions src/widgets/DateRangeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from './Widget'
import { DateField } from '../common/DateField'
import { WidgetTooltip } from '../common/WidgetTooltip'
import { useBypassRequired } from '../utils'
import { useBypassRequired, useWidgetSelection } from '../utils'
import { useReadLocalStorage } from 'usehooks-ts'

type ValidateDateFn = (
Expand Down Expand Up @@ -181,6 +181,27 @@ export const getAvailableMonths = (
return new Array(12).fill(1).map((_, i) => i + 1)
}

export const getInitialSelection = (
name: string,
selection?: Record<string, string[]>
) => {
if (selection) {
const firstEntry = selection[name]?.[0]
if (firstEntry) {
const [rawStart, rawEnd] = firstEntry.split('/')

return {
start: rawStart ? parseDate(rawStart) : undefined,
end: rawEnd ? parseDate(rawEnd) : undefined
}
}
}
return {
start: undefined,
end: undefined
}
}

export interface DateRangeWidgetConfiguration {
type: 'DateRangeWidget'
help: string | null
Expand Down Expand Up @@ -211,7 +232,7 @@ const DateRangeWidget = ({
error
}: DateRangeWidgetProps) => {
const fieldSetRef = React.useRef<HTMLFieldSetElement>(null)

const inputRef = React.useRef<HTMLInputElement>(null)
const bypassed = useBypassRequired(
fieldSetRef,
bypassRequiredForConstraints,
Expand All @@ -220,45 +241,49 @@ const DateRangeWidget = ({

const persistedSelection = useReadLocalStorage<{
dataset: { id: string }
inputs: { [k: string]: string }
inputs: Record<string, string[]>
}>('formSelection')

const persistedSelectionRef = React.useRef(persistedSelection)

const { selection, setSelection } = useWidgetSelection(configuration.name)

const [startDate, setStartDate] = React.useState(
parseDate(configuration.details.defaultStart)
)
const [endDate, setEndDate] = React.useState(
parseDate(configuration.details.defaultEnd)
)

const finalValue = React.useMemo(() => {
return `${startDate?.toString()}/${endDate?.toString()}`
}, [startDate, endDate])

React.useEffect(() => {
const getInitialSelection = () => {
const inputs = persistedSelectionRef.current?.inputs

if (inputs) {
const start = inputs[`${configuration.name}_start`],
end = inputs[`${configuration.name}_end`]

return {
startDate: start ? parseDate(start) : undefined,
endDate: end ? parseDate(end) : undefined
}
}
const v = `${startDate?.toString()}/${endDate?.toString()}`
setSelection(prev => ({
...prev,
[configuration.name]: [v]
}))
}, [startDate, endDate])

return {
startDate: parseDate(configuration.details.defaultStart),
endDate: parseDate(configuration.details.defaultEnd)
}
const notifyForm = () => {
if (inputRef.current) {
const v = `${startDate?.toString()}/${endDate?.toString()}`
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
)?.set
nativeInputValueSetter?.call(inputRef.current, v)

const evt = new Event('input', { bubbles: true })
inputRef.current.dispatchEvent(evt)
}
}

const { startDate, endDate } = getInitialSelection()
setStartDate(d => startDate ?? d)
setEndDate(d => endDate ?? d)
React.useEffect(() => {
const { start, end } = getInitialSelection(
configuration.name,
persistedSelectionRef.current?.inputs
)
setStartDate(d => start ?? d)
setEndDate(d => end ?? d)
}, [configuration])

const isDateUnavailable = React.useCallback(
Expand Down Expand Up @@ -343,13 +368,19 @@ const DateRangeWidget = ({
</ReservedSpace>
<Fieldset name={configuration.name} ref={fieldSetRef}>
<Legend>{configuration.label}</Legend>
<HiddenInput readOnly value={finalValue} name={configuration.name} />
<HiddenInput
readOnly
defaultValue={selection[configuration.name]}
name={configuration.name}
ref={inputRef}
/>
<Row>
<DateField
value={startDate}
onChange={setStartDate}
label='Start date'
error={startDateError}
onBlur={() => notifyForm()}
defaultValue={parseDate(configuration.details.defaultStart)}
minStart={startMinDate}
maxEnd={startMaxDate}
Expand All @@ -364,6 +395,7 @@ const DateRangeWidget = ({
onChange={setEndDate}
label='End date'
error={endDateError}
onBlur={() => notifyForm()}
defaultValue={parseDate(configuration.details.defaultEnd)}
maxEnd={endMaxDate}
minStart={endMinDate}
Expand Down Expand Up @@ -391,7 +423,9 @@ const Row = styled.div`
`

const HiddenInput = styled.input`
display: none;
width: 0px;
height: 0px;
visibility: hidden;
`

export { DateRangeWidget }

0 comments on commit 6913216

Please sign in to comment.