Skip to content

Commit d26d3d3

Browse files
committed
wip fix reviews
1 parent 5c7b424 commit d26d3d3

12 files changed

+123
-137
lines changed

ui-core/src/components/inputs/datePicker/Calendar.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,18 @@ export type CalendarProps = {
1414
type DayProps = {
1515
date: Date;
1616
isSelectable: boolean;
17-
isToDay: boolean;
17+
isToday: boolean;
1818
dayWrapperClassName: string;
1919
onClick: (date: Date) => void;
2020
};
2121

22-
const Day: React.FC<DayProps> = ({ date, isToDay, isSelectable, dayWrapperClassName, onClick }) => (
22+
const Day: React.FC<DayProps> = ({
23+
date,
24+
isToday: isToDay,
25+
isSelectable,
26+
dayWrapperClassName,
27+
onClick,
28+
}) => (
2329
<div
2430
onClick={() => {
2531
if (isSelectable) onClick(date);
@@ -54,7 +60,7 @@ const Calendar: React.FC<CalendarProps> = (props) => {
5460
<Day
5561
key={index}
5662
date={date}
57-
isToDay={isToday(date)}
63+
isToday={isToday(date)}
5864
isSelectable={isDateSelectable(date)}
5965
dayWrapperClassName={buildDayWrapperClassName(date)}
6066
onClick={onDayClick}

ui-core/src/components/inputs/datePicker/DatePicker.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import CalendarPicker, { CalendarPickerPublicProps } from './CalendarPicker';
66
import useDatePicker from './useDatePicker';
77
import { CalendarSlot } from '.';
88

9-
export type BaseDatePickerProps = {
9+
type BaseDatePickerProps = {
1010
inputProps: InputProps;
1111
calendarPickerProps?: CalendarPickerPublicProps;
1212
};

ui-core/src/components/inputs/datePicker/__tests__/useCalendar.spec.ts

+31-51
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,27 @@ import { renderHook } from '@testing-library/react';
22
import { describe, expect, it } from 'vitest';
33
import useCalendar from '../useCalendar';
44

5-
describe('useCalendar', () => {
6-
const february = 1;
7-
const march = 2;
8-
const november = 10;
9-
const april = 3;
10-
const july = 6;
11-
const august = 7;
12-
const june = 5;
13-
const december = 11;
5+
const february = 1;
6+
const march = 2;
7+
const april = 3;
8+
const june = 5;
9+
const july = 6;
10+
const august = 7;
11+
const november = 10;
12+
const december = 11;
13+
14+
const displayedMonthStartDate = new Date(2024, july, 1);
15+
const selectedSlot = { start: new Date(2024, july, 10), end: new Date(2024, july, 20) };
16+
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
17+
18+
const { result } = renderHook(() =>
19+
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
20+
);
1421

22+
describe('useCalendar', () => {
1523
describe('day calculation', () => {
1624
it('should include days from the previous month when the first day of the displayed month is not a Monday', () => {
17-
const displayedMonthStartDate = new Date(2024, march, 1);
25+
const displayedMonthStartDate = new Date(2024, march, 1); //friday
1826
const { result } = renderHook(() =>
1927
useCalendar({ displayedMonthStartDate, onDayClick: () => {} })
2028
);
@@ -23,7 +31,8 @@ describe('useCalendar', () => {
2331
});
2432

2533
it('should include days from the next month when the last day of the displayed month is not a Sunday', () => {
26-
const displayedMonthStartDate = new Date(2024, november, 1);
34+
//the last day of november 2024 is a saturday
35+
const displayedMonthStartDate = new Date(2024, november, 1); //friday
2736
const { result } = renderHook(() =>
2837
useCalendar({ displayedMonthStartDate, onDayClick: () => {} })
2938
);
@@ -32,7 +41,7 @@ describe('useCalendar', () => {
3241
});
3342

3443
it('should not include days from the previous month when the displayed month starts on a Monday', () => {
35-
const displayedMonthStartDate = new Date(2024, july, 1);
44+
const displayedMonthStartDate = new Date(2024, july, 1); //monday
3645
const { result } = renderHook(() =>
3746
useCalendar({ displayedMonthStartDate, onDayClick: () => {} })
3847
);
@@ -41,7 +50,7 @@ describe('useCalendar', () => {
4150
});
4251

4352
it('should not include days from the next month when the displayed month ends on a Sunday', () => {
44-
const displayedMonthStartDate = new Date(2024, june, 1);
53+
const displayedMonthStartDate = new Date(2024, june, 1); //saturday
4554
const { result } = renderHook(() =>
4655
useCalendar({ displayedMonthStartDate, onDayClick: () => {} })
4756
);
@@ -52,25 +61,12 @@ describe('useCalendar', () => {
5261

5362
describe('day wrapper classnames', () => {
5463
it('should have "day-wrapper" classname for all days', () => {
55-
const displayedMonthStartDate = new Date(2024, july, 1);
56-
const selectedSlot = { start: new Date(2024, july, 10), end: new Date(2024, july, 20) };
57-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
58-
const { result } = renderHook(() =>
59-
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
60-
);
6164
expect(result.current.buildDayWrapperClassName(new Date(2024, july, 15))).toContain(
6265
'day-wrapper'
6366
);
6467
});
6568

6669
describe('selectable slot classname', () => {
67-
const displayedMonthStartDate = new Date(2024, july, 1);
68-
const selectedSlot = { start: new Date(2024, july, 10), end: new Date(2024, july, 20) };
69-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
70-
const { result } = renderHook(() =>
71-
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
72-
);
73-
7470
it('should have "outside-selectable-slot" classname for days outside the selectable slot', () => {
7571
expect(result.current.buildDayWrapperClassName(new Date(2024, april, 1))).toContain(
7672
'outside-selectable-slot'
@@ -85,13 +81,6 @@ describe('useCalendar', () => {
8581
});
8682

8783
describe('selected slot classname', () => {
88-
const displayedMonthStartDate = new Date(2024, july, 1);
89-
const selectedSlot = { start: new Date(2024, july, 10), end: new Date(2024, july, 20) };
90-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
91-
const { result } = renderHook(() =>
92-
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
93-
);
94-
9584
it('should have "start" classname for the start day of the selected slot', () => {
9685
expect(result.current.buildDayWrapperClassName(selectedSlot.start)).toContain('start');
9786
});
@@ -105,22 +94,23 @@ describe('useCalendar', () => {
10594
'within-selected-slot'
10695
);
10796
});
97+
98+
it('should not have "within-selected-slot" classname for days within the selected slot', () => {
99+
expect(result.current.buildDayWrapperClassName(new Date(2024, july, 21))).not.toContain(
100+
'within-selected-slot'
101+
);
102+
});
108103
});
109104

110105
describe('other classnames', () => {
111-
const displayedMonthStartDate = new Date(2024, july, 1);
112-
const selectedSlot = { start: new Date(2024, july, 10), end: null };
113-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
114-
const { result } = renderHook(() =>
115-
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
116-
);
117-
118106
it('should have "past" classname for days before today', () => {
119107
expect(result.current.buildDayWrapperClassName(new Date(1900, june, 30))).toContain('past');
120108
});
121109

122110
it('should not assign past if the day is after selected start', () => {
123-
expect(result.current.buildDayWrapperClassName(new Date(2024, august, 1))).not.toContain('past');
111+
expect(result.current.buildDayWrapperClassName(new Date(2024, august, 1))).not.toContain(
112+
'past'
113+
);
124114
});
125115

126116
it('should have "current-month" classname for days in the displayed month', () => {
@@ -132,13 +122,6 @@ describe('useCalendar', () => {
132122
});
133123

134124
describe('isDateSelectable', () => {
135-
const displayedMonthStartDate = new Date(2024, july, 1);
136-
const selectedSlot = { start: new Date(2024, july, 10), end: null };
137-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
138-
const { result } = renderHook(() =>
139-
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
140-
);
141-
142125
it('should return false for days outside the selectable slot', () => {
143126
expect(result.current.isDateSelectable(new Date(2024, april, 1))).toBe(false);
144127
});
@@ -159,9 +142,6 @@ describe('useCalendar', () => {
159142
describe('isToday', () => {
160143
const today = new Date();
161144
today.setHours(0, 0, 0, 0);
162-
const displayedMonthStartDate = new Date(2024, july, 1);
163-
const selectedSlot = { start: new Date(2024, july, 10), end: null };
164-
const selectableSlot = { start: new Date(2024, july, 5), end: new Date(2024, july, 25) };
165145
const { result } = renderHook(() =>
166146
useCalendar({ displayedMonthStartDate, selectedSlot, selectableSlot, onDayClick: () => {} })
167147
);

ui-core/src/components/inputs/datePicker/__tests__/useCalendarPicker.spec.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
import { renderHook, act } from '@testing-library/react';
22
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
3-
import useCalendarPicker, {
3+
import useCalendarPicker from '../useCalendarPicker';
4+
import {
5+
generateSequentialDates,
46
INVALID_SELECTED_SLOT_ERROR,
57
INVALID_SELECTABLE_SLOT_ERROR,
68
INVALID_SELECTED_SLOT_BASED_ON_SELECTABLE_SLOT_ERROR,
79
INVALID_INITIAL_DATE_ERROR,
8-
} from '../useCalendarPicker';
9-
import { generateSequentialDates } from '../utils';
10+
} from '../utils';
1011

1112
const errorsToIgnore = [
1213
INVALID_SELECTED_SLOT_ERROR,
1314
INVALID_SELECTABLE_SLOT_ERROR,
1415
INVALID_SELECTED_SLOT_BASED_ON_SELECTABLE_SLOT_ERROR,
1516
INVALID_INITIAL_DATE_ERROR,
1617
];
17-
describe('useCalendarPicker', () => {
18-
const january = 0;
19-
const february = 1;
20-
const june = 5;
21-
const july = 6;
22-
const august = 7;
23-
const december = 11;
24-
const onDayClick = vi.fn();
2518

19+
const january = 0;
20+
const february = 1;
21+
const june = 5;
22+
const july = 6;
23+
const august = 7;
24+
const december = 11;
25+
const onDayClick = vi.fn();
26+
27+
describe('useCalendarPicker', () => {
2628
describe('Initialization', () => {
2729
let originalConsoleError: typeof console.error;
2830

ui-core/src/components/inputs/datePicker/__tests__/utils.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ describe('formatInputValue', () => {
305305
});
306306

307307
describe('formatValueToSlot', () => {
308-
it('should return the input as is for range mode', () => {
308+
it('should return the input as it is for range mode', () => {
309309
const rangeValue = { start: new Date('2023-04-01'), end: new Date('2023-04-15') };
310310
const result = formatValueToSlot(rangeValue, true);
311311
expect(result).toEqual(rangeValue);
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
export {
22
default as DatePicker,
33
type DatePickerProps,
4-
type BaseDatePickerProps,
54
type RangeDatePickerProps,
65
type SingleDatePickerProps,
76
} from './DatePicker';
8-
export { type CalendarPickerPublicProps } from './CalendarPicker';
97
export { type CalendarSlot } from './type';

ui-core/src/components/inputs/datePicker/useCalendar.ts

-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ export default function useCalendar({
6161
// Check if the date is in the currently displayed month
6262
if (date.getMonth() !== displayedMonth) return false;
6363

64-
// Check if the date is not in the past
65-
if (normalizeDate(date) < normalizeDate(today)) return false;
66-
6764
return true;
6865
};
6966

ui-core/src/components/inputs/datePicker/useCalendarPicker.ts

+7-49
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
import { useState } from 'react';
22
import { CalendarPickerProps } from './CalendarPicker';
3-
import {
4-
getAllDatesInMonth,
5-
isValidSlot,
6-
isSameDay,
7-
generateSequentialDates,
8-
normalizeDate,
9-
} from './utils';
10-
11-
export const INVALID_SELECTED_SLOT_ERROR =
12-
'Invalid selectedSlot: If start and end are defined, the start date must be before the end date.';
13-
export const INVALID_SELECTABLE_SLOT_ERROR =
14-
'Invalid selectableSlot: If start and end are defined, the start date must be before the end date.';
15-
export const INVALID_SELECTED_SLOT_BASED_ON_SELECTABLE_SLOT_ERROR =
16-
'selectedSlot must be within selectableSlot';
17-
export const INVALID_INITIAL_DATE_ERROR = 'initialDate must be within selectableSlot';
3+
import { generateSequentialDates, validateSlots } from './utils';
184

195
export default function useCalendarPicker({
206
initialDate,
@@ -23,54 +9,26 @@ export default function useCalendarPicker({
239
numberOfMonths = 1,
2410
onDayClick,
2511
}: Omit<CalendarPickerProps, 'modalPosition' | 'calendarPickerRef'>) {
26-
if (selectedSlot && !isValidSlot(selectedSlot)) {
27-
throw new Error(INVALID_SELECTED_SLOT_ERROR);
28-
}
29-
30-
if (selectableSlot && !isValidSlot(selectableSlot)) {
31-
throw new Error(INVALID_SELECTABLE_SLOT_ERROR);
32-
}
33-
34-
if (
35-
selectedSlot?.start &&
36-
selectedSlot?.end &&
37-
selectableSlot?.start &&
38-
selectableSlot?.end &&
39-
(normalizeDate(selectedSlot.start) < normalizeDate(selectableSlot.start) ||
40-
normalizeDate(selectedSlot.end) > normalizeDate(selectableSlot.end))
41-
) {
42-
throw new Error(INVALID_SELECTED_SLOT_BASED_ON_SELECTABLE_SLOT_ERROR);
43-
}
44-
45-
if (
46-
initialDate &&
47-
selectableSlot?.start &&
48-
selectableSlot?.end &&
49-
(normalizeDate(initialDate) < normalizeDate(selectableSlot.start) ||
50-
normalizeDate(initialDate) > normalizeDate(selectableSlot.end))
51-
) {
52-
throw new Error(INVALID_INITIAL_DATE_ERROR);
53-
}
54-
12+
validateSlots(selectedSlot, selectableSlot, initialDate);
5513
const initialActiveDate =
5614
initialDate ?? selectedSlot?.start ?? selectableSlot?.start ?? new Date();
5715
const [activeDate, setActiveDate] = useState<Date>(initialActiveDate);
5816
const displayedMonthsStartDates = generateSequentialDates(activeDate, numberOfMonths);
5917

6018
const activeYear = activeDate.getFullYear();
6119
const activeMonth = activeDate.getMonth();
62-
const daysInMonth = displayedMonthsStartDates
63-
.map((date) => getAllDatesInMonth(date.getMonth(), date.getFullYear()))
64-
.flat();
20+
const firstDayOfMonth = new Date(activeYear, activeMonth, 1);
21+
const lastDayOfMonth = new Date(activeYear, activeMonth + 1, 0);
22+
6523
const canGoToPreviousMonth =
6624
selectableSlot === undefined || selectableSlot.start === null
6725
? true
68-
: !daysInMonth.some((d) => isSameDay(d, selectableSlot.start));
26+
: selectableSlot.start < firstDayOfMonth;
6927

7028
const canGoToNextMonth =
7129
selectableSlot === undefined || selectableSlot.end === null
7230
? true
73-
: !daysInMonth.some((d) => isSameDay(d, selectableSlot.end));
31+
: selectableSlot.end > lastDayOfMonth;
7432

7533
const showNavigationBtn = canGoToPreviousMonth || canGoToNextMonth;
7634

ui-core/src/components/inputs/datePicker/useDatePicker.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ import { useState, useRef, useEffect } from 'react';
22
import useClickOutside from '../../hooks/useOutsideClick';
33
import { useModalPosition } from '../../hooks/useModalPosition';
44
import { computeNewSelectedSlot, formatInputValue, formatValueToSlot } from './utils';
5-
import { DatePickerProps, RangeDatePickerProps, SingleDatePickerProps } from './DatePicker';
5+
import { DatePickerProps } from './DatePicker';
66

77
const MODAL_HORIZONTAL_OFFSET = -24;
88
const MODAL_VERTICAL_OFFSET = 3;
99

10-
export default function useDatePicker({
11-
inputProps,
12-
value,
13-
isRangeMode,
14-
onDateChange,
15-
}: DatePickerProps) {
10+
export default function useDatePicker(datePickerProps: DatePickerProps) {
11+
const { inputProps, value, isRangeMode, onDateChange } = datePickerProps;
1612
const [showPicker, setShowPicker] = useState(false);
1713
const [inputValue, setInputValue] = useState(formatInputValue(value, isRangeMode));
1814
const [selectedSlot, setSelectedSlot] = useState(formatValueToSlot(value, isRangeMode));
@@ -39,13 +35,10 @@ export default function useDatePicker({
3935

4036
const handleDayClick = (clickedDate: Date) => {
4137
if (isRangeMode) {
42-
const newSelectedSlot = computeNewSelectedSlot(
43-
clickedDate,
44-
value as RangeDatePickerProps['value']
45-
);
46-
(onDateChange as RangeDatePickerProps['onDateChange'])(clickedDate, newSelectedSlot);
38+
const newSelectedSlot = computeNewSelectedSlot(clickedDate, value);
39+
onDateChange(clickedDate, newSelectedSlot);
4740
} else {
48-
(onDateChange as SingleDatePickerProps['onDateChange'])(clickedDate);
41+
onDateChange(clickedDate);
4942
}
5043
};
5144

0 commit comments

Comments
 (0)