[FIX] web: fix datetime picker disabling incorrect periods

Previously, if you specified a min or max date for the datetime picker
and zoomed out to the month/year/decade view, it would disable some
months/year/decade incorrectly, this was caused by the code checking if
the beginning of the period was in the min-max range instead of checking
if the period had any overlap with the min-max range.

This commit fixes that by checking for overlaps instead.

closes odoo/odoo#133911

X-original-commit: 7cb61dca99be4b0a1c35f0ec762c1bda318ec043
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Samuel Degueldre
2023-09-01 15:09:57 +00:00
parent 4d7c669b73
commit 8faef71242
3 changed files with 339 additions and 9 deletions
@@ -180,11 +180,12 @@ const PRECISION_LEVELS = new Map()
// Generate all days of the week
for (let d = 0; d < 7; d++) {
const day = startOfNextWeek.plus({ day: d });
const range = [day, day.endOf("day")];
const dayItem = toDateItem({
isOutOfRange: !isInRange(day, monthRange),
isInvalid: !isInRange(day, [minDate, maxDate]),
isInvalid: !isInRange(range, [minDate, maxDate]),
label: "day",
range: [day, day.endOf("day")],
range,
});
weekDayItems.push(dayItem);
if (d === 6) {
@@ -222,10 +223,11 @@ const PRECISION_LEVELS = new Map()
const startOfYear = date.startOf("year");
return numberRange(0, 12).map((i) => {
const startOfMonth = startOfYear.plus({ month: i });
const range = [startOfMonth, startOfMonth.endOf("month")];
return toDateItem({
isInvalid: !isInRange(startOfMonth, [minDate, maxDate]),
isInvalid: !isInRange(range, [minDate, maxDate]),
label: "monthShort",
range: [startOfMonth, startOfMonth.endOf("month")],
range,
});
});
},
@@ -240,11 +242,12 @@ const PRECISION_LEVELS = new Map()
const startOfDecade = date.startOf("year").set({ year: getStartOfDecade(date) });
return numberRange(-GRID_MARGIN, GRID_COUNT + GRID_MARGIN).map((i) => {
const startOfYear = startOfDecade.plus({ year: i });
const range = [startOfYear, startOfYear.endOf("year")];
return toDateItem({
isOutOfRange: i < 0 || i >= GRID_COUNT,
isInvalid: !isInRange(startOfYear, [minDate, maxDate]),
isInvalid: !isInRange(range, [minDate, maxDate]),
label: "year",
range: [startOfYear, startOfYear.endOf("year")],
range,
});
});
},
@@ -259,11 +262,12 @@ const PRECISION_LEVELS = new Map()
const startOfCentury = date.startOf("year").set({ year: getStartOfCentury(date) });
return numberRange(-GRID_MARGIN, GRID_COUNT + GRID_MARGIN).map((i) => {
const startOfDecade = startOfCentury.plus({ year: i * 10 });
const range = [startOfDecade, startOfDecade.plus({ year: 10, millisecond: -1 })];
return toDateItem({
label: "year",
isOutOfRange: i < 0 || i >= GRID_COUNT,
isInvalid: !isInRange(startOfDecade, [minDate, maxDate]),
range: [startOfDecade, startOfDecade.plus({ year: 10, millisecond: -1 })],
isInvalid: !isInRange(range, [minDate, maxDate]),
range,
});
});
},
@@ -403,6 +407,7 @@ export class DateTimePicker extends Component {
this.adjustFocus(this.values, props.focusedDateIndex);
this.handle12HourSystem();
this.state.timeValues = this.state.timeValues.map((timeValue) => timeValue.map(String));
}
onWillRender() {
@@ -120,7 +120,7 @@
class="text-center"
t-att-value="unit[0]"
t-esc="unit[1]"
t-att-selected="timeValue[unitIndex] === unit[0]"
t-att-selected="timeValue[unitIndex] === unit[0].toString()"
/>
</t>
</select>
@@ -102,6 +102,331 @@ QUnit.module("Components", ({ beforeEach }) => {
);
});
QUnit.test("minDate: correct days/month/year/decades are disabled", async (assert) => {
const fixture = getFixture();
await mountPicker({
minDate: DateTime.fromISO("2023-04-20T00:00:00.000"),
});
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, -1],
[-2, -3, -4, -5, -6, -7, -8],
[-9, -10, -11, -12, -13, -14, -15],
[-16, -17, -18, -19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, 29],
[30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
const [hourSelect, minuteSelect] = getTimePickers().at(0);
assert.deepEqual(getTexts(hourSelect, "option"), range(24, String));
assert.deepEqual(
getTexts(minuteSelect, "option"),
range(12, (i) => pad2(i * 5))
);
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2023");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["Jan", "Feb", "Mar"],
"correct months are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"correct months are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "Apr");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2019 - 2030");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["2019", "2020", "2021", "2022"],
"correct years are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["2023", "2024", "2025", "2026", "2027", "2028", "2029", "2030"],
"correct years are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2023");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "1990 - 2100");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["1990", "2000", "2010"],
"correct decades are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["2020", "2030", "2040", "2050", "2060", "2070", "2080", "2090", "2100"],
"correct decades are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2020");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, -1],
[-2, -3, -4, -5, -6, -7, -8],
[-9, -10, -11, -12, -13, -14, -15],
[-16, -17, -18, -19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, 29],
[30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
});
QUnit.test("maxDate: correct days/month/year/decades are disabled", async (assert) => {
const fixture = getFixture();
await mountPicker({
maxDate: DateTime.fromISO("2023-04-28T00:00:00.000"),
});
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, 1],
[2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, -29],
[-30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
const [hourSelect, minuteSelect] = getTimePickers().at(0);
assert.deepEqual(getTexts(hourSelect, "option"), range(24, String));
assert.deepEqual(
getTexts(minuteSelect, "option"),
range(12, (i) => pad2(i * 5))
);
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2023");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"correct months are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["Jan", "Feb", "Mar", "Apr"],
"correct months are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "Apr");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2019 - 2030");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["2024", "2025", "2026", "2027", "2028", "2029", "2030"],
"correct years are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["2019", "2020", "2021", "2022", "2023"],
"correct years are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2023");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "1990 - 2100");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["2030", "2040", "2050", "2060", "2070", "2080", "2090", "2100"],
"correct decades are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["1990", "2000", "2010", "2020"],
"correct decades are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2020");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, 1],
[2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, -29],
[-30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
});
QUnit.test("min+max date: correct days/month/year/decades are disabled", async (assert) => {
const fixture = getFixture();
await mountPicker({
minDate: DateTime.fromISO("2023-04-20T00:00:00.000"),
maxDate: DateTime.fromISO("2023-04-28T00:00:00.000"),
});
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, -1],
[-2, -3, -4, -5, -6, -7, -8],
[-9, -10, -11, -12, -13, -14, -15],
[-16, -17, -18, -19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, -29],
[-30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
const [hourSelect, minuteSelect] = getTimePickers().at(0);
assert.deepEqual(getTexts(hourSelect, "option"), range(24, String));
assert.deepEqual(
getTexts(minuteSelect, "option"),
range(12, (i) => pad2(i * 5))
);
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2023");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
["Jan", "Feb", "Mar", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"correct months are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["Apr"],
"correct months are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "Apr");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "2019 - 2030");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
[
"2019",
"2020",
"2021",
"2022",
"2024",
"2025",
"2026",
"2027",
"2028",
"2029",
"2030",
],
"correct years are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["2023"],
"correct years are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2023");
await click(fixture, ".o_zoom_out");
assert.equal(fixture.querySelector(".o_datetime_picker_header").textContent, "1990 - 2100");
assert.deepEqual(
getTexts(".o_date_item_cell[disabled]"),
[
"1990",
"2000",
"2010",
"2030",
"2040",
"2050",
"2060",
"2070",
"2080",
"2090",
"2100",
],
"correct decades are disabled"
);
assert.deepEqual(
getTexts(".o_date_item_cell:not([disabled])"),
["2020"],
"correct decades are enabled"
);
assert.equal($(fixture).find(".o_date_item_cell.o_today").text(), "2020");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
await click(fixture, ".o_today");
assertDateTimePicker({
title: "April 2023",
date: [
{
cells: [
[-26, -27, -28, -29, -30, -31, -1],
[-2, -3, -4, -5, -6, -7, -8],
[-9, -10, -11, -12, -13, -14, -15],
[-16, -17, -18, -19, 20, 21, 22],
[23, 24, "25", 26, 27, 28, -29],
[-30, -1, -2, -3, -4, -5, -6],
],
daysOfWeek: ["#", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
weekNumbers: [13, 14, 15, 16, 17, 18],
},
],
time: [[13, 0]],
});
});
QUnit.test("twelve-hour clock", async (assert) => {
useTwelveHourClockFormat();