CASH-2572 | Add in auto-select functionality
This commit is contained in:
parent
5c10137df1
commit
8a472aa335
2 changed files with 269 additions and 3 deletions
|
|
@ -133,9 +133,12 @@ describe("date-picker.vue", () => {
|
|||
|
||||
test("does not emit when a disabled card is clicked", async () => {
|
||||
const wrapper = mountDesktop();
|
||||
const emissionCountBeforeClick = (wrapper.emitted("update:modelValue") ?? []).length;
|
||||
const disabledCard = wrapper.find(".date-picker__day-card--disabled");
|
||||
await disabledCard.trigger("click");
|
||||
expect(wrapper.emitted("update:modelValue")).toBeFalsy();
|
||||
expect((wrapper.emitted("update:modelValue") ?? []).length).toBe(
|
||||
emissionCountBeforeClick
|
||||
);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
|
|
@ -292,4 +295,225 @@ describe("date-picker.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("mounted initialization", () => {
|
||||
test("emits first available date when mounted with no modelValue and dates present", () => {
|
||||
const wrapper = mountDesktop();
|
||||
expect(wrapper.emitted("update:modelValue")).toBeTruthy();
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toBe("2026-01-21");
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("goBack auto-selection", () => {
|
||||
test("emits update:modelValue with first available date in the new window", async () => {
|
||||
// modelValue set → mounted() uses scroll path, no emission on mount
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["2026-01-01", "2026-01-07"],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
modelValue: "2026-01-07",
|
||||
});
|
||||
// "2026-01-07" is index 6 → mounted sets windowStart = Math.floor(6/5)*5 = 5
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
expect(wrapper.emitted("update:modelValue")).toBeFalsy();
|
||||
await wrapper.vm.goBack();
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions).toBeTruthy();
|
||||
expect(emissions[emissions.length - 1][0]).toBe("2026-01-01");
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("goForward pending flow", () => {
|
||||
test("emits requestMoreDates when at the last window", async () => {
|
||||
// 5 dates, desktop window size 5 → already at end on mount
|
||||
const wrapper = mountDesktop({ modelValue: "2026-01-21" });
|
||||
await wrapper.vm.goForward();
|
||||
expect(wrapper.emitted("requestMoreDates")).toBeTruthy();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("sets pendingAutoSelect when at the last window", async () => {
|
||||
const wrapper = mountDesktop({ modelValue: "2026-01-21" });
|
||||
await wrapper.vm.goForward();
|
||||
expect(wrapper.vm.pendingAutoSelect).toBe(true);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("emits update:modelValue with first available date in the new window when not at the end", async () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["2026-01-01", "2026-01-07"],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
modelValue: "2026-01-01",
|
||||
});
|
||||
// windowStart = 0, not at end (0 + 5 < 10), no emission on mount
|
||||
await wrapper.vm.goForward();
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions).toBeTruthy();
|
||||
expect(emissions[emissions.length - 1][0]).toBe("2026-01-07");
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("advanceWindowAndAutoSelect", () => {
|
||||
test("advances windowStart by windowSize", () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["2026-01-01", "2026-01-07"],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
modelValue: "2026-01-01",
|
||||
});
|
||||
expect(wrapper.vm.windowStart).toBe(0);
|
||||
wrapper.vm.advanceWindowAndAutoSelect();
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("caps windowStart at maxStart when already near the end", () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["2026-01-01", "2026-01-07"],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
modelValue: "2026-01-07",
|
||||
});
|
||||
// mounted() sets windowStart = 5 (index 6, Math.floor(6/5)*5)
|
||||
// maxStart = Math.max(0, 10 - 5) = 5 → min(5, 5+5) = 5
|
||||
wrapper.vm.advanceWindowAndAutoSelect();
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("emits update:modelValue with first available date in the advanced window", () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["2026-01-01", "2026-01-07"],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
modelValue: "2026-01-01",
|
||||
});
|
||||
wrapper.vm.advanceWindowAndAutoSelect();
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions).toBeTruthy();
|
||||
expect(emissions[emissions.length - 1][0]).toBe("2026-01-07");
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("autoSelectFirstAvailable", () => {
|
||||
test("emits update:modelValue with the first available date in the visible window", () => {
|
||||
const wrapper = mountDesktop({ modelValue: "2026-01-21" });
|
||||
wrapper.vm.autoSelectFirstAvailable();
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions[emissions.length - 1][0]).toBe("2026-01-21");
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("emits null when no available dates are visible", () => {
|
||||
// availableDates outside the date range → all visible dates are unavailable
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: ["9999-12-31"],
|
||||
startDate: "2026-01-21",
|
||||
endDate: "2026-01-25",
|
||||
modelValue: "2026-01-21",
|
||||
});
|
||||
// modelValue set → mounted uses scroll path, no emission on mount
|
||||
wrapper.vm.autoSelectFirstAvailable();
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions[emissions.length - 1][0]).toBeNull();
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("initializeWindow", () => {
|
||||
test("sets the initialized flag to true", async () => {
|
||||
const wrapper = mountDesktop({ availableDates: [] });
|
||||
expect(wrapper.vm.initialized).toBe(false);
|
||||
await wrapper.setProps({ availableDates: AVAILABLE_DATES });
|
||||
expect(wrapper.vm.initialized).toBe(true);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("positions windowStart to the window containing the first available date", async () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: [],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
});
|
||||
// "2026-01-07" is index 6 → Math.floor(6/5)*5 = 5
|
||||
await wrapper.setProps({ availableDates: ["2026-01-07"] });
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("emits update:modelValue with the first available date", async () => {
|
||||
const wrapper = mountDesktop({ availableDates: [] });
|
||||
await wrapper.setProps({ availableDates: AVAILABLE_DATES });
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toBe("2026-01-21");
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("positions windowStart to the last window and does not emit when no dates are available", async () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: [],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
});
|
||||
// Dates are present but none fall in the Jan 1–10 range
|
||||
await wrapper.setProps({ availableDates: ["9999-12-31"] });
|
||||
// Math.max(0, 10 - 5) = 5
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
expect(wrapper.emitted("update:modelValue")).toBeFalsy();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("does not re-run after initialized flag is set", async () => {
|
||||
const wrapper = mountDesktop({ availableDates: [] });
|
||||
await wrapper.setProps({ availableDates: AVAILABLE_DATES });
|
||||
const emissionCount = wrapper.emitted("update:modelValue").length;
|
||||
// Add a newly available date within the range to trigger the watcher again
|
||||
await wrapper.setProps({ availableDates: [...AVAILABLE_DATES, "2026-01-24"] });
|
||||
// initialized=true and pendingAutoSelect=false → watcher takes no action
|
||||
expect(wrapper.emitted("update:modelValue").length).toBe(emissionCount);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("allDates watcher", () => {
|
||||
test("calls initializeWindow when dates first arrive with no modelValue set", async () => {
|
||||
const wrapper = mountDesktop({ availableDates: [] });
|
||||
// No dates → mounted() does nothing; initialized stays false
|
||||
expect(wrapper.vm.initialized).toBe(false);
|
||||
await wrapper.setProps({ availableDates: AVAILABLE_DATES });
|
||||
expect(wrapper.vm.initialized).toBe(true);
|
||||
expect(wrapper.emitted("update:modelValue")[0][0]).toBe("2026-01-21");
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("clears pendingAutoSelect and advances the window when pendingAutoSelect is true", async () => {
|
||||
const wrapper = mountDesktop({
|
||||
availableDates: [],
|
||||
startDate: "2026-01-01",
|
||||
endDate: "2026-01-10",
|
||||
});
|
||||
wrapper.vm.initialized = true;
|
||||
wrapper.vm.pendingAutoSelect = true;
|
||||
await wrapper.setProps({ availableDates: ["2026-01-01", "2026-01-07"] });
|
||||
expect(wrapper.vm.pendingAutoSelect).toBe(false);
|
||||
expect(wrapper.vm.windowStart).toBe(5);
|
||||
const emissions = wrapper.emitted("update:modelValue");
|
||||
expect(emissions[emissions.length - 1][0]).toBe("2026-01-07");
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("takes no action when allDates becomes empty", async () => {
|
||||
const wrapper = mountDesktop({ availableDates: AVAILABLE_DATES });
|
||||
// mounted() calls initializeWindow() → emits; record count
|
||||
const emissionCount = wrapper.emitted("update:modelValue").length;
|
||||
await wrapper.setProps({ availableDates: [] });
|
||||
// watcher fires with newDates=[] → early return, nothing changes
|
||||
expect(wrapper.emitted("update:modelValue").length).toBe(emissionCount);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ export default {
|
|||
window.innerWidth >= DESKTOP_BREAKPOINT_PX
|
||||
? DESKTOP_WINDOW_SIZE
|
||||
: MOBILE_WINDOW_SIZE,
|
||||
pendingAutoSelect: false,
|
||||
initialized: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -157,6 +159,21 @@ export default {
|
|||
return true;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
allDates(newDates) {
|
||||
if (!newDates.length) return;
|
||||
|
||||
if (!this.initialized && !this.modelValue) {
|
||||
this.initializeWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pendingAutoSelect) {
|
||||
this.pendingAutoSelect = false;
|
||||
this.advanceWindowAndAutoSelect();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateWindowSize();
|
||||
window.addEventListener("resize", this.updateWindowSize);
|
||||
|
|
@ -165,6 +182,8 @@ export default {
|
|||
if (index !== -1) {
|
||||
this.windowStart = Math.floor(index / this.windowSize) * this.windowSize;
|
||||
}
|
||||
} else if (!this.modelValue && this.allDates.length) {
|
||||
this.initializeWindow();
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
|
|
@ -179,15 +198,38 @@ export default {
|
|||
},
|
||||
goBack() {
|
||||
this.windowStart = Math.max(0, this.windowStart - this.windowSize);
|
||||
this.autoSelectFirstAvailable();
|
||||
},
|
||||
goForward() {
|
||||
if (this.isLoadingDates) return;
|
||||
const isAtEnd = this.windowStart + this.windowSize >= this.allDates.length;
|
||||
if (isAtEnd) {
|
||||
this.$emit("requestMoreDates");
|
||||
this.pendingAutoSelect = true;
|
||||
} else {
|
||||
const maxStart = Math.max(0, this.allDates.length - this.windowSize);
|
||||
this.windowStart = Math.min(maxStart, this.windowStart + this.windowSize);
|
||||
this.advanceWindowAndAutoSelect();
|
||||
}
|
||||
},
|
||||
advanceWindowAndAutoSelect() {
|
||||
const maxStart = Math.max(0, this.allDates.length - this.windowSize);
|
||||
this.windowStart = Math.min(maxStart, this.windowStart + this.windowSize);
|
||||
this.autoSelectFirstAvailable();
|
||||
},
|
||||
autoSelectFirstAvailable() {
|
||||
const first = this.visibleDates.find((d) => d.isAvailable);
|
||||
this.$emit("update:modelValue", first ? first.value : null);
|
||||
},
|
||||
initializeWindow() {
|
||||
this.initialized = true;
|
||||
if (this.modelValue !== null) return;
|
||||
|
||||
const firstAvailable = this.allDates.find((d) => d.isAvailable);
|
||||
if (firstAvailable) {
|
||||
const index = this.allDates.indexOf(firstAvailable);
|
||||
this.windowStart = Math.floor(index / this.windowSize) * this.windowSize;
|
||||
this.$emit("update:modelValue", firstAvailable.value);
|
||||
} else {
|
||||
this.windowStart = Math.max(0, this.allDates.length - this.windowSize);
|
||||
}
|
||||
},
|
||||
selectDate(date) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue