diff --git a/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png b/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png
index d618ee1d5e..0b3ad1515b 100644
Binary files a/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png and b/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png differ
diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx
index 23a5f373b3..821993c92f 100644
--- a/packages/lib/src/date-input/DateInput.stories.tsx
+++ b/packages/lib/src/date-input/DateInput.stories.tsx
@@ -8,9 +8,10 @@ import DxcContainer from "../container/Container";
import Calendar from "./Calendar";
import DxcDateInput from "./DateInput";
import DxcDatePicker from "./DatePicker";
-import YearPicker from "./YearPicker";
+import DateUnitPicker from "./DateUnitPicker";
import { fireEvent, screen, userEvent, within } from "storybook/internal/test";
import { HalstackProvider } from "../HalstackContext";
+import { getMonthPickerItems, getYearPickerItems } from "./utils";
export default {
title: "Date Input",
@@ -40,7 +41,7 @@ const DateInputChromatic = () => (
-
+
@@ -147,86 +148,160 @@ const DatePickerButtonStates = () => (
- {}} id="test-calendar1" />
+ {}}
+ id="test-calendar1"
+ format="dd/mm/yy"
+ />
- {}} id="test-calendar2" />
+ {}}
+ id="test-calendar2"
+ format="dd/mm/yy"
+ />
- {}} id="test-calendar3" />
+ {}}
+ id="test-calendar3"
+ format="dd/mm/yy"
+ />
- {}} id="test-calendar3" />
+ {}}
+ id="test-calendar3"
+ format="dd/mm/yy"
+ />
>
);
-const YearPickerButtonStates = () => (
- <>
-
-
- {}}
- today={dayjs("1904-04-03", "YYYY-MM-DD")}
- />
-
-
-
- {}}
- today={dayjs("1904-04-03", "YYYY-MM-DD")}
- />
-
-
-
- {}}
- today={dayjs("1904-04-03", "YYYY-MM-DD")}
- />
-
- >
-);
+const YearPickerButtonStates = () => {
+ const yearItems = getYearPickerItems();
+ return (
+ <>
+
+
+ {}}
+ today={dayjs("1904-04-03", "YYYY-MM-DD")}
+ items={yearItems}
+ />
+
+
+
+ {}}
+ today={dayjs("1904-04-03", "YYYY-MM-DD")}
+ items={yearItems}
+ />
+
+
+
+ {}}
+ today={dayjs("1904-04-03", "YYYY-MM-DD")}
+ items={yearItems}
+ />
+
+ >
+ );
+};
-const DatePickerToday = () => (
- <>
-
-
- {}}
- onDaySelect={() => {}}
- innerDate={dayjs("06-04-1904", "DD-MM-YYYY")}
- />
-
-
-
- {}}
- today={dayjs("1904-04-03", "YYYY-MM-DD")}
- />
-
- >
-);
+const DatePickerToday = () => {
+ const monthLabels = {
+ calendar: {
+ months: [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ ],
+ },
+ };
+ const monthItems = getMonthPickerItems(monthLabels.calendar.months);
+ const yearItems = getYearPickerItems();
+
+ return (
+ <>
+
+
+ {}}
+ onDaySelect={() => {}}
+ innerDate={dayjs("06-04-1904", "DD-MM-YYYY")}
+ />
+
+
+
+ {}}
+ today={dayjs("1904-04-03", "YYYY-MM-DD")}
+ items={monthItems}
+ unit="month"
+ />
+
+
+
+ {}}
+ today={dayjs("1904-04-03", "YYYY-MM-DD")}
+ items={yearItems}
+ />
+
+ >
+ );
+};
const Tooltip = () => (
<>
- {}} id="test-calendar-tooltip" />
+ {}}
+ id="test-calendar-tooltip"
+ format="dd/mm/yy"
+ />
>
);
+const YearMonthPickerZhCn = () => (
+
+
+
+
+
+
+);
+
type Story = StoryObj;
export const Chromatic: Story = {
@@ -290,3 +365,17 @@ export const DatePickerTooltipAfter: Story = {
}
},
};
+
+export const YearMonthPickerWithLocale: Story = {
+ render: YearMonthPickerZhCn,
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ const calendarAction = await canvas.findByRole("combobox");
+ await userEvent.click(calendarAction);
+ const dialog = await screen.findByRole("dialog");
+ const yearMonthTrigger = within(dialog).getAllByRole("button")[1];
+ if (yearMonthTrigger != null) {
+ await userEvent.click(yearMonthTrigger);
+ }
+ },
+};
diff --git a/packages/lib/src/date-input/DateInput.test.tsx b/packages/lib/src/date-input/DateInput.test.tsx
index b954489a2a..9dd3a17feb 100644
--- a/packages/lib/src/date-input/DateInput.test.tsx
+++ b/packages/lib/src/date-input/DateInput.test.tsx
@@ -1,5 +1,6 @@
import { fireEvent, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
+import "@testing-library/jest-dom";
import dayjs from "dayjs";
import DxcDateInput from "./DateInput";
import MockDOMRect from "../../test/mocks/domRectMock";
@@ -201,7 +202,7 @@ describe("DateInput component tests", () => {
});
expect(input.value).toBe(d.format("DD-MM-YYYY"));
});
- test("Selecting a year from the calendar year picker", () => {
+ test("Selecting a year and month from the calendar year picker", () => {
const { getByText, getByRole } = render();
const input = getByRole("textbox") as HTMLInputElement;
const calendarAction = getByRole("combobox");
@@ -210,9 +211,11 @@ describe("DateInput component tests", () => {
userEvent.click(getByText(d.format("MMMM YYYY")));
expect(getByText("2024")).toBeTruthy();
userEvent.click(getByText("2024"));
- userEvent.click(getByText(d.set("year", 2024).format("MMMM YYYY")));
+ expect(getByText("February")).toBeTruthy();
+ userEvent.click(getByText("February"));
+ userEvent.click(getByText(d.set("year", 2024).set("month", 1).format("MMMM YYYY")));
fireEvent.keyDown(document, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 });
- expect(input.value).toBe(d.format("DD-MM-YYYY"));
+ expect(input.value).toBe(d.set("year", 2024).set("month", 1).format("DD-MM-YYYY"));
});
test("Selecting a date from the calendar (using keyboard presses)", () => {
const { getByRole, getAllByText, getByText } = render();
@@ -488,6 +491,7 @@ describe("DateInput component tests", () => {
expect(getByText("October 1910")).toBeTruthy();
userEvent.click(getByText("October 1910"));
userEvent.click(getByText("2010"));
+ userEvent.click(getByText("October 2010"));
const day1 = getAllByText("1")[0];
if (day1 != null) {
userEvent.click(day1);
@@ -530,4 +534,43 @@ describe("DateInput component tests", () => {
userEvent.type(calendarAction, "{enter}");
expect(onSubmit).not.toHaveBeenCalled();
});
+ test("Two listboxes are shown", () => {
+ const { getByRole, container } = render();
+ const calendarAction = getByRole("combobox");
+ userEvent.click(calendarAction);
+
+ const monthYearButton = getByRole("button", { name: "March 2020" });
+ expect(monthYearButton).toBeTruthy();
+ userEvent.click(monthYearButton);
+
+ const listboxes = container.querySelectorAll("[role='listbox']");
+ expect(listboxes.length).toBe(2);
+ });
+ test("Calendar header formatting", () => {
+ const { getByRole, container } = render();
+ const calendarAction = getByRole("combobox");
+ userEvent.click(calendarAction);
+
+ const monthYearButton = getByRole("button", { name: "2020 March" });
+ expect(monthYearButton).toBeTruthy();
+ userEvent.click(monthYearButton);
+
+ const listboxes = container.querySelectorAll("[role='listbox']");
+ expect(listboxes.length).toBe(2);
+ expect(listboxes[0]).toHaveTextContent("2019");
+ expect(listboxes[1]).toHaveTextContent("February");
+ });
+ // listbox keyboard control
+ test("Listbox keyboard control", () => {
+ const { getByRole, getAllByRole } = render();
+ const calendarAction = getByRole("combobox");
+ userEvent.click(calendarAction);
+ expect(getByRole("button", { name: "March 2020" })).toBeTruthy();
+ userEvent.click(getByRole("button", { name: "March 2020" }));
+ const yearListbox = getAllByRole("listbox")[1];
+ expect(yearListbox).toBeTruthy();
+ userEvent.keyboard("{ArrowDown}");
+ userEvent.keyboard("{Enter}");
+ expect(getByRole("button", { name: "March 2021" })).toBeTruthy();
+ });
});
diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx
index 6af1c10ea4..9a031ebece 100644
--- a/packages/lib/src/date-input/DateInput.tsx
+++ b/packages/lib/src/date-input/DateInput.tsx
@@ -322,7 +322,12 @@ const DxcDateInput = forwardRef(
onKeyDown={handleDatePickerEscKeydown}
ref={popoverContentRef}
>
-
+
)}
diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx
index af68c49ff5..b6eaf29b35 100644
--- a/packages/lib/src/date-input/DatePicker.tsx
+++ b/packages/lib/src/date-input/DatePicker.tsx
@@ -1,12 +1,13 @@
-import { memo, useContext, useState } from "react";
+import { memo, useContext, useMemo, useState } from "react";
import dayjs, { Dayjs } from "dayjs";
import styled from "@emotion/styled";
import { DatePickerPropsType } from "./types";
import Calendar from "./Calendar";
-import YearPicker from "./YearPicker";
import DxcIcon from "../icon/Icon";
import { Tooltip } from "../tooltip/Tooltip";
import { HalstackLanguageContext } from "../HalstackContext";
+import { YearMonthPicker } from "./YearMonthPicker";
+import { calculateIsYearFirst } from "./utils";
const DatePickerContainer = styled.div`
padding: var(--spacing-padding-m) var(--spacing-padding-xs) var(--spacing-padding-xs) var(--spacing-padding-xs);
@@ -61,7 +62,7 @@ const HeaderButton = styled.button`
}
`;
-const HeaderYearTrigger = styled(HeaderButton)`
+const HeaderTrigger = styled(HeaderButton)`
gap: var(--spacing-gap-s);
padding: 0px var(--spacing-padding-xs) 0px var(--spacing-padding-m);
height: var(--height-m);
@@ -71,7 +72,7 @@ const HeaderYearTrigger = styled(HeaderButton)`
}
`;
-const HeaderYearTriggerLabel = styled.span`
+const HeaderTriggerLabel = styled.span`
display: flex;
align-items: center;
justify-content: center;
@@ -80,25 +81,24 @@ const HeaderYearTriggerLabel = styled.span`
const today = dayjs();
-const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Element => {
+const DatePicker = ({ date, onDateSelect, id, format }: DatePickerPropsType): JSX.Element => {
const [innerDate, setInnerDate] = useState(date?.isValid() ? date : dayjs());
const [content, setContent] = useState("calendar");
const selectedDate = date?.isValid() ? date : dayjs(null);
- const translatedLabels = useContext(HalstackLanguageContext).labels;
+ const languageContext = useContext(HalstackLanguageContext);
+ const translatedLabels = languageContext.labels;
+ const isYearFirst = calculateIsYearFirst(format);
const handleDateSelect = (chosenDate: Dayjs) => {
setInnerDate(chosenDate);
onDateSelect(chosenDate);
};
- const handleOnYearSelect = (year: number) => {
- setInnerDate(innerDate.set("year", year));
- setContent("calendar");
- };
-
- const handleMonthChange = (chosenDate: Dayjs) => {
- setInnerDate(chosenDate);
- };
+ const monthYearLabel = useMemo(() => {
+ const month = translatedLabels.calendar.months[innerDate.get("month")];
+ const year = innerDate.format("YYYY");
+ return isYearFirst ? `${year} ${month}` : `${month} ${year}`;
+ }, [translatedLabels, innerDate, isYearFirst]);
return (
@@ -106,26 +106,30 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen
handleMonthChange(innerDate.set("month", innerDate.get("month") - 1))}
+ onClick={() => setInnerDate(innerDate.set("month", innerDate.get("month") - 1))}
type="button"
>
- setContent((currentContent) => (currentContent === "yearPicker" ? "calendar" : "yearPicker"))}
+ onClick={() => {
+ if (content === "calendar") {
+ setContent("yearMonthPicker");
+ } else {
+ setContent("calendar");
+ }
+ }}
type="button"
>
-
- {translatedLabels.calendar.months[innerDate.get("month")]} {innerDate.format("YYYY")}
-
-
-
+ {monthYearLabel}
+
+
handleMonthChange(innerDate.set("month", innerDate.get("month") + 1))}
+ onClick={() => setInnerDate(innerDate.set("month", innerDate.get("month") + 1))}
type="button"
>
@@ -141,8 +145,13 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen
today={today}
/>
)}
- {content === "yearPicker" && (
-
+ {content !== "calendar" && (
+
)}
);
diff --git a/packages/lib/src/date-input/DateUnitPicker.tsx b/packages/lib/src/date-input/DateUnitPicker.tsx
new file mode 100644
index 0000000000..f8247d68ec
--- /dev/null
+++ b/packages/lib/src/date-input/DateUnitPicker.tsx
@@ -0,0 +1,131 @@
+import dayjs from "dayjs";
+import { KeyboardEvent, useEffect, useId, useState } from "react";
+import styled from "@emotion/styled";
+import { DateUnitPickerPropsType } from "./types";
+import scrollbarStyles from "../styles/scroll";
+
+const PickerContainer = styled.div`
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-gap-xs);
+ align-items: center;
+ overflow-y: scroll;
+ width: 50%;
+ max-width: 292px;
+ height: 312px;
+ box-shadow: var(--shadow-200);
+ ${scrollbarStyles}
+`;
+
+const PickerButton = styled.button<{
+ selected: boolean;
+ isCurrent: boolean;
+}>`
+ min-width: fit-content;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 80px;
+ min-height: var(--height-m);
+ height: var(--height-m);
+ background-color: transparent;
+ border: none;
+ border-radius: var(--border-radius-xl);
+ cursor: pointer;
+ font-family: var(--typography-font-family);
+ font-size: var(--typography-label-m);
+ color: var(--color-fg-neutral-dark);
+ font-weight: var(--typography-label-regular);
+
+ ${(props) =>
+ props.selected
+ ? `font-size: var(--typography-label-xl);
+ color: var(--color-fg-neutral-bright) !important;
+ background-color: var(--color-bg-primary-strong) !important;`
+ : props.isCurrent
+ ? `border: var(--border-width-s) var(--border-style-default) var(--border-color-primary-lighter);
+ color: var(--color-fg-primary-strong);`
+ : ``}
+
+ &:hover, &:focus, &:active {
+ font-size: var(--typography-label-xl);
+ }
+ &:hover {
+ background-color: var(--color-bg-primary-light);
+ color: var(--color-fg-neutral-dark);
+ }
+ &:focus {
+ outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);
+ }
+ &:active {
+ color: var(--color-fg-neutral-bright);
+ background-color: var(--color-bg-primary-stronger);
+ }
+`;
+
+const DateUnitPicker = ({
+ onDateUnitSelect,
+ selectedDate,
+ today,
+ items,
+ unit = "year",
+}: DateUnitPickerPropsType): JSX.Element => {
+ const selectedValue = selectedDate?.isValid() && selectedDate ? selectedDate.get(unit) : undefined;
+ const currentValue = today?.isValid() && today ? today.get(unit) : dayjs().get(unit);
+ const ariaLabel = `${unit.charAt(0).toUpperCase() + unit.slice(1)} Picker`;
+
+ const id = useId();
+ const [itemToFocus, setItemToFocus] = useState(selectedDate ? selectedDate.get(unit) : dayjs().get(unit));
+
+ useEffect(() => {
+ const itemToFocusEl = document.getElementById(`${id}_${unit}_${itemToFocus}`);
+ itemToFocusEl?.scrollIntoView?.({ block: "center", inline: "center" });
+ itemToFocusEl?.focus();
+ }, [itemToFocus, id, unit]);
+
+ const handleKeyboardEvent = (event: KeyboardEvent) => {
+ switch (event.key) {
+ case "ArrowUp":
+ setItemToFocus((prev) => (prev > 0 ? prev - 1 : prev));
+ break;
+ case "ArrowDown":
+ setItemToFocus((prev) => {
+ const lastValue = items[items.length - 1]?.value;
+ return lastValue !== undefined && prev < lastValue ? prev + 1 : prev;
+ });
+ break;
+ default:
+ break;
+ }
+ };
+
+ return (
+
+ {items.map((item) => {
+ const isSelected = item.value === selectedValue;
+ return (
+ handleKeyboardEvent(event)}
+ id={`${id}_${unit}_${item.value}`}
+ onClick={() => {
+ onDateUnitSelect(item.value);
+ }}
+ role="option"
+ type="button"
+ >
+ {item.label}
+
+ );
+ })}
+
+ );
+};
+
+export default DateUnitPicker;
diff --git a/packages/lib/src/date-input/YearMonthPicker.tsx b/packages/lib/src/date-input/YearMonthPicker.tsx
new file mode 100644
index 0000000000..40e653aa49
--- /dev/null
+++ b/packages/lib/src/date-input/YearMonthPicker.tsx
@@ -0,0 +1,71 @@
+import { useContext, useState } from "react";
+import styled from "@emotion/styled";
+import { HalstackLanguageContext } from "../HalstackContext";
+import DateUnitPicker from "./DateUnitPicker";
+import { getMonthPickerItems, getYearPickerItems } from "./utils";
+import { YearMonthPickerProps } from "./types";
+
+const YearMonthPickersContainer = styled.div`
+ display: flex;
+ width: 292px;
+ justify-content: center;
+`;
+
+export const YearMonthPicker = ({ isYearFirst, selectedDate, today, onYearMonthComplete }: YearMonthPickerProps) => {
+ const [selectedYear, setSelectedYear] = useState(selectedDate?.get("year") || null);
+ const [selectedMonth, setSelectedMonth] = useState(selectedDate?.get("month") || null);
+ const languageContext = useContext(HalstackLanguageContext);
+
+ const yearItems = getYearPickerItems();
+ const monthItems = getMonthPickerItems(languageContext.labels.calendar.months);
+
+ const handleOnYearSelect = (year: number) => {
+ const newDate = selectedDate.set("year", year);
+ const finalDate = selectedMonth != null ? newDate.set("month", selectedMonth) : newDate;
+ setSelectedYear(year);
+ onYearMonthComplete(finalDate);
+ };
+
+ const handleOnMonthSelect = (month: number) => {
+ const newDate = selectedDate.set("month", month);
+ const finalDate = selectedYear != null ? newDate.set("year", selectedYear) : newDate;
+ setSelectedMonth(month);
+ onYearMonthComplete(finalDate);
+ };
+
+ const yearPicker = (
+
+ );
+
+ const monthPicker = (
+
+ );
+
+ return (
+
+ {isYearFirst ? (
+ <>
+ {yearPicker}
+ {monthPicker}
+ >
+ ) : (
+ <>
+ {monthPicker}
+ {yearPicker}
+ >
+ )}
+
+ );
+};
diff --git a/packages/lib/src/date-input/YearPicker.tsx b/packages/lib/src/date-input/YearPicker.tsx
deleted file mode 100644
index d32ecc103c..0000000000
--- a/packages/lib/src/date-input/YearPicker.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import dayjs from "dayjs";
-import { useEffect, useId, useState, memo, KeyboardEvent } from "react";
-import styled from "@emotion/styled";
-import { YearPickerPropsType } from "./types";
-
-const YearPickerContainer = styled.div`
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- gap: var(--spacing-gap-xs);
- align-items: center;
- overflow-y: scroll;
- width: 292px;
- height: 312px;
- box-shadow: var(--shadow-200);
-`;
-
-const YearPickerButton = styled.button<{
- selected: boolean;
- isCurrentYear: boolean;
-}>`
- display: flex;
- align-items: center;
- justify-content: center;
- width: 80px;
- min-height: var(--height-m);
- height: var(--height-m);
- background-color: transparent;
- border: none;
- border-radius: var(--border-radius-xl);
- cursor: pointer;
- font-family: var(--typography-font-family);
- font-size: var(--typography-label-m);
- color: var(--color-fg-neutral-dark);
- font-weight: var(--typography-label-regular);
-
- ${(props) =>
- props.selected
- ? `font-size: var(--typography-label-xl);
- color: var(--color-fg-neutral-bright) !important;
- background-color: var(--color-bg-primary-strong) !important;`
- : props.isCurrentYear
- ? `border: var(--border-width-s) var(--border-style-default) var(--border-color-primary-lighter);
- color: var(--color-fg-primary-strong);`
- : ``}
-
- &:hover, &:focus, &:active {
- font-size: var(--typography-label-xl);
- }
- &:hover {
- background-color: var(--color-bg-primary-light);
- color: var(--color-fg-neutral-dark);
- }
- &:focus {
- outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);
- }
- &:active {
- color: var(--color-fg-neutral-bright);
- background-color: var(--color-bg-primary-stronger);
- }
-`;
-
-const getYearsArray = () => {
- const yearList = [];
- for (let i = 1899; i <= 2100; i++) {
- yearList.push(i);
- }
- return yearList;
-};
-
-const yearList = getYearsArray();
-
-const YearPicker = ({ onYearSelect, selectedDate, today }: YearPickerPropsType): JSX.Element => {
- const id = useId();
- const [yearToFocus, setYearToFocus] = useState(selectedDate ? selectedDate.get("year") : dayjs().get("year"));
-
- useEffect(() => {
- const yearToFocusEl = document.getElementById(`${id}_year_${yearToFocus}`);
- yearToFocusEl?.scrollIntoView?.({ block: "nearest", inline: "start" });
- yearToFocusEl?.focus();
- }, [yearToFocus]);
-
- const handleDayKeyboardEvent = (event: KeyboardEvent) => {
- switch (event.key) {
- case "ArrowUp":
- setYearToFocus((prev) => (prev > 1899 ? prev - 1 : prev));
- break;
- case "ArrowDown":
- setYearToFocus((prev) => (prev < 2100 ? prev + 1 : prev));
- break;
- default:
- break;
- }
- };
-
- return (
-
- {yearList.map((year) => (
- handleDayKeyboardEvent(event)}
- id={`${id}_year_${year}`}
- onClick={() => {
- onYearSelect(year);
- }}
- role="option"
- type="button"
- >
- {year}
-
- ))}
-
- );
-};
-
-export default memo(YearPicker);
diff --git a/packages/lib/src/date-input/types.ts b/packages/lib/src/date-input/types.ts
index 5913922443..a9bdf8f9ed 100644
--- a/packages/lib/src/date-input/types.ts
+++ b/packages/lib/src/date-input/types.ts
@@ -1,6 +1,8 @@
import { Dayjs } from "dayjs";
import { Margin, Space } from "../common/utils";
+export type RefType = HTMLDivElement;
+
type Props = {
/**
* Initial value of the input element, only when it is uncontrolled.
@@ -114,6 +116,10 @@ export type DatePickerPropsType = {
* Id assigned to the date picker.
*/
id: string;
+ /**
+ * The format in which the date value will be displayed.
+ */
+ format: string;
};
export type CalendarPropsType = {
@@ -139,23 +145,39 @@ export type CalendarPropsType = {
today: Dayjs;
};
-export type YearPickerPropsType = {
+export interface YearMonthPickerProps {
+ isYearFirst: boolean;
+ selectedDate: Dayjs;
+ today: Dayjs;
+ onYearMonthComplete: (newDate: Dayjs) => void;
+}
+
+export type PickerItem = {
+ value: number;
+ label: string;
+};
+
+export type DateUnitPickerPropsType = {
/**
* Initial selected date value. If invalid the actual date will be used instead.
*/
- selectedDate: Dayjs;
+ selectedDate: Dayjs | null;
/**
* Function called when a year is selected.
*/
- onYearSelect: (year: number) => void;
+ onDateUnitSelect: (year: number) => void;
/**
* Current date.
*/
today: Dayjs;
+ /**
+ * Array of items (months or years) to display in the picker.
+ */
+ items: PickerItem[];
+ /**
+ * Date unit to display in the picker (month or year).
+ */
+ unit?: "month" | "year";
};
-/**
- * Reference to the component.
- */
-export type RefType = HTMLDivElement;
export default Props;
diff --git a/packages/lib/src/date-input/utils.tsx b/packages/lib/src/date-input/utils.tsx
index 09d426a90b..21c5c16f25 100644
--- a/packages/lib/src/date-input/utils.tsx
+++ b/packages/lib/src/date-input/utils.tsx
@@ -1,8 +1,10 @@
import { Dispatch, SetStateAction } from "react";
import dayjs, { Dayjs } from "dayjs";
-import { DateType } from "./types";
+import { DateType, PickerItem } from "./types";
const INVIS_CHARS = /[\u200E\u200F\u061C]/g;
+const MIN_YEAR = 1899;
+const MAX_YEAR = 2100;
export const getValueForPicker = (value: string, format: string) => dayjs(value, format.toUpperCase(), true);
@@ -109,3 +111,27 @@ export const divideDaysIntoWeeks = (data: DateType[], weekSize: number) =>
Array.from({ length: Math.ceil(data.length / weekSize) }, (_, rowIndex) =>
data.slice(rowIndex * weekSize, (rowIndex + 1) * weekSize)
);
+
+export const getMonthPickerItems = (months: string[]): PickerItem[] =>
+ months.map((month: string, index: number) => ({
+ value: index,
+ label: month,
+ }));
+
+export const getYearPickerItems = (): PickerItem[] => {
+ const yearList: number[] = [];
+ for (let i = MIN_YEAR; i <= MAX_YEAR; i++) {
+ yearList.push(i);
+ }
+ return yearList.map((year: number) => ({
+ value: year,
+ label: year.toString(),
+ }));
+};
+
+export const calculateIsYearFirst = (localeFormat: string): boolean => {
+ const lower = localeFormat.toLowerCase();
+ const yearIndices = lower.indexOf("y");
+ const monthIndices = lower.indexOf("m");
+ return yearIndices < monthIndices;
+};