From 886f3de768b647f4b402c97098abf274d6258f75 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Fri, 30 Sep 2022 06:23:25 +0000 Subject: [PATCH] [IMP] web,*: force props validation for components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *: base_automation, lunch, mail, mrp, project, web_editor, website This commit adds a warning if the props validation is not set for a component. The props validation is important to tell how a component should be used, by looking at its code, it's a good documentation of the component. It's also critical, to test if the component is correctly used, if all the obligatory props are passed and that there are of the correct type. For more information, see: https://github.com/odoo/owl/blob/master/doc/reference/props.md#props-validation closes odoo/odoo#103723 Related: odoo/enterprise#33044 Signed-off-by: Géry Debongnie --- .../static/src/components/lunch_dashboard.xml | 2 + .../activity_menu_container.js | 1 + .../call_systray_menu_container.js | 1 + .../chat_window_manager_container.js | 1 + .../dialog_manager_container.js | 1 + .../messaging_menu_container.js | 1 + .../notification_alert_container.js | 5 ++ .../popover_manager_container.js | 1 + .../static/src/views/form/form_renderer.js | 7 +++ .../tests/helpers/model_definitions_setup.js | 2 +- addons/mrp/static/src/widgets/timer.js | 7 +++ .../src/project_sharing/project_sharing.js | 1 + .../src/core/commands/command_palette.js | 16 +++++ .../web/static/src/core/debug/debug_menu.js | 1 + .../src/core/dialog/dialog_container.js | 3 + .../src/core/effects/effect_container.js | 5 +- .../static/src/core/effects/rainbow_man.js | 8 +++ .../static/src/core/errors/error_dialogs.js | 21 +++++++ .../static/src/core/errors/error_handlers.js | 1 + .../src/core/main_components_container.js | 1 + .../notifications/notification_container.js | 3 + .../src/core/popover/popover_container.js | 12 +++- addons/web/static/src/core/ui/block_ui.js | 5 +- .../web/static/src/core/utils/components.js | 3 +- .../web/static/src/legacy/legacy_component.js | 3 + .../src/search/control_panel/control_panel.js | 4 ++ .../search/control_panel/control_panel.xml | 2 +- .../favorite_menu/custom_favorite_item.js | 1 + .../src/search/favorite_menu/favorite_menu.js | 1 + .../search/filter_menu/custom_filter_item.js | 1 + .../src/search/filter_menu/filter_menu.js | 3 + .../group_by_menu/custom_group_by_item.js | 4 ++ .../src/search/group_by_menu/group_by_menu.js | 5 +- .../src/search/search_bar/search_bar.js | 5 +- addons/web/static/src/start.js | 1 + .../src/views/fields/binary/binary_field.xml | 1 - addons/web/static/src/views/fields/field.js | 1 + .../static/src/views/fields/file_handler.js | 9 +++ .../src/views/fields/image/image_field.xml | 1 - .../many2many_tags_avatar_field.xml | 2 +- .../fields/pdf_viewer/pdf_viewer_field.xml | 1 - .../src/views/fields/relational_utils.js | 21 +++++++ .../src/views/fields/relational_utils.xml | 2 +- .../static/src/views/form/form_compiler.js | 1 - .../static/src/views/form/form_controller.js | 2 +- .../web/static/src/views/form/form_label.js | 8 +++ .../static/src/views/form/form_renderer.js | 14 +++++ .../status_bar_buttons/status_bar_buttons.js | 3 + .../kanban/kanban_column_quick_create.js | 7 +++ .../kanban/kanban_dropdown_menu_wrapper.js | 3 + .../kanban/kanban_record_quick_create.js | 7 +++ .../src/views/pivot/pivot_group_by_menu.js | 7 +++ .../static/src/views/pivot/pivot_renderer.xml | 1 - addons/web/static/src/views/view.js | 4 +- .../view_dialogs/select_create_dialog.js | 28 ++++----- addons/web/static/src/views/widgets/widget.js | 3 + .../src/webclient/actions/action_container.js | 1 + .../src/webclient/actions/action_service.js | 9 +++ .../src/webclient/burger_menu/burger_menu.js | 1 + .../loading_indicator/loading_indicator.js | 1 + .../src/webclient/menus/menu_providers.js | 6 ++ .../web/static/src/webclient/navbar/navbar.js | 1 + .../form_label_highlight_text.js | 8 +++ .../settings_form_controller.js | 2 +- .../settings_form_renderer.js | 5 ++ .../switch_company_menu.js | 1 + .../src/webclient/user_menu/user_menu.js | 1 + addons/web/static/src/webclient/webclient.js | 1 + .../tests/core/errors/error_service_tests.js | 2 + addons/web/static/tests/search/helpers.js | 58 +++++++++++++++---- .../tests/views/form/form_compiler_tests.js | 8 +-- addons/web/static/tests/views/helpers.js | 2 +- .../select_create_dialog_tests.js | 1 - .../upload_progress_toast.js | 11 ++++ .../upload_progress_toast.xml | 3 +- .../fullscreen_indication.js | 5 +- .../website_loader/website_loader.js | 5 +- 77 files changed, 343 insertions(+), 54 deletions(-) diff --git a/addons/lunch/static/src/components/lunch_dashboard.xml b/addons/lunch/static/src/components/lunch_dashboard.xml index 36226c4315a..59a8c02552b 100644 --- a/addons/lunch/static/src/components/lunch_dashboard.xml +++ b/addons/lunch/static/src/components/lunch_dashboard.xml @@ -49,6 +49,7 @@ value="props.username" resModel="'res.users'" getDomain="getDomain" + fieldString="props.username" activeActions="{}" update.bind="props.onUpdateUser" /> @@ -61,6 +62,7 @@ value="props.location" resModel="'lunch.location'" getDomain="getDomain" + fieldString="props.location" activeActions="{}" update.bind="props.onUpdateLunchLocation" /> diff --git a/addons/mail/static/src/components/activity_menu_container/activity_menu_container.js b/addons/mail/static/src/components/activity_menu_container/activity_menu_container.js index 0980f769bd0..b9d7f260123 100644 --- a/addons/mail/static/src/components/activity_menu_container/activity_menu_container.js +++ b/addons/mail/static/src/components/activity_menu_container/activity_menu_container.js @@ -20,6 +20,7 @@ export class ActivityMenuContainer extends Component { } } +ActivityMenuContainer.props = {}; Object.assign(ActivityMenuContainer, { components: { ActivityMenuView: getMessagingComponent('ActivityMenuView') }, diff --git a/addons/mail/static/src/components/call_systray_menu_container/call_systray_menu_container.js b/addons/mail/static/src/components/call_systray_menu_container/call_systray_menu_container.js index 153c9a47b91..2da088e3b75 100644 --- a/addons/mail/static/src/components/call_systray_menu_container/call_systray_menu_container.js +++ b/addons/mail/static/src/components/call_systray_menu_container/call_systray_menu_container.js @@ -22,6 +22,7 @@ export class CallSystrayMenuContainer extends Component { } } +CallSystrayMenuContainer.props = {}; Object.assign(CallSystrayMenuContainer, { components: { CallSystrayMenu: getMessagingComponent('CallSystrayMenu') }, diff --git a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js index e5f5b64ba43..77607a99667 100644 --- a/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js +++ b/addons/mail/static/src/components/chat_window_manager_container/chat_window_manager_container.js @@ -21,6 +21,7 @@ export class ChatWindowManagerContainer extends Component { return this.env.services.messaging.modelManager.messaging; } } +ChatWindowManagerContainer.props = {}; Object.assign(ChatWindowManagerContainer, { components: { ChatWindowManager: getMessagingComponent('ChatWindowManager') }, diff --git a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js index 2024aef1cd0..2431254c666 100644 --- a/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js +++ b/addons/mail/static/src/components/dialog_manager_container/dialog_manager_container.js @@ -21,6 +21,7 @@ export class DialogManagerContainer extends Component { return this.env.services.messaging.modelManager.messaging; } } +DialogManagerContainer.props = {}; Object.assign(DialogManagerContainer, { components: { DialogManager: getMessagingComponent('DialogManager') }, diff --git a/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js b/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js index 5a45b20a226..59c96ad3b72 100644 --- a/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js +++ b/addons/mail/static/src/components/messaging_menu_container/messaging_menu_container.js @@ -21,6 +21,7 @@ export class MessagingMenuContainer extends Component { return this.env.services.messaging.modelManager.messaging; } } +MessagingMenuContainer.props = {}; Object.assign(MessagingMenuContainer, { components: { MessagingMenu: getMessagingComponent('MessagingMenu') }, diff --git a/addons/mail/static/src/components/notification_alert_container/notification_alert_container.js b/addons/mail/static/src/components/notification_alert_container/notification_alert_container.js index cb1c8ba46fb..1360ea06eb2 100644 --- a/addons/mail/static/src/components/notification_alert_container/notification_alert_container.js +++ b/addons/mail/static/src/components/notification_alert_container/notification_alert_container.js @@ -9,6 +9,11 @@ export class NotificationAlertContainer extends Component {} Object.assign(NotificationAlertContainer, { components: { NotificationAlert: getMessagingComponent('NotificationAlert') }, template: 'mail.NotificationAlertContainer', + props: { + // Should call standard_widget_props.js + readonly: { type: Boolean, optional: true }, + record: { type: Object }, + }, }); registry.category("view_widgets").add( "notification_alert", diff --git a/addons/mail/static/src/components/popover_manager_container/popover_manager_container.js b/addons/mail/static/src/components/popover_manager_container/popover_manager_container.js index 7d04816c272..9cdbdd33aa6 100644 --- a/addons/mail/static/src/components/popover_manager_container/popover_manager_container.js +++ b/addons/mail/static/src/components/popover_manager_container/popover_manager_container.js @@ -21,6 +21,7 @@ export class PopoverManagerContainer extends Component { return this.env.services.messaging.modelManager.messaging; } } +PopoverManagerContainer.props = {}; Object.assign(PopoverManagerContainer, { components: { PopoverManager: getMessagingComponent('PopoverManager') }, diff --git a/addons/mail/static/src/views/form/form_renderer.js b/addons/mail/static/src/views/form/form_renderer.js index c9fca166eea..e59b82c60b0 100644 --- a/addons/mail/static/src/views/form/form_renderer.js +++ b/addons/mail/static/src/views/form/form_renderer.js @@ -15,6 +15,13 @@ patch(FormRenderer.prototype, 'mail', { }, }); +patch(FormRenderer.props, "mail", { + hasAttachmentViewerInArch: { type: Boolean, optional: true }, + // Template props : added by the FormCompiler + hasAttachmentViewer: { type: Boolean, optional: true }, + chatter: { type: Object, optional: true }, +}); + Object.assign(FormRenderer.components, { ChatterContainer, WebClientViewAttachmentViewContainer, diff --git a/addons/mail/static/tests/helpers/model_definitions_setup.js b/addons/mail/static/tests/helpers/model_definitions_setup.js index 9efad779e15..ef8ce699495 100644 --- a/addons/mail/static/tests/helpers/model_definitions_setup.js +++ b/addons/mail/static/tests/helpers/model_definitions_setup.js @@ -28,7 +28,7 @@ addFakeModel('res.fake', { addFakeModel('m2x.avatar.user', { user_id: { type: 'many2one', relation: 'res.users' }, - user_ids: { type: 'many2many', relation: 'res.users' }, + user_ids: { type: 'many2many', relation: 'res.users', string: 'Users'}, }); //-------------------------------------------------------------------------- diff --git a/addons/mrp/static/src/widgets/timer.js b/addons/mrp/static/src/widgets/timer.js index c8111f32251..0e64226be2a 100644 --- a/addons/mrp/static/src/widgets/timer.js +++ b/addons/mrp/static/src/widgets/timer.js @@ -3,6 +3,7 @@ import { registry } from "@web/core/registry"; import { parseFloatTime } from "@web/views/fields/parsers"; import { useInputField } from "@web/views/fields/input_field_hook"; +import { standardFieldProps } from "@web/views/fields/standard_field_props"; const { Component, useState, onWillUpdateProps, onWillStart, onWillDestroy } = owl; @@ -73,6 +74,12 @@ export class MrpTimer extends Component { } MrpTimer.supportedTypes = ["float"]; +MrpTimer.props = { + ...standardFieldProps, + duration: { type: Number, optional: true }, + ongoing: { type: Boolean, optional: true }, + value: { optional: true }, +}; MrpTimer.template = "mrp.MrpTimer"; registry.category("fields").add("mrp_timer", MrpTimer); diff --git a/addons/project/static/src/project_sharing/project_sharing.js b/addons/project/static/src/project_sharing/project_sharing.js index 0983f1388fb..dabca7e38e1 100644 --- a/addons/project/static/src/project_sharing/project_sharing.js +++ b/addons/project/static/src/project_sharing/project_sharing.js @@ -66,5 +66,6 @@ export class ProjectSharingWebClient extends Component { } } +ProjectSharingWebClient.props = {}; ProjectSharingWebClient.components = { ActionContainer, MainComponentsContainer }; ProjectSharingWebClient.template = 'project.ProjectSharingWebClient'; diff --git a/addons/web/static/src/core/commands/command_palette.js b/addons/web/static/src/core/commands/command_palette.js index b72d3739a82..0dc29ecc611 100644 --- a/addons/web/static/src/core/commands/command_palette.js +++ b/addons/web/static/src/core/commands/command_palette.js @@ -15,6 +15,7 @@ const { Component, onWillStart, onWillDestroy, + EventBus, useRef, useState, markRaw, @@ -87,6 +88,15 @@ export function splitCommandName(name, searchValue) { export class DefaultCommandItem extends Component {} DefaultCommandItem.template = "web.DefaultCommandItem"; +DefaultCommandItem.props = { + slots: { type: Object, optional: true }, + // Props send by the command palette: + hotkey: { type: String, optional: true }, + hotkeyOptions: { type: String, optional: true }, + name: { type: String, optional: true }, + searchValue: { type: String, optional: true }, + executeCommand: { type: Function, optional: true }, +}; export class CommandPalette extends Component { setup() { @@ -366,5 +376,11 @@ export class CommandPalette extends Component { } } CommandPalette.lastSessionId = 0; +CommandPalette.props = { + bus: { type: EventBus, optional: true }, + close: Function, + config: Object, + closeMe: { type: Function, optional: true }, +}; CommandPalette.template = "web.CommandPalette"; CommandPalette.components = { Dialog }; diff --git a/addons/web/static/src/core/debug/debug_menu.js b/addons/web/static/src/core/debug/debug_menu.js index aca4019d6ab..9b9993d726e 100644 --- a/addons/web/static/src/core/debug/debug_menu.js +++ b/addons/web/static/src/core/debug/debug_menu.js @@ -59,3 +59,4 @@ export class DebugMenu extends DebugMenuBasic { } } DebugMenu.components = { Dropdown, DropdownItem }; +DebugMenu.props = {}; diff --git a/addons/web/static/src/core/dialog/dialog_container.js b/addons/web/static/src/core/dialog/dialog_container.js index 51c53b44e9a..f9bfb874e55 100644 --- a/addons/web/static/src/core/dialog/dialog_container.js +++ b/addons/web/static/src/core/dialog/dialog_container.js @@ -13,6 +13,9 @@ export class DialogContainer extends Component { } } DialogContainer.components = { ErrorHandler, WithEnv }; +DialogContainer.props = { + dialogs: Object, +}; //Legacy : The div wrapping the t-foreach, is placed to avoid owl to delete non-owl dialogs. //This div can be removed after removing all legacy dialogs. DialogContainer.template = xml` diff --git a/addons/web/static/src/core/effects/effect_container.js b/addons/web/static/src/core/effects/effect_container.js index 61a663e015e..47a9730ddce 100644 --- a/addons/web/static/src/core/effects/effect_container.js +++ b/addons/web/static/src/core/effects/effect_container.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -const { Component, xml, onWillDestroy } = owl; +const { EventBus, Component, xml, onWillDestroy } = owl; export class EffectContainer extends Component { setup() { @@ -18,6 +18,9 @@ export class EffectContainer extends Component { this.render(); } } +EffectContainer.props = { + bus: EventBus, +}; EffectContainer.template = xml`
diff --git a/addons/web/static/src/core/effects/rainbow_man.js b/addons/web/static/src/core/effects/rainbow_man.js index a6e4e3c87e0..9de597727c6 100644 --- a/addons/web/static/src/core/effects/rainbow_man.js +++ b/addons/web/static/src/core/effects/rainbow_man.js @@ -63,4 +63,12 @@ export class RainbowMan extends Component { } } RainbowMan.template = "web.RainbowMan"; +RainbowMan.props = { + fadeout: String, + close: Function, + message: String, + imgUrl: String, + Component: { type: Function, optional: true }, + props: { type: Object, optional: true }, +}; RainbowMan.rainbowFadeouts = { slow: 4500, medium: 3500, fast: 2000, no: false }; diff --git a/addons/web/static/src/core/errors/error_dialogs.js b/addons/web/static/src/core/errors/error_dialogs.js index 38fee8c3d8b..ebd4a8b66d8 100644 --- a/addons/web/static/src/core/errors/error_dialogs.js +++ b/addons/web/static/src/core/errors/error_dialogs.js @@ -9,6 +9,19 @@ import { capitalize } from "../utils/strings"; const { Component, useState } = owl; +// This props are added by the error handler +export const standardErrorDialogProps = { + traceback: { type: [String, { value: null }], optional: true }, + message: { type: String, optional: true }, + name: { type: String, optional: true }, + exceptionName: { type: [String, { value: null }], optional: true }, + data: { type: [Object, { value: null }], optional: true }, + subType: { type: [String, { value: null }], optional: true }, + code: { type: [Number, String, { value: null }], optional: true }, + type: { type: [String, { value: null }], optional: true }, + close: Function, // prop added by the Dialog service +}; + export const odooExceptionTitleMap = new Map( Object.entries({ "odoo.addons.base.models.ir_mail_server.MailDeliveryException": _lt( @@ -41,6 +54,7 @@ export class ErrorDialog extends Component { ErrorDialog.template = "web.ErrorDialog"; ErrorDialog.components = { Dialog }; ErrorDialog.title = _lt("Odoo Error"); +ErrorDialog.props = { ...standardErrorDialogProps }; // ----------------------------------------------------------------------------- // Client Error Dialog @@ -118,6 +132,10 @@ export class WarningDialog extends Component { } WarningDialog.template = "web.WarningDialog"; WarningDialog.components = { Dialog }; +WarningDialog.props = { + ...standardErrorDialogProps, + title: { type: String, optional: true }, +}; // ----------------------------------------------------------------------------- // Redirect Warning Dialog @@ -144,6 +162,7 @@ export class RedirectWarningDialog extends Component { } RedirectWarningDialog.template = "web.RedirectWarningDialog"; RedirectWarningDialog.components = { Dialog }; +RedirectWarningDialog.props = { ...standardErrorDialogProps }; // ----------------------------------------------------------------------------- // Error 504 Dialog @@ -152,6 +171,7 @@ export class Error504Dialog extends Component {} Error504Dialog.template = "web.Error504Dialog"; Error504Dialog.components = { Dialog }; Error504Dialog.title = _lt("Request timeout"); +Error504Dialog.props = { ...standardErrorDialogProps }; // ----------------------------------------------------------------------------- // Expired Session Error Dialog @@ -164,6 +184,7 @@ export class SessionExpiredDialog extends Component { SessionExpiredDialog.template = "web.SessionExpiredDialog"; SessionExpiredDialog.components = { Dialog }; SessionExpiredDialog.title = _lt("Odoo Session Expired"); +SessionExpiredDialog.props = { ...standardErrorDialogProps }; registry .category("error_dialogs") diff --git a/addons/web/static/src/core/errors/error_handlers.js b/addons/web/static/src/core/errors/error_handlers.js index 3a35daec50a..1c32d6f3608 100644 --- a/addons/web/static/src/core/errors/error_handlers.js +++ b/addons/web/static/src/core/errors/error_handlers.js @@ -117,6 +117,7 @@ export function rpcErrorHandler(env, error, originalError) { return true; } } + errorHandlerRegistry.add("rpcErrorHandler", rpcErrorHandler, { sequence: 97 }); // ----------------------------------------------------------------------------- diff --git a/addons/web/static/src/core/main_components_container.js b/addons/web/static/src/core/main_components_container.js index a4bb7908be3..25478539eaa 100644 --- a/addons/web/static/src/core/main_components_container.js +++ b/addons/web/static/src/core/main_components_container.js @@ -40,3 +40,4 @@ MainComponentsContainer.template = xml`
`; MainComponentsContainer.components = { ErrorHandler }; +MainComponentsContainer.props = {}; diff --git a/addons/web/static/src/core/notifications/notification_container.js b/addons/web/static/src/core/notifications/notification_container.js index 0eb9617186f..cd67b4ff540 100644 --- a/addons/web/static/src/core/notifications/notification_container.js +++ b/addons/web/static/src/core/notifications/notification_container.js @@ -10,6 +10,9 @@ export class NotificationContainer extends Component { this.notifications = useState(this.props.notifications); } } +NotificationContainer.props = { + notifications: Object, +}; NotificationContainer.template = xml`
diff --git a/addons/web/static/src/core/popover/popover_container.js b/addons/web/static/src/core/popover/popover_container.js index c5e04ce4dd2..c87fd2bbfa9 100644 --- a/addons/web/static/src/core/popover/popover_container.js +++ b/addons/web/static/src/core/popover/popover_container.js @@ -2,7 +2,7 @@ import { Popover } from "./popover"; -const { Component, onMounted, onWillUnmount, useExternalListener, useState, xml } = owl; +const { EventBus, Component, onMounted, onWillUnmount, useExternalListener, useState, xml } = owl; class PopoverController extends Component { setup() { @@ -44,8 +44,10 @@ class PopoverController extends Component { } } onClickAway(ev) { - if (this.target.contains(ev.target) - || ev.target.closest(`.o_popover[popover-id="${this.props.id}"]`)) { + if ( + this.target.contains(ev.target) || + ev.target.closest(`.o_popover[popover-id="${this.props.id}"]`) + ) { return; } if (this.props.preventClose && this.props.preventClose(ev)) { @@ -80,6 +82,10 @@ export class PopoverContainer extends Component { } } PopoverContainer.components = { PopoverController }; +PopoverContainer.props = { + popovers: Object, + bus: EventBus, +}; PopoverContainer.template = xml`
diff --git a/addons/web/static/src/core/ui/block_ui.js b/addons/web/static/src/core/ui/block_ui.js index 8cd95abf8ba..8ed0cabd432 100644 --- a/addons/web/static/src/core/ui/block_ui.js +++ b/addons/web/static/src/core/ui/block_ui.js @@ -2,7 +2,7 @@ import { browser } from "@web/core/browser/browser"; -const { Component, useState, xml } = owl; +const { EventBus, Component, useState, xml } = owl; export class BlockUI extends Component { setup() { @@ -69,6 +69,9 @@ export class BlockUI extends Component { this.state.line2 = ""; } } +BlockUI.props = { + bus: EventBus, +}; BlockUI.template = xml`
diff --git a/addons/web/static/src/core/utils/components.js b/addons/web/static/src/core/utils/components.js index e0dcd5e19e5..e15cae3389c 100644 --- a/addons/web/static/src/core/utils/components.js +++ b/addons/web/static/src/core/utils/components.js @@ -10,7 +10,7 @@ export class ErrorHandler extends Component { } } ErrorHandler.template = xml``; -ErrorEvent.props = ["onError"]; +ErrorHandler.props = ["onError", "slots"]; export class WithEnv extends Component { setup() { @@ -18,3 +18,4 @@ export class WithEnv extends Component { } } WithEnv.template = xml``; +WithEnv.props = ["env", "slots"]; diff --git a/addons/web/static/src/legacy/legacy_component.js b/addons/web/static/src/legacy/legacy_component.js index f16d9d693d0..b88e5b338a4 100644 --- a/addons/web/static/src/legacy/legacy_component.js +++ b/addons/web/static/src/legacy/legacy_component.js @@ -90,3 +90,6 @@ export class LegacyComponent extends owl.Component { } } } +LegacyComponent.props = { + "*": 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 2f8c47d5c3f..e07aa7f67ea 100644 --- a/addons/web/static/src/search/control_panel/control_panel.js +++ b/addons/web/static/src/search/control_panel/control_panel.js @@ -208,3 +208,7 @@ ControlPanel.components = { Dropdown, }; ControlPanel.template = "web.ControlPanel"; +ControlPanel.props = { + display: { type: Object, optional: true }, + slots: { type: Object, optional: true }, +}; 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 ff8270e3edb..75c1fc0f003 100644 --- a/addons/web/static/src/search/control_panel/control_panel.xml +++ b/addons/web/static/src/search/control_panel/control_panel.xml @@ -119,7 +119,7 @@ /> - +
`; +ActionContainer.props = {}; diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js index 9b1feb356d4..2a1be80d135 100644 --- a/addons/web/static/src/webclient/actions/action_service.js +++ b/addons/web/static/src/webclient/actions/action_service.js @@ -48,6 +48,12 @@ export async function clearUncommittedChanges(env) { return !res.includes(false); } +export const standardActionServiceProps = { + action: Object, // prop added by _getActionInfo + actionId: { type: Number, optional: true }, // prop added by _getActionInfo + className: String, // prop added by the ActionContainer +}; + function parseActiveIds(ids) { const activeIds = []; if (typeof ids === "string") { @@ -734,6 +740,9 @@ function makeActionManager(env) { } ControllerComponent.template = ControllerComponentTemplate; ControllerComponent.Component = controller.Component; + ControllerComponent.props = { + "*": true, + }; let nextDialog = null; if (action.target === "new") { diff --git a/addons/web/static/src/webclient/burger_menu/burger_menu.js b/addons/web/static/src/webclient/burger_menu/burger_menu.js index 1e2e15d83f6..f3d767537d1 100644 --- a/addons/web/static/src/webclient/burger_menu/burger_menu.js +++ b/addons/web/static/src/webclient/burger_menu/burger_menu.js @@ -85,6 +85,7 @@ BurgerMenu.components = { MobileSwitchCompanyMenu, Transition, }; +BurgerMenu.props = {}; const systrayItem = { Component: BurgerMenu, diff --git a/addons/web/static/src/webclient/loading_indicator/loading_indicator.js b/addons/web/static/src/webclient/loading_indicator/loading_indicator.js index 0a90f29c704..c9b5f4f7f1b 100644 --- a/addons/web/static/src/webclient/loading_indicator/loading_indicator.js +++ b/addons/web/static/src/webclient/loading_indicator/loading_indicator.js @@ -70,6 +70,7 @@ export class LoadingIndicator extends Component { LoadingIndicator.template = "web.LoadingIndicator"; LoadingIndicator.components = { Transition }; +LoadingIndicator.props = {}; registry.category("main_components").add("LoadingIndicator", { Component: LoadingIndicator, diff --git a/addons/web/static/src/webclient/menus/menu_providers.js b/addons/web/static/src/webclient/menus/menu_providers.js index 73c9a5b9526..85022ec9784 100644 --- a/addons/web/static/src/webclient/menus/menu_providers.js +++ b/addons/web/static/src/webclient/menus/menu_providers.js @@ -4,11 +4,17 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { fuzzyLookup } from "@web/core/utils/search"; import { computeAppsAndMenuItems } from "@web/webclient/menus/menu_helpers"; +import { DefaultCommandItem } from "@web/core/commands/command_palette"; const { Component } = owl; class AppIconCommand extends Component {} AppIconCommand.template = "web.AppIconCommand"; +AppIconCommand.props = { + webIconData: { type: String, optional: true }, + webIcon: { type: Object, optional: true }, + ...DefaultCommandItem.props, +}; const commandCategoryRegistry = registry.category("command_categories"); commandCategoryRegistry.add("apps", { namespace: "/" }, { sequence: 10 }); diff --git a/addons/web/static/src/webclient/navbar/navbar.js b/addons/web/static/src/webclient/navbar/navbar.js index 00c806d40ef..3548bb2f267 100644 --- a/addons/web/static/src/webclient/navbar/navbar.js +++ b/addons/web/static/src/webclient/navbar/navbar.js @@ -202,3 +202,4 @@ export class NavBar extends Component { } NavBar.template = "web.NavBar"; NavBar.components = { Dropdown, DropdownItem, MenuDropdown, ErrorHandler }; +NavBar.props = {}; diff --git a/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js b/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js index 6013b368c4d..48baf45be5e 100644 --- a/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js +++ b/addons/web/static/src/webclient/settings_form_view/highlight_text/form_label_highlight_text.js @@ -25,3 +25,11 @@ export class FormLabelHighlightText extends FormLabel { FormLabelHighlightText.template = "web.FormLabelHighlightText"; FormLabelHighlightText.components = { HighlightText }; +FormLabelHighlightText.props = { + fieldInfo: { type: Object, optional: true }, + className: { type: String, optional: true }, + record: { type: Object, optional: true }, + fieldName: { type: String, optional: true }, + string: { type: String }, + id: { type: String }, +}; diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js b/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js index 713870ccf0a..de6963c6d5f 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js @@ -40,7 +40,7 @@ export class SettingsFormController extends formView.Controller { } }); - this.initialApp = "module" in this.props.context && this.props.context.module; + this.initialApp = "module" in this.props.context ? this.props.context.module : ""; } /** diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js b/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js index 2511249d0a3..c84bdb702f5 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_renderer.js @@ -59,3 +59,8 @@ SettingsFormRenderer.components = { HighlightText, FormLabel: FormLabelHighlightText, }; +SettingsFormRenderer.props = { + ...FormRenderer.props, + initialApp: String, + slots: Object, +}; diff --git a/addons/web/static/src/webclient/switch_company_menu/switch_company_menu.js b/addons/web/static/src/webclient/switch_company_menu/switch_company_menu.js index aa508bb255a..4462a36058c 100644 --- a/addons/web/static/src/webclient/switch_company_menu/switch_company_menu.js +++ b/addons/web/static/src/webclient/switch_company_menu/switch_company_menu.js @@ -41,6 +41,7 @@ export class SwitchCompanyMenu extends Component { SwitchCompanyMenu.template = "web.SwitchCompanyMenu"; SwitchCompanyMenu.components = { Dropdown, DropdownItem }; SwitchCompanyMenu.toggleDelay = 1000; +SwitchCompanyMenu.props = {}; export const systrayItem = { Component: SwitchCompanyMenu, diff --git a/addons/web/static/src/webclient/user_menu/user_menu.js b/addons/web/static/src/webclient/user_menu/user_menu.js index fb8a9cdc244..011fa8db9f3 100644 --- a/addons/web/static/src/webclient/user_menu/user_menu.js +++ b/addons/web/static/src/webclient/user_menu/user_menu.js @@ -33,6 +33,7 @@ export class UserMenu extends Component { } UserMenu.template = "web.UserMenu"; UserMenu.components = { Dropdown, DropdownItem, CheckBox }; +UserMenu.props = {}; export const systrayItem = { Component: UserMenu, diff --git a/addons/web/static/src/webclient/webclient.js b/addons/web/static/src/webclient/webclient.js index 56c0a76faf7..646b68d14e6 100644 --- a/addons/web/static/src/webclient/webclient.js +++ b/addons/web/static/src/webclient/webclient.js @@ -114,3 +114,4 @@ WebClient.components = { MainComponentsContainer, }; WebClient.template = "web.WebClient"; +WebClient.props = {}; diff --git a/addons/web/static/tests/core/errors/error_service_tests.js b/addons/web/static/tests/core/errors/error_service_tests.js index 012425719a6..f5e60883248 100644 --- a/addons/web/static/tests/core/errors/error_service_tests.js +++ b/addons/web/static/tests/core/errors/error_service_tests.js @@ -6,6 +6,7 @@ import { ClientErrorDialog, RPCErrorDialog, NetworkErrorDialog, + standardErrorDialogProps, } from "@web/core/errors/error_dialogs"; import { errorService } from "@web/core/errors/error_service"; import { ConnectionLostError, RPCError } from "@web/core/network/rpc_service"; @@ -98,6 +99,7 @@ QUnit.test( class CustomDialog extends Component {} CustomDialog.template = xml``; CustomDialog.components = { RPCErrorDialog }; + CustomDialog.props = { ...standardErrorDialogProps }; const error = new RPCError(); error.code = 701; error.message = "Some strange error occured"; diff --git a/addons/web/static/tests/search/helpers.js b/addons/web/static/tests/search/helpers.js index 01f4b6f5d19..5a9f7e1faf6 100644 --- a/addons/web/static/tests/search/helpers.js +++ b/addons/web/static/tests/search/helpers.js @@ -60,22 +60,24 @@ export async function makeWithSearch(params) { class Parent extends Component { setup() { this.withSearchProps = props; - this.componentProps = componentProps; } - getDisplay(display) { - return Object.assign({}, display, componentProps.display); + + getProps(search) { + const props = Object.assign({}, componentProps, { + context: search.context, + domain: search.domain, + groupBy: search.groupBy, + orderBy: search.orderBy, + comparison: search.comparison, + display: Object.assign({}, search.display, componentProps.display), + }); + return filterPropsForComponent(params.Component, props); } } + Parent.template = xml` - + `; Parent.components = { Component: params.Component, WithSearch }; @@ -89,6 +91,40 @@ export async function makeWithSearch(params) { return component; } +/** This function is aim to be used only in the tests. + * It will filter the props that are needed by the Component. + * This is to avoid errors of props validation. This occurs for example, on ControlPanel tests. + * In production, View use WithSearch for the Controllers, and the Layout send only the props that + * need to the ControlPanel. + * + * @param {Component} Component + * @param {Object} props + * @returns {Object} filtered props + */ +function filterPropsForComponent(Component, props) { + // This if, can be removed once all the Components have the props defined + if (Component.props) { + let componentKeys = null; + if (Component.props instanceof Array) { + componentKeys = Component.props.map((x) => x.replace("?", "")); + } else { + componentKeys = Object.keys(Component.props); + } + if (componentKeys.includes("*")) { + return props; + } else { + return Object.keys(props) + .filter((k) => componentKeys.includes(k)) + .reduce((o, k) => { + o[k] = props[k]; + return o; + }, {}); + } + } else { + return props; + } +} + function getUniqueChild(node) { return Object.values(node.children)[0]; } diff --git a/addons/web/static/tests/views/form/form_compiler_tests.js b/addons/web/static/tests/views/form/form_compiler_tests.js index a469c3f3e07..1e116f06ac8 100644 --- a/addons/web/static/tests/views/form/form_compiler_tests.js +++ b/addons/web/static/tests/views/form/form_compiler_tests.js @@ -184,7 +184,7 @@ QUnit.module("Form Compiler", (hooks) => { const expected = /*xml*/ `
-
+
someDiv
`; @@ -207,7 +207,7 @@ QUnit.module("Form Compiler", (hooks) => {
-
+
someDiv
inside sheet
@@ -268,7 +268,7 @@ QUnit.module("Form Compiler", (hooks) => { const expected = /*xml*/ `
- +
someDiv
@@ -286,7 +286,7 @@ QUnit.module("Form Compiler", (hooks) => { const expected = /*xml*/ `
- +
`; assert.areContentEquivalent(compileTemplate(arch), expected); diff --git a/addons/web/static/tests/views/helpers.js b/addons/web/static/tests/views/helpers.js index 586b79fd57b..ae043c5b742 100644 --- a/addons/web/static/tests/views/helpers.js +++ b/addons/web/static/tests/views/helpers.js @@ -88,7 +88,7 @@ export async function makeView(params) { const target = getFixture(); const viewEnv = Object.assign(Object.create(env), { config }); const view = await mount(View, target, { env: viewEnv, props }); - await mount(MainComponentsContainer, target, { env, props }); + await mount(MainComponentsContainer, target, { env }); const viewNode = view.__owl__; const withSearchNode = Object.values(viewNode.children)[0]; diff --git a/addons/web/static/tests/views/view_dialogs/select_create_dialog_tests.js b/addons/web/static/tests/views/view_dialogs/select_create_dialog_tests.js index 514719a486e..d93456c91b7 100644 --- a/addons/web/static/tests/views/view_dialogs/select_create_dialog_tests.js +++ b/addons/web/static/tests/views/view_dialogs/select_create_dialog_tests.js @@ -227,7 +227,6 @@ QUnit.module("ViewDialogs", (hooks) => { const webClient = await createWebClient({ serverData, mockRPC }); webClient.env.services.dialog.add(SelectCreateDialog, { noCreate: true, - readonly: true, //Not used resModel: "partner", domain: [["id", "=", session.user_context.uid]], }); diff --git a/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.js b/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.js index e814129c429..7c4e5a602b2 100644 --- a/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.js +++ b/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.js @@ -9,6 +9,14 @@ export class ProgressBar extends Component { } } ProgressBar.template = 'web_editor.ProgressBar'; +ProgressBar.props = { + progress: Number, + hasError: Boolean, + uploaded: Boolean, + name: String, + size: String, + errorMessage: String, +}; export class UploadProgressToast extends Component { setup() { @@ -21,3 +29,6 @@ UploadProgressToast.template = 'web_editor.UploadProgressToast'; UploadProgressToast.components = { ProgressBar }; +UploadProgressToast.props = { + close: Function, +}; diff --git a/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.xml b/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.xml index e08fcaf00b1..c3f676ed627 100644 --- a/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.xml +++ b/addons/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.xml @@ -28,8 +28,7 @@ hasError="file_value.hasError" name="file_value.name" uploaded="file_value.uploaded" - size="file_value.size" - id="file_value.id"/> + size="file_value.size"/>
diff --git a/addons/website/static/src/components/fullscreen_indication/fullscreen_indication.js b/addons/website/static/src/components/fullscreen_indication/fullscreen_indication.js index f7ef058d72e..da5d1ca8808 100644 --- a/addons/website/static/src/components/fullscreen_indication/fullscreen_indication.js +++ b/addons/website/static/src/components/fullscreen_indication/fullscreen_indication.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -const { Component, xml, useState } = owl; +const { EventBus, Component, xml, useState } = owl; export class FullscreenIndication extends Component { setup() { @@ -21,6 +21,9 @@ export class FullscreenIndication extends Component { } } } +FullscreenIndication.props = { + bus: EventBus, +}; FullscreenIndication.template = xml`

Press esc to exit full screen

diff --git a/addons/website/static/src/components/website_loader/website_loader.js b/addons/website/static/src/components/website_loader/website_loader.js index 6af7b1fe6ff..2e9d6a04af0 100644 --- a/addons/website/static/src/components/website_loader/website_loader.js +++ b/addons/website/static/src/components/website_loader/website_loader.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -const { Component, useState } = owl; +const { EventBus, Component, useState } = owl; export class WebsiteLoader extends Component { setup() { @@ -25,4 +25,7 @@ export class WebsiteLoader extends Component { }); } } +WebsiteLoader.props = { + bus: EventBus, +}; WebsiteLoader.template = 'website.website_loader';