Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions packages/ui/src/components/Datepicker/Datepicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,114 @@ describe("Components / Datepicker", () => {
await userEvent.click(document.body);
});

it("should render 1990 - 2100 year range when selecting decade", async () => {
const testDate = new Date(2024, 6, 20);
render(<Datepicker value={testDate.getTime()} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
expect(titleButton.textContent).toBe("2024");
await userEvent.click(titleButton);
expect(titleButton.textContent).toBe("2020 - 2031");
await userEvent.click(titleButton);
expect(titleButton.textContent).toBe("1990 - 2100");
});

it("should allow selecting earlier decades when setting max date", async () => {
const testDate = new Date(2024, 6, 20);
render(<Datepicker value={testDate.getTime()} maxDate={testDate} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
await userEvent.click(titleButton);
await userEvent.click(titleButton);

const earlierDecadeButton = screen.getByText("2010");
expect(earlierDecadeButton).instanceOf(HTMLButtonElement);
expect(earlierDecadeButton).toBeEnabled();
});

it("should disallow selecting later decades when setting max date", async () => {
const testDate = new Date(2024, 6, 20);
render(<Datepicker value={testDate.getTime()} maxDate={testDate} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
await userEvent.click(titleButton);
await userEvent.click(titleButton);

const laterDecadeButton = screen.getByText("2030");
expect(laterDecadeButton).instanceOf(HTMLButtonElement);
expect(laterDecadeButton).toBeDisabled();
});

it("should disallow selecting earlier decades when setting min date", async () => {
const testDate = new Date(2024, 6, 20);
render(<Datepicker value={testDate.getTime()} minDate={testDate} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
await userEvent.click(titleButton);
await userEvent.click(titleButton);

const earlierDecadeButton = screen.getByText("2010");
expect(earlierDecadeButton).instanceOf(HTMLButtonElement);
expect(earlierDecadeButton).toBeDisabled();
});

it("should allow selecting later decades when setting min date", async () => {
const testDate = new Date(2024, 6, 20);
render(<Datepicker value={testDate.getTime()} minDate={testDate} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
await userEvent.click(titleButton);
await userEvent.click(titleButton);

const laterDecadeButton = screen.getByText("2030");
expect(laterDecadeButton).instanceOf(HTMLButtonElement);
expect(laterDecadeButton).toBeEnabled();
});

it("should allow selecting decades within the range set by max date and min date and disallow selecting outside the range", async () => {
const minDate = new Date(2010, 1, 1);
const maxDate = new Date(2030, 1, 1);
const testDate = new Date(2024, 6, 1);

render(<Datepicker value={testDate.getTime()} minDate={minDate} maxDate={maxDate} />);

const textBox = screen.getByRole("textbox");
await userEvent.click(textBox);

const titleButton = screen.getByText("July 2024");
await userEvent.click(titleButton);
await userEvent.click(titleButton);
await userEvent.click(titleButton);

const inRange = screen.getByText("2010");
expect(inRange).instanceOf(HTMLButtonElement);
expect(inRange).toBeEnabled();

const outsideRange = screen.getByText("2000");
expect(outsideRange).instanceOf(HTMLButtonElement);
expect(outsideRange).toBeDisabled();
});

it("should focus the input when ref.current.focus is called", () => {
const {
result: { current: ref },
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/components/Datepicker/Datepicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const Template: StoryFn<DatepickerProps> = (args) => {
// update defaultDate based on the range
if (args.minDate && args.maxDate) {
if (args.defaultDate) {
args.defaultDate = getFirstDateInRange(args.defaultDate, args.minDate, args.maxDate);
// https://github.com/storybookjs/storybook/issues/11822
args.defaultDate = getFirstDateInRange(new Date(args.defaultDate), args.minDate, args.maxDate);
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/Datepicker/Datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ const DatepickerRender: ForwardRefRenderFunction<DatepickerRef, DatepickerProps>
const getViewTitle = (): string => {
switch (view) {
case Views.Decades:
return `${startOfYearPeriod(viewDate, 100)} - ${startOfYearPeriod(viewDate, 100) + 90}`;
return `${startOfYearPeriod(viewDate, 100) - 10} - ${startOfYearPeriod(viewDate, 100) + 100}`;
case Views.Years:
return `${startOfYearPeriod(viewDate, 10)} - ${startOfYearPeriod(viewDate, 10) + 9}`;
return `${startOfYearPeriod(viewDate, 10)} - ${startOfYearPeriod(viewDate, 10) + 11}`;
case Views.Months:
return getFormattedDate(language, viewDate, { year: "numeric" });
case Views.Days:
Expand Down
14 changes: 7 additions & 7 deletions packages/ui/src/components/Datepicker/Views/Decades.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ export interface DatepickerViewsDecadesProps {
}

export const DatepickerViewsDecades: FC<DatepickerViewsDecadesProps> = ({ theme: customTheme = {} }) => {
const { theme: rootTheme, selectedDate, viewDate, setViewDate, setView } = useDatePickerContext();
const { theme: rootTheme, viewDate, selectedDate, minDate, maxDate, setViewDate, setView } = useDatePickerContext();

const theme = mergeDeep(rootTheme.views.decades, customTheme);

const first = startOfYearPeriod(viewDate, 100);
return (
<div className={theme.items.base}>
{[...Array(12)].map((_year, index) => {
const first = startOfYearPeriod(viewDate, 100);
const year = first - 10 + index * 10;
const newDate = new Date(viewDate.getTime());
newDate.setFullYear(year + (viewDate.getFullYear() % 10));
const firstDate = new Date(year, 0, 1);
const lastDate = addYears(firstDate, 9);

const isSelected = isDateInDecade(viewDate, year);
const isDisabled = !isDateInRange(viewDate, firstDate, lastDate);
const isSelected = isDateInDecade(selectedDate, year);
const isDisabled = !isDateInRange(firstDate, minDate, maxDate) && !isDateInRange(lastDate, minDate, maxDate);

return (
<button
Expand All @@ -47,8 +48,7 @@ export const DatepickerViewsDecades: FC<DatepickerViewsDecadesProps> = ({ theme:
)}
onClick={() => {
if (isDisabled) return;

setViewDate(addYears(viewDate, year - selectedDate.getFullYear()));
setViewDate(newDate);
setView(Views.Years);
}}
>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/Datepicker/Views/Years.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const DatepickerViewsYears: FC<DatepickerViewsYearsProps> = ({ theme: cus
<div className={theme.items.base}>
{[...Array(12)].map((_year, index) => {
const first = startOfYearPeriod(viewDate, 10);
const year = first - 1 + index * 1;
const year = first + index;
const newDate = new Date(viewDate.getTime());
newDate.setFullYear(year);

Expand Down