Skip to content

Commit 17a764d

Browse files
authored
Merge pull request #520 from abilpraju-aot/feature/FWF-4370
initial fix for task list
2 parents d950fdf + 811dc82 commit 17a764d

File tree

15 files changed

+1171
-79
lines changed

15 files changed

+1171
-79
lines changed

forms-flow-components/src/__test__/dateFilter.test.tsx

Lines changed: 250 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -257,28 +257,6 @@ describe('DateRangePicker Component', () => {
257257
expect(screen.getByTestId('calendar-month-year').textContent).toBe('January 2025');
258258
});
259259

260-
test('resets to initial range when calendar is closed without selection', () => {
261-
const initialDateRange = {
262-
startDate: null,
263-
endDate: null
264-
};
265-
266-
render(<DateRangePicker initialDateRange={initialDateRange} />);
267-
268-
// Open calendar
269-
fireEvent.click(screen.getByTestId('date-range-display'));
270-
271-
// Select only start date
272-
const startDateElement = screen.getAllByTestId('calendar-day-10')?.[0];
273-
fireEvent.click(startDateElement);
274-
275-
// Close calendar without selecting end date
276-
fireEvent.click(screen.getByTestId('date-range-close-btn'));
277-
278-
// Date range should reset to initial
279-
const dateRangeText = screen.getByTestId('date-range-text');
280-
expect(dateRangeText.textContent).toBe('MM/DD/YYYY - MM/DD/YYYY');
281-
});
282260

