diff --git a/components/calendar/package.json b/components/calendar/package.json
index dc6cdc5e97..fc94c283f2 100644
--- a/components/calendar/package.json
+++ b/components/calendar/package.json
@@ -38,7 +38,7 @@
"@dhis2-ui/input": "9.9.0-alpha.1",
"@dhis2-ui/layer": "9.9.0-alpha.1",
"@dhis2-ui/popper": "9.9.0-alpha.1",
- "@dhis2/multi-calendar-dates": "^1.1.1",
+ "@dhis2/multi-calendar-dates": "v1.0.0-alpha.26",
"@dhis2/prop-types": "^3.1.2",
"@dhis2/ui-constants": "9.9.0-alpha.1",
"@dhis2/ui-icons": "9.9.0-alpha.1",
diff --git a/components/calendar/src/calendar-input/calendar-input.js b/components/calendar/src/calendar-input/calendar-input.js
index a2753110b2..c670653b9c 100644
--- a/components/calendar/src/calendar-input/calendar-input.js
+++ b/components/calendar/src/calendar-input/calendar-input.js
@@ -1,11 +1,16 @@
import { Button } from '@dhis2-ui/button'
import { Card } from '@dhis2-ui/card'
-import { InputField, InputFieldProps } from '@dhis2-ui/input'
+import { InputField } from '@dhis2-ui/input'
import { Layer } from '@dhis2-ui/layer'
import { Popper } from '@dhis2-ui/popper'
+import {
+ useDatePicker,
+ useResolvedDirection,
+} from '@dhis2/multi-calendar-dates'
import cx from 'classnames'
-import React, { useRef, useState } from 'react'
-import { Calendar, CalendarProps } from '../calendar/calendar.js'
+import React, { useRef, useState, useMemo } from 'react'
+import { CalendarContainer } from '../calendar/calendar-container.js'
+import { CalendarProps } from '../calendar/calendar.js'
import i18n from '../locales/index.js'
const offsetModifier = {
@@ -16,7 +21,7 @@ const offsetModifier = {
}
export const CalendarInput = ({
- onDateSelect,
+ onDateSelect: parentOnDateSelect,
calendar,
date,
dir,
@@ -27,45 +32,67 @@ export const CalendarInput = ({
width,
cellSize,
clearable,
+ minDate,
+ maxDate,
+ format, // todo: props and types for format and validation
+ strictValidation,
...rest
} = {}) => {
const ref = useRef()
const [open, setOpen] = useState(false)
- const calendarProps = React.useMemo(() => {
- const onDateSelectWrapper = (selectedDate) => {
- setOpen(false)
- onDateSelect?.(selectedDate)
- }
- return {
- onDateSelect: onDateSelectWrapper,
+ const useDatePickerOptions = useMemo(
+ () => ({
calendar,
- date,
- dir,
locale,
+ timeZone, // todo: we probably shouldn't have had timezone here in the first place
numberingSystem,
weekDayFormat,
- timeZone,
- width,
- cellSize,
- }
- }, [
- calendar,
- cellSize,
+ }),
+ [calendar, locale, numberingSystem, timeZone, weekDayFormat]
+ )
+
+ const pickerResults = useDatePicker({
+ onDateSelect: (result) => {
+ setOpen(false)
+ parentOnDateSelect?.(result)
+ },
date,
- dir,
- locale,
- numberingSystem,
- onDateSelect,
- timeZone,
- weekDayFormat,
- width,
- ])
+ minDate: minDate,
+ maxDate: maxDate,
+ strictValidation: strictValidation,
+ format: format,
+ options: useDatePickerOptions,
+ })
+
+ const handleChange = (e) => {
+ parentOnDateSelect?.({ calendarDateString: e.value })
+ }
const onFocus = () => {
setOpen(true)
}
+ const languageDirection = useResolvedDirection(dir, locale)
+
+ const calendarProps = useMemo(() => {
+ return {
+ date,
+ width,
+ cellSize,
+ isValid: pickerResults.isValid,
+ calendarWeekDays: pickerResults.calendarWeekDays,
+ weekDayLabels: pickerResults.weekDayLabels,
+ currMonth: pickerResults.currMonth,
+ currYear: pickerResults.currYear,
+ nextMonth: pickerResults.nextMonth,
+ nextYear: pickerResults.nextYear,
+ prevMonth: pickerResults.prevMonth,
+ prevYear: pickerResults.prevYear,
+ languageDirection,
+ }
+ }, [cellSize, date, pickerResults, width, languageDirection])
+
return (
<>
@@ -75,13 +102,17 @@ export const CalendarInput = ({
type="text"
onFocus={onFocus}
value={date}
+ onChange={handleChange}
+ validationText={
+ pickerResults.errorMessage ||
+ pickerResults.warningMessage
+ }
+ error={!!pickerResults.errorMessage}
+ warning={!!pickerResults.warningMessage}
/>
{clearable && (
calendarProps.onDateSelect(null)}
+ onClick={() => {
+ parentOnDateSelect?.(null)
+ }}
type="button"
>
{i18n.t('Clear')}
@@ -114,7 +147,7 @@ export const CalendarInput = ({
modifiers={[offsetModifier]}
>
-
+
@@ -130,7 +163,6 @@ export const CalendarInput = ({
inset-inline-end: 6px;
inset-block-start: 27px;
}
-
.calendar-clear-button.with-icon {
inset-inline-end: 36px;
}
@@ -148,5 +180,4 @@ CalendarInput.defaultProps = {
}
CalendarInput.propTypes = {
...CalendarProps,
- ...InputFieldProps,
}
diff --git a/components/calendar/src/calendar/calendar-container.js b/components/calendar/src/calendar/calendar-container.js
new file mode 100644
index 0000000000..4d8fb004b2
--- /dev/null
+++ b/components/calendar/src/calendar/calendar-container.js
@@ -0,0 +1,92 @@
+import { colors } from '@dhis2/ui-constants'
+import PropTypes from 'prop-types'
+import React, { useMemo } from 'react'
+import { CalendarTable, CalendarTableProps } from './calendar-table.js'
+import {
+ NavigationContainer,
+ NavigationContainerProps,
+} from './navigation-container.js'
+
+const wrapperBorderColor = colors.grey300
+const backgroundColor = 'none'
+
+export const CalendarContainer = ({
+ date,
+ width,
+ cellSize,
+ calendarWeekDays,
+ weekDayLabels,
+ currMonth,
+ currYear,
+ nextMonth,
+ nextYear,
+ prevMonth,
+ prevYear,
+ languageDirection,
+}) => {
+ const navigationProps = useMemo(() => {
+ return {
+ currMonth,
+ currYear,
+ nextMonth,
+ nextYear,
+ prevMonth,
+ prevYear,
+ languageDirection,
+ }
+ }, [
+ currMonth,
+ currYear,
+ languageDirection,
+ nextMonth,
+ nextYear,
+ prevMonth,
+ prevYear,
+ ])
+ return (
+
+ )
+}
+
+CalendarContainer.defaultProps = {
+ cellSize: '32px',
+ width: '240px',
+}
+
+CalendarContainer.propTypes = {
+ /** the currently selected date using an iso-like format YYYY-MM-DD, in the calendar system provided (not iso8601) */
+ date: PropTypes.string,
+ ...CalendarTableProps,
+ ...NavigationContainerProps,
+}
diff --git a/components/calendar/src/calendar/calendar-table.js b/components/calendar/src/calendar/calendar-table.js
index b4d0ea1940..a9ea2ac5cf 100644
--- a/components/calendar/src/calendar/calendar-table.js
+++ b/components/calendar/src/calendar/calendar-table.js
@@ -48,7 +48,7 @@ export const CalendarTable = ({
)
-CalendarTable.propTypes = {
+export const CalendarTableProps = {
calendarWeekDays: PropTypes.arrayOf(
PropTypes.arrayOf(
PropTypes.shape({
@@ -70,3 +70,5 @@ CalendarTable.propTypes = {
weekDayLabels: PropTypes.arrayOf(PropTypes.string),
width: PropTypes.string,
}
+
+CalendarTable.propTypes = CalendarTableProps
diff --git a/components/calendar/src/calendar/calendar.js b/components/calendar/src/calendar/calendar.js
index 51176ea25d..f03a415293 100644
--- a/components/calendar/src/calendar/calendar.js
+++ b/components/calendar/src/calendar/calendar.js
@@ -2,11 +2,9 @@ import {
useDatePicker,
useResolvedDirection,
} from '@dhis2/multi-calendar-dates'
-import { colors } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
-import React, { useState } from 'react'
-import { CalendarTable } from './calendar-table.js'
-import { NavigationContainer } from './navigation-container.js'
+import React, { useMemo, useState } from 'react'
+import { CalendarContainer } from './calendar-container.js'
export const Calendar = ({
onDateSelect,
@@ -20,9 +18,6 @@ export const Calendar = ({
width,
cellSize,
}) => {
- const wrapperBorderColor = colors.grey300
- const backgroundColor = 'none'
-
const [selectedDateString, setSelectedDateString] = useState(date)
const languageDirection = useResolvedDirection(dir, locale)
@@ -34,7 +29,7 @@ export const Calendar = ({
weekDayFormat,
}
- const pickerOptions = useDatePicker({
+ const pickerResults = useDatePicker({
onDateSelect: (result) => {
const { calendarDateString } = result
setSelectedDateString(calendarDateString)
@@ -44,43 +39,33 @@ export const Calendar = ({
options,
})
- const { calendarWeekDays, weekDayLabels } = pickerOptions
+ const calendarProps = useMemo(() => {
+ return {
+ date,
+ dir,
+ locale,
+ width,
+ cellSize,
+ // minDate,
+ // maxDate,
+ // validation, // todo: clarify how we use validation props (and format) in Calendar (not CalendarInput)
+ // format,
+ isValid: pickerResults.isValid,
+ calendarWeekDays: pickerResults.calendarWeekDays,
+ weekDayLabels: pickerResults.weekDayLabels,
+ currMonth: pickerResults.currMonth,
+ currYear: pickerResults.currYear,
+ nextMonth: pickerResults.nextMonth,
+ nextYear: pickerResults.nextYear,
+ prevMonth: pickerResults.prevMonth,
+ prevYear: pickerResults.prevYear,
+ languageDirection,
+ }
+ }, [cellSize, date, dir, locale, pickerResults, width, languageDirection])
return (
)
}
diff --git a/components/calendar/src/calendar/navigation-container.js b/components/calendar/src/calendar/navigation-container.js
index 933f9d6e0d..87f9a1953d 100644
--- a/components/calendar/src/calendar/navigation-container.js
+++ b/components/calendar/src/calendar/navigation-container.js
@@ -7,15 +7,20 @@ import i18n from '../locales/index.js'
const wrapperBorderColor = colors.grey300
const headerBackground = colors.grey050
-export const NavigationContainer = ({ languageDirection, pickerOptions }) => {
+export const NavigationContainer = ({
+ languageDirection,
+ currMonth,
+ currYear,
+ nextMonth,
+ nextYear,
+ prevMonth,
+ prevYear,
+}) => {
const PreviousIcon =
languageDirection === 'ltr' ? IconChevronLeft16 : IconChevronRight16
const NextIcon =
languageDirection === 'ltr' ? IconChevronRight16 : IconChevronLeft16
- const { currMonth, currYear, nextMonth, nextYear, prevMonth, prevYear } =
- pickerOptions
-
// Ethiopic years - when localised to English - add the era (i.e. 2015 ERA1), which is redundant in practice (like writing AD for gregorian years)
// there is an ongoing discussion in JS-Temporal polyfill whether the era should be included or not, but for our case, it's safer to remove it
const currentYearLabel = currYear.label?.toString().replace(/ERA1/, '')
@@ -155,30 +160,30 @@ export const NavigationContainer = ({ languageDirection, pickerOptions }) => {
)
}
-NavigationContainer.propTypes = {
+export const NavigationContainerProps = {
+ currMonth: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ }),
+ currYear: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ }),
languageDirection: PropTypes.oneOf(['ltr', 'rtl']),
- pickerOptions: PropTypes.shape({
- currMonth: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- }),
- currYear: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- }),
- nextMonth: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- navigateTo: PropTypes.func,
- }),
- nextYear: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- navigateTo: PropTypes.func,
- }),
- prevMonth: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- navigateTo: PropTypes.func,
- }),
- prevYear: PropTypes.shape({
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- navigateTo: PropTypes.func,
- }),
+ nextMonth: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ navigateTo: PropTypes.func,
+ }),
+ nextYear: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ navigateTo: PropTypes.func,
+ }),
+ prevMonth: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ navigateTo: PropTypes.func,
+ }),
+ prevYear: PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ navigateTo: PropTypes.func,
}),
}
+
+NavigationContainer.propTypes = NavigationContainerProps
diff --git a/components/calendar/src/stories/calendar-input.stories.js b/components/calendar/src/stories/calendar-input.stories.js
index 13cc451670..7d3917ad00 100644
--- a/components/calendar/src/stories/calendar-input.stories.js
+++ b/components/calendar/src/stories/calendar-input.stories.js
@@ -114,3 +114,27 @@ export const CalendarWithClearButton = ({
>
)
}
+
+export function CalendarWithEditiableInput() {
+ const [date, setDate] = useState('2020-07-03')
+ return (
+
+ <>
+ {
+ const date = selectedDate?.calendarDateString
+ setDate(date)
+ }}
+ width={'700px'}
+ inputWidth="900px"
+ timeZone={'UTC'}
+ minDate={'2020-07-01'}
+ maxDate={'2020-07-09'}
+ />
+ >
+
+ )
+}
diff --git a/yarn.lock b/yarn.lock
index 15c684e11d..d2ef2cfceb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2629,12 +2629,12 @@
i18next "^10.3"
moment "^2.24.0"
-"@dhis2/multi-calendar-dates@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.1.1.tgz#fb76a77114ce0b757db7dd9f588d1a47809732da"
- integrity sha512-kaisVuRGfdqY/Up6sWqgc81K67ymPVoRYgYRcT29z61ol2WhiTXTSTuRX/gDO1VKjmskeB5/badRrdLMf4BBUA==
+"@dhis2/multi-calendar-dates@v1.0.0-alpha.26":
+ version "1.0.0-alpha.26"
+ resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-1.0.0-alpha.26.tgz#33c3384ee96219f5500005058a69bd3104d1a5b9"
+ integrity sha512-85oj4Ji/UOwt4nWDrzUfyl5tkcF1YrB1kBh1kCjPL0Md1+XDzM6nee9DFx4Eh9BNJN/cOgWJo9mWDsPycXO0aA==
dependencies:
- "@js-temporal/polyfill" "^0.4.2"
+ "@js-temporal/polyfill" "0.4.3"
classnames "^2.3.2"
"@dhis2/prop-types@^1.6.4":
@@ -3812,7 +3812,7 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
-"@js-temporal/polyfill@^0.4.2":
+"@js-temporal/polyfill@0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@js-temporal/polyfill/-/polyfill-0.4.3.tgz#e8f8cf86745eb5050679c46a5ebedb9a9cc1f09b"
integrity sha512-6Fmjo/HlkyVCmJzAPnvtEWlcbQUSRhi8qlN9EtJA/wP7FqXsevLLrlojR44kzNzrRkpf7eDJ+z7b4xQD/Ycypw==