Skip to content

Commit

Permalink
feat: verify date input only on blur on calendar input, close calenda…
Browse files Browse the repository at this point in the history
…r popup on blur as well
  • Loading branch information
flaminic committed Aug 7, 2024
1 parent eb7e90d commit d8b0d1c
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { fireEvent, render, waitFor, within } from '@testing-library/react'
import React from 'react'
import { CalendarInput } from '../calendar-input.js'

describe('Calendar Input', () => {
it('allow selection of a date through the calendar widget', async () => {
const onDateSelectMock = jest.fn()
const screen = render(
<CalendarInput calendar="gregory" onDateSelect={onDateSelectMock} />
)

const dateInput = within(
screen.getByTestId('dhis2-uicore-input')
).getByRole('textbox')
fireEvent.focus(dateInput)
const calendar = await screen.findByTestId('calendar')
expect(calendar).toBeInTheDocument()

const todayString = new Date().toISOString().slice(0, -14)
const today = within(calendar).getByTestId(todayString)

fireEvent.click(today)

await waitFor(() => {
expect(calendar).not.toBeInTheDocument()
})
expect(onDateSelectMock).toHaveBeenCalledWith(
expect.objectContaining({
calendarDateString: todayString,
})
)
})

it('allow selection of a date through the input', async () => {
const onDateSelectMock = jest.fn()
const screen = render(
<CalendarInput calendar="gregory" onDateSelect={onDateSelectMock} />
)

const dateInputString = '2024/10/12'
const dateInput = within(
screen.getByTestId('dhis2-uicore-input')
).getByRole('textbox')

fireEvent.change(dateInput, { target: { value: dateInputString } })
fireEvent.blur(dateInput)

expect(onDateSelectMock).toHaveBeenCalledWith(
expect.objectContaining({
calendarDateString: dateInputString,
})
)
})
})
28 changes: 24 additions & 4 deletions components/calendar/src/calendar-input/calendar-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useResolvedDirection,
} from '@dhis2/multi-calendar-dates'
import cx from 'classnames'
import React, { useRef, useState, useMemo } from 'react'
import React, { useRef, useState, useMemo, useEffect } from 'react'
import { CalendarContainer } from '../calendar/calendar-container.js'
import { CalendarProps } from '../calendar/calendar.js'
import i18n from '../locales/index.js'
Expand Down Expand Up @@ -40,6 +40,11 @@ export const CalendarInput = ({
} = {}) => {
const ref = useRef()
const [open, setOpen] = useState(false)
const [partialDate, setPartialDate] = useState(date)

const excludeRef = useRef(null)

useEffect(() => setPartialDate(date), [date])

const useDatePickerOptions = useMemo(
() => ({
Expand All @@ -66,7 +71,17 @@ export const CalendarInput = ({
})

const handleChange = (e) => {
parentOnDateSelect?.({ calendarDateString: e.value })
setPartialDate(e.value)
}

const handleBlur = (_, e) => {
parentOnDateSelect?.({ calendarDateString: partialDate })
if (
excludeRef.current &&
!excludeRef.current.contains(e.relatedTarget)
) {
setOpen(false)
}
}

const onFocus = () => {
Expand Down Expand Up @@ -101,8 +116,9 @@ export const CalendarInput = ({
{...rest}
type="text"
onFocus={onFocus}
value={date}
value={partialDate}
onChange={handleChange}
onBlur={handleBlur}
validationText={
pickerResults.errorMessage ||
pickerResults.warningMessage
Expand Down Expand Up @@ -147,7 +163,11 @@ export const CalendarInput = ({
modifiers={[offsetModifier]}
>
<Card>
<CalendarContainer {...calendarProps} />
<CalendarContainer
{...calendarProps}
excludedRef={excludeRef}
unfocusable
/>
</Card>
</Popper>
</Layer>
Expand Down
26 changes: 18 additions & 8 deletions components/calendar/src/calendar/calendar-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const CalendarContainer = ({
prevMonth,
prevYear,
languageDirection,
excludedRef,
unfocusable,
}) => {
const navigationProps = useMemo(() => {
return {
Expand Down Expand Up @@ -50,14 +52,20 @@ export const CalendarContainer = ({
dir={languageDirection}
data-test="calendar"
>
<NavigationContainer {...navigationProps} />
<CalendarTable
selectedDate={date}
calendarWeekDays={calendarWeekDays}
weekDayLabels={weekDayLabels}
cellSize={cellSize}
width={width}
/>
<div ref={excludedRef}>
<NavigationContainer
{...navigationProps}
unfocusable={unfocusable}
/>
<CalendarTable
selectedDate={date}
calendarWeekDays={calendarWeekDays}
weekDayLabels={weekDayLabels}
cellSize={cellSize}
width={width}
unfocusable={unfocusable}
/>
</div>
</div>
<style jsx>{`
.calendar-wrapper {
Expand All @@ -82,11 +90,13 @@ export const CalendarContainer = ({
CalendarContainer.defaultProps = {
cellSize: '32px',
width: '240px',
unfocusable: false,
}

CalendarContainer.propTypes = {
/** the currently selected date using an iso-like format YYYY-MM-DD, in the calendar system provided (not iso8601) */
date: PropTypes.string,
unfocusable: PropTypes.bool,
...CalendarTableProps,
...NavigationContainerProps,
}
9 changes: 8 additions & 1 deletion components/calendar/src/calendar/calendar-table-cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import cx from 'classnames'
import PropTypes from 'prop-types'
import React from 'react'

export const CalendarTableCell = ({ day, cellSize, selectedDate }) => {
export const CalendarTableCell = ({
day,
cellSize,
selectedDate,
unfocusable,
}) => {
const dayHoverBackgroundColor = colors.grey200
const selectedDayBackgroundColor = colors.teal700

Expand All @@ -16,6 +21,7 @@ export const CalendarTableCell = ({ day, cellSize, selectedDate }) => {
isToday: day.isToday,
otherMonth: !day.isInCurrentMonth,
})}
tabIndex={unfocusable ? -1 : 0}
>
{day.label}
</button>
Expand Down Expand Up @@ -94,4 +100,5 @@ CalendarTableCell.propTypes = {
onClick: PropTypes.func,
}),
selectedDate: PropTypes.string,
unfocusable: PropTypes.bool,
}
3 changes: 3 additions & 0 deletions components/calendar/src/calendar/calendar-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const CalendarTable = ({
width,
cellSize,
selectedDate,
unfocusable,
}) => (
<div className="calendar-table-wrapper">
<table className="calendar-table">
Expand All @@ -24,6 +25,7 @@ export const CalendarTable = ({
key={day?.calendarDate}
cellSize={cellSize}
width={width}
unfocusable={unfocusable}
/>
))}
</tr>
Expand Down Expand Up @@ -67,6 +69,7 @@ export const CalendarTableProps = {
).isRequired,
cellSize: PropTypes.string,
selectedDate: PropTypes.string,
unfocusable: PropTypes.bool,
weekDayLabels: PropTypes.arrayOf(PropTypes.string),
width: PropTypes.string,
}
Expand Down
6 changes: 6 additions & 0 deletions components/calendar/src/calendar/navigation-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const NavigationContainer = ({
nextYear,
prevMonth,
prevYear,
unfocusable,
}) => {
const PreviousIcon =
languageDirection === 'ltr' ? IconChevronLeft16 : IconChevronRight16
Expand All @@ -36,6 +37,7 @@ export const NavigationContainer = ({
data-test="calendar-previous-month"
aria-label={`${i18n.t(`Go to ${prevMonth.label}`)}`}
type="button"
tabIndex={unfocusable ? -1 : 0}
>
<PreviousIcon />
</button>
Expand All @@ -50,6 +52,7 @@ export const NavigationContainer = ({
name="next-month"
aria-label={`${i18n.t(`Go to ${nextMonth.label}`)}`}
type="button"
tabIndex={unfocusable ? -1 : 0}
>
<NextIcon />
</button>
Expand All @@ -62,6 +65,7 @@ export const NavigationContainer = ({
name="previous-year"
aria-label={`${i18n.t('Go to previous year')}`}
type="button"
tabIndex={unfocusable ? -1 : 0}
>
<PreviousIcon />
</button>
Expand All @@ -80,6 +84,7 @@ export const NavigationContainer = ({
name="next-year"
aria-label={`${i18n.t('Go to next year')}`}
type="button"
tabIndex={unfocusable ? -1 : 0}
>
<NextIcon />
</button>
Expand Down Expand Up @@ -184,6 +189,7 @@ export const NavigationContainerProps = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
navigateTo: PropTypes.func,
}),
unfocusable: PropTypes.bool,
}

NavigationContainer.propTypes = NavigationContainerProps

0 comments on commit d8b0d1c

Please sign in to comment.