283261
test('handles selection when end date is before start date', () => {
284262
render(<DateRangePicker onChange={mockOnChange} />);
@@ -315,4 +293,254 @@ describe('DateRangePicker Component', () => {
315293
const dateRangePicker = screen.getByTestId('date-range-picker');
316294
expect(dateRangePicker).toHaveClass('custom-class');
317295
});
296+
297+
// Additional tests to add to the existing test suite
298+
299+
test('resets date range when clicking close button', () => {
300+
const initialDateRange = {
301+
startDate: new Date(2025, 0, 1), // Jan 1, 2025
302+
endDate: new Date(2025, 0, 15) // Jan 15, 2025
303+
};
304+
305+
render(<DateRangePicker initialDateRange={initialDateRange} onChange={mockOnChange} />);
306+
307+
// Open calendar
308+
fireEvent.click(screen.getByTestId('date-range-display'));
309+
310+
// Click close button
311+
fireEvent.click(screen.getByTestId('close-icon'));
312+
313+
// Check if onChange was called with null dates
314+
expect(mockOnChange).toHaveBeenCalledWith({
315+
startDate: null,
316+
endDate: null
317+
});
318+
319+
// Check if the display text was reset
320+
expect(screen.getByTestId('date-range-text').textContent).toBe('MM/DD/YYYY - MM/DD/YYYY');
321+
});
322+
323+
test('navigates to previous and next month', () => {
324+
const initialDateRange = {
325+
startDate: new Date(2025, 2, 15), // Mar 15, 2025
326+
endDate: new Date(2025, 2, 20) // Mar 20, 2025
327+
};
328+
329+
render(<DateRangePicker initialDateRange={initialDateRange} />);
330+
331+
// Open calendar
332+
fireEvent.click(screen.getByTestId('date-range-display'));
333+
334+
// Initial month
335+
expect(screen.getByTestId('calendar-month-year').textContent).toBe('March 2025');
336+
337+
// Go to next month
338+
fireEvent.click(screen.getByTestId('angle-right-icon'));
339+
expect(screen.getByTestId('calendar-month-year').textContent).toBe('April 2025');
340+
341+
// Go to previous month
342+
fireEvent.click(screen.getByTestId('angle-left-icon'));
343+
expect(screen.getByTestId('calendar-month-year').textContent).toBe('March 2025');
344+
});
345+
346+
test('completes selection when clicking outside with only start date selected', async () => {
347+
render(<DateRangePicker onChange={mockOnChange} />);
348+
349+
// Open calendar
350+
fireEvent.click(screen.getByTestId('date-range-display'));
351+
352+
// Select only start date (15th of current month)
353+
const startDateElement = screen.getByTestId('calendar-day-15');
354+
fireEvent.click(startDateElement);
355+
356+
// Simulate clicking outside
357+
fireEvent.mouseDown(document.body);
358+
359+
// Check if onChange was called with start and end date being the same
360+
await waitFor(() => {
361+
expect(mockOnChange).toHaveBeenCalledTimes(1);
362+
const onChangeCalls = mockOnChange.mock.calls[0][0];
363+
364+
// Start and end date should be the same (15th)
365+
expect(onChangeCalls.startDate.getDate()).toBe(15);
366+
expect(onChangeCalls.endDate.getDate()).toBe(15);
367+
});
368+
});
369+
370+
371+
test('renders correct weekday headers', () => {
372+
render(<DateRangePicker />);
373+
374+
// Open calendar
375+
fireEvent.click(screen.getByTestId('date-range-display'));
376+
377+
// Check if all weekday headers are present
378+
const weekdays = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'];
379+
const weekdayElements = screen.getByTestId('calendar-days-header').children;
380+
381+
expect(weekdayElements.length).toBe(7);
382+
383+
for (let i = 0; i < weekdays.length; i++) {
384+
expect(weekdayElements[i].textContent).toBe(weekdays[i]);
385+
}
386+
});
387+
388+
test('generates correct number of days in calendar', () => {
389+
render(<DateRangePicker />);
390+
391+
// Open calendar
392+
fireEvent.click(screen.getByTestId('date-range-display'));
393+
394+
// Calendar should have 6 weeks (42 days)
395+
const daysContainer = screen.getByTestId('calendar-days');
396+
expect(daysContainer.children.length).toBe(42);
397+
});
398+
399+
test('handles space key for date selection', () => {
400+
render(<DateRangePicker onChange={mockOnChange} />);
401+
402+
// Open calendar
403+
fireEvent.click(screen.getByTestId('date-range-display'));
404+
405+
// Select start date with space key
406+
const startDateElement = screen.getByTestId('calendar-day-15');
407+
fireEvent.keyDown(startDateElement, { key: ' ' });
408+
409+
// Select end date with space key
410+
const endDateElement = screen.getByTestId('calendar-day-20');
411+
fireEvent.keyDown(endDateElement, { key: ' ' });
412+
413+
// Check if onChange was called
414+
expect(mockOnChange).toHaveBeenCalledTimes(1);
415+
416+
const onChangeCalls = mockOnChange.mock.calls[0][0];
417+
expect(onChangeCalls.startDate.getDate()).toBe(15);
418+
expect(onChangeCalls.endDate.getDate()).toBe(20);
419+
});
420+
421+
test('updates when initialDateRange prop changes', () => {
422+
const { rerender } = render(
423+
<DateRangePicker
424+
initialDateRange={{
425+
startDate: new Date(2025, 0, 1),
426+
endDate: new Date(2025, 0, 15)
427+
}}
428+
/>
429+
);
430+
431+
// Check initial render
432+
expect(screen.getByTestId('date-range-text').textContent).toBe('01/01/2025 - 01/15/2025');
433+
434+
// Update props
435+
rerender(
436+
<DateRangePicker
437+
initialDateRange={{
438+
startDate: new Date(2026, 5, 10),
439+
endDate: new Date(2026, 5, 20)
440+
}}
441+
/>
442+
);
443+
444+
// Check if display updated
445+
expect(screen.getByTestId('date-range-text').textContent).toBe('06/10/2026 - 06/20/2026');
446+
});
447+
448+
test('displays days from previous and next months', () => {
449+
// Use a specific date to ensure consistent test results
450+
const initialDateRange = {
451+
startDate: new Date(2025, 0, 15), // Jan 15, 2025
452+
endDate: new Date(2025, 0, 20) // Jan 20, 2025
453+
};
454+
455+
render(<DateRangePicker initialDateRange={initialDateRange} />);
456+
457+
// Open calendar
458+
fireEvent.click(screen.getByTestId('date-range-display'));
459+
460+
// Find all day elements
461+
const dayElements = screen.getByTestId('calendar-days').children;
462+
463+
// Check if there are days from previous month (should have "other-month" class)
464+
const previousMonthDays = Array.from(dayElements).filter(
465+
el => el.classList.contains('other-month') && parseInt(el.textContent || '0') > 20
466+
);
467+
expect(previousMonthDays.length).toBeGreaterThan(0);
468+
469+
// Check if there are days from next month (should have "other-month" class)
470+
const nextMonthDays = Array.from(dayElements).filter(
471+
el => el.classList.contains('other-month') && parseInt(el.textContent || '0') < 15
472+
);
473+
expect(nextMonthDays.length).toBeGreaterThan(0);
474+
});
475+
476+
test('correctly marks start and end dates with appropriate classes', () => {
477+
// Use a specific date range
478+
const initialDateRange = {
479+
startDate: new Date(2025, 0, 10), // Jan 10, 2025
480+
endDate: new Date(2025, 0, 20) // Jan 20, 2025
481+
};
482+
483+
render(<DateRangePicker initialDateRange={initialDateRange} />);
484+
485+
// Open calendar
486+
fireEvent.click(screen.getByTestId('date-range-display'));
487+
488+
// Find all day elements for the 10th and 20th
489+
const allDays = screen.getByTestId('calendar-days').children;
490+
491+
// Find the current month's day 10 (start date)
492+
const startDateElement = Array.from(allDays).find(
493+
el => el.textContent === '10' && el.classList.contains('current-month')
494+
);
495+
496+
// Find the current month's day 20 (end date)
497+
const endDateElement = Array.from(allDays).find(
498+
el => el.textContent === '20' && el.classList.contains('current-month')
499+
);
500+
501+
// Check if they have the correct classes
502+
expect(startDateElement).toHaveClass('start-date');
503+
expect(startDateElement).toHaveClass('selected');
504+
expect(endDateElement).toHaveClass('end-date');
505+
expect(endDateElement).toHaveClass('selected');
506+
507+
// Check if days in between are also selected
508+
const day15Element = Array.from(allDays).find(
509+
el => el.textContent === '15' && el.classList.contains('current-month')
510+
);
511+
expect(day15Element).toHaveClass('selected');
512+
expect(day15Element).not.toHaveClass('start-date');
513+
expect(day15Element).not.toHaveClass('end-date');
514+
});
515+
516+
test('toggles calendar open/close when clicking display area', () => {
517+
render(<DateRangePicker />);
518+
519+
// Calendar should be closed initially
520+
expect(screen.queryByTestId('calendar-container')).not.toBeInTheDocument();
521+
522+
// Open calendar
523+
fireEvent.click(screen.getByTestId('date-range-display'));
524+
expect(screen.getByTestId('calendar-container')).toBeInTheDocument();
525+
526+
// Close calendar by clicking display again
527+
fireEvent.click(screen.getByTestId('date-range-display'));
528+
expect(screen.queryByTestId('calendar-container')).not.toBeInTheDocument();
529+
});
530+
531+
test('shows correct toggle icon based on calendar state', () => {
532+
render(<DateRangePicker />);
533+
534+
// When closed, should show down arrow
535+
expect(screen.getByTestId('down-arrow-icon')).toBeInTheDocument();
536+
expect(screen.queryByTestId('up-arrow-icon')).not.toBeInTheDocument();
537+
538+
// Open calendar
539+
fireEvent.click(screen.getByTestId('date-range-display'));
540+
541+
// When open, should show up arrow
542+
expect(screen.queryByTestId('down-arrow-icon')).not.toBeInTheDocument();
543+
expect(screen.getByTestId('up-arrow-icon')).toBeInTheDocument();
544+
});
545+
318546
});

