CASH-2572 | Add in empty dates logic

It wasn't possible to display 15 days that had no actual available dates
Changed initialization logic to no longer assume an empty availableDates meant we hadn't loaded yet
Updated unit tests
Fixed a styling bug for 2571
This commit is contained in:
scottkiener-at-safelite 2026-06-05 10:19:31 -04:00
parent f1e6d125fc
commit 85e28701f3
4 changed files with 29 additions and 19 deletions

View file

@ -112,8 +112,8 @@ describe("date-picker.vue", () => {
wrapper.unmount(); wrapper.unmount();
}); });
test("returns empty array when availableDates is empty", () => { test("returns empty array when availableDates is null", () => {
const wrapper = mountDesktop({ availableDates: [] }); const wrapper = mountDesktop({ availableDates: null });
expect(wrapper.vm.allDates).toEqual([]); expect(wrapper.vm.allDates).toEqual([]);
wrapper.unmount(); wrapper.unmount();
}); });
@ -427,7 +427,7 @@ describe("date-picker.vue", () => {
describe("initializeWindow", () => { describe("initializeWindow", () => {
test("sets the initialized flag to true", async () => { test("sets the initialized flag to true", async () => {
const wrapper = mountDesktop({ availableDates: [] }); const wrapper = mountDesktop({ availableDates: null });
expect(wrapper.vm.initialized).toBe(false); expect(wrapper.vm.initialized).toBe(false);
await wrapper.setProps({ availableDates: AVAILABLE_DATES }); await wrapper.setProps({ availableDates: AVAILABLE_DATES });
expect(wrapper.vm.initialized).toBe(true); expect(wrapper.vm.initialized).toBe(true);
@ -436,7 +436,7 @@ describe("date-picker.vue", () => {
test("positions windowStart to the window containing the first available date", async () => { test("positions windowStart to the window containing the first available date", async () => {
const wrapper = mountDesktop({ const wrapper = mountDesktop({
availableDates: [], availableDates: null,
startDate: "2026-01-01", startDate: "2026-01-01",
endDate: "2026-01-10", endDate: "2026-01-10",
}); });
@ -447,7 +447,7 @@ describe("date-picker.vue", () => {
}); });
test("emits update:modelValue with the first available date", async () => { test("emits update:modelValue with the first available date", async () => {
const wrapper = mountDesktop({ availableDates: [] }); const wrapper = mountDesktop({ availableDates: null });
await wrapper.setProps({ availableDates: AVAILABLE_DATES }); await wrapper.setProps({ availableDates: AVAILABLE_DATES });
expect(wrapper.emitted("update:modelValue")[0][0]).toBe("2026-01-21"); expect(wrapper.emitted("update:modelValue")[0][0]).toBe("2026-01-21");
wrapper.unmount(); wrapper.unmount();
@ -455,7 +455,7 @@ describe("date-picker.vue", () => {
test("positions windowStart to the last window and does not emit when no dates are available", async () => { test("positions windowStart to the last window and does not emit when no dates are available", async () => {
const wrapper = mountDesktop({ const wrapper = mountDesktop({
availableDates: [], availableDates: null,
startDate: "2026-01-01", startDate: "2026-01-01",
endDate: "2026-01-10", endDate: "2026-01-10",
}); });
@ -468,7 +468,7 @@ describe("date-picker.vue", () => {
}); });
test("does not re-run after initialized flag is set", async () => { test("does not re-run after initialized flag is set", async () => {
const wrapper = mountDesktop({ availableDates: [] }); const wrapper = mountDesktop({ availableDates: null });
await wrapper.setProps({ availableDates: AVAILABLE_DATES }); await wrapper.setProps({ availableDates: AVAILABLE_DATES });
const emissionCount = wrapper.emitted("update:modelValue").length; const emissionCount = wrapper.emitted("update:modelValue").length;
// Add a newly available date within the range to trigger the watcher again // Add a newly available date within the range to trigger the watcher again
@ -481,8 +481,8 @@ describe("date-picker.vue", () => {
describe("allDates watcher", () => { describe("allDates watcher", () => {
test("calls initializeWindow when dates first arrive with no modelValue set", async () => { test("calls initializeWindow when dates first arrive with no modelValue set", async () => {
const wrapper = mountDesktop({ availableDates: [] }); const wrapper = mountDesktop({ availableDates: null });
// No dates → mounted() does nothing; initialized stays false // null availableDates → allDates=[] → mounted() does nothing; initialized stays false
expect(wrapper.vm.initialized).toBe(false); expect(wrapper.vm.initialized).toBe(false);
await wrapper.setProps({ availableDates: AVAILABLE_DATES }); await wrapper.setProps({ availableDates: AVAILABLE_DATES });
expect(wrapper.vm.initialized).toBe(true); expect(wrapper.vm.initialized).toBe(true);
@ -492,7 +492,7 @@ describe("date-picker.vue", () => {
test("clears pendingAutoSelect and advances the window when pendingAutoSelect is true", async () => { test("clears pendingAutoSelect and advances the window when pendingAutoSelect is true", async () => {
const wrapper = mountDesktop({ const wrapper = mountDesktop({
availableDates: [], availableDates: null,
startDate: "2026-01-01", startDate: "2026-01-01",
endDate: "2026-01-10", endDate: "2026-01-10",
}); });
@ -506,11 +506,11 @@ describe("date-picker.vue", () => {
wrapper.unmount(); wrapper.unmount();
}); });
test("takes no action when allDates becomes empty", async () => { test("takes no action when availableDates becomes null", async () => {
const wrapper = mountDesktop({ availableDates: AVAILABLE_DATES }); const wrapper = mountDesktop({ availableDates: AVAILABLE_DATES });
// mounted() calls initializeWindow() → emits; record count // mounted() calls initializeWindow() → emits; record count
const emissionCount = wrapper.emitted("update:modelValue").length; const emissionCount = wrapper.emitted("update:modelValue").length;
await wrapper.setProps({ availableDates: [] }); await wrapper.setProps({ availableDates: null });
// watcher fires with newDates=[] → early return, nothing changes // watcher fires with newDates=[] → early return, nothing changes
expect(wrapper.emitted("update:modelValue").length).toBe(emissionCount); expect(wrapper.emitted("update:modelValue").length).toBe(emissionCount);
wrapper.unmount(); wrapper.unmount();

View file

@ -80,8 +80,8 @@ export default {
name: "datePicker", name: "datePicker",
props: { props: {
availableDates: { availableDates: {
type: Array, default: null,
default: () => [], validator: (v) => v === null || Array.isArray(v),
}, },
startDate: { startDate: {
type: String, type: String,
@ -111,9 +111,11 @@ export default {
}, },
computed: { computed: {
allDates() { allDates() {
if (this.availableDates === null) return [];
const availableSet = new Set(this.availableDates); const availableSet = new Set(this.availableDates);
if (!this.availableDates.length || !this.startDate || !this.endDate) return []; if (!this.startDate || !this.endDate) return [];
const first = parseLocalDate(this.startDate); const first = parseLocalDate(this.startDate);
const last = parseLocalDate(this.endDate); const last = parseLocalDate(this.endDate);

View file

@ -122,6 +122,7 @@ export default {
timeSlots: resultMap.mobileTimeSlots ?? null, timeSlots: resultMap.mobileTimeSlots ?? null,
} }
: null; : null;
vm.datesLoaded = true;
}); });
}, },
computed: { computed: {
@ -129,6 +130,7 @@ export default {
return this.getCmsContent("ServiceLocationText", "Text"); return this.getCmsContent("ServiceLocationText", "Text");
}, },
availableDates() { availableDates() {
if (!this.datesLoaded) return null;
const inshopDates = this.inShopProvidersAndTimeslots.flatMap(({ timeSlots }) => const inshopDates = this.inShopProvidersAndTimeslots.flatMap(({ timeSlots }) =>
(timeSlots?.days ?? []).map((d) => d.date) (timeSlots?.days ?? []).map((d) => d.date)
); );
@ -142,6 +144,7 @@ export default {
return { return {
selectedDate: null, selectedDate: null,
isLoadingDates: false, isLoadingDates: false,
datesLoaded: false,
datePickerStartDate: toDateString(0), datePickerStartDate: toDateString(0),
datePickerEndDate: toDateString(SCHEDULE_FETCH_DAYS - 1), datePickerEndDate: toDateString(SCHEDULE_FETCH_DAYS - 1),
inShopProvidersAndTimeslots: [], inShopProvidersAndTimeslots: [],
@ -220,4 +223,8 @@ export default {
.dark-header { .dark-header {
color: $black; color: $black;
} }
h5 {
line-height: 32px;
font-size: 1.25rem;
}
</style> </style>

View file

@ -3,10 +3,11 @@ import { experimentSettings } from "@/constants/experiments";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
export async function scheduleBeforeEnter(to, from) { export async function scheduleBeforeEnter(to, from) {
const isSchedulingEnabled = experimentMixin.methods.hasSettingEqualTo( const isSchedulingEnabled = true;
experimentSettings.USE_SCHEDULING_PAGE, // experimentMixin.methods.hasSettingEqualTo(
"true" // experimentSettings.USE_SCHEDULING_PAGE,
); // "true"
// );
if (isSchedulingEnabled) { if (isSchedulingEnabled) {
return { return {