Skip to content

Commit b85a407

Browse files
authored
Merge pull request #546 from abilpraju-aot/feature/FWF-4485
Feature/fwf 4485
2 parents 570fbeb + e075f49 commit b85a407

File tree

30 files changed

+3142
-1911
lines changed

30 files changed

+3142
-1911
lines changed

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

Lines changed: 446 additions & 402 deletions
Large diffs are not rendered by default.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ export const ButtonDropdown: React.FC<ButtonDropdownProps> = ({
6565
const toggleWidth = toggleRef.current.getBoundingClientRect().width;
6666
const totalWidth = buttonWidth + toggleWidth - 1;
6767
setMenuStyle({
68-
minWidth: `${totalWidth}px`,
68+
width: `${totalWidth}px`,
69+
maxHeight: "200px",
70+
overflowY: "auto",
6971
border: "2px solid var(--primary-btn-bg-color)",
7072
borderTopLeftRadius: "0",
7173
borderTopRightRadius: "0",

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

Lines changed: 122 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,39 @@ interface DateRange {
1717
}
1818

1919
interface DateRangePickerProps {
20-
onChange?: (dateRange: DateRange) => void;
21-
initialDateRange?: DateRange;
20+
/**
21+
* Callback function triggered when date range changes
22+
* @param dateRange Object containing startDate and endDate
23+
*/
24+
onChange: (dateRange: DateRange) => void;
25+
26+
/**
27+
* Initial date range to display
28+
*/
29+
value?: DateRange;
30+
31+
/**
32+
* Format for displaying dates (default: MM/DD/YYYY)
33+
*/
2234
dateFormat?: string;
35+
36+
/**
37+
* Additional CSS class names
38+
*/
2339
className?: string;
40+
41+
/**
42+
* Placeholder text when no dates are selected
43+
*/
44+
placeholder?: string;
2445
}
2546

2647
export const DateRangePicker: FC<DateRangePickerProps> = ({
2748
onChange,
28-
initialDateRange,
49+
value,
2950
dateFormat = "MM/DD/YYYY",
3051
className = "",
52+
placeholder = "Select date range",
3153
}) => {
3254
const { t } = useTranslation();
3355
const calendarRef = useRef<HTMLDivElement>(null);
@@ -50,26 +72,17 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
5072

5173
// Set default initial date range
5274
const getDefaultDateRange = (): DateRange => {
53-
const today = new Date();
54-
const thirtyDaysLater = new Date();
55-
thirtyDaysLater.setDate(today.getDate() + 30);
5675
return {
57-
startDate: today,
58-
endDate: thirtyDaysLater,
76+
startDate: null,
77+
endDate: null,
5978
};
6079
};
6180

6281
// Parse initial date range with fallbacks for null values
6382
const parsedInitialRange = (): DateRange => {
64-
if (!initialDateRange) return getDefaultDateRange();
65-
6683
return {
67-
startDate: initialDateRange.startDate
68-
? parseDate(initialDateRange.startDate)
69-
: new Date(),
70-
endDate: initialDateRange.endDate
71-
? parseDate(initialDateRange.endDate)
72-
: new Date(new Date().setDate(new Date().getDate() + 30)),
84+
startDate: value?.startDate ? parseDate(value.startDate) : null,
85+
endDate: value?.endDate ? parseDate(value.endDate) : null,
7386
};
7487
};
7588

@@ -78,55 +91,77 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
7891
const [currentMonth, setCurrentMonth] = useState<Date>(() => {
7992
// Safely get start date for current month
8093
const range = parsedInitialRange();
81-
return range.startDate instanceof Date
82-
? range.startDate
83-
: parseDate(range.startDate);
94+
if (range.startDate) {
95+
return range.startDate instanceof Date
96+
? range.startDate
97+
: parseDate(range.startDate);
98+
}
99+
return new Date(); // Default to current month if no range provided
84100
});
85101

86-
// Update state when initialDateRange prop changes
102+
// Update state when value prop changes
87103
useEffect(() => {
88-
if (initialDateRange) {
104+
if (value) {
89105
const newDateRange = {
90-
startDate: initialDateRange.startDate
91-
? parseDate(initialDateRange.startDate)
92-
: new Date(),
93-
endDate: initialDateRange.endDate
94-
? parseDate(initialDateRange.endDate)
95-
: new Date(new Date().setDate(new Date().getDate() + 30)),
106+
startDate: value?.startDate ? parseDate(value.startDate) : null,
107+
endDate: value?.endDate ? parseDate(value.endDate) : null,
96108
};
109+
97110
setDateRange(newDateRange);
98111

99-
// Safely update current month
112+
// Safely update current month if startDate exists
100113
if (newDateRange.startDate) {
101114
setCurrentMonth(
102115
newDateRange.startDate instanceof Date
103-
? newDateRange.startDate
116+
? new Date(newDateRange.startDate)
104117
: parseDate(newDateRange.startDate)
105118
);
106119
}
120+
} else {
121+
// Reset to empty range if value is undefined/null
122+
setDateRange(getDefaultDateRange());
107123
}
108-
}, [initialDateRange]);
124+
}, [value]);
109125

110126
// Format date according to specified format (default: MM/DD/YYYY)
111-
const formatDate = (date: Date | null): string => {
112-
if (!date) return "MM/DD/YYYY";
113-
114-
// Default format (MM/DD/YYYY)
115-
const month = String(date.getMonth() + 1).padStart(2, "0");
116-
const day = String(date.getDate()).padStart(2, "0");
117-
const year = date.getFullYear();
118-
return `${month}/${day}/${year}`;
119-
};
120-
121-
const getFormattedDate = (date: Date | string | null): string => {
122-
if (!date) return "MM/DD/YYYY";
123-
const parsedDate = date instanceof Date ? date : parseDate(date);
124-
return formatDate(parsedDate);
127+
const formatDateValue = (
128+
date: Date | string | null,
129+
format: string = dateFormat
130+
): string => {
131+
// If no date, return formatted placeholder
132+
if (!date) {
133+
return format
134+
.replace(/M+/g, "MM")
135+
.replace(/D+/g, "DD")
136+
.replace(/Y+/g, "YYYY");
137+
}
138+
139+
// Convert to Date object if string
140+
const dateObj = date instanceof Date ? date : parseDate(date);
141+
142+
// Format according to specified pattern
143+
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
144+
const day = String(dateObj.getDate()).padStart(2, "0");
145+
const year = dateObj.getFullYear();
146+
147+
let formattedDate = format;
148+
formattedDate = formattedDate.replace(/M+/g, month);
149+
formattedDate = formattedDate.replace(/D+/g, day);
150+
formattedDate = formattedDate.replace(/Y+/g, year.toString());
151+
152+
return formattedDate;
125153
};
126-
154+
127155
const formatDateRange = (): string => {
128-
const start = getFormattedDate(dateRange?.startDate ?? null);
129-
const end = getFormattedDate(dateRange?.endDate ?? null);
156+
// If neither date is selected, show placeholder
157+
if (!dateRange.startDate && !dateRange.endDate) {
158+
return t(placeholder);
159+
}
160+
161+
// Simplified - no need for conditional check as formatDateValue handles nulls
162+
const start = formatDateValue(dateRange.startDate);
163+
const end = formatDateValue(dateRange.endDate);
164+
130165
return `${start} - ${end}`;
131166
};
132167

@@ -265,13 +300,18 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
265300

266301
// Clone the date to avoid reference issues
267302
const selectedDate = new Date(date);
303+
selectedDate.setHours(0, 0, 0, 0);
268304

269305
if (!dateRange.startDate || (dateRange.startDate && dateRange.endDate)) {
270306
// Start new selection
271-
setDateRange({
307+
const newRange = {
272308
startDate: selectedDate,
273309
endDate: null,
274-
});
310+
};
311+
312+
setDateRange(newRange);
313+
// Always notify parent of changes, even for partial selections
314+
onChange(newRange);
275315
} else {
276316
// Complete the selection
277317
let newStartDate: Date;
@@ -290,18 +330,14 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
290330
newEndDate = selectedDate;
291331
}
292332

293-
setDateRange({
333+
const newRange = {
294334
startDate: newStartDate,
295335
endDate: newEndDate,
296-
});
336+
};
297337

298-
// Trigger onChange after completing selection
299-
if (onChange) {
300-
onChange({
301-
startDate: newStartDate,
302-
endDate: newEndDate,
303-
});
304-
}
338+
setDateRange(newRange);
339+
// Notify parent component of the complete selection
340+
onChange(newRange);
305341
}
306342
};
307343

@@ -356,16 +392,6 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
356392
}
357393
};
358394

359-
// Keep track of last valid date range
360-
const [lastValidDateRange, setLastValidDateRange] = useState<DateRange>(parsedInitialRange());
361-
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-
369395
// Handle calendar toggle
370396
const toggleCalendar = (): void => {
371397
setIsOpen(!isOpen);
@@ -377,19 +403,17 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
377403
event.stopPropagation();
378404
}
379405
setIsOpen(false);
380-
406+
381407
// Reset both dates to null when closing with the X button
382408
const emptyDateRange = {
383409
startDate: null,
384-
endDate: null
410+
endDate: null,
385411
};
386-
412+
387413
setDateRange(emptyDateRange);
388-
414+
389415
// Notify parent component of the reset
390-
if (onChange) {
391-
onChange(emptyDateRange);
392-
}
416+
onChange(emptyDateRange);
393417
};
394418

395419
// Format month and year for display
@@ -421,21 +445,18 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
421445
!calendarRef.current.contains(event.target as Node)
422446
) {
423447
setIsOpen(false);
424-
448+
425449
// If selection is incomplete, complete it with current dates
426-
// instead of resetting to lastValidDateRange
427450
if (dateRange.startDate && !dateRange.endDate) {
428451
const completedRange = {
429452
startDate: dateRange.startDate,
430-
endDate: dateRange.startDate // Set end date same as start date
453+
endDate: dateRange.startDate, // Set end date same as start date
431454
};
432-
455+
433456
setDateRange(completedRange);
434-
457+
435458
// Notify parent component of the completed selection
436-
if (onChange) {
437-
onChange(completedRange);
438-
}
459+
onChange(completedRange);
439460
}
440461
}
441462
};
@@ -472,25 +493,29 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
472493
aria-expanded={isOpen}
473494
type="button"
474495
>
475-
<span data-testid="date-range-text">{formatDateRange()}</span>
496+
<span className={`date-range-text ${isOpen ? "open" : ""}`} data-testid="date-range-text">{formatDateRange()}</span>
476497
<div className="date-range-controls">
477-
<button
478-
className="date-range-close button-as-div"
479-
data-testid="date-range-close-btn"
480-
aria-label={t("Close calendar")}
481-
type="button"
482-
onKeyDown={(e) => handleNavKeyDown(e, () => handleCloseCalendar())}
483-
>
484-
<CloseIcon color="white" onClick={(e) => handleCloseCalendar(e)} />
485-
</button>
498+
{isOpen && (
499+
<button
500+
className="date-range-close button-as-div"
501+
data-testid="date-range-close-btn"
502+
aria-label={t("Close calendar")}
503+
type="button"
504+
onClick={(e) => handleCloseCalendar(e)}
505+
onKeyDown={(e) =>
506+
handleNavKeyDown(e, () => handleCloseCalendar())
507+
}
508+
>
509+
<CloseIcon width={10} height={10} color="white" />
510+
</button>
511+
)}
486512
<span
487-
className="date-range-toggle-icon"
513+
className={`date-range-toggle-icon ${isOpen ? "open" : ""}`}
488514
data-testid="date-range-toggle-icon"
489515
aria-hidden="true"
490516
>
491517
{isOpen ? (
492-
<UpArrowIcon color="white" />
493-
518+
<UpArrowIcon color="white" />
494519
) : (
495520
<DownArrowIcon color="white" />
496521
)}
@@ -622,4 +647,4 @@ export const DateRangePicker: FC<DateRangePickerProps> = ({
622647
)}
623648
</div>
624649
);
625-
};
650+
};

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export const SortableHeader: React.FC<SortableHeaderProps> = ({
2525
}) => {
2626
const { t } = useTranslation();
2727
const sortedOrder = currentSort[columnKey]?.sortOrder;
28-
console.log("currentSort", currentSort,sortedOrder);
2928
const isSorted = currentSort.activeKey === columnKey;
3029
const iconColor = isSorted
3130
? StyleServices.getCSSVariable("--ff-primary")

0 commit comments

Comments
 (0)