[IMP] web: datepicker WOWL
This commit adapts the "old" Owl Datepicker to work with the new Owl web client. The behaviors have been mostly preserved (i.e. working with the bootstrap datepicker) and simply adapted to make use of the new services and registries. The previous datepicker template and tests have been marked as "legacy". Part-of: odoo/odoo#73311
This commit is contained in:
committed by
Aaron Bohy
parent
a19ad0fe24
commit
42cbcad9c8
@@ -0,0 +1,287 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { localization } from "@web/core/l10n/localization";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useAutofocus } from "@web/core/utils/hooks";
|
||||
|
||||
const { Component, hooks } = owl;
|
||||
const { DateTime } = luxon;
|
||||
const { useExternalListener, useRef, useState } = hooks;
|
||||
|
||||
const formatters = registry.category("formatters");
|
||||
const parsers = registry.category("parsers");
|
||||
|
||||
let datePickerId = 0;
|
||||
|
||||
/**
|
||||
* @param {DateTime} date
|
||||
* @returns {moment}
|
||||
*/
|
||||
const luxonDateToMomentDate = (date) => {
|
||||
return window.moment(String(date));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} format
|
||||
* @returns {string}
|
||||
*/
|
||||
const luxonFormatToMomentFormat = (format) => {
|
||||
return format.replace(/[dy]/g, (x) => x.toUpperCase());
|
||||
};
|
||||
|
||||
/**
|
||||
* Date picker
|
||||
*
|
||||
* This component exposes the API of the tempusdominus datepicker library.
|
||||
* As such, its template is a simple input that will open the TD datepicker
|
||||
* when clicked on. The component will also synchronize any user-input value
|
||||
* with the library widget and vice-versa.
|
||||
*
|
||||
* Note that all props given to this component will be passed as arguments to
|
||||
* instantiate the picker widget. Also any luxon date is automatically
|
||||
* stringified since tempusdominus only works with moment objects.
|
||||
* @extends Component
|
||||
*/
|
||||
export class DatePicker extends Component {
|
||||
setup() {
|
||||
this.inputRef = useRef("input");
|
||||
this.state = useState({ warning: false });
|
||||
|
||||
this.datePickerId = `o_datepicker_${datePickerId++}`;
|
||||
|
||||
this.initFormat();
|
||||
this.setDate(this.props);
|
||||
|
||||
useAutofocus();
|
||||
useExternalListener(window, "scroll", this.onWindowScroll);
|
||||
}
|
||||
|
||||
mounted() {
|
||||
window.$(this.el).on("show.datetimepicker", () => this.inputRef.el.select());
|
||||
window.$(this.el).on("hide.datetimepicker", () => this.onDateChange());
|
||||
window.$(this.el).on("error.datetimepicker", () => false); // Ignores datepicker errors
|
||||
|
||||
this.bootstrapDateTimePicker(this.props);
|
||||
this.updateInput();
|
||||
}
|
||||
|
||||
willUpdateProps(nextProps) {
|
||||
const pickerParams = {};
|
||||
for (const prop in nextProps) {
|
||||
if (this.props[prop] !== nextProps[prop]) {
|
||||
pickerParams[prop] = nextProps[prop];
|
||||
if (prop === "date") {
|
||||
this.setDate(nextProps);
|
||||
this.updateInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.bootstrapDateTimePicker(pickerParams);
|
||||
}
|
||||
|
||||
willUnmount() {
|
||||
window.$(this.el).off(); // Removes all jQuery events
|
||||
|
||||
this.bootstrapDateTimePicker("destroy");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Getters
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
get options() {
|
||||
return {
|
||||
// Fallback to default localization format in `core/l10n/dates.js`.
|
||||
format: this.props.format,
|
||||
locale: this.props.locale || this.date.locale,
|
||||
timezone: this.isLocal,
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Protected
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialises formatting and parsing parameters
|
||||
*/
|
||||
initFormat() {
|
||||
this.defaultFormat = localization.dateFormat;
|
||||
this.format = formatters.get("date");
|
||||
this.parse = parsers.get("date");
|
||||
this.isLocal = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current date value. If a locale is provided, the given date
|
||||
* will first be set in that locale.
|
||||
* @param {object} params
|
||||
* @param {DateTime} params.date
|
||||
* @param {string} [params.locale]
|
||||
*/
|
||||
setDate({ date, locale }) {
|
||||
this.date = locale ? date.setLocale(locale) : date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the input element with the current formatted date value.
|
||||
*/
|
||||
updateInput() {
|
||||
try {
|
||||
this.inputRef.el.value = this.format(this.date, this.options);
|
||||
} catch (err) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Bootstrap datepicker
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles bootstrap datetimepicker calls.
|
||||
* @param {string | Object} commandOrParams
|
||||
*/
|
||||
bootstrapDateTimePicker(commandOrParams) {
|
||||
if (typeof commandOrParams === "object") {
|
||||
const format = luxonFormatToMomentFormat(this.props.format || this.defaultFormat);
|
||||
const params = { ...commandOrParams, format };
|
||||
for (const prop in params) {
|
||||
if (params[prop] instanceof DateTime) {
|
||||
const luxonDate = params[prop];
|
||||
const momentDate = luxonDateToMomentDate(luxonDate);
|
||||
if (momentDate.locale() !== luxonDate.locale) {
|
||||
momentDate.locale(params.locale);
|
||||
}
|
||||
params[prop] = momentDate;
|
||||
}
|
||||
}
|
||||
commandOrParams = params;
|
||||
}
|
||||
window.$(this.el).datetimepicker(commandOrParams);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Handlers
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
onInputClick() {
|
||||
this.bootstrapDateTimePicker("toggle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called either when the input value has changed or when the boostrap
|
||||
* datepicker is closed. The actual "datetime-changed" emitted by the
|
||||
* component is only triggered if the date value has changed.
|
||||
*/
|
||||
onDateChange() {
|
||||
try {
|
||||
const date = this.parse(this.inputRef.el.value, this.options);
|
||||
if (!date.equals(this.props.date)) {
|
||||
this.state.warning = date > DateTime.local();
|
||||
this.trigger("datetime-changed", { date });
|
||||
}
|
||||
} catch (err) {
|
||||
// Reset to default (= given) date.
|
||||
this.updateInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onWindowScroll(ev) {
|
||||
if (ev.target !== this.inputRef.el) {
|
||||
this.bootstrapDateTimePicker("hide");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DatePicker.defaultProps = {
|
||||
calendarWeeks: true,
|
||||
icons: {
|
||||
clear: "fa fa-delete",
|
||||
close: "fa fa-check primary",
|
||||
date: "fa fa-calendar",
|
||||
down: "fa fa-chevron-down",
|
||||
next: "fa fa-chevron-right",
|
||||
previous: "fa fa-chevron-left",
|
||||
time: "fa fa-clock-o",
|
||||
today: "fa fa-calendar-check-o",
|
||||
up: "fa fa-chevron-up",
|
||||
},
|
||||
maxDate: DateTime.fromObject({ year: 9999, month: 12, day: 31 }),
|
||||
minDate: DateTime.fromObject({ year: 1000 }),
|
||||
useCurrent: false,
|
||||
widgetParent: "body",
|
||||
};
|
||||
DatePicker.props = {
|
||||
// Components props
|
||||
date: DateTime,
|
||||
warn_future: { type: Boolean, optional: true },
|
||||
// Bootstrap datepicker options
|
||||
buttons: {
|
||||
type: Object,
|
||||
shape: {
|
||||
showClear: Boolean,
|
||||
showClose: Boolean,
|
||||
showToday: Boolean,
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
calendarWeeks: Boolean,
|
||||
format: { type: String, optional: true },
|
||||
icons: {
|
||||
type: Object,
|
||||
shape: {
|
||||
clear: String,
|
||||
close: String,
|
||||
date: String,
|
||||
down: String,
|
||||
next: String,
|
||||
previous: String,
|
||||
time: String,
|
||||
today: String,
|
||||
up: String,
|
||||
},
|
||||
},
|
||||
keyBinds: { validate: (kb) => typeof kb === "object" || kb === null, optional: true },
|
||||
locale: { type: String, optional: true },
|
||||
maxDate: DateTime,
|
||||
minDate: DateTime,
|
||||
readonly: { type: Boolean, optional: true },
|
||||
useCurrent: Boolean,
|
||||
widgetParent: String,
|
||||
};
|
||||
DatePicker.template = "web.DatePicker";
|
||||
|
||||
/**
|
||||
* Date/time picker
|
||||
*
|
||||
* Similar to the DatePicker component, adding the handling of more specific
|
||||
* time values: hour-minute-second.
|
||||
*
|
||||
* Once again, refer to the tempusdominus documentation for implementation
|
||||
* details.
|
||||
* @extends DatePicker
|
||||
*/
|
||||
export class DateTimePicker extends DatePicker {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
initFormat() {
|
||||
this.defaultFormat = localization.dateTimeFormat;
|
||||
this.format = formatters.get("datetime");
|
||||
this.parse = parsers.get("datetime");
|
||||
this.isLocal = true;
|
||||
}
|
||||
}
|
||||
|
||||
DateTimePicker.defaultProps = {
|
||||
...DatePicker.defaultProps,
|
||||
buttons: {
|
||||
showClear: false,
|
||||
showClose: true,
|
||||
showToday: false,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
|
||||
<t t-name="web.DatePicker" owl="1">
|
||||
<div class="o_datepicker" aria-atomic="true" t-att-id="datePickerId" data-target-input="nearest">
|
||||
<input type="text"
|
||||
class="o_datepicker_input o_input datetimepicker-input"
|
||||
autofocus=""
|
||||
t-att-name="props.name"
|
||||
t-att-placeholder="props.placeholder"
|
||||
t-attf-data-target="#{{ datePickerId }}"
|
||||
t-att-readonly="props.readonly"
|
||||
t-ref="input"
|
||||
t-on-change="onDateChange"
|
||||
t-on-click="onInputClick"
|
||||
/>
|
||||
<span t-if="props.warn_future and state.warning" class="fa fa-exclamation-triangle text-danger o_tz_warning o_datepicker_warning">
|
||||
<t>This date is on the future. Make sure it is what you expected.</t>
|
||||
</span>
|
||||
<span class="o_datepicker_button" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -221,7 +221,7 @@ odoo.define('web.DatePickerOwl', function (require) {
|
||||
useCurrent: Boolean,
|
||||
widgetParent: String,
|
||||
};
|
||||
DatePicker.template = 'web.DatePicker';
|
||||
DatePicker.template = "web.Legacy.DatePicker";
|
||||
|
||||
/**
|
||||
* Date/time picker
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="web.DatePicker" owl="1">
|
||||
<t t-name="web.Legacy.DatePicker" owl="1">
|
||||
<div class="o_datepicker" aria-atomic="true" t-att-id="datePickerId" data-target-input="nearest">
|
||||
<input type="text" class="o_datepicker_input o_input datetimepicker-input" autofocus=""
|
||||
t-att-name="props.name"
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { DatePicker, DateTimePicker } from "@web/core/datepicker/datepicker";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { registerCleanup } from "../helpers/cleanup";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import { makeFakeLocalizationService } from "../helpers/mock_services";
|
||||
import { click, getFixture, triggerEvent } from "../helpers/utils";
|
||||
|
||||
const { DateTime } = luxon;
|
||||
const { Component, mount, tags } = owl;
|
||||
const { xml } = tags;
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
/**
|
||||
* @param {typeof DatePicker} Picker
|
||||
* @param {{ props: any, onDateChange: () => any }} [params={}]
|
||||
* @returns {Promise<DatePicker>}
|
||||
*/
|
||||
const mountPicker = async (Picker, { props, onDateChange } = {}) => {
|
||||
serviceRegistry
|
||||
.add(
|
||||
"localization",
|
||||
makeFakeLocalizationService({
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateTimeFormat: "dd/MM/yyyy HH:mm:ss",
|
||||
})
|
||||
)
|
||||
.add("ui", uiService);
|
||||
|
||||
class Parent extends Component {}
|
||||
Parent.template = xml/* xml */ `
|
||||
<t t-component="props.Picker" t-props="props.props" t-on-datetime-changed="props.onDateChange" />
|
||||
`;
|
||||
|
||||
const env = await makeTestEnv();
|
||||
const target = getFixture();
|
||||
const parent = await mount(Parent, { env, props: { Picker, props, onDateChange }, target });
|
||||
registerCleanup(() => parent.destroy());
|
||||
return parent;
|
||||
};
|
||||
|
||||
const useFRLocale = () => {
|
||||
if (!window.moment.locales().includes("fr")) {
|
||||
// Mocks the FR locale if not loaded
|
||||
const originalLocale = window.moment.locale();
|
||||
window.moment.defineLocale("fr", {
|
||||
months: "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split(
|
||||
"_"
|
||||
),
|
||||
monthsShort: "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split(
|
||||
"_"
|
||||
),
|
||||
code: "fr",
|
||||
monthsParseExact: true,
|
||||
week: { dow: 1, doy: 4 },
|
||||
});
|
||||
// Moment automatically assigns newly defined locales.
|
||||
window.moment.locale(originalLocale);
|
||||
registerCleanup(() => window.moment.updateLocale("fr", null));
|
||||
}
|
||||
return "fr";
|
||||
};
|
||||
|
||||
QUnit.module("Components", () => {
|
||||
QUnit.module("DatePicker");
|
||||
|
||||
QUnit.test("basic rendering", async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
const picker = await mountPicker(DatePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy", { zone: "utc" }),
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(picker, "input.o_input.o_datepicker_input");
|
||||
assert.containsOnce(picker, "span.o_datepicker_button");
|
||||
assert.containsNone(document.body, "div.bootstrap-datetimepicker-widget");
|
||||
|
||||
const input = picker.el.querySelector("input.o_input.o_datepicker_input");
|
||||
assert.strictEqual(input.value, "09/01/1997", "Value should be the one given");
|
||||
assert.strictEqual(
|
||||
input.dataset.target,
|
||||
`#${picker.el.id}`,
|
||||
"DatePicker id should match its input target"
|
||||
);
|
||||
|
||||
await click(input);
|
||||
|
||||
assert.containsOnce(document.body, "div.bootstrap-datetimepicker-widget .datepicker");
|
||||
assert.containsNone(document.body, "div.bootstrap-datetimepicker-widget .timepicker");
|
||||
assert.strictEqual(
|
||||
document.querySelector(".datepicker .day.active").dataset.day,
|
||||
"01/09/1997",
|
||||
"Datepicker should have set the correct day"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("pick a date", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const picker = await mountPicker(DatePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy", { zone: "utc" }),
|
||||
},
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy"),
|
||||
"08/02/1997",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
await click(input);
|
||||
await click(document.querySelector(".datepicker th.next")); // next month
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
await click(document.querySelectorAll(".datepicker table td")[15]); // previous day
|
||||
|
||||
assert.strictEqual(input.value, "08/02/1997");
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
});
|
||||
|
||||
QUnit.test("pick a date with locale", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const picker = await mountPicker(DatePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy", { zone: "utc" }),
|
||||
format: "dd MMM, yyyy",
|
||||
locale: useFRLocale(),
|
||||
},
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy"),
|
||||
"01/09/1997",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
assert.strictEqual(input.value, "09 janv., 1997");
|
||||
|
||||
await click(input);
|
||||
await click(document.querySelector(".datepicker .picker-switch")); // month picker
|
||||
await click(document.querySelectorAll(".datepicker .month")[8]); // september
|
||||
await click(document.querySelector(".datepicker .day")); // first day
|
||||
|
||||
assert.strictEqual(input.value, "01 sept., 1997");
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
});
|
||||
|
||||
QUnit.test("enter a date value", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const picker = await mountPicker(DatePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy", { zone: "utc" }),
|
||||
},
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy"),
|
||||
"08/02/1997",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
input.value = "08/02/1997";
|
||||
await triggerEvent(picker.el, ".o_datepicker_input", "change");
|
||||
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
|
||||
await click(input);
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector(".datepicker .day.active").dataset.day,
|
||||
"02/08/1997",
|
||||
"Datepicker should have set the correct day"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("Date format is correctly set", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const picker = await mountPicker(DatePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy", { zone: "utc" }),
|
||||
format: "yyyy/MM/dd",
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
assert.strictEqual(input.value, "1997/01/09");
|
||||
|
||||
// Forces an update to assert that the registered format is the correct one
|
||||
await click(input);
|
||||
|
||||
assert.strictEqual(input.value, "1997/01/09");
|
||||
});
|
||||
|
||||
QUnit.module("DateTimePicker");
|
||||
|
||||
QUnit.test("basic rendering", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
const picker = await mountPicker(DateTimePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997 12:30:01", "dd/MM/yyyy HH:mm:ss"),
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsOnce(picker, "input.o_input.o_datepicker_input");
|
||||
assert.containsOnce(picker, "span.o_datepicker_button");
|
||||
assert.containsNone(document.body, "div.bootstrap-datetimepicker-widget");
|
||||
|
||||
const input = picker.el.querySelector("input.o_input.o_datepicker_input");
|
||||
assert.strictEqual(input.value, "09/01/1997 12:30:01", "Value should be the one given");
|
||||
assert.strictEqual(
|
||||
input.dataset.target,
|
||||
`#${picker.el.id}`,
|
||||
"DateTimePicker id should match its input target"
|
||||
);
|
||||
|
||||
await click(input);
|
||||
|
||||
assert.containsOnce(document.body, "div.bootstrap-datetimepicker-widget .datepicker");
|
||||
assert.containsOnce(document.body, "div.bootstrap-datetimepicker-widget .timepicker");
|
||||
assert.strictEqual(
|
||||
document.querySelector(".datepicker .day.active").dataset.day,
|
||||
"01/09/1997",
|
||||
"Datepicker should have set the correct day"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-hour").innerText.trim(),
|
||||
"12",
|
||||
"Datepicker should have set the correct hour"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-minute").innerText.trim(),
|
||||
"30",
|
||||
"Datepicker should have set the correct minute"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-second").innerText.trim(),
|
||||
"01",
|
||||
"Datepicker should have set the correct second"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("pick a date and time", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const picker = await mountPicker(DateTimePicker, {
|
||||
props: { date: DateTime.fromFormat("09/01/1997 12:30:01", "dd/MM/yyyy HH:mm:ss") },
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy HH:mm:ss"),
|
||||
"08/02/1997 15:45:05",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector("input.o_input.o_datepicker_input");
|
||||
|
||||
await click(input);
|
||||
await click(document.querySelector(".datepicker th.next")); // February
|
||||
await click(document.querySelectorAll(".datepicker table td")[15]); // 08
|
||||
await click(document.querySelector('a[title="Select Time"]'));
|
||||
await click(document.querySelector(".timepicker .timepicker-hour"));
|
||||
await click(document.querySelectorAll(".timepicker .hour")[15]); // 15h
|
||||
await click(document.querySelector(".timepicker .timepicker-minute"));
|
||||
await click(document.querySelectorAll(".timepicker .minute")[9]); // 45m
|
||||
await click(document.querySelector(".timepicker .timepicker-second"));
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
await click(document.querySelectorAll(".timepicker .second")[1]); // 05s
|
||||
|
||||
assert.strictEqual(input.value, "08/02/1997 15:45:05");
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
});
|
||||
|
||||
QUnit.test("pick a date and time with locale", async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const picker = await mountPicker(DateTimePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997 12:30:01", "dd/MM/yyyy HH:mm:ss"),
|
||||
format: "dd MMM, yyyy HH:mm:ss",
|
||||
locale: useFRLocale(),
|
||||
},
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy HH:mm:ss"),
|
||||
"01/09/1997 15:45:05",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const input = picker.el.querySelector("input.o_input.o_datepicker_input");
|
||||
|
||||
assert.strictEqual(input.value, "09 janv., 1997 12:30:01");
|
||||
|
||||
await click(input);
|
||||
|
||||
await click(document.querySelector(".datepicker .picker-switch")); // month picker
|
||||
await click(document.querySelectorAll(".datepicker .month")[8]); // september
|
||||
await click(document.querySelector(".datepicker .day")); // first day
|
||||
|
||||
await click(document.querySelector('a[title="Select Time"]'));
|
||||
await click(document.querySelector(".timepicker .timepicker-hour"));
|
||||
await click(document.querySelectorAll(".timepicker .hour")[15]); // 15h
|
||||
await click(document.querySelector(".timepicker .timepicker-minute"));
|
||||
await click(document.querySelectorAll(".timepicker .minute")[9]); // 45m
|
||||
await click(document.querySelector(".timepicker .timepicker-second"));
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
await click(document.querySelectorAll(".timepicker .second")[1]); // 05s
|
||||
|
||||
assert.strictEqual(input.value, "01 sept., 1997 15:45:05");
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
});
|
||||
|
||||
QUnit.test("enter a datetime value", async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
const picker = await mountPicker(DateTimePicker, {
|
||||
props: { date: DateTime.fromFormat("09/01/1997 12:30:01", "dd/MM/yyyy HH:mm:ss") },
|
||||
onDateChange: (ev) => {
|
||||
assert.step("datetime-changed");
|
||||
assert.strictEqual(
|
||||
ev.detail.date.toFormat("dd/MM/yyyy HH:mm:ss"),
|
||||
"08/02/1997 15:45:05",
|
||||
"Event should transmit the correct date"
|
||||
);
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
input.value = "08/02/1997 15:45:05";
|
||||
await triggerEvent(picker.el, ".o_datepicker_input", "change");
|
||||
|
||||
assert.verifySteps(["datetime-changed"]);
|
||||
|
||||
await click(input);
|
||||
|
||||
assert.strictEqual(input.value, "08/02/1997 15:45:05");
|
||||
assert.strictEqual(
|
||||
document.querySelector(".datepicker .day.active").dataset.day,
|
||||
"02/08/1997",
|
||||
"Datepicker should have set the correct day"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-hour").innerText.trim(),
|
||||
"15",
|
||||
"Datepicker should have set the correct hour"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-minute").innerText.trim(),
|
||||
"45",
|
||||
"Datepicker should have set the correct minute"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".timepicker .timepicker-second").innerText.trim(),
|
||||
"05",
|
||||
"Datepicker should have set the correct second"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("Date time format is correctly set", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const picker = await mountPicker(DateTimePicker, {
|
||||
props: {
|
||||
date: DateTime.fromFormat("09/01/1997 12:30:01", "dd/MM/yyyy HH:mm:ss"),
|
||||
format: "HH:mm:ss yyyy/MM/dd",
|
||||
},
|
||||
});
|
||||
const input = picker.el.querySelector(".o_datepicker_input");
|
||||
|
||||
assert.strictEqual(input.value, "12:30:01 1997/01/09");
|
||||
|
||||
// Forces an update to assert that the registered format is the correct one
|
||||
await click(input);
|
||||
|
||||
assert.strictEqual(input.value, "12:30:01 1997/01/09");
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ odoo.define('web.datepicker_tests', function (require) {
|
||||
|
||||
QUnit.module('Components', {}, function () {
|
||||
|
||||
QUnit.module('DatePicker');
|
||||
QUnit.module('DatePicker (legacy)');
|
||||
|
||||
QUnit.test("basic rendering", async function (assert) {
|
||||
assert.expect(8);
|
||||
@@ -164,7 +164,7 @@ odoo.define('web.datepicker_tests', function (require) {
|
||||
testUtils.mock.unpatch(time);
|
||||
});
|
||||
|
||||
QUnit.module('DateTimePicker');
|
||||
QUnit.module('DateTimePicker (legacy)');
|
||||
|
||||
QUnit.test("basic rendering", async function (assert) {
|
||||
assert.expect(11);
|
||||
|
||||
Reference in New Issue
Block a user