Skip to content

Commit

Permalink
Fix/datepicker decade picker unclickable (#1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maraket authored Jul 20, 2024
1 parent 3217f88 commit 83e5583
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 11 deletions.
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

0 comments on commit 83e5583

Please sign in to comment.