From 5bcf16311a6dc77bfc58c379dd9cb48ab0b4b37e Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Thu, 9 Sep 2021 12:02:23 +0000 Subject: [PATCH] [IMP] web: View layout This commit introduces a new component: the layout component. Its purpose is to make abstraction of most generic components and classes when writing a view template or an action. To make use of this component, we also needed a new system to pass slot from the parent of the layout to the control panel. This was done in a hacky way and is meant to be replaced as soon as Owl provides a built-in way to pass slots. The control panel has also been slightly tweaked to default its props from an environment's newly introduced key: the 'config'. This has been done to reduce the amount of props passed to the layout and search subcomponents. X-original-commit: 3b29dd230acd961a72fb0cb971f9363958f65b60 Part-of: odoo/odoo#77463 Co-authored-by: Aaron Bohy Co-authored-by: Julien Mougenot --- addons/web/static/src/legacy/legacy_views.js | 12 +- .../src/search/control_panel/control_panel.js | 62 ++-- .../search/control_panel/control_panel.xml | 8 +- .../favorite_menu/custom_favorite_item.js | 4 +- addons/web/static/src/search/search_model.js | 21 +- .../src/search/with_search/with_search.js | 55 +--- addons/web/static/src/views/debug_items.js | 5 +- addons/web/static/src/views/layout.js | 32 ++ addons/web/static/src/views/layout.xml | 17 ++ .../web/static/src/views/onboarding_banner.js | 6 +- addons/web/static/src/views/view.js | 105 +++---- .../src/webclient/actions/action_service.js | 91 +++--- addons/web/static/tests/helpers/mock_env.js | 1 + .../tests/legacy/views/state_mapping_tests.js | 3 +- .../web/static/tests/search/control_panel.js | 4 +- .../search/custom_favorite_item_tests.js | 6 +- .../tests/search/favorite_menu_tests.js | 8 +- addons/web/static/tests/search/helpers.js | 17 +- .../static/tests/search/search_model_tests.js | 6 +- .../static/tests/search/search_panel_tests.js | 6 +- addons/web/static/tests/views/helpers.js | 30 +- addons/web/static/tests/views/layout_tests.js | 273 ++++++++++++++++++ addons/web/static/tests/views/view_tests.js | 134 +++++---- .../webclient/actions/client_action_tests.js | 29 +- .../webclient/actions/concurrency_tests.js | 2 +- 25 files changed, 636 insertions(+), 301 deletions(-) create mode 100644 addons/web/static/src/views/layout.js create mode 100644 addons/web/static/src/views/layout.xml create mode 100644 addons/web/static/tests/views/layout_tests.js diff --git a/addons/web/static/src/legacy/legacy_views.js b/addons/web/static/src/legacy/legacy_views.js index f0c02eafaf4..48876eed3e2 100644 --- a/addons/web/static/src/legacy/legacy_views.js +++ b/addons/web/static/src/legacy/legacy_views.js @@ -51,10 +51,11 @@ function registerView(name, LegacyView) { searchPanel = globalState.searchPanel; } - this.viewParams = Object.assign({}, this.props.actionFlags, { + const { actionFlags, breadcrumbs = [] } = this.env.config; + this.viewParams = Object.assign({}, actionFlags, { action: this.props.action, // legacy views automatically add the last part of the breadcrumbs - breadcrumbs: breadcrumbsToLegacy(this.props.breadcrumbs), + breadcrumbs: breadcrumbsToLegacy(breadcrumbs), modelName: this.props.resModel, currentId: this.props.resId, controllerState: { @@ -99,7 +100,7 @@ function registerView(name, LegacyView) { context: this.props.context, }; const options = { - actionId: this.props.action.id, + actionId: this.env.config.actionId, loadActionMenus: this.props.loadActionMenus, loadIrFilters: this.props.loadIrFilters, }; @@ -120,10 +121,11 @@ function registerView(name, LegacyView) { viewFields: result.fields_views.search.fields, }); } + const { viewSwitcherEntries = [] } = this.env.config; const views = this.viewParams.action.views - .filter(([vid, vtype]) => vtype !== "search") + .filter(([, vtype]) => vtype !== "search") .map(([vid, vtype]) => { - const view = this.props.viewSwitcherEntries.find((v) => v.type === vtype); + const view = viewSwitcherEntries.find((v) => v.type === vtype); if (view) { return Object.assign({}, view, { viewID: vid }); } else { 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 dac80643c39..28603350257 100644 --- a/addons/web/static/src/search/control_panel/control_panel.js +++ b/addons/web/static/src/search/control_panel/control_panel.js @@ -1,11 +1,11 @@ /** @odoo-module **/ +import { useService } from "@web/core/utils/hooks"; import { ComparisonMenu } from "../comparison_menu/comparison_menu"; import { FavoriteMenu } from "../favorite_menu/favorite_menu"; import { FilterMenu } from "../filter_menu/filter_menu"; import { GroupByMenu } from "../group_by_menu/group_by_menu"; import { SearchBar } from "../search_bar/search_bar"; -import { useService } from "@web/core/utils/hooks"; const { Component } = owl; @@ -21,6 +21,44 @@ export class ControlPanel extends Component { this.actionService = useService("action"); } + /** + * !!! What follows is a hack, do not copy it !!! + * + * Duplicates the slots defined for the parent s.t. they are also available + * for the current control panel. + * + * This hack is necessary since Owl does not support manual slots + * assignment/transfer yet. This must be removed as soon as Owl implements + * such a system. + * + * @strongly_discouraged_override + */ + __render() { + const { slots } = this.env.qweb.constructor; + const { __owl__ } = this; + const originalSlots = {}; + const transferredSlotNames = [ + "control-panel-top-left", + "control-panel-top-right", + "control-panel-bottom-left", + "control-panel-bottom-right", + ]; + for (const slotName of transferredSlotNames) { + const parentSlotkey = `${__owl__.parent.__owl__.slotId}_${slotName}`; + if (parentSlotkey in slots) { + const cpSlotKey = `${__owl__.slotId}_${slotName}`; + originalSlots[cpSlotKey] = slots[cpSlotKey]; + slots[cpSlotKey] = function (scope, extra) { + slots[parentSlotkey].call(this, __owl__.parent.__owl__.scope, extra); + }; + } + } + const res = super.__render(...arguments); + // Clean up + Object.assign(slots, originalSlots); + return res; + } + /** * @returns {Object} */ @@ -78,25 +116,3 @@ export class ControlPanel extends Component { ControlPanel.components = { ComparisonMenu, FavoriteMenu, FilterMenu, GroupByMenu, SearchBar }; ControlPanel.template = "web.ControlPanel"; -// ControlPanel.props = { -// breadcrumbs: { type: Array, element: { jsId: String, name: String }, optional: true }, -// display: { type: Object, optional: true }, -// displayName: { type: String, optional: true }, -// viewSwitcherEntries: { -// type: Array, -// element: { -// type: Object, -// shape: { -// active: { type: Boolean, optional: true }, -// icon: String, -// multiRecord: { type: Boolean, optional: true }, -// name: [Object, String], -// type: String, -// }, -// }, -// optional: true, -// }, -// }; -ControlPanel.defaultProps = { - breadcrumbs: [], -}; 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 617c77b2d98..a374b9f0a19 100644 --- a/addons/web/static/src/search/control_panel/control_panel.xml +++ b/addons/web/static/src/search/control_panel/control_panel.xml @@ -30,9 +30,9 @@
- +