Skip to content

Commit b8710bc

Browse files
authored
fix(DatePicker/CalendarMonth): better range styling when some dates are disabled (#10398)
* fix(DatePicker/CalendarMonth): range styling when disabled dates * chore(Datepicker): getElementSelectorToFocus function description
1 parent 5e42bba commit b8710bc

File tree

2 files changed

+38
-20
lines changed

2 files changed

+38
-20
lines changed

packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export const CalendarMonth = ({
168168
const yearFormatted = yearFormat(focusedDate);
169169
const [yearInput, setYearInput] = React.useState(yearFormatted.toString());
170170

171-
const [hoveredDate, setHoveredDate] = React.useState(new Date(focusedDate));
171+
const [hoveredDate, setHoveredDate] = React.useState<Date>(undefined);
172172
const focusRef = React.useRef<HTMLButtonElement>();
173173
const [hiddenMonthId] = React.useState(getUniqueId('hidden-month-span'));
174174
const [shouldFocus, setShouldFocus] = React.useState(false);
@@ -192,7 +192,6 @@ export const CalendarMonth = ({
192192

193193
const onMonthClick = (ev: React.MouseEvent, newDate: Date) => {
194194
setFocusedDate(newDate);
195-
setHoveredDate(newDate);
196195
setShouldFocus(false);
197196
onMonthChange(ev, newDate);
198197
setYearInput(yearFormat(newDate).toString());
@@ -212,7 +211,6 @@ export const CalendarMonth = ({
212211
if (newDate.getTime() !== focusedDate.getTime() && isValidated(newDate)) {
213212
ev.preventDefault();
214213
setFocusedDate(newDate);
215-
setHoveredDate(newDate);
216214
setShouldFocus(true);
217215
}
218216
};
@@ -238,7 +236,6 @@ export const CalendarMonth = ({
238236
if (yearNum >= MIN_YEAR && yearNum <= MAX_YEAR) {
239237
const newDate = changeYear(yearNum);
240238
setFocusedDate(newDate);
241-
setHoveredDate(newDate);
242239
setShouldFocus(false);
243240

244241
// We need to manually focus the year input in FireFox when the scroll buttons are clicked, as FireFox doesn't place focus automatically
@@ -282,10 +279,9 @@ export const CalendarMonth = ({
282279
.map(({ date }) => date)[0];
283280
if (toFocus) {
284281
setFocusedDate(toFocus);
285-
setHoveredDate(toFocus);
286282
}
287283
}
288-
const isHoveredDateValid = isValidated(hoveredDate);
284+
const isHoveredDateValid = hoveredDate && isValidated(hoveredDate);
289285
const monthFormatted = monthFormat(focusedDate);
290286

291287
const calendarToRender = (
@@ -331,7 +327,6 @@ export const CalendarMonth = ({
331327
onSelectToggle(false);
332328
const newDate = changeMonth(Number(monthNum as string));
333329
setFocusedDate(newDate);
334-
setHoveredDate(newDate);
335330
setShouldFocus(false);
336331
onMonthChange(ev, newDate);
337332
}, 0);
@@ -369,7 +364,7 @@ export const CalendarMonth = ({
369364
</Button>
370365
</div>
371366
</div>
372-
<table className={styles.calendarMonthCalendar}>
367+
<table className={styles.calendarMonthCalendar} onMouseLeave={() => setHoveredDate(undefined)}>
373368
<thead className={styles.calendarMonthDays}>
374369
<tr>
375370
{calendar[0].map(({ date }, index) => (
@@ -392,16 +387,21 @@ export const CalendarMonth = ({
392387
const isRangeStart = isValidDate(rangeStart) && isSameDate(date, rangeStart);
393388
let isInRange = false;
394389
let isRangeEnd = false;
395-
if (isValidDate(rangeStart) && isValidDate(dateProp)) {
396-
isInRange = date > rangeStart && date < dateProp;
397-
isRangeEnd = isSameDate(date, dateProp);
398-
} else if (isValidDate(rangeStart) && isHoveredDateValid) {
399-
if (hoveredDate > rangeStart || isSameDate(hoveredDate, rangeStart)) {
400-
isInRange = date > rangeStart && date < hoveredDate;
401-
isRangeEnd = isSameDate(date, hoveredDate);
390+
if (isValidDate(rangeStart)) {
391+
let rangeEndDate: Date;
392+
393+
if (isValidDate(dateProp)) {
394+
rangeEndDate = dateProp;
395+
}
396+
if (isHoveredDateValid && (!isValidDate(dateProp) || hoveredDate > dateProp)) {
397+
rangeEndDate = hoveredDate;
398+
}
399+
400+
if (rangeEndDate) {
401+
isInRange = date >= rangeStart && date <= rangeEndDate;
402+
isRangeEnd = isSameDate(date, rangeEndDate);
402403
}
403-
// Don't handle focused dates before start dates for now.
404-
// Core would likely need new styles
404+
// Core would likely need new styles for "is selected but disabled"
405405
}
406406

407407
return (
@@ -412,8 +412,8 @@ export const CalendarMonth = ({
412412
isAdjacentMonth && styles.modifiers.adjacentMonth,
413413
isToday && styles.modifiers.current,
414414
(isSelected || isRangeStart) && styles.modifiers.selected,
415-
!isValid && styles.modifiers.disabled,
416-
(isInRange || isRangeStart || isRangeEnd) && styles.modifiers.inRange,
415+
!isValid && !(isInRange || isRangeStart || isRangeEnd || isSelected) && styles.modifiers.disabled,
416+
isInRange && styles.modifiers.inRange,
417417
isRangeStart && styles.modifiers.startRange,
418418
isRangeEnd && styles.modifiers.endRange
419419
)}

packages/react-core/src/components/DatePicker/DatePicker.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,30 @@ const DatePickerBase = (
231231
const createFocusSelectorString = (modifierClass: string) =>
232232
`.${calendarMonthStyles.calendarMonthDatesCell}.${modifierClass} .${calendarMonthStyles.calendarMonthDate}`;
233233
const focusSelectorForSelectedDate = createFocusSelectorString(calendarMonthStyles.modifiers.selected);
234+
const focusSelectorForSelectedEndRangeDate = createFocusSelectorString(
235+
`${calendarMonthStyles.modifiers.selected}.${calendarMonthStyles.modifiers.endRange}`
236+
);
234237
const focusSelectorForUnselectedDate = createFocusSelectorString(calendarMonthStyles.modifiers.current);
235238

239+
/**
240+
* Returns a CSS selector for a date button element which will receive initial focus after opening calendar popover.
241+
* In case of a range picker it returns the end date, if it is selected, start date otherwise.
242+
* In case of a normal datepicker it returns the selected date, if present, today otherwise.
243+
*/
244+
const getElementSelectorToFocus = () => {
245+
if (isValidDate(valueDate) && isValidDate(rangeStart)) {
246+
return focusSelectorForSelectedEndRangeDate;
247+
}
248+
if (isValidDate(valueDate) || isValidDate(rangeStart)) {
249+
return focusSelectorForSelectedDate;
250+
}
251+
return focusSelectorForUnselectedDate;
252+
};
253+
236254
return (
237255
<div className={css(styles.datePicker, className)} ref={datePickerWrapperRef} style={style} {...props}>
238256
<Popover
239-
elementToFocus={isValidDate(valueDate) ? focusSelectorForSelectedDate : focusSelectorForUnselectedDate}
257+
elementToFocus={getElementSelectorToFocus()}
240258
position="bottom"
241259
bodyContent={
242260
<CalendarMonth

0 commit comments

Comments
 (0)