diff --git a/components/calendar/src/calendar-input/__tests__/calendar-input.test.js b/components/calendar/src/calendar-input/__tests__/calendar-input.test.js
new file mode 100644
index 0000000000..75be8ec2fe
--- /dev/null
+++ b/components/calendar/src/calendar-input/__tests__/calendar-input.test.js
@@ -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(
+
+ )
+
+ 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(
+
+ )
+
+ 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,
+ })
+ )
+ })
+})
diff --git a/components/calendar/src/calendar-input/calendar-input.js b/components/calendar/src/calendar-input/calendar-input.js
index c670653b9c..b308e85234 100644
--- a/components/calendar/src/calendar-input/calendar-input.js
+++ b/components/calendar/src/calendar-input/calendar-input.js
@@ -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'
@@ -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(
() => ({
@@ -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 = () => {
@@ -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
@@ -147,7 +163,11 @@ export const CalendarInput = ({
modifiers={[offsetModifier]}
>
-
+
diff --git a/components/calendar/src/calendar/calendar-container.js b/components/calendar/src/calendar/calendar-container.js
index 4d8fb004b2..5bea2e0e93 100644
--- a/components/calendar/src/calendar/calendar-container.js
+++ b/components/calendar/src/calendar/calendar-container.js
@@ -23,6 +23,8 @@ export const CalendarContainer = ({
prevMonth,
prevYear,
languageDirection,
+ excludedRef,
+ unfocusable,
}) => {
const navigationProps = useMemo(() => {
return {
@@ -50,14 +52,20 @@ export const CalendarContainer = ({
dir={languageDirection}
data-test="calendar"
>
-
-
+
+
+
+