forms-flow-components/src/components/CustomComponents/DateFilter.tsx

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -356,33 +356,41 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
356356
}
357357
};
358358

359+
// Keep track of last valid date range
360+
const [lastValidDateRange, setLastValidDateRange] = useState<DateRange>(parsedInitialRange());
359361

360-
// Handle calendar toggle
361-
const toggleCalendar = (): void => {
362-
setIsOpen(!isOpen);
363-
if (!isOpen) {
364-
// Only reset when opening the calendar if needed
365-
// Don't reset when closing
366-
} else {
367-
// When closing, reset both dates to null
368-
setDateRange({
362+
// Update lastValidDateRange whenever we have a complete valid selection
363+
useEffect(() => {
364+
if (dateRange.startDate && dateRange.endDate) {
365+
setLastValidDateRange({...dateRange});
366+
}
367+
}, [dateRange]);
368+
369+
// Handle calendar toggle
370+
const toggleCalendar = (): void => {
371+
setIsOpen(!isOpen);
372+
};
373+
374+
// Handle close calendar (X button)
375+
const handleCloseCalendar = (event?: React.MouseEvent): void => {
376+
if (event) {
377+
event.stopPropagation();
378+
}
379+
setIsOpen(false);
380+
381+
// Reset both dates to null when closing with the X button
382+
const emptyDateRange = {
369383
startDate: null,
370384
endDate: null
371-
});
372-
}
373-
};
374-
// Handle close calendar
375-
const handleCloseCalendar = (event?: React.MouseEvent): void => {
376-
if (event) {
377-
event.stopPropagation();
378-
}
379-
setIsOpen(false);
380-
// Reset both start and end date to null when closing
381-
setDateRange({
382-
startDate: null,
383-
endDate: null
384-
});
385-
};
385+
};
386+
387+
setDateRange(emptyDateRange);
388+
389+
// Notify parent component of the reset
390+
if (onChange) {
391+
onChange(emptyDateRange);
392+
}
393+
};
386394

387395
// Format month and year for display
388396
const formatMonthYear = (): string => {
@@ -413,9 +421,21 @@ const handleCloseCalendar = (event?: React.MouseEvent): void => {
413421
!calendarRef.current.contains(event.target as Node)
414422
) {
415423
setIsOpen(false);
416-
// Reset if selection is incomplete
417-
if (!dateRange?.endDate) {
418-
setDateRange(parsedInitialRange());
424+
425+
// If selection is incomplete, complete it with current dates
426+
// instead of resetting to lastValidDateRange
427+
if (dateRange.startDate && !dateRange.endDate) {
428+
const completedRange = {
429+
startDate: dateRange.startDate,
430+
endDate: dateRange.startDate // Set end date same as start date
431+
};
432+
433+
setDateRange(completedRange);
434+
435+
// Notify parent component of the completed selection
436+
if (onChange) {
437+
onChange(completedRange);
438+
}
419439
}
420440
}
421441
};
@@ -424,7 +444,7 @@ const handleCloseCalendar = (event?: React.MouseEvent): void => {
424444
return () => {
425445
document.removeEventListener("mousedown", handleClickOutside);
426446
};
427-
}, [dateRange]);
447+
}, [dateRange, onChange]);
428448

429449
// Reset current month when opening calendar
430450
useEffect(() => {
@@ -602,4 +622,4 @@ const handleCloseCalendar = (event?: React.MouseEvent): void => {
602622
)}
603623
</div>
604624
);
605-
};
625+
};

0 commit comments

Comments
 (0)