Enforce outbound↔return week coupling in Schedule filter (TZ §4.1.9.4)
- Tie return calendar minDate to outbound dateTo so earlier days grey out in the picker (Table 16: return cannot start before outbound ends; same week allowed). - Auto-clear the return range when the user moves outbound forward and strands the previously-chosen return in an invalid state. - Clearing outbound via the X button now cascades to the return range. Rewrote two tests that previously asserted the submit-time error path; the new proactive clearing makes that path unreachable for this case, which is closer to the intent of the TZ.
This commit is contained in:
@@ -305,7 +305,7 @@ describe("ScheduleFilter – validation per TZ §4.1.9.4", () => {
|
||||
// §4.1.9.4 return date must be >= outbound dateTo
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
it("4.1.9.4-R: return date before outbound dateTo shows error + blocks submit", () => {
|
||||
it("4.1.9.4-R: return date before outbound dateTo auto-clears on mount (Table 16)", () => {
|
||||
render(
|
||||
<ScheduleFilter
|
||||
initialDeparture="SVO"
|
||||
@@ -317,9 +317,11 @@ describe("ScheduleFilter – validation per TZ §4.1.9.4", () => {
|
||||
initialReturnDateTo="20260610"
|
||||
/>,
|
||||
);
|
||||
fireEvent.submit(screen.getByTestId("search-form"));
|
||||
expect(screen.queryByTestId("schedule-return-before-outbound-error")).toBeTruthy();
|
||||
expect(mockNavigate).not.toHaveBeenCalled();
|
||||
// TZ §4.1.9.4 Table 16: an invalid retFrom < outTo combination is
|
||||
// blanked before the user can even submit, rather than only raising
|
||||
// an error after submit. The return X button is therefore hidden.
|
||||
expect(screen.queryByTestId("schedule-return-date-clear")).toBeNull();
|
||||
expect(screen.queryByTestId("schedule-return-before-outbound-error")).toBeNull();
|
||||
});
|
||||
|
||||
it("4.1.9.4-R: return date equal to outbound dateTo passes validation", () => {
|
||||
@@ -356,7 +358,7 @@ describe("ScheduleFilter – validation per TZ §4.1.9.4", () => {
|
||||
expect(mockNavigate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("4.1.9.4-R: return error clears after user clears the return date range", () => {
|
||||
it("4.1.9.4-R: clearing outbound also clears return (Table 16 cascade)", () => {
|
||||
render(
|
||||
<ScheduleFilter
|
||||
initialDeparture="SVO"
|
||||
@@ -364,17 +366,17 @@ describe("ScheduleFilter – validation per TZ §4.1.9.4", () => {
|
||||
initialDateFrom="20260601"
|
||||
initialDateTo="20260607"
|
||||
initialReturnFlights={true}
|
||||
initialReturnDateFrom="20260603"
|
||||
initialReturnDateTo="20260610"
|
||||
initialReturnDateFrom="20260608"
|
||||
initialReturnDateTo="20260614"
|
||||
/>,
|
||||
);
|
||||
fireEvent.submit(screen.getByTestId("search-form"));
|
||||
expect(screen.queryByTestId("schedule-return-before-outbound-error")).toBeTruthy();
|
||||
// Sanity: both ranges are populated.
|
||||
expect(screen.queryByTestId("schedule-date-clear")).toBeTruthy();
|
||||
expect(screen.queryByTestId("schedule-return-date-clear")).toBeTruthy();
|
||||
|
||||
const clearBtn = screen.queryByTestId("schedule-return-date-clear");
|
||||
if (clearBtn) {
|
||||
fireEvent.click(clearBtn);
|
||||
expect(screen.queryByTestId("schedule-return-before-outbound-error")).toBeNull();
|
||||
}
|
||||
// Clicking the outbound X must clear both ranges.
|
||||
fireEvent.click(screen.getByTestId("schedule-date-clear"));
|
||||
expect(screen.queryByTestId("schedule-date-clear")).toBeNull();
|
||||
expect(screen.queryByTestId("schedule-return-date-clear")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,6 +230,27 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
[submitLockedUntil, nowTs],
|
||||
);
|
||||
|
||||
// TZ §4.1.9.4 Table 16: when the outbound range moves forward such
|
||||
// that the already-chosen return starts before the new outbound
|
||||
// dateTo, blank the return picker and any coupled error so the user
|
||||
// isn't left with an invalid combo. Intentionally does not fire when
|
||||
// outbound is cleared — that's a separate "user actively cleared" flow
|
||||
// handled on the outbound X button (see the clear handler below).
|
||||
useEffect(() => {
|
||||
if (!returnFlights) return;
|
||||
const [, outTo] = dateRange;
|
||||
const [retFrom] = returnDateRange;
|
||||
if (!retFrom || !outTo) return;
|
||||
const retFromDay = new Date(retFrom);
|
||||
retFromDay.setHours(0, 0, 0, 0);
|
||||
const outToDay = new Date(outTo);
|
||||
outToDay.setHours(0, 0, 0, 0);
|
||||
if (retFromDay.getTime() < outToDay.getTime()) {
|
||||
setReturnDateRange([null, null]);
|
||||
setReturnBeforeOutboundError(null);
|
||||
}
|
||||
}, [dateRange, returnFlights, returnDateRange]);
|
||||
|
||||
// Swap the Calendar input's displayed text to "Текущая неделя" per
|
||||
// TZ §4.1.9 Table 14 when the selected range equals Mon-Sun of the
|
||||
// current week. Uses inputRef + useEffect to override PrimeReact's
|
||||
@@ -452,8 +473,13 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
aria-label={t("SHARED.A11Y-CLEAR")}
|
||||
data-testid="schedule-date-clear"
|
||||
onClick={() => {
|
||||
// TZ §4.1.9.4 Table 16: clearing outbound also
|
||||
// clears return (return picker is only meaningful
|
||||
// relative to the outbound range).
|
||||
setDateRange([null, null]);
|
||||
setRangeError(null);
|
||||
setReturnDateRange([null, null]);
|
||||
setReturnBeforeOutboundError(null);
|
||||
}}
|
||||
>
|
||||
×
|
||||
@@ -543,7 +569,11 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
if (returnBeforeOutboundError) setReturnBeforeOutboundError(null);
|
||||
}}
|
||||
selectionMode="range"
|
||||
minDate={scheduleMinDate}
|
||||
// TZ §4.1.9.4: return cannot start before outbound's
|
||||
// dateTo. Tie the return picker's minDate to it so
|
||||
// earlier days grey out in the calendar UI. Same
|
||||
// week (retFrom = outTo) is allowed.
|
||||
minDate={dateRange[1] ?? scheduleMinDate}
|
||||
maxDate={scheduleMaxDate}
|
||||
disabledDates={returnDisabledDates}
|
||||
dateFormat="dd.mm.yy"
|
||||
|
||||
Reference in New Issue
Block a user