From eef262abf491ce71362761f0a94dea9e31d4b156 Mon Sep 17 00:00:00 2001 From: Pierre Paridans Date: Wed, 26 Apr 2023 13:32:57 +0000 Subject: [PATCH] [FIX] *: adapt QUnit tests and tours [FIX] *: selectors in tours [FIX][TMP] account: CogMenu selector in tours [FIX][TMP] web*: Breadcrumb targetting in tours Adds a `o_breadcrumb` class to target the whole breadcrumb, no matter how much elements it contains (collapsed parts, visible path, single name...). add classname on last breadcrumb item [FIX][TMP] project: View buttons selector in tours (moved away from CP) [FIX][TMP] project: Kanban selectors in tours (quick create) [FIX][TMP] *: SearchBar selectors in tours (toggle menu) [FIX][TMP] *: ButtonBox selector in tours [WIP][IMP] web: add toggleSearchBarMenu in search helpers adapt and unskip 3 list tests adapt and unskip calendar tests unskip web_tour test that actually pass post rebase fix allow to lose cell focus after multi edition (given to searchbar) - bug reported, to check later post rebase fixes fix Part-of: odoo/odoo#116641 --- .../account_dashboard_setup_bar_tests.js | 7 +- .../static/tests/import_action_tests.js | 121 +- .../static/tests/import_records_tests.js | 63 +- .../static/tests/add_to_dashboard_tests.js | 32 +- addons/board/static/tests/board_test.js | 5 +- .../crm/static/tests/forecast_view_tests.js | 6 +- .../static/tests/google_calendar_tests.js | 13 +- .../static/tests/m2x_avatar_employee_tests.js | 16 +- .../static/src/tours/hr_holidays_tour.js | 5 - .../static/tests/tours/skills_tour.js | 5 - .../static/src/views/form/form_compiler.js | 10 +- .../static/tests/discuss_app/discuss_tests.js | 2 +- addons/mail/static/tests/web/chatter_tests.js | 4 +- .../mail/static/tests/web/debug_menu_tests.js | 5 +- .../tests/web/fields/m2x_avatar_user_tests.js | 8 +- .../static/tests/web/form_renderer_tests.js | 4 +- .../tests/test_user_modify_own_profile.py | 2 +- addons/project/static/src/js/tours/project.js | 4 +- .../static/tests/burndown_chart_tests.js | 14 +- .../tours/project_burndown_chart_tour.js | 15 +- .../tests/tours/project_sharing_tour.js | 27 +- .../static/src/js/tours/purchase_stock.js | 4 +- .../static/tests/tours/sale_timesheet_tour.js | 4 +- .../tests/dashboard/dashboard_action_test.js | 8 +- .../tests/inventory_report_list_tests.js | 14 +- .../static/tests/tours/stock_report_tests.js | 2 +- .../survey/static/tests/tours/survey_form.js | 2 +- .../test_mail/static/tests/activity_tests.js | 9 +- .../static/tests/mobile/activity_tests.js | 2 +- addons/web/static/src/core/popover/popover.js | 3 +- .../static/src/search/cog_menu/cog_menu.js | 2 +- .../src/search/control_panel/control_panel.js | 14 +- .../search/control_panel/control_panel.xml | 12 +- .../src/search/search_bar/search_bar.xml | 2 +- .../search/search_bar_menu/search_bar_menu.js | 41 +- .../search_bar_menu/search_bar_menu.xml | 2 +- .../src/views/calendar/calendar_controller.js | 1 + .../many2many_tags_avatar_field.scss | 3 + .../fields/percent_pie/percent_pie_field.xml | 2 +- .../static/src/views/form/form_compiler.js | 10 +- .../static/src/views/form/form_controller.js | 30 +- .../static/src/views/form/form_controller.xml | 10 +- .../static/src/views/form/form_renderer.js | 4 - .../form_status_indicator.js | 15 +- .../form_status_indicator.xml | 5 +- .../src/views/pivot/pivot_controller.xml | 6 +- .../static/src/views/pivot/pivot_renderer.xml | 3 +- .../src/views/view_button/view_button.js | 6 - .../src/views/view_button/view_button_hook.js | 23 +- .../src/webclient/actions/action_service.js | 3 +- .../tests/core/debug/debug_manager_tests.js | 28 +- addons/web/static/tests/helpers/utils.js | 32 +- .../mobile/search/control_panel_tests.js | 88 +- .../tests/mobile/views/calendar_view_tests.js | 81 +- .../views/fields/statusbar_field_tests.js | 46 +- .../tests/mobile/views/list_view_tests.js | 6 +- .../burger_menu/burger_menu_tests.js | 4 +- .../tests/search/comparison_menu_tests.js | 12 +- .../web/static/tests/search/control_panel.js | 46 +- .../search/custom_favorite_item_tests.js | 96 +- .../search/custom_group_by_item_tests.js | 28 +- .../tests/search/favorite_menu_tests.js | 11 +- .../static/tests/search/filter_menu_tests.js | 42 +- .../tests/search/group_by_menu_tests.js | 66 +- addons/web/static/tests/search/helpers.js | 55 +- .../tests/search/search_bar_menu_tests.js | 1710 +++++++++++++++++ .../static/tests/search/search_bar_tests.js | 128 +- .../views/calendar/calendar_view_tests.js | 75 +- .../static/tests/views/calendar/helpers.js | 2 +- .../tests/views/fields/binary_field_tests.js | 9 +- .../views/fields/color_picker_field_tests.js | 6 +- .../tests/views/fields/float_field_tests.js | 6 +- .../tests/views/fields/image_field_tests.js | 2 +- .../tests/views/fields/integer_field_tests.js | 6 +- .../fields/label_selection_field_tests.js | 6 +- .../many2many_tags_avatar_field_tests.js | 28 +- .../views/fields/many2one_field_tests.js | 57 +- .../views/fields/monetary_field_tests.js | 6 +- .../views/fields/percent_pie_field_tests.js | 175 +- .../tests/views/fields/phone_field_tests.js | 6 +- .../views/fields/priority_field_tests.js | 15 +- .../views/fields/progress_bar_field_tests.js | 21 +- .../views/fields/reference_field_tests.js | 12 +- .../fields/state_selection_field_tests.js | 80 +- .../tests/views/fields/text_field_tests.js | 6 +- .../tests/views/fields/url_field_tests.js | 6 +- .../tests/views/form/form_compiler_tests.js | 12 +- .../tests/views/form/form_view_tests.js | 304 ++- .../static/tests/views/graph_view_tests.js | 53 +- .../static/tests/views/kanban_view_tests.js | 68 +- addons/web/static/tests/views/layout_tests.js | 24 +- .../web/static/tests/views/list_view_tests.js | 707 ++++--- .../static/tests/views/pivot_view_tests.js | 253 ++- .../view_dialogs/export_data_dialog_tests.js | 9 +- .../select_create_dialog_tests.js | 6 +- .../webclient/actions/client_action_tests.js | 2 +- .../webclient/actions/concurrency_tests.js | 75 +- .../webclient/actions/error_handling_tests.js | 4 +- .../webclient/actions/load_state_tests.js | 168 +- .../tests/webclient/actions/misc_tests.js | 21 +- .../actions/reports/report_action_tests.js | 13 +- .../tests/webclient/actions/target_tests.js | 40 +- .../webclient/actions/window_action_tests.js | 480 ++--- .../settings_form_view_tests.js | 51 +- .../static/src/tour_service/tour_utils.js | 11 +- .../static/tests/tours/page_manager.js | 6 +- .../website_sale_restricted_editor_ui.js | 2 +- .../tours/website_sale_shop_cart_recovery.js | 2 +- .../static/tests/tours/main_flow.js | 67 +- .../addons/test_main_flows/tests/test_flow.py | 3 +- .../static/tests/tours/constraint.js | 2 +- .../test_new_api/static/tests/tours/x2many.js | 6 +- 112 files changed, 3673 insertions(+), 2329 deletions(-) create mode 100644 addons/web/static/tests/search/search_bar_menu_tests.js diff --git a/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js b/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js index f3a99e9d0a5..2a61917394f 100644 --- a/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js +++ b/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js @@ -16,11 +16,12 @@ position: 'bottom', }, { trigger: '.o_data_row:first .o_data_cell', - extra_trigger: '.breadcrumb', + extra_trigger: '.o_breadcrumb', }, { - trigger: '.o_control_panel button:contains("' + _t('Print') + '")', + trigger: '.o_control_panel .o_cp_action_menus .dropdown-toggle', + extra_trigger: '.o_breadcrumb .active:contains("INV/")', }, { - trigger: '.o_control_panel .o-dropdown--menu span:contains("' + _t('Invoices without Payment') + '")', + trigger: '.o_control_panel .o_cp_action_menus .o_menu_item:contains("' + _t('Invoices without Payment') + '")', }, { trigger: 'iframe .o_report_layout_standard h2', content: 'Primary color is correct', diff --git a/addons/base_import/static/tests/import_action_tests.js b/addons/base_import/static/tests/import_action_tests.js index aa6ace453ff..644c9fa1ef1 100644 --- a/addons/base_import/static/tests/import_action_tests.js +++ b/addons/base_import/static/tests/import_action_tests.js @@ -341,10 +341,11 @@ QUnit.module("Base Import Tests", (hooks) => { "button has the right download url" ); assert.verifySteps(["partner/get_import_templates", "base_import.import/create"]); + // Contains invisible mobile buttons assert.containsN( target, ".o_control_panel button", - 2, + 5, "only two buttons are visible by default" ); }); @@ -380,7 +381,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); assert.verifySteps([ "partner/get_import_templates", "base_import.import/create", @@ -515,8 +516,10 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); - await click(target.querySelector(".o_control_panel button:first-child")); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + ); assert.verifySteps(["execute_import"]); }); @@ -533,7 +536,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.csv", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); assert.containsOnce( target, ".o_import_data_sidepanel .o_import_formatting", @@ -563,7 +566,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.csv", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); await nextTick(); assert.containsOnce( @@ -604,7 +607,11 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput( + target, + ".o_control_panel_main_buttons .d-none input[type='file']", + file + ); assert.strictEqual( target.querySelector(".o_import_data_sidepanel input[type=checkbox]").checked, true, @@ -637,7 +644,9 @@ QUnit.module("Base Import Tests", (hooks) => { "as the column couldn't match with the database, user must make a choice" ); - await click(target.querySelector(".o_control_panel button:first-child")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + ); assert.containsNone( target, ".o_notification_body", @@ -660,7 +669,9 @@ QUnit.module("Base Import Tests", (hooks) => { ); await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Display name"); - await click(target.querySelector(".o_control_panel button:first-child")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + ); assert.verifySteps([ "base_import.import/execute_import", "1 records successfully imported", @@ -700,10 +711,12 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); // For this test, we force the display of an error message if this field is set await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Selection"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-danger").textContent, "The file contains blocking errors (see below)", @@ -730,7 +743,9 @@ QUnit.module("Base Import Tests", (hooks) => { "prevent option is selected by default" ); editSelect(target, ".o_import_field_selection select", "item_2"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-info").textContent, "Everything seems valid.", @@ -774,10 +789,12 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); // For this test, we force the display of an error message if this field is set await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Bar"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-danger").textContent, "The file contains blocking errors (see below)", @@ -789,7 +806,9 @@ QUnit.module("Base Import Tests", (hooks) => { "options are 'prevent', choose a default boolean value or 'skip'" ); editSelect(target, ".o_import_field_boolean select", "false"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-info").textContent, "Everything seems valid.", @@ -837,10 +856,12 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); // For this test, we force the display of an error message if this field is set await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Many2Many"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-danger").textContent, "The file contains blocking errors (see below)", @@ -852,14 +873,18 @@ QUnit.module("Base Import Tests", (hooks) => { "options are 'prevent', choose a default boolean value or 'skip'" ); editSelect(target, ".o_import_field_many2many select", "name_create_enabled_fields"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-info").textContent, "Everything seems valid.", "import is now successful" ); editSelect(target, ".o_import_field_many2many select", "import_skip_records"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-info").textContent, "Everything seems valid.", @@ -914,8 +939,10 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); - await click(target.querySelector(".o_control_panel button:nth-child(1)")); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(1)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-danger").textContent, "The file contains blocking errors (see below)", @@ -991,7 +1018,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); assert.strictEqual( target.querySelector("input#o_import_batch_limit").value, "2000", @@ -1004,7 +1031,9 @@ QUnit.module("Base Import Tests", (hooks) => { ); await editInput(target, "input#o_import_batch_limit", 1); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-info").textContent, "Everything seems valid.", @@ -1068,9 +1097,11 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); await editInput(target, "input#o_import_batch_limit", 1); - await click(target.querySelector(".o_control_panel button:first-child")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + ); // Since a nextTick is added to each batch, we must wait twice before the end of the second batch await nextTick(); await nextTick(); @@ -1086,7 +1117,8 @@ QUnit.module("Base Import Tests", (hooks) => { "a message is shown to indicate the user to resume from the third row" ); assert.strictEqual( - target.querySelector(".o_control_panel button:first-child").textContent, + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + .textContent, "Resume", "button contains the right text" ); @@ -1157,9 +1189,11 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); await editInput(target, "input#o_import_batch_limit", 1); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); // Since a nextTick is added to each batch, we must wait twice before the end of the second batch await nextTick(); await nextTick(); @@ -1169,7 +1203,8 @@ QUnit.module("Base Import Tests", (hooks) => { "Everything seems valid." ); assert.strictEqual( - target.querySelector(".o_control_panel button:first-child").textContent, + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + .textContent, "Import", "after testing, 'Resume' text is not shown" ); @@ -1245,7 +1280,11 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput( + target, + ".o_control_panel_main_buttons .d-none input[type='file']", + file + ); assert.strictEqual( target.querySelector( @@ -1261,7 +1300,9 @@ QUnit.module("Base Import Tests", (hooks) => { "The relational field should be selected by default and the name should be the full path." ); - await click(target.querySelector(".o_control_panel button:first-child")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:first-child") + ); } ); @@ -1320,10 +1361,12 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); // For this test, we force the display of an error message if this field is set await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Many2Many"); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + target.querySelector(".o_control_panel_main_buttons .d-none button:nth-child(2)") + ); assert.strictEqual( target.querySelector(".o_import_data_content .alert-danger").textContent, @@ -1387,13 +1430,17 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.csv", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); await editInput(target, ".o_import_date_format#date_format-3", "YYYYMMDD"); // Parse the file again with the updated date format to check that // the format is correctly formatted in the UI await click(target.querySelector(".o_import_formatting button")); - await click(target.querySelector(".o_control_panel button:nth-child(2)")); + await click( + $(target).find( + ".o_control_panel_main_buttons > div:visible > button:contains(Import)" + )[0] + ); assert.verifySteps(["execute_import"]); assert.strictEqual( target.querySelector(".o_import_date_format").value, @@ -1412,7 +1459,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_main_buttons .d-none input[type='file']", file); await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Bar"); assert.containsN( target, @@ -1444,7 +1491,7 @@ QUnit.module("Base Import Tests", (hooks) => { // Set and trigger the change of a file for the input const file = new File(["fake_file"], "fake_file.csv", { type: "text/plain" }); - await editInput(target, "input[type='file']", file); + await editInput(target, ".o_control_panel_collapsed_create input[type='file']", file); assert.strictEqual( target.querySelector(".o_import_date_format").list.id, "list-3", diff --git a/addons/base_import/static/tests/import_records_tests.js b/addons/base_import/static/tests/import_records_tests.js index 87b758b2fa9..7a578157f82 100644 --- a/addons/base_import/static/tests/import_records_tests.js +++ b/addons/base_import/static/tests/import_records_tests.js @@ -5,10 +5,9 @@ import { importRecordsItem } from "@base_import/import_records/import_records"; import { registry } from "@web/core/registry"; import { click, getFixture, selectDropdownItem } from "@web/../tests/helpers/utils"; -import { toggleFavoriteMenu } from "@web/../tests/search/helpers"; +import { toggleActionMenu } from "@web/../tests/search/helpers"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; - -const favoriteMenuRegistry = registry.category("favoriteMenu"); +import { clearRegistryWithCleanup } from "@web/../tests/helpers/mock_env"; let serverData; let target; @@ -27,12 +26,14 @@ QUnit.module("Base Import Tests", (hooks) => { }, }; setupViewRegistries(); - favoriteMenuRegistry.add("import-menu", importRecordsItem); + const cogMenuRegistry = registry.category("cogMenu"); + clearRegistryWithCleanup(cogMenuRegistry); + cogMenuRegistry.add("import-menu", importRecordsItem); }); QUnit.module("ImportRecords"); - QUnit.test("import in favorite dropdown in list", async function (assert) { + QUnit.test("import in cog menu dropdown in list", async function (assert) { assert.expect(3); const actionService = { @@ -56,14 +57,14 @@ QUnit.module("Base Import Tests", (hooks) => { }, }); - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); + await toggleActionMenu(target); + assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu"); assert.containsOnce(target, ".o_import_menu"); await click(target.querySelector(".o_import_menu")); }); QUnit.test( - 'import favorite dropdown item should not be in list with create="0"', + 'import should not be available in cog menu dropdown in list with create="0"', async function (assert) { await makeView({ type: "list", @@ -75,14 +76,14 @@ QUnit.module("Base Import Tests", (hooks) => { }, }); - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); + await toggleActionMenu(target); + assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu"); assert.containsNone(target, ".o_import_menu"); } ); QUnit.test( - 'import favorite dropdown item should not be in list with import="0"', + 'import should not be available in cog menu dropdown in list with import="0"', async function (assert) { await makeView({ type: "list", @@ -94,13 +95,13 @@ QUnit.module("Base Import Tests", (hooks) => { }, }); - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); + await toggleActionMenu(target); + assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu"); assert.containsNone(target, ".o_import_menu"); } ); - QUnit.test("import in favorite dropdown in kanban", async function (assert) { + QUnit.test("import in cog menu dropdown in kanban", async function (assert) { assert.expect(3); const actionService = { @@ -131,14 +132,14 @@ QUnit.module("Base Import Tests", (hooks) => { }, }); - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); + await toggleActionMenu(target); + assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu"); assert.containsOnce(target, ".o_import_menu"); await click(target.querySelector(".o_import_menu")); }); QUnit.test( - 'import favorite dropdown item should not be in list with create="0"', + 'import should not be available in cog menu dropdown in kanban with create="0"', async function (assert) { await makeView({ type: "kanban", @@ -156,15 +157,13 @@ QUnit.module("Base Import Tests", (hooks) => { actionType: "ir.actions.act_window", }, }); - - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); - assert.containsNone(target, ".o_import_menu"); + // Cog menu will not show when empty + assert.containsNone(target, ".o_cp_action_menus"); } ); QUnit.test( - 'import dropdown favorite should not be in kanban with import="0"', + 'import should not be available in cog menu dropdown in kanban with import="0"', async function (assert) { await makeView({ type: "kanban", @@ -182,15 +181,13 @@ QUnit.module("Base Import Tests", (hooks) => { actionType: "ir.actions.act_window", }, }); - - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); - assert.containsNone(target, ".o_import_menu"); + // Cog menu will not show when empty + assert.containsNone(target, ".o_cp_action_menus"); } ); QUnit.test( - "import should not be available in favorite dropdown in pivot (other than kanban or list)", + "import should not be available in cog menu dropdown in pivot (other than kanban or list)", async function (assert) { serverData.models.foo.fields.foobar = { string: "Fubar", @@ -207,15 +204,13 @@ QUnit.module("Base Import Tests", (hooks) => { actionType: "ir.actions.act_window", }, }); - - await toggleFavoriteMenu(target); - assert.containsOnce(target, ".o_favorite_menu .o-dropdown--menu"); - assert.containsNone(target, ".o_import_menu"); + // Cog menu will not show when empty + assert.containsNone(target, ".o_cp_action_menus"); } ); QUnit.test( - "import should not be available in favorite dropdown in dialog view", + "import should not be available in cog menu dropdown in dialog view", async function (assert) { serverData.models.bar = { fields: { @@ -244,8 +239,8 @@ QUnit.module("Base Import Tests", (hooks) => { await selectDropdownItem(target, "m2o", "Search More..."); const dialog = target.querySelector(".modal"); - await toggleFavoriteMenu(dialog); - assert.containsOnce(dialog, ".o_favorite_menu .o-dropdown--menu"); + await toggleActionMenu(dialog); + assert.containsOnce(dialog, ".o_cp_action_menus .o-dropdown--menu"); assert.containsNone(dialog, ".o_import_menu"); } ); diff --git a/addons/board/static/tests/add_to_dashboard_tests.js b/addons/board/static/tests/add_to_dashboard_tests.js index ddea0e7e56c..410b76a0dc5 100644 --- a/addons/board/static/tests/add_to_dashboard_tests.js +++ b/addons/board/static/tests/add_to_dashboard_tests.js @@ -12,10 +12,7 @@ import { applyGroup, removeFacet, toggleAddCustomGroup, - toggleComparisonMenu, - toggleFavoriteMenu, - toggleFilterMenu, - toggleGroupByMenu, + toggleSearchBarMenu, toggleMenuItem, toggleMenuItemOption, } from "@web/../tests/search/helpers"; @@ -142,13 +139,11 @@ QUnit.module("Board", (hooks) => { await click(document.querySelector(".o_column_sortable")); // Group It - await toggleGroupByMenu(target); + await toggleSearchBarMenu(target); await toggleAddCustomGroup(target); await applyGroup(target); // add this action to dashboard - await toggleFavoriteMenu(target); - await testUtils.dom.triggerEvent($(".o_add_to_board button.dropdown-toggle"), "mouseenter"); await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); await testUtils.dom.click($(".o_add_to_board .dropdown-menu button")); @@ -202,11 +197,10 @@ QUnit.module("Board", (hooks) => { var filter_count = 0; // Add a first filter - await toggleFilterMenu(target); + await toggleSearchBarMenu(target); await toggleMenuItem(target, "Filter on a"); // Add it to dashboard - await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent($(".o_add_to_board button.dropdown-toggle"), "mouseenter"); await testUtils.dom.click($(".o_add_to_board .dropdown-menu button")); @@ -214,11 +208,10 @@ QUnit.module("Board", (hooks) => { await removeFacet(target); // Add the second filter - await toggleFilterMenu(target); + await toggleSearchBarMenu(target); await toggleMenuItem(target, "Filter on b"); // Add it to dashboard - await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent( target.querySelector(".o_add_to_board button.dropdown-toggle"), "mouseenter" @@ -263,11 +256,10 @@ QUnit.module("Board", (hooks) => { }); // Add a filter - await toggleFilterMenu(target); + await toggleSearchBarMenu(target); await toggleMenuItem(target, "Filter"); // Add it to dashboard - await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent( target.querySelector(".o_add_to_board button.dropdown-toggle"), "mouseenter" @@ -292,7 +284,7 @@ QUnit.module("Board", (hooks) => { type: "ir.actions.act_window", views: [[false, "pivot"]], }); - await toggleFavoriteMenu(target); + await toggleSearchBarMenu(target); assert.containsNone(target, ".o_add_to_board"); // Sanity check @@ -302,7 +294,7 @@ QUnit.module("Board", (hooks) => { type: "ir.actions.act_window", views: [[false, "pivot"]], }); - await toggleFavoriteMenu(target); + await toggleSearchBarMenu(target); assert.containsOnce(target, ".o_add_to_board"); }); @@ -357,16 +349,14 @@ QUnit.module("Board", (hooks) => { }); // filter on July 2020 - await toggleFilterMenu(target); + await toggleSearchBarMenu(target); await toggleMenuItem(target, "Date"); await toggleMenuItemOption(target, "Date", "July"); // compare July 2020 to June 2020 - await toggleComparisonMenu(target); - await toggleMenuItem(target, 0); + await toggleMenuItem(target.querySelector(".o_comparison_menu"), 0); // add the view to the dashboard - await toggleFavoriteMenu(target); await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); const input = target.querySelector(".o_add_to_board .dropdown-menu input"); @@ -403,7 +393,7 @@ QUnit.module("Board", (hooks) => { views: [[false, "pivot"]], }); - await toggleFavoriteMenu(target); + await toggleSearchBarMenu(target); await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); const input = target.querySelector(".o_add_to_board .dropdown-menu input"); await testUtils.fields.editInput(input, "Pipeline"); @@ -445,7 +435,7 @@ QUnit.module("Board", (hooks) => { context: { search_default_filter: 1 }, }); - await toggleFavoriteMenu(target); + await toggleSearchBarMenu(target); await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); const input = target.querySelector(".o_add_to_board .dropdown-menu input"); await testUtils.fields.editInput(input, "Pipeline"); diff --git a/addons/board/static/tests/board_test.js b/addons/board/static/tests/board_test.js index 68386724144..6dc805a20aa 100644 --- a/addons/board/static/tests/board_test.js +++ b/addons/board/static/tests/board_test.js @@ -131,7 +131,10 @@ QUnit.module("Board", (hooks) => { return Promise.resolve(true); } if (args.method === "get_views" && args.model == "partner") { - assert.deepEqual(args.kwargs.views.find((v) => v[1] === 'list'), [4, "list"]); + assert.deepEqual( + args.kwargs.views.find((v) => v[1] === "list"), + [4, "list"] + ); } }, }); diff --git a/addons/crm/static/tests/forecast_view_tests.js b/addons/crm/static/tests/forecast_view_tests.js index c9e3fedd78e..3f72591c552 100644 --- a/addons/crm/static/tests/forecast_view_tests.js +++ b/addons/crm/static/tests/forecast_view_tests.js @@ -3,8 +3,7 @@ import { getFixture, patchWithCleanup } from "@web/../tests/helpers/utils"; import { menuService } from "@web/webclient/menus/menu_service"; import { - toggleFilterMenu, - toggleGroupByMenu, + toggleSearchBarMenu, toggleMenuItem, toggleMenuItemOption, } from "@web/../tests/search/helpers"; @@ -115,7 +114,7 @@ QUnit.module("Views", (hooks) => { }, }); - await toggleGroupByMenu(target); + await toggleSearchBarMenu(target); await toggleMenuItem(target, "Bar"); await toggleMenuItem(target, "Date Field"); @@ -123,7 +122,6 @@ QUnit.module("Views", (hooks) => { await toggleMenuItemOption(target, "Date Field", "Year"); - await toggleFilterMenu(target); await toggleMenuItem(target, "Forecast Filter"); unpatchDate(); diff --git a/addons/google_calendar/static/tests/google_calendar_tests.js b/addons/google_calendar/static/tests/google_calendar_tests.js index 5a9199ca7b9..2862bf8aab6 100644 --- a/addons/google_calendar/static/tests/google_calendar_tests.js +++ b/addons/google_calendar/static/tests/google_calendar_tests.js @@ -98,7 +98,7 @@ QUnit.module('Google Calendar', { }, function () { QUnit.test('sync google calendar', async function (assert) { - assert.expect(11); + assert.expect(13); let id = 7; await makeView({ @@ -136,8 +136,11 @@ QUnit.module('Google Calendar', { // sync_data was called a first time without filter, event from google calendar was created twice assert.containsN(target, '.fc-event-container', 4, "should display 4 events on the month"); - await click(target.querySelector('.o_calendar_button_next')); - await click(target.querySelector('.o_calendar_button_prev')); + await click(target.querySelector('.ui-datepicker-next')); + await click(target.querySelector('.ui-datepicker-calendar > tbody > tr:nth-child(2) > td:nth-child(4) > a')); + await click(target.querySelector('.o_view_scale_selector .dropdown-toggle')); + await click(target.querySelector('.o_scale_button_month')); + await click(target.querySelector('.o_calendar_button_today')); assert.verifySteps([ '/google_calendar/sync_data', @@ -148,8 +151,10 @@ QUnit.module('Google Calendar', { '/web/dataset/call_kw/calendar.event/search_read', '/google_calendar/sync_data', '/web/dataset/call_kw/calendar.event/search_read', + "/google_calendar/sync_data", + "/web/dataset/call_kw/calendar.event/search_read", ], 'should do a search_read before and after the call to sync_data'); - assert.containsN(target, '.fc-event-container', 6, "should now display 6 events on the month"); + assert.containsN(target, '.fc-event-container', 7, "should now display 7 events on the month"); }); }); diff --git a/addons/hr/static/tests/m2x_avatar_employee_tests.js b/addons/hr/static/tests/m2x_avatar_employee_tests.js index 5a874d39169..1b7adcf4642 100644 --- a/addons/hr/static/tests/m2x_avatar_employee_tests.js +++ b/addons/hr/static/tests/m2x_avatar_employee_tests.js @@ -214,22 +214,22 @@ QUnit.test("many2many_avatar_employee widget in form view", async function (asse }); assert.containsN( document.body, - ".o_field_many2many_avatar_employee .badge", + ".o_field_many2many_avatar_employee .o_tag", 2, "should have 2 records" ); assert.strictEqual( document - .querySelector(".o_field_many2many_avatar_employee .badge img") + .querySelector(".o_field_many2many_avatar_employee .o_tag img") .getAttribute("data-src"), `/web/image/hr.employee.public/${employeeId_1}/avatar_128` ); await dom.click( - document.querySelector(".o_field_many2many_avatar_employee .badge .o_m2m_avatar") + document.querySelector(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar") ); await dom.click( - document.querySelectorAll(".o_field_many2many_avatar_employee .badge .o_m2m_avatar")[1] + document.querySelectorAll(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar")[1] ); assert.verifySteps([ `read m2x.avatar.employee ${avatarId_1}`, @@ -413,19 +413,19 @@ QUnit.test( res_id: avatarId, views: [[false, "form"]], }); - assert.containsN(document.body, ".o_field_many2many_avatar_employee .badge", 2); + assert.containsN(document.body, ".o_field_many2many_avatar_employee .o_tag", 2); assert.strictEqual( document - .querySelector(".o_field_many2many_avatar_employee .badge img") + .querySelector(".o_field_many2many_avatar_employee .o_tag img") .getAttribute("data-src"), `/web/image/hr.employee.public/${employeeId_1}/avatar_128` ); await dom.click( - document.querySelector(".o_field_many2many_avatar_employee .badge .o_m2m_avatar") + document.querySelector(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar") ); await dom.click( - document.querySelectorAll(".o_field_many2many_avatar_employee .badge .o_m2m_avatar")[1] + document.querySelectorAll(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar")[1] ); assert.verifySteps([ `read m2x.avatar.employee ${employeeId_1}`, diff --git a/addons/hr_holidays/static/src/tours/hr_holidays_tour.js b/addons/hr_holidays/static/src/tours/hr_holidays_tour.js index 0e25e70c81a..529e42e9d87 100644 --- a/addons/hr_holidays/static/src/tours/hr_holidays_tour.js +++ b/addons/hr_holidays/static/src/tours/hr_holidays_tour.js @@ -82,10 +82,5 @@ registry.category("web_tour.tours").add('hr_holidays_tour', { trigger: 'button[name="action_approve"]', content: _t("Let's approve it"), position: 'bottom' - }, - { - trigger: 'a[data-menu-xmlid="hr_holidays.menu_hr_holidays_root"]', - content: _t("State is now confirmed. We can go back to the calendar"), - position: 'bottom' } ]}); diff --git a/addons/hr_skills/static/tests/tours/skills_tour.js b/addons/hr_skills/static/tests/tours/skills_tour.js index f044e379918..7d97db8d8ca 100644 --- a/addons/hr_skills/static/tests/tours/skills_tour.js +++ b/addons/hr_skills/static/tests/tours/skills_tour.js @@ -147,9 +147,4 @@ registry.category("web_tour.tours").add('hr_skills_tour', { run: () => {}, }, ...stepUtils.saveForm(), - { - content: "Go back to employees", - trigger: 'a[data-menu-xmlid="hr.menu_hr_root"]', - run: "click", - } ]}); diff --git a/addons/mail/static/src/views/form/form_compiler.js b/addons/mail/static/src/views/form/form_compiler.js index f9e48883a1e..b460cf96eb8 100644 --- a/addons/mail/static/src/views/form/form_compiler.js +++ b/addons/mail/static/src/views/form/form_compiler.js @@ -46,9 +46,9 @@ function compileChatter(node, params) { hasParentReloadOnFollowersUpdate, hasParentReloadOnMessagePosted, isAttachmentBoxVisibleInitially, - threadId: "__comp__.props.record.model.root.resId or undefined", - threadModel: "__comp__.props.record.model.root.resModel", - webRecord: "__comp__.props.record.model.root", + threadId: "__comp__.props.record.resId or undefined", + threadModel: "__comp__.props.record.resModel", + webRecord: "__comp__.props.record", saveRecord: "() => __comp__.saveButtonClicked and __comp__.saveButtonClicked()", }); const chatterContainerHookXml = createElement("div"); @@ -63,8 +63,8 @@ function compileAttachmentPreview(node, params) { const webClientViewAttachmentViewContainerXml = createElement("t"); setAttributes(webClientViewAttachmentViewContainerXml, { "t-component": "__comp__.mailComponents.AttachmentView", - threadId: "__comp__.props.record.model.root.resId or undefined", - threadModel: "__comp__.props.record.model.root.resModel", + threadId: "__comp__.props.record.resId or undefined", + threadModel: "__comp__.props.record.resModel", }); append(webClientViewAttachmentViewContainerHookXml, webClientViewAttachmentViewContainerXml); return webClientViewAttachmentViewContainerHookXml; diff --git a/addons/mail/static/tests/discuss_app/discuss_tests.js b/addons/mail/static/tests/discuss_app/discuss_tests.js index 76261d419d5..12443fdd6a8 100644 --- a/addons/mail/static/tests/discuss_app/discuss_tests.js +++ b/addons/mail/static/tests/discuss_app/discuss_tests.js @@ -1994,6 +1994,6 @@ QUnit.test( await click(".o-mail-NotificationItem:contains(General)"); await click(".o-mail-ChatWindow [title='Open in Discuss']"); await click(".o-mail-DiscussCategoryItem:contains(General) [title='Channel settings']"); - assert.strictEqual($(".breadcrumb").text(), "DiscussGeneral"); + assert.strictEqual($(".o_breadcrumb").text(), "DiscussGeneral"); } ); diff --git a/addons/mail/static/tests/web/chatter_tests.js b/addons/mail/static/tests/web/chatter_tests.js index 0b7a14448bf..7c7836b665d 100644 --- a/addons/mail/static/tests/web/chatter_tests.js +++ b/addons/mail/static/tests/web/chatter_tests.js @@ -190,10 +190,10 @@ QUnit.test("Composer type is kept when switching from aside to bottom", async (a }); assert.hasClass( $("button:contains(Log note)"), - "btn-odoo", + "btn-primary", "Active button should be the log note button" ); - assert.doesNotHaveClass($("button:contains(Send message)"), "btn-odoo"); + assert.doesNotHaveClass($("button:contains(Send message)"), "btn-primary"); }); QUnit.test("chatter: drop attachments", async (assert) => { diff --git a/addons/mail/static/tests/web/debug_menu_tests.js b/addons/mail/static/tests/web/debug_menu_tests.js index fc5a4edc166..5a90cd0c678 100644 --- a/addons/mail/static/tests/web/debug_menu_tests.js +++ b/addons/mail/static/tests/web/debug_menu_tests.js @@ -54,5 +54,8 @@ QUnit.test("Manage Messages", async (assert) => { await click(dropdownItems[0]); await legacyExtraNextTick(); assert.verifySteps(["message_read"]); - assert.strictEqual($(".breadcrumb-item.active")[0].innerText.trim(), "Manage Messages"); + assert.strictEqual( + target.querySelector(".o_breadcrumb .active > span").innerText.trim(), + "Manage Messages" + ); }); diff --git a/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js b/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js index 7db434a06c2..5a86cf17760 100644 --- a/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js +++ b/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js @@ -330,9 +330,9 @@ QUnit.test( views: [[false, "form"]], }); assert.strictEqual( - $(".o_field_many2many_avatar_user.o_field_widget .badge img")[0].getAttribute( - "data-src" - ), + document.body + .querySelector(".o_field_many2many_avatar_user.o_field_widget .o_avatar img") + .getAttribute("data-src"), `/web/image/res.users/${userId}/avatar_128` ); } @@ -374,7 +374,7 @@ QUnit.test("many2many_avatar_user widget in form view", async (assert) => { res_id: avatarUserId, views: [[false, "form"]], }); - await click(document.body, ".o_field_many2many_avatar_user .badge .o_m2m_avatar"); + await click(document.body, ".o_field_many2many_avatar_user .o_avatar img"); assert.containsOnce($, ".o-mail-ChatWindow"); assert.strictEqual($(".o-mail-ChatWindow-name").text(), "Partner 1"); }); diff --git a/addons/mail/static/tests/web/form_renderer_tests.js b/addons/mail/static/tests/web/form_renderer_tests.js index 6c4527e7aba..41551565fc9 100644 --- a/addons/mail/static/tests/web/form_renderer_tests.js +++ b/addons/mail/static/tests/web/form_renderer_tests.js @@ -70,12 +70,12 @@ QUnit.test("Form view not scrolled when switching record", async (assert) => { }, { resIds: [partnerId_1, partnerId_2] } ); - assert.strictEqual($(".breadcrumb-item.active").text(), "Partner 1"); + assert.strictEqual($(".o_breadcrumb .active").text(), "Partner 1"); assert.strictEqual($(".o_content")[0].scrollTop, 0); $(".o_content")[0].scrollTop = 150; await click(".o_pager_next"); - assert.strictEqual($(".breadcrumb-item.active").text(), "Partner 2"); + assert.strictEqual($(".o_breadcrumb .active").text(), "Partner 2"); assert.strictEqual($(".o_content")[0].scrollTop, 0); await click(".o_pager_previous"); diff --git a/addons/mail/tests/test_user_modify_own_profile.py b/addons/mail/tests/test_user_modify_own_profile.py index 7d3e7846ed4..40b6f245df4 100644 --- a/addons/mail/tests/test_user_modify_own_profile.py +++ b/addons/mail/tests/test_user_modify_own_profile.py @@ -16,5 +16,5 @@ class TestUserModifyOwnProfile(HttpCase): # avoid 'reload_context' action in the middle of the tour to ease steps and form save checks with patch.object(Users, 'preference_save', lambda self: True): - self.start_tour("/web", "mail/static/tests/tours/user_modify_own_profile_tour.js", login="demo") + self.start_tour("/web", "mail/static/tests/tours/user_modify_own_profile_tour.js", login="demo", step_delay=100) self.assertEqual(self.env.ref('base.user_demo').email, "updatedemail@example.com") diff --git a/addons/project/static/src/js/tours/project.js b/addons/project/static/src/js/tours/project.js index 1e7830b0193..9bf40892772 100644 --- a/addons/project/static/src/js/tours/project.js +++ b/addons/project/static/src/js/tours/project.js @@ -38,7 +38,7 @@ registry.category("web_tour.tours").add('project_tour', { actions.auto('.modal:visible .btn.btn-primary'); }, }, { - trigger: ".o_kanban_project_tasks .o_column_quick_create .input-group .o_input", + trigger: ".o_kanban_project_tasks .o_column_quick_create .o_kanban_header input", content: Markup(_t("Add columns to organize your tasks into stages e.g. New - In Progress - Done.")), position: 'bottom', run: "text Test", @@ -46,7 +46,7 @@ registry.category("web_tour.tours").add('project_tour', { trigger: ".o_kanban_project_tasks .o_column_quick_create .o_kanban_add", position: 'right', }, { - trigger: ".o_kanban_project_tasks .o_column_quick_create .input-group .o_input", + trigger: ".o_kanban_project_tasks .o_column_quick_create .o_kanban_header input", extra_trigger: '.o_kanban_group', content: Markup(_t("Add columns to organize your tasks into stages e.g. New - In Progress - Done.")), position: 'bottom', diff --git a/addons/project/static/tests/burndown_chart_tests.js b/addons/project/static/tests/burndown_chart_tests.js index a4d658dec98..1da286af8e0 100644 --- a/addons/project/static/tests/burndown_chart_tests.js +++ b/addons/project/static/tests/burndown_chart_tests.js @@ -2,7 +2,7 @@ import { browser } from "@web/core/browser/browser"; import { click, getFixture, patchWithCleanup } from "@web/../tests/helpers/utils"; -import { setupControlPanelServiceRegistry, toggleGroupByMenu, toggleMenuItem, toggleMenuItemOption } from "@web/../tests/search/helpers"; +import { setupControlPanelServiceRegistry, toggleSearchBarMenu, toggleMenuItem, toggleMenuItemOption } from "@web/../tests/search/helpers"; import { makeView } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; import { makeFakeNotificationService, fakeCookieService } from "@web/../tests/helpers/mock_services"; @@ -133,7 +133,7 @@ QUnit.module("Project", {}, () => { } async function openGroupByMainMenu(target) { - await toggleGroupByMenu(target); + await toggleSearchBarMenu(target); } async function openGroupByDateMenu(target) { @@ -164,12 +164,8 @@ QUnit.module("Project", {}, () => { QUnit.test("check that removing the group by 'Date: Month > Stage' in the search bar triggers a notification", async function (assert) { const stepsTriggeringNotification = async () => { - const removeFilterXpath = `//div[contains(@class, 'o_searchview_facet')] - [.//span[@class='o_facet_value'] - [contains(., 'Date: Month')]] - /i[contains(@class, 'o_facet_remove')]`; - const removeFilterElement = getFirstElementForXpath(target, removeFilterXpath); - await click(removeFilterElement); + // There's only one possibility here + await click(target, ".o_facet_remove"); }; await testBurnDownChartWithSearchView(stepsTriggeringNotification, assert); }); @@ -216,7 +212,7 @@ QUnit.module("Project", {}, () => { function checkGroupByOrder(assert) { const dateSearchFacetXpath = `//div[contains(@class, 'o_searchview_facet')] - [.//span[@class='o_facet_value'] + [.//small[@class='o_facet_value'] [contains(., 'Date: Month')]]`; const dateSearchFacetElement = getFirstElementForXpath(target, dateSearchFacetXpath); const dateSearchFacetParts = dateSearchFacetElement.querySelectorAll('.o_facet_value'); diff --git a/addons/project/static/tests/tours/project_burndown_chart_tour.js b/addons/project/static/tests/tours/project_burndown_chart_tour.js index af3b2ac549e..ba38e91c718 100644 --- a/addons/project/static/tests/tours/project_burndown_chart_tour.js +++ b/addons/project/static/tests/tours/project_burndown_chart_tour.js @@ -16,7 +16,7 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { trigger: '.o_kanban_record:contains("Burndown Chart Test") .o_kanban_manage_reporting div[role="menuitem"] a:contains("Burndown Chart")', }, { content: 'The sort buttons are not rendered', - trigger: '.o_cp_bottom_left:not(:has(.btn-group[role=toolbar][aria-label="Sort graph"]))', + trigger: '.o_graph_renderer:not(:has(.btn-group[role=toolbar][aria-label="Sort graph"]))', extra_trigger: '.o_graph_renderer', }, { content: 'Remove the project search "Burndown Chart Test"', @@ -35,8 +35,8 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { content: 'A "The Burndown Chart must be grouped by Date and Stage" notification is shown when trying to remove the group by "Date: Month > Stage"', trigger: '.o_notification_manager .o_notification:contains("The Burndown Chart must be grouped by Date and Stage") button.o_notification_close', }, { - content: 'Open the group by menu', - trigger: '.o_group_by_menu button', + content: 'Open the search panel menu', + trigger: '.o_control_panel .o_searchview_dropdown_toggler', }, { content: 'The Stage group menu item is invisible', trigger: '.o_group_by_menu:not(:has(.o_menu_item:contains("Stage")))', @@ -56,8 +56,8 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { content: 'A "The Burndown Chart must be grouped by Date" notification is shown when trying to remove the group by "Date: Month > Stage"', trigger: '.o_notification_manager .o_notification:contains("The Burndown Chart must be grouped by Date") button.o_notification_close', }, { - content: 'Open the filter menu', - trigger: '.o_filter_menu button', + content: 'Open the search panel menu', + trigger: '.o_control_panel .o_searchview_dropdown_toggler', }, { content: 'Open the Date filter sub menu', trigger: '.o_filter_menu button.o_menu_item:contains("Date")', @@ -73,7 +73,10 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { }, { content: 'Close the Date filter menu', trigger: '.o_graph_renderer', +}, { + content: 'Open the search panel menu', + trigger: '.o_control_panel .o_searchview_dropdown_toggler', }, { content: 'The comparison menu is not rendered', - trigger: '.o_search_options:not(:has(.o_comparison_menu))', + trigger: ':not(:has(.o_comparison_menu))', }]}); diff --git a/addons/project/static/tests/tours/project_sharing_tour.js b/addons/project/static/tests/tours/project_sharing_tour.js index fbe50508cf4..7dbaaafa75b 100644 --- a/addons/project/static/tests/tours/project_sharing_tour.js +++ b/addons/project/static/tests/tours/project_sharing_tour.js @@ -62,38 +62,23 @@ const projectSharingSteps = [...stepUtils.goToAppSteps("project.menu_main_pm", ' trigger: 'iframe ol.breadcrumb > li.o_back_button > a:contains(Project Sharing)', content: 'Go back to the kanban view', }, { - trigger: 'iframe .o_filter_menu > button', - content: 'click on filter menu in the search view', + trigger: 'iframe .o_searchview_dropdown_toggler', + content: 'open the search panel menu', }, { - trigger: 'iframe .o_filter_menu > .dropdown-menu > .dropdown-item:first-child', + trigger: 'iframe .o_filter_menu .dropdown-item:first-child', content: 'click on the first item in the filter menu', }, { - trigger: 'iframe .o_group_by_menu > button', - content: 'click on group by menu in the search view', -}, { - trigger: 'iframe .o_group_by_menu > .dropdown-menu > .dropdown-item:first-child', + trigger: 'iframe .o_group_by_menu .dropdown-item:first-child', content: 'click on the first item in the group by menu', -}, { - trigger: 'iframe .o_favorite_menu > button', - content: 'click on the favorite menu in the search view', }, { trigger: 'iframe .o_favorite_menu .o_add_favorite > button', content: 'click to "save current search" button in favorite menu', }, { - trigger: 'iframe .o_filter_menu > button', - content: 'click on filter menu in the search view', -}, { - trigger: 'iframe .o_filter_menu > .dropdown-menu > .dropdown-item:first-child', + trigger: 'iframe .o_filter_menu .dropdown-item:first-child', content: 'click on the first item in the filter menu', }, { - trigger: 'iframe .o_group_by_menu > button', - content: 'click on group by menu in the search view', -}, { - trigger: 'iframe .o_group_by_menu > .dropdown-menu > .dropdown-item:first-child', + trigger: 'iframe .o_group_by_menu .dropdown-item:first-child', content: 'click on the first item in the group by menu', -}, { - trigger: 'iframe .o_favorite_menu > button', - content: 'click on the favorite menu in the search view', }, { trigger: 'iframe .o_favorite_menu .o_add_favorite > button', content: 'click to "save current search" button in favorite menu', diff --git a/addons/purchase_stock/static/src/js/tours/purchase_stock.js b/addons/purchase_stock/static/src/js/tours/purchase_stock.js index 6f626b25add..52f6344eff1 100644 --- a/addons/purchase_stock/static/src/js/tours/purchase_stock.js +++ b/addons/purchase_stock/static/src/js/tours/purchase_stock.js @@ -14,8 +14,8 @@ PurchaseAdditionalTourSteps.include({ _get_purchase_stock_steps: function () { this._super.apply(this, arguments); return [{ - trigger: ".oe_button_box button[name='action_view_picking']", - extra_trigger: ".oe_button_box button[name='action_view_picking']", + trigger: ".o-form-buttonbox button[name='action_view_picking']", + extra_trigger: ".o-form-buttonbox button[name='action_view_picking']", content: _t("Receive the ordered products."), position: "bottom", run: 'click', diff --git a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js index 10468ab7063..f402185ad4c 100644 --- a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js +++ b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js @@ -185,7 +185,7 @@ registry.category("web_tour.tours").add('sale_timesheet_tour', { content: 'Set Project name', run: 'text Project with employee mapping', }, { - trigger: '.dropdown-item[data-menu-xmlid="project.menu_main_pm"]', + trigger: '[data-menu-xmlid="project.menu_projects"]', content: 'Select Project main menu', }, { trigger: '.o_kanban_record:contains("Project for Freeman") .o_dropdown_kanban .dropdown-toggle', @@ -231,7 +231,7 @@ registry.category("web_tour.tours").add('sale_timesheet_tour', { // timer: 300, isCheck: true, }, { - trigger: ".o_rightpanel_section .oe_button_box .o_stat_text:contains('Sales Orders')", + trigger: ".o_rightpanel_section .o-form-buttonbox .o_stat_text:contains('Sales Orders')", content: 'Check the user sees Sales Orders Stat Button', isCheck: true, }, { diff --git a/addons/spreadsheet_dashboard/static/tests/dashboard/dashboard_action_test.js b/addons/spreadsheet_dashboard/static/tests/dashboard/dashboard_action_test.js index fdb2caac705..5412856eb83 100644 --- a/addons/spreadsheet_dashboard/static/tests/dashboard/dashboard_action_test.js +++ b/addons/spreadsheet_dashboard/static/tests/dashboard/dashboard_action_test.js @@ -174,7 +174,7 @@ QUnit.test( const serverData = getServerData(spreadsheetData); const fixture = getFixture(); await createSpreadsheetDashboard({ serverData }); - const year = fixture.querySelector(".o_cp_top_right input.o_datetime_input"); + const year = fixture.querySelector(".o_control_panel_actions input.o_datetime_input"); const this_year = luxon.DateTime.local().year; assert.equal(year.value, String(this_year)); const input = fixture.querySelector("input.o_datetime_input"); @@ -183,10 +183,10 @@ QUnit.test( await nextTick(); assert.equal(year.value, String(this_year - 1)); - assert.containsOnce(fixture, ".o_cp_top_right .fa-times"); - await click(fixture.querySelector(".o_cp_top_right .fa-times")); + assert.containsOnce(fixture, ".o_control_panel_actions .fa-times"); + await click(fixture.querySelector(".o_control_panel_actions .fa-times")); - assert.containsNone(fixture, ".o_cp_top_right .fa-times"); + assert.containsNone(fixture, ".o_control_panel_actions .fa-times"); assert.equal(year.placeholder, "Select year..."); } ); diff --git a/addons/stock/static/tests/inventory_report_list_tests.js b/addons/stock/static/tests/inventory_report_list_tests.js index 3b34c8ae4ca..6aa23fbdfc0 100644 --- a/addons/stock/static/tests/inventory_report_list_tests.js +++ b/addons/stock/static/tests/inventory_report_list_tests.js @@ -88,10 +88,10 @@ QUnit.module( assert.containsN(target, '.o_data_row', 3, "should have 3 records"); // Create a new line... - await click(target.querySelector(".o_list_button_add")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_add")); await editInput(target, "[name=name] input", 'Bilou'); await editInput(target, "[name=age] input", '24'); - await click(target.querySelector(".o_list_button_save")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_save")); // Check new record is in the list assert.containsN(target, '.o_data_row', 4, "should now have 4 records"); @@ -117,10 +117,10 @@ QUnit.module( // Create a new line for an existing record... let name = serverData.models.person.records[2].name, age = '72'; - await click(target.querySelector(".o_list_button_add")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_add")); await editInput(target, "[name=name] input", name); await editInput(target, "[name=age] input", age); - await click(target.querySelector(".o_list_button_save")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_save")); // Check we still have 3 records... assert.containsN(target, '.o_data_row', 3, "should still have 3 records"); @@ -157,7 +157,7 @@ QUnit.module( await click(target, ".o_group_field_row_add a"); await editInput(target, "[name=name] input", name); await editInput(target, "[name=age] input", age); - await click(target.querySelector(".o_list_button_save")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_save")); // Check we have 2 records... assert.containsN(target, '.o_data_row', 2, "should have 2 records"); @@ -167,7 +167,7 @@ QUnit.module( await click(target, ".o_group_field_row_add a"); await editInput(target, "[name=name] input", name); await editInput(target, "[name=age] input", age); - await click(target.querySelector(".o_list_button_save")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_save")); // Check we still have 2 records... assert.containsN(target, '.o_data_row', 2, "should still have 2 records"); @@ -177,7 +177,7 @@ QUnit.module( await editInput(target, "[name=name] input", name); await editInput(target, "[name=age] input", age); await editInput(target, "[name=job] input", "Soldier"); // let it in its original group - await click(target.querySelector(".o_list_button_save")); + await click(target.querySelector(".o_control_panel_main_buttons .d-none.d-xl-inline-flex .o_list_button_save")); // Check we have 3 records... assert.containsN(target, '.o_data_row', 3, "should have 3 records"); diff --git a/addons/stock/static/tests/tours/stock_report_tests.js b/addons/stock/static/tests/tours/stock_report_tests.js index c08aced29f2..bdc6986b342 100644 --- a/addons/stock/static/tests/tours/stock_report_tests.js +++ b/addons/stock/static/tests/tours/stock_report_tests.js @@ -7,7 +7,7 @@ steps: [ { trigger: '.o_kanban_record', - extra_trigger:'.breadcrumb', + extra_trigger: '.o_breadcrumb', }, { trigger: '.nav-item > a:contains("Inventory")', diff --git a/addons/survey/static/tests/tours/survey_form.js b/addons/survey/static/tests/tours/survey_form.js index 3d815570038..6c15780f1d2 100644 --- a/addons/survey/static/tests/tours/survey_form.js +++ b/addons/survey/static/tests/tours/survey_form.js @@ -205,7 +205,7 @@ registry.category("web_tour.tours").add('survey_tour_test_survey_form_triggers', run: () => {}, // it's a check }, { content: 'Go back to Kanban View', - trigger: 'a[data-menu-xmlid="survey.menu_surveys"]', + trigger: '[data-menu-xmlid="survey.menu_survey_form"]', }, { content: "Check that we arrived on the kanban view", trigger: ".o-kanban-button-new", diff --git a/addons/test_mail/static/tests/activity_tests.js b/addons/test_mail/static/tests/activity_tests.js index b68d32b47db..999bc6e9bad 100644 --- a/addons/test_mail/static/tests/activity_tests.js +++ b/addons/test_mail/static/tests/activity_tests.js @@ -14,6 +14,7 @@ import { } from "@web/../tests/helpers/utils"; import { doAction } from "@web/../tests/webclient/helpers"; import { session } from "@web/session"; +import { toggleSearchBarMenu } from "@web/../tests/search/helpers"; let serverData; let pyEnv; @@ -511,19 +512,19 @@ QUnit.module("test_mail", {}, function () { const { webClient } = await start({ serverData, mockRPC }); await doAction(webClient, 1); - + await toggleSearchBarMenu(document); assert.containsN( document.body, - ".o_search_options .dropdown button:visible", + ".o_cp_searchview .o_dropdown_container", 2, "only two elements should be available in view search" ); assert.isVisible( - document.querySelector(".o_search_options .dropdown.o_filter_menu > button"), + document.querySelector(".o_cp_searchview .o_dropdown_container.o_filter_menu"), "filter should be available in view search" ); assert.isVisible( - document.querySelector(".o_search_options .dropdown.o_favorite_menu > button"), + document.querySelector(".o_cp_searchview .o_dropdown_container.o_favorite_menu"), "favorites should be available in view search" ); }); diff --git a/addons/test_mail/static/tests/mobile/activity_tests.js b/addons/test_mail/static/tests/mobile/activity_tests.js index 64b9b68cb62..c6622887069 100644 --- a/addons/test_mail/static/tests/mobile/activity_tests.js +++ b/addons/test_mail/static/tests/mobile/activity_tests.js @@ -15,7 +15,7 @@ QUnit.test( const o_view_controller = document.querySelector(".o_view_controller"); const o_content = o_view_controller.querySelector(".o_content"); const o_cp_item = o_view_controller.querySelector( - ".o_control_panel .breadcrumb-item.active" + ".o_control_panel .o_breadcrumb .active" ); const initialXCpItem = o_cp_item.getBoundingClientRect().x; const o_header_cell = o_content.querySelector(".o_activity_type_cell"); diff --git a/addons/web/static/src/core/popover/popover.js b/addons/web/static/src/core/popover/popover.js index 3497bdc41d0..7f472b50d07 100644 --- a/addons/web/static/src/core/popover/popover.js +++ b/addons/web/static/src/core/popover/popover.js @@ -34,7 +34,8 @@ export class Popover extends Component { } // reset all arrow classes - const arrowEl = el.getElementsByClassName('popover-arrow')[0]; + const arrowEl = el.querySelector(".popover-arrow"); + arrowEl.className = "popover-arrow"; switch (position) { case "tm": // top-middle case "bm": // bottom-middle diff --git a/addons/web/static/src/search/cog_menu/cog_menu.js b/addons/web/static/src/search/cog_menu/cog_menu.js index 7c2a110236a..282604bb688 100644 --- a/addons/web/static/src/search/cog_menu/cog_menu.js +++ b/addons/web/static/src/search/cog_menu/cog_menu.js @@ -23,7 +23,7 @@ export class CogMenu extends Component { static template = "web.CogMenu"; static components = { Dropdown, - } + }; static props = { slots: { type: Object, optional: true }, }; diff --git a/addons/web/static/src/search/control_panel/control_panel.js b/addons/web/static/src/search/control_panel/control_panel.js index a316254b0bc..6b3aef48a31 100644 --- a/addons/web/static/src/search/control_panel/control_panel.js +++ b/addons/web/static/src/search/control_panel/control_panel.js @@ -75,14 +75,18 @@ export class ControlPanel extends Component { useEffect(() => { // on small screen, clean-up the dropdown elements - const dropdownButtons = this.mainButtons.el.querySelectorAll(".o_control_panel_collapsed_create.dropdown-menu button"); + const dropdownButtons = this.mainButtons.el.querySelectorAll( + ".o_control_panel_collapsed_create.dropdown-menu button" + ); if (!dropdownButtons.length) { this.mainButtons.el - .querySelectorAll(".o_control_panel_collapsed_create.dropdown-menu, .o_control_panel_collapsed_create.dropdown-toggle") + .querySelectorAll( + ".o_control_panel_collapsed_create.dropdown-menu, .o_control_panel_collapsed_create.dropdown-toggle" + ) .forEach((el) => el.classList.add("d-none")); this.mainButtons.el - .querySelectorAll(".o_control_panel_collapsed_create.btn-group") - .forEach((el) => el.classList.remove("btn-group")); + .querySelectorAll(".o_control_panel_collapsed_create.btn-group") + .forEach((el) => el.classList.remove("btn-group")); return; } for (const button of dropdownButtons) { @@ -190,7 +194,7 @@ export class ControlPanel extends Component { /** * @param {KeyboardEvent} ev */ - onBottomLeftKeydown(ev) { + onMainButtonsKeydown(ev) { const hotkey = getActiveHotkey(ev); if (hotkey === "arrowdown") { this.env.searchModel.trigger("focus-view"); diff --git a/addons/web/static/src/search/control_panel/control_panel.xml b/addons/web/static/src/search/control_panel/control_panel.xml index f61c6544021..f383d631496 100644 --- a/addons/web/static/src/search/control_panel/control_panel.xml +++ b/addons/web/static/src/search/control_panel/control_panel.xml @@ -5,7 +5,7 @@
-
+
diff --git a/addons/web/static/src/search/search_bar_menu/search_bar_menu.js b/addons/web/static/src/search/search_bar_menu/search_bar_menu.js index 802363a8ad2..3c04519b542 100644 --- a/addons/web/static/src/search/search_bar_menu/search_bar_menu.js +++ b/addons/web/static/src/search/search_bar_menu/search_bar_menu.js @@ -1,9 +1,8 @@ /** @odoo-module **/ -import { AdvancedSearchDialog } from "@web/search/filter_menu/advanced_search_dialog"; import { Component } from "@odoo/owl"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; -import { Domain } from "@web/core/domain"; +import { DomainSelectorDialog } from "../../core/domain_selector_dialog/domain_selector_dialog"; import { Dropdown } from "@web/core/dropdown/dropdown"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { registry } from "@web/core/registry"; @@ -12,6 +11,8 @@ import { useBus, useService } from "@web/core/utils/hooks"; import { CustomGroupByItem } from "@web/search/group_by_menu/custom_group_by_item"; import { SearchDropdownItem } from "@web/search/search_dropdown_item/search_dropdown_item"; import { FACET_ICONS, GROUPABLE_TYPES } from "@web/search/utils/misc"; +import { useGetDefaultLeafDomain } from "@web/core/domain_selector/utils"; +import { _t } from "@web/core/l10n/translation"; const favoriteMenuRegistry = registry.category("favoriteMenu"); @@ -29,7 +30,7 @@ export class SearchBarMenu extends Component { this.facet_icons = FACET_ICONS; // Filter this.dialogService = useService("dialog"); - this.notification = useService("notification"); + this.getDefaultLeafDomain = useGetDefaultLeafDomain(); // GroupBy const fields = []; for (const [fieldName, field] of Object.entries(this.env.searchModel.searchViewFields)) { @@ -51,33 +52,23 @@ export class SearchBarMenu extends Component { ); } - onAdvancedSearchClick() { - const domains = [this.env.searchModel._getDomain({ withGlobal: false })]; - if (this.env.searchModel.comparison && !this.env.searchModel.globalComparison) { - const { range } = this.env.searchModel.getFullComparison(); - domains.push(range); - } - const domain = Domain.and(domains).toString(); - this.dialogService.add(AdvancedSearchDialog, { + async onAddCustomFilterClick() { + const { domainEvalContext: context, resModel } = this.env.searchModel; + const domain = await this.getDefaultLeafDomain(resModel); + this.dialogService.add(DomainSelectorDialog, { + resModel, + defaultConnector: "|", domain, - onConfirm: (domain) => this.onConfirm(domain), - resModel: this.env.searchModel.resModel, + context, + onConfirm: (domain) => this.env.searchModel.splitAndAddDomain(domain), + disableConfirmButton: (domain) => domain === `[]`, + title: _t("Add Custom Filter"), + confirmButtonText: _t("Add"), + discardButtonText: _t("Cancel"), isDebugMode: !!this.env.debug, }); } - async onConfirm(domain) { - const isValid = await this.env.searchModel.isValidDomain(domain); - if (!isValid) { - this.notification.add(this.env._t("Domain is invalid. Please correct it"), { - type: "danger", - }); - return false; - } - this.env.searchModel.createAdvancedDomain(domain); - return true; - } - /** * @param {Object} param0 * @param {number} param0.itemId diff --git a/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml b/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml index 70169f293d6..4e45b4085de 100644 --- a/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml +++ b/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml @@ -54,7 +54,7 @@ diff --git a/addons/web/static/src/views/calendar/calendar_controller.js b/addons/web/static/src/views/calendar/calendar_controller.js index 1c0f50f8c9d..36898b30934 100644 --- a/addons/web/static/src/views/calendar/calendar_controller.js +++ b/addons/web/static/src/views/calendar/calendar_controller.js @@ -65,6 +65,7 @@ export class CalendarController extends Component { createRecord: this.createRecord.bind(this), deleteRecord: this.deleteRecord.bind(this), editRecord: this.editRecord.bind(this), + setDate: this.setDate.bind(this), displayName: this.displayName, }; } diff --git a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss index 3710e1c6308..0c94e0262a4 100644 --- a/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss +++ b/addons/web/static/src/views/fields/many2many_tags_avatar/many2many_tags_avatar_field.scss @@ -62,4 +62,7 @@ padding-bottom: 0.25rem; background-color: var(--o-input-background-color, transparent); } + &.o_field_widget.o_field_many2many_tags_avatar .o_field_many2many_selection .o-autocomplete { + height: inherit; + } } diff --git a/addons/web/static/src/views/fields/percent_pie/percent_pie_field.xml b/addons/web/static/src/views/fields/percent_pie/percent_pie_field.xml index 68a3a68fecf..8ae7f6acbcc 100644 --- a/addons/web/static/src/views/fields/percent_pie/percent_pie_field.xml +++ b/addons/web/static/src/views/fields/percent_pie/percent_pie_field.xml @@ -11,7 +11,7 @@
- +
diff --git a/addons/web/static/src/views/form/form_compiler.js b/addons/web/static/src/views/form/form_compiler.js index a692ffc4f50..bd1fd9b72e5 100644 --- a/addons/web/static/src/views/form/form_compiler.js +++ b/addons/web/static/src/views/form/form_compiler.js @@ -170,10 +170,7 @@ export class FormCompiler extends ViewCompiler { } compileButton(el, params) { - const compiled = super.compileButton(el, params); - compiled.setAttribute("disable", "__comp__.props.disableViewButtons"); - compiled.setAttribute("enable", "__comp__.props.enableViewButtons"); - return compiled; + return super.compileButton(el, params); } /** @@ -648,9 +645,12 @@ export class FormCompiler extends ViewCompiler { append(sheetBG, sheetFG); for (const child of el.childNodes) { const compiled = this.compileNode(child, params); - if (!compiled || compiled.nodeName === "ButtonBox") { + if (!compiled) { continue; } + if (compiled.nodeName === "ButtonBox") { + compiled.setAttribute("t-if", "__comp__.env.inDialog"); + } if (getTag(child, true) === "field") { compiled.setAttribute("showTooltip", true); } diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index 46db5cd192b..f493455342d 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -23,7 +23,7 @@ import { Field } from "@web/views/fields/field"; import { CogMenu } from "@web/search/cog_menu/cog_menu"; import { ActionMenusItems } from "@web/search/cog_menu/action_menus_items"; -import { Component, onRendered, useEffect, useRef, useState } from "@odoo/owl"; +import { Component, onRendered, useEffect, useRef } from "@odoo/owl"; import { useViewCompiler } from "../view_compiler"; import { FormCompiler } from "./form_compiler"; import { evalDomain } from "../utils"; @@ -108,9 +108,6 @@ export class FormController extends Component { this.user = useService("user"); this.viewService = useService("view"); this.ui = useService("ui"); - this.state = useState({ - isDisabled: false, - }); useBus(this.ui.bus, "resize", this.render); this.archInfo = this.props.archInfo; @@ -120,6 +117,8 @@ export class FormController extends Component { this.canCreate = create && !this.props.preventCreate; this.canEdit = edit && !this.props.preventEdit; + this.disabledButtons = null; + let mode = this.props.mode || "edit"; if (!this.canEdit) { mode = "readonly"; @@ -198,8 +197,8 @@ export class FormController extends Component { this.buttonBoxTemplate = buttonBoxTemplates.ButtonBox; } - const rootRef = useRef("root"); - useViewButtons(this.model, rootRef, { + this.rootRef = useRef("root"); + useViewButtons(this.model, this.rootRef, { beforeExecuteAction: this.beforeExecuteActionButton.bind(this), afterExecuteAction: this.afterExecuteActionButton.bind(this), }); @@ -213,7 +212,7 @@ export class FormController extends Component { }; useSetupView({ - rootRef, + rootRef: this.rootRef, beforeLeave: () => this.beforeLeave(), beforeUnload: (ev) => this.beforeUnload(ev), getLocalState: () => { @@ -248,9 +247,11 @@ export class FormController extends Component { (isInEdition) => { if ( !isInEdition && - !rootRef.el.querySelector(".o_content").contains(document.activeElement) + !this.rootRef.el + .querySelector(".o_content") + .contains(document.activeElement) ) { - const elementToFocus = rootRef.el.querySelector( + const elementToFocus = this.rootRef.el.querySelector( ".o_content button.btn-primary" ); if (elementToFocus) { @@ -398,11 +399,18 @@ export class FormController extends Component { } disableButtons() { - this.state.isDisabled = true; + const btns = [...this.rootRef.el.querySelectorAll("button:not([disabled])")]; + for (const btn of btns) { + btn.setAttribute("disabled", "1"); + } + this.disabledButtons = btns; } enableButtons() { - this.state.isDisabled = false; + for (const btn of this.disabledButtons) { + btn.removeAttribute("disabled"); + } + this.disabledButtons = null; } async beforeExecuteActionButton(clickParams) { diff --git a/addons/web/static/src/views/form/form_controller.xml b/addons/web/static/src/views/form/form_controller.xml index 1207d7cef31..eb11532e4c8 100644 --- a/addons/web/static/src/views/form/form_controller.xml +++ b/addons/web/static/src/views/form/form_controller.xml @@ -7,14 +7,14 @@ - + - + @@ -47,15 +47,15 @@ - + - + - +
diff --git a/addons/web/static/src/views/form/form_renderer.js b/addons/web/static/src/views/form/form_renderer.js index c36596a4910..b1f58ce66f3 100644 --- a/addons/web/static/src/views/form/form_renderer.js +++ b/addons/web/static/src/views/form/form_renderer.js @@ -114,8 +114,6 @@ FormRenderer.props = { // Template props : added by the FormCompiler class: { type: String, optional: 1 }, translateAlert: { type: [Object, { value: null }], optional: true }, - enableViewButtons: { type: Function, optional: true }, - disableViewButtons: { type: Function, optional: true }, onNotebookPageChange: { type: Function, optional: true }, activeNotebookPages: { type: Object, optional: true }, setFieldAsDirty: { type: Function, optional: true }, @@ -123,6 +121,4 @@ FormRenderer.props = { FormRenderer.defaultProps = { activeNotebookPages: {}, onNotebookPageChange: () => {}, - enableViewButtons: () => {}, - disableViewButtons: () => {}, }; diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js index 658e43dc34e..54585d78468 100644 --- a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { Component, useState } from "@odoo/owl"; +import { Component, useEffect, useRef, useState } from "@odoo/owl"; import { useBus } from "@web/core/utils/hooks"; export class FormStatusIndicator extends Component { @@ -13,6 +13,18 @@ export class FormStatusIndicator extends Component { "FIELD_IS_DIRTY", (ev) => (this.state.fieldIsDirty = ev.detail) ); + useEffect( + () => { + if (!this.props.model.root.isNew && this.indicatorMode === "invalid") { + this.saveButton.el.setAttribute("disabled", "1"); + } else { + this.saveButton.el.removeAttribute("disabled"); + } + }, + () => [this.props.model.root.isValid] + ); + + this.saveButton = useRef("save"); } get displayButtons() { @@ -43,5 +55,4 @@ FormStatusIndicator.props = { model: Object, save: Function, discard: Function, - isDisabled: Boolean, }; diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml index 095b039a49b..e5fbfd60129 100644 --- a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.xml @@ -7,17 +7,16 @@