From 0fc7b3e999f54faff7cf354f7e16897cfde1d16e Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Thu, 23 Nov 2023 15:10:32 +0000 Subject: [PATCH] [FIX] web: give enough space to display datepicker on small screens Before this commit, the datepicker was not displayed entirely on small screens if the current view wasn't tall enough. This commit adds a margin (supposedly) big enough to render the entire picker even on small screens. Task 3585878 closes odoo/odoo#149510 X-original-commit: 47c0627d218bd846596c9deb84720fc289047365 Signed-off-by: Aaron Bohy (aab) Signed-off-by: Julien Mougenot (jum) --- .../core/datetime/datetimepicker_service.js | 13 +++ .../core/datetime_input_mobile_tests.js | 91 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 addons/web/static/tests/mobile/core/datetime_input_mobile_tests.js diff --git a/addons/web/static/src/core/datetime/datetimepicker_service.js b/addons/web/static/src/core/datetime/datetimepicker_service.js index cf03620ba17..8852a7a6524 100644 --- a/addons/web/static/src/core/datetime/datetimepicker_service.js +++ b/addons/web/static/src/core/datetime/datetimepicker_service.js @@ -67,6 +67,10 @@ export const datetimePickerService = { updateValueFromInputs(); apply(); setFocusClass(null); + if (restoreTargetMargin) { + restoreTargetMargin(); + restoreTargetMargin = null; + } }, }); // Hook methods @@ -238,7 +242,14 @@ export const datetimePickerService = { if (!popover.isOpen) { const popoverTarget = getPopoverTarget(); if (env.isSmall) { + const { marginBottom } = popoverTarget.style; + // Adds enough space for the popover to be displayed below the target + // even on small screens. + popoverTarget.style.marginBottom = `100vh`; popoverTarget.scrollIntoView(true); + restoreTargetMargin = async () => { + popoverTarget.style.marginBottom = marginBottom; + }; } popover.open(popoverTarget, { pickerProps }); } @@ -416,6 +427,8 @@ export const datetimePickerService = { /** @type {DateTimePickerProps | null} */ let lastInitialProps = null; let lastIsRange = pickerProps.range; + /** @type {(() => void) | null} */ + let restoreTargetMargin = null; let shouldFocus = false; /** diff --git a/addons/web/static/tests/mobile/core/datetime_input_mobile_tests.js b/addons/web/static/tests/mobile/core/datetime_input_mobile_tests.js new file mode 100644 index 00000000000..4ac92d9261f --- /dev/null +++ b/addons/web/static/tests/mobile/core/datetime_input_mobile_tests.js @@ -0,0 +1,91 @@ +/** @odoo-module **/ + +import { Component, xml } from "@odoo/owl"; +import { clearRegistryWithCleanup, makeTestEnv } from "@web/../tests/helpers/mock_env"; +import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; +import { click, getFixture, mount } from "@web/../tests/helpers/utils"; +import { DateTimeInput } from "@web/core/datetime/datetime_input"; +import { datetimePickerService } from "@web/core/datetime/datetimepicker_service"; +import { hotkeyService } from "@web/core/hotkeys/hotkey_service"; +import { popoverService } from "@web/core/popover/popover_service"; +import { registry } from "@web/core/registry"; +import { uiService } from "@web/core/ui/ui_service"; + +const { DateTime } = luxon; + +/** + * @typedef {import("@web/core/datetime/datetime_input").DateTimeInputProps} DateTimeInputProps + */ + +/** + * @param {DateTimeInputProps} props + */ +const mountInput = async (props) => { + const env = await makeTestEnv(); + await mount(Root, getFixture(), { env, props }); + return getFixture().querySelector(".o_datetime_input"); +}; + +class Root extends Component { + static components = { DateTimeInput }; + + static template = xml` +
+ +
+ + + + `; + + setup() { + this.mainComponentEntries = mainComponentRegistry.getEntries(); + } +} + +const mainComponentRegistry = registry.category("main_components"); +const serviceRegistry = registry.category("services"); + +QUnit.module("Components", ({ beforeEach }) => { + beforeEach(() => { + clearRegistryWithCleanup(mainComponentRegistry); + + serviceRegistry + .add("datetime_picker", datetimePickerService) + .add("hotkey", hotkeyService) + .add( + "localization", + makeFakeLocalizationService({ + dateFormat: "dd/MM/yyyy", + dateTimeFormat: "dd/MM/yyyy HH:mm:ss", + }) + ) + .add("popover", popoverService) + .add("ui", uiService); + }); + + QUnit.module("DateTimeInput (date)"); + + QUnit.test("popover should have enough space to be displayed", async (assert) => { + const { parentElement: parent } = await mountInput({ + value: DateTime.fromFormat("09/01/1997", "dd/MM/yyyy"), + type: "date", + }); + + const initialParentRect = parent.getBoundingClientRect(); + + await click(parent, ".o_datetime_input"); + + const pickerRect = getFixture().querySelector(".o_datetime_picker").getBoundingClientRect(); + const finalParentRect = parent.getBoundingClientRect(); + + assert.ok( + initialParentRect.height < pickerRect.height, + "initial height shouldn't be big enough to display the picker" + ); + assert.ok( + finalParentRect.height > pickerRect.height, + "initial height should be big enough to display the picker" + ); + }); +});