Skip to content

Commit

Permalink
feat(DatePicker): fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
marcinsawicki committed Dec 18, 2024
1 parent 2ebf49f commit 1abb2fe
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,16 @@ $base-class: 'date-picker';
border-bottom-left-radius: 0;
background-color: var(--action-primary-default);
}

&__disabled {
opacity: 0.4;

&:hover {
background-color: transparent;
}

button {
cursor: not-allowed;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { within } from '@testing-library/react';
import { vi } from 'vitest';

import { render, userEvent } from 'test-utils';
Expand All @@ -21,10 +20,10 @@ describe('<DatePicker> component', () => {

userEvent.click(nextYearButton);
// Datepicker has 12 o'clock as default hour
expect(onMonthChange).toHaveBeenCalledWith(new Date(2023, 0, 1, 12));
expect(onMonthChange).toHaveBeenCalledWith(new Date(2023, 0, 1));
userEvent.click(previousYearButton);
// Datepicker has 12 o'clock as default hour
expect(onMonthChange).toHaveBeenCalledWith(new Date(2021, 0, 1, 12));
expect(onMonthChange).toHaveBeenCalledWith(new Date(2021, 0, 1));
});

it('should call onMonthChange when next and previous month button user click', () => {
Expand All @@ -38,10 +37,10 @@ describe('<DatePicker> component', () => {

userEvent.click(nextMonthButton);
// Datepicker has 12 o'clock as default hour
expect(onMonthChange).toHaveBeenCalledWith(new Date(2022, 1, 1, 12));
expect(onMonthChange).toHaveBeenCalledWith(new Date(2022, 1, 1));
userEvent.click(previousMonthButton);
// Datepicker has 12 o'clock as default hour
expect(onMonthChange).toHaveBeenCalledWith(new Date(2021, 12, 1, 12));
expect(onMonthChange).toHaveBeenCalledWith(new Date(2021, 11, 1));
});

it('should call onDayClick when user click on a day', () => {
Expand All @@ -57,14 +56,10 @@ describe('<DatePicker> component', () => {
});

it('should have Monday as a default first weekday', () => {
const { getAllByRole } = renderComponent({ weekStartsOn: undefined });

const weekdays = getAllByRole('columnheader');
const { container } = renderComponent({ weekStartsOn: undefined });
const weekdays = container.querySelectorAll('th');

expect(weekdays).toHaveLength(7);

const firstWeekday = within(weekdays[0]).getByTitle('Monday');

expect(firstWeekday).toBeDefined();
expect(weekdays[0]).toHaveAttribute('aria-label', 'Monday');
});
});
Original file line number Diff line number Diff line change
@@ -1,25 +1,125 @@
import * as React from 'react';

import { Meta } from '@storybook/react';
import { Meta, StoryFn } from '@storybook/react';

import { DatePicker } from './DatePicker';
import { IDatePickerProps } from './types';

const DISABLED_RANGE_OPTIONS = [
'No Disabled Dates',
'Disable Before Current Date',
'Disable After Current Date',
];

export default {
title: 'Components/DatePicker',
component: DatePicker,
argTypes: {
weekStartsOn: {
description: 'Number representing first day of the week',
table: {
type: {
summary: 'number: 0-6',
detail: '0 - Sunday, 1 - Monday and so on, up to 6 - Saturday',
},
},
defaultValue: 1,
control: {
type: 'number',
},
},
disabled: {
description: 'Range of disabled dates',
control: {
type: 'select',
},
options: DISABLED_RANGE_OPTIONS,
defaultValue: DISABLED_RANGE_OPTIONS[0],
table: {
type: {
summary: 'object',
detail:
'{ before: Date } | { after: Date } | { dayOfWeek: []: array of numbers 0-6 } | undefined',
},
},
},
month: {
description: 'Providing this prop will make the date picker controlled',
control: {
type: 'date',
},
},
startMonth: {
description: 'Start month for the date picker',
control: {
type: 'date',
},
},
endMonth: {
description: 'End month for the date picker',
control: {
type: 'date',
},
},
},
} as Meta<typeof DatePicker>;

export const Default = (): React.ReactElement => <DatePicker />;
const StoryTemplate = (args: IDatePickerProps) => {
const { month, ...restArgs } = args;

const [selectedDate, setSelectedDate] = React.useState<Date | undefined>();
const [selectedMonth, setSelectedMonth] = React.useState<Date | undefined>();
const [initialMonth] = React.useState<Date | undefined>(
month ? new Date(month) : undefined
);

React.useEffect(() => {
const newMonth = month ? new Date(month) : undefined;

if (newMonth !== initialMonth) {
setSelectedMonth(newMonth);
}
}, [month, initialMonth]);

const getDisabledDates = () => {
switch (restArgs.disabled as unknown) {
case DISABLED_RANGE_OPTIONS[1]:
return { before: new Date() };
case DISABLED_RANGE_OPTIONS[2]:
return { after: new Date() };
default:
return undefined;
}
};

return (
<DatePicker
{...restArgs}
mode="single"
selected={selectedDate}
month={selectedMonth || initialMonth}
onSelect={setSelectedDate}
onMonthChange={setSelectedMonth}
disabled={getDisabledDates()}
/>
);
};

export const Default: StoryFn = StoryTemplate.bind({});
Default.args = {};

export const WithCustomCurrentDate = () => (
<DatePicker today={new Date('01/20/2024')} />
);
export const WithCustomCurrentDate: StoryFn = StoryTemplate.bind({});
WithCustomCurrentDate.args = {
today: new Date('01/20/2024'),
};

export const WithDatesBetweenTwoMonths = () => (
<DatePicker
startMonth={new Date('11/20/2023')}
endMonth={new Date('03/20/2024')}
/>
);
export const WithDatesBetweenTwoMonths: StoryFn = StoryTemplate.bind({});
WithDatesBetweenTwoMonths.args = {
startMonth: new Date('11/20/2023'),
endMonth: new Date('01/20/2024'),
};

export const WithCustomFirstDayOfWeek = () => <DatePicker weekStartsOn={0} />;
export const WithCustomFirstDayOfWeek: StoryFn = StoryTemplate.bind({});
WithCustomFirstDayOfWeek.args = {
weekStartsOn: 0,
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const DatePicker: React.FC<IDatePickerProps> = ({
startMonth,
toMonth,
endMonth,
onMonthChange,
...props
}) => {
const [currentMonth, setCurrentMonth] = React.useState(month || new Date());
Expand All @@ -41,16 +42,16 @@ export const DatePicker: React.FC<IDatePickerProps> = ({
}, [month, currentMonth, toMonth, fromMonth]);

const handleMonthChange = React.useCallback(
(month: Date) => {
if (props.onMonthChange && month) {
props.onMonthChange(month);
(newMonth: Date) => {
if (onMonthChange) {
onMonthChange(newMonth);

return;
}

setCurrentMonth(month);
setCurrentMonth(newMonth);
},
[month, props.onMonthChange]
[month, onMonthChange]
);

return (
Expand All @@ -68,6 +69,7 @@ export const DatePicker: React.FC<IDatePickerProps> = ({
range_start: cx(styles[`${baseClass}__range-start`]),
range_middle: cx(styles[`${baseClass}__range-middle`]),
range_end: cx(styles[`${baseClass}__range-end`]),
disabled: cx(styles[`${baseClass}__disabled`]),
}}
onMonthChange={handleMonthChange}
month={currentMonth}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { format } from 'date-fns';
import { vi } from 'vitest';

import { render, userEvent } from 'test-utils';
Expand All @@ -10,6 +11,8 @@ import {
IRangeDatePickerChildrenPayload,
} from './types';

const formattedDate = (date: Date) => format(date, 'EEEE, MMMM do, yyyy');

const options = [
{
id: 'custom_date',
Expand All @@ -34,15 +37,14 @@ describe('<RangeDatePicker> component', () => {
it('should call onChange callback if user chooses custom date', () => {
const onChange = vi.fn();
const { getByLabelText } = renderComponent({
initialFromDate: new Date(2022, 0, 1),
initialToDate: new Date(2022, 1, 1),
toMonth: new Date(2022, 1, 1),
onChange,
options,
initialSelectedItemKey: 'custom_date',
children,
});
const startDate = getByLabelText(new Date(2022, 0, 1).toDateString());
const endDate = getByLabelText(new Date(2022, 1, 1).toDateString());
const startDate = getByLabelText(formattedDate(new Date(2022, 0, 1)));
const endDate = getByLabelText(formattedDate(new Date(2022, 1, 1)));

userEvent.click(startDate);
userEvent.click(endDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,15 @@ export const DatePickerCustomNavigation: React.FC<
<div className={styles[baseClass]}>
<div className={styles[`${baseClass}__nav-wrapper`]}>
<Button
data-testid="date-picker-prev-year-button"
kind="plain"
size="xcompact"
icon={<Icon source={DoubleArrowLeft} />}
onClick={handlePreviousYearClick}
disabled={!shouldDisablePreviousButton}
/>
<Button
data-testid="date-picker-prev-month-button"
kind="plain"
size="xcompact"
icon={<Icon source={ChevronLeft} />}
Expand All @@ -97,13 +99,15 @@ export const DatePickerCustomNavigation: React.FC<
</div>
<div className={styles[`${baseClass}__nav-wrapper`]}>
<Button
data-testid="date-picker-next-month-button"
kind="plain"
size="xcompact"
icon={<Icon source={ChevronRight} />}
onClick={handleNextMonth}
disabled={!shouldDisableNextButton}
/>
<Button
data-testid="date-picker-next-year-button"
kind="plain"
size="xcompact"
icon={<Icon source={DoubleArrowRight} />}
Expand Down

0 comments on commit 1abb2fe

Please sign in to comment.