[REF] web: rework env.config
The motivation of this commit comes from breadcrumbs. Breadcrumbs work as follows: for each controller in the stack of the ActionService, there is an entry in the breadcrumbs. Except for the last entry (which corresponds to the currently displayed controller), the value to display is computed by the ActionService and stored in this.env.config.breadcrumbs. For the last entry, we use the displayName set in this.env.config. When a view wants to update its displayName (e.g. the form view when we switch to another record), it updates the displayName in the config, which thus correctly updates the breadcrumbs. However, this doesn't change the internal values in the ActionService, so if another controller is stacked over the current one, the penultimate entry is wrong. To update the internal state of the ActionService, the event 'controller-title-updated' must be triggered as well, which is cumbersome. Note that we don't really face the issue yet because among already converted views, none of them need to update its displayName in the breadcrumbs. This commit aims at uniformizing the way the (n-1) first entries and the last one behave, by making the "breadcrumbs" key encode all breadcrumbs entries (the n-1 first ones, and the last, current one). We also replace the "displayName" key by "getDisplayName" such that it uses a single source of truth, located in the ActionService, and we provide a function "setDisplayName" to update it. Finally, this commit introduces the notion of "default config" which ensures that standalone views, or views in tests, have a valid config with expected keys. closes odoo/odoo#81031 Related: odoo/enterprise#22799 Signed-off-by: Géry Debongnie <ged@odoo.com>
This commit is contained in:
@@ -24,7 +24,7 @@ export class AddToBoard extends Component {
|
||||
setup() {
|
||||
this.notification = useService("notification");
|
||||
this.rpc = useService("rpc");
|
||||
this.state = useState({ name: this.env.config.displayName });
|
||||
this.state = useState({ name: this.env.config.getDisplayName() });
|
||||
|
||||
useAutofocus();
|
||||
}
|
||||
@@ -58,7 +58,7 @@ export class AddToBoard extends Component {
|
||||
type: "warning",
|
||||
}
|
||||
);
|
||||
this.state.name = this.env.config.displayName;
|
||||
this.state.name = this.env.config.getDisplayName();
|
||||
} else {
|
||||
this.notification.add(this.env._t("Could not add filter to dashboard"), {
|
||||
type: "danger",
|
||||
|
||||
@@ -44,7 +44,7 @@ class ActionAdapter extends ComponentAdapter {
|
||||
});
|
||||
originalUpdateControlPanel = this.__widget.updateControlPanel.bind(this.__widget);
|
||||
this.__widget.updateControlPanel = (newProps) => {
|
||||
this.trigger("controller-title-updated", this.__widget.getTitle());
|
||||
this.wowlEnv.config.setDisplayName(this.__widget.getTitle());
|
||||
return originalUpdateControlPanel(newProps);
|
||||
};
|
||||
core.bus.trigger("DOM_updated");
|
||||
@@ -77,7 +77,7 @@ class ActionAdapter extends ComponentAdapter {
|
||||
if (this.widget) {
|
||||
const actionTitle = this.widget.getTitle();
|
||||
if (actionTitle) {
|
||||
this.trigger("controller-title-updated", actionTitle);
|
||||
this.wowlEnv.config.setDisplayName(actionTitle);
|
||||
}
|
||||
}
|
||||
this.router.pushState(query);
|
||||
|
||||
@@ -264,7 +264,7 @@ export function breadcrumbsToLegacy(breadcrumbs) {
|
||||
if (!breadcrumbs) {
|
||||
return;
|
||||
}
|
||||
return breadcrumbs.slice().map((bc) => {
|
||||
return breadcrumbs.slice(0, -1).map((bc) => {
|
||||
return { title: bc.name, controllerID: bc.jsId };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function registerView(name, LegacyView) {
|
||||
context: Object.assign({}, this.user.context, this.props.action.context),
|
||||
});
|
||||
|
||||
const { actionFlags, breadcrumbs = [] } = this.env.config;
|
||||
const { actionFlags, breadcrumbs } = this.env.config;
|
||||
this.viewParams = Object.assign({}, actionFlags, {
|
||||
action,
|
||||
// legacy views automatically add the last part of the breadcrumbs
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
t-on-click.prevent="trigger('breadcrumb-clicked', { controllerID: bc.controllerID })"
|
||||
title="Previous menu"
|
||||
>
|
||||
<a t-if="bc.title" href="#" t-esc="bc.title"/>
|
||||
<em t-else="" class="text-warning">Unnamed</em>
|
||||
<a href="#">
|
||||
<t t-if="bc.title" t-esc="bc.title"/>
|
||||
<em t-else="" class="text-warning">Unnamed</em>
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
<span t-attf-class="{{props.breadcrumbs.length ? 'text-muted' : 'text-900' }}" t-if="props.title" t-esc="props.title"/>
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
</t>
|
||||
|
||||
<div class="o_cp_pager" role="search">
|
||||
<t t-if="env.config.getPagerProps">
|
||||
<t t-set="pagerProps" t-value="env.config.getPagerProps()" />
|
||||
<t t-set="pagerProps" t-value="env.config.getPagerProps()" />
|
||||
<t t-if="pagerProps">
|
||||
<Pager t-if="pagerProps.total > 0" t-props="pagerProps" />
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<t t-if="(env.config.viewSwitcherEntries or []).length">
|
||||
<t t-if="env.config.viewSwitcherEntries.length">
|
||||
<nav class="btn-group o_cp_switch_buttons">
|
||||
<t t-foreach="env.config.viewSwitcherEntries" t-as="view" t-key="view.type">
|
||||
<button class="btn btn-light fa fa-lg o_switch_view "
|
||||
@@ -53,20 +53,19 @@
|
||||
|
||||
<t t-name="web.Breadcrumbs" owl="1">
|
||||
<ol class="breadcrumb">
|
||||
<t t-foreach="env.config.breadcrumbs or []" t-as="breadcrumb" t-key="breadcrumb.jsId">
|
||||
<li class="breadcrumb-item"
|
||||
t-att-class="{ o_back_button: breadcrumb_last}"
|
||||
t-on-click.prevent="onBreadcrumbClicked(breadcrumb.jsId)"
|
||||
>
|
||||
<t t-foreach="env.config.breadcrumbs" t-as="breadcrumb" t-key="breadcrumb.jsId">
|
||||
<t t-set="isPenultimate" t-value="breadcrumb_index === env.config.breadcrumbs.length - 2"/>
|
||||
<li t-if="!breadcrumb_last" class="breadcrumb-item" t-att-class="{ o_back_button: isPenultimate}" t-on-click.prevent="onBreadcrumbClicked(breadcrumb.jsId)">
|
||||
<a href="#">
|
||||
<t t-esc="breadcrumb.name"/>
|
||||
<t t-if="breadcrumb.name" t-esc="breadcrumb.name"/>
|
||||
<em t-else="" class="text-warning">Unnamed</em>
|
||||
</a>
|
||||
</li>
|
||||
<li t-else="" class="breadcrumb-item active">
|
||||
<t t-if="breadcrumb.name" t-esc="breadcrumb.name"/>
|
||||
<em t-else="" class="text-warning">Unnamed</em>
|
||||
</li>
|
||||
</t>
|
||||
<li class="breadcrumb-item active">
|
||||
<t t-if="env.config.displayName" t-esc="env.config.displayName" />
|
||||
<em t-else="" class="text-warning">Unnamed</em>
|
||||
</li>
|
||||
</ol>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export class CustomFavoriteItem extends Component {
|
||||
this.descriptionRef = useRef("description");
|
||||
useAutofocus();
|
||||
this.state = useState({
|
||||
description: this.env.config.displayName,
|
||||
description: this.env.config.getDisplayName(),
|
||||
isDefault: false,
|
||||
isShared: false,
|
||||
});
|
||||
@@ -46,7 +46,7 @@ export class CustomFavoriteItem extends Component {
|
||||
this.env.searchModel.createNewFavorite({ description, isDefault, isShared });
|
||||
|
||||
Object.assign(this.state, {
|
||||
description: this.env.config.displayName,
|
||||
description: this.env.config.getDisplayName(),
|
||||
isDefault: false,
|
||||
isShared: false,
|
||||
});
|
||||
|
||||
@@ -14,6 +14,47 @@ const viewRegistry = registry.category("views");
|
||||
const { Component, hooks } = owl;
|
||||
const { useSubEnv } = hooks;
|
||||
|
||||
/** @typedef {Object} Config
|
||||
* @property {integer|false} actionId
|
||||
* @property {string|false} actionType
|
||||
* @property {Object} actionFlags
|
||||
* @property {() => []} breadcrumbs
|
||||
* @property {() => string} getDisplayName
|
||||
* @property {(string) => void} setDisplayName
|
||||
* @property {() => Object} getPagerProps
|
||||
* @property {Object[]} viewSwitcherEntry
|
||||
* @property {Object[]} viewSwitcherEntry
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the default config to use if no config, or an incomplete config has
|
||||
* been provided in the env, which can happen with standalone views.
|
||||
* @returns {Config}
|
||||
*/
|
||||
export function getDefaultConfig() {
|
||||
let displayName;
|
||||
const config = {
|
||||
actionId: false,
|
||||
actionType: false,
|
||||
actionFlags: {},
|
||||
breadcrumbs: [
|
||||
{
|
||||
get name() {
|
||||
return displayName;
|
||||
},
|
||||
},
|
||||
],
|
||||
getDisplayName: () => displayName,
|
||||
getPagerProps: () => {},
|
||||
setDisplayName: (newDisplayName) => {
|
||||
displayName = newDisplayName;
|
||||
},
|
||||
viewSwitcherEntries: [],
|
||||
views: [],
|
||||
};
|
||||
return config;
|
||||
}
|
||||
|
||||
/** @typedef {Object} ViewProps
|
||||
* @property {string} resModel
|
||||
* @property {string} type
|
||||
@@ -106,7 +147,10 @@ export class View extends Component {
|
||||
|
||||
useSubEnv({
|
||||
keepLast: new KeepLast(),
|
||||
config: { ...(this.env.config || {}) },
|
||||
config: {
|
||||
...getDefaultConfig(),
|
||||
...this.env.config,
|
||||
},
|
||||
});
|
||||
useActionLinks({ resModel });
|
||||
}
|
||||
@@ -119,7 +163,7 @@ export class View extends Component {
|
||||
// determine views for which descriptions should be obtained
|
||||
let { viewId, searchViewId } = this.props;
|
||||
|
||||
const views = deepCopy(this.env.config.views || []);
|
||||
const views = deepCopy(this.env.config.views);
|
||||
const view = views.find((v) => v[1] === type) || [];
|
||||
if (view.length) {
|
||||
view[0] = viewId !== undefined ? viewId : view[0];
|
||||
|
||||
@@ -76,8 +76,7 @@ const CTX_KEY_REGEX = /^(?:(?:default_|search_default_|show_).+|.+_view_ref|grou
|
||||
// only register this template once for all dynamic classes ControllerComponent
|
||||
const ControllerComponentTemplate = tags.xml`<t t-component="Component" t-props="props"
|
||||
t-ref="component"
|
||||
t-on-history-back="onHistoryBack"
|
||||
t-on-controller-title-updated.stop="onTitleUpdated"/>`;
|
||||
t-on-history-back="onHistoryBack"/>`;
|
||||
|
||||
function makeActionManager(env) {
|
||||
const keepLast = new KeepLast();
|
||||
@@ -242,7 +241,9 @@ function makeActionManager(env) {
|
||||
.map((controller) => {
|
||||
return {
|
||||
jsId: controller.jsId,
|
||||
name: controller.title || controller.action.name || env._t("Undefined"),
|
||||
get name() {
|
||||
return controller.displayName;
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -310,7 +311,7 @@ function makeActionManager(env) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ClientAction | ActWindowAction} action
|
||||
* @param {ClientAction} action
|
||||
* @param {Object} props
|
||||
* @returns {{ props: ActionProps, config: Config }}
|
||||
*/
|
||||
@@ -319,11 +320,10 @@ function makeActionManager(env) {
|
||||
props: Object.assign({}, props, { action, actionId: action.id }),
|
||||
config: {
|
||||
actionId: action.id,
|
||||
actionType: action.type,
|
||||
actionType: "ir.actions.client",
|
||||
actionFlags: action.flags,
|
||||
displayName: action.display_name || action.name || "",
|
||||
views: action.views,
|
||||
},
|
||||
displayName: action.display_name || action.name || "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -449,12 +449,12 @@ function makeActionManager(env) {
|
||||
props: viewProps,
|
||||
config: {
|
||||
actionId: action.id,
|
||||
actionType: action.type,
|
||||
actionType: "ir.actions.act_window",
|
||||
actionFlags: action.flags,
|
||||
displayName: action.display_name || action.name || "",
|
||||
views: action.views,
|
||||
viewSwitcherEntries,
|
||||
},
|
||||
displayName: action.display_name || action.name || "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -527,10 +527,15 @@ function makeActionManager(env) {
|
||||
controllerArray.unshift(options.lazyController);
|
||||
}
|
||||
const nextStack = controllerStack.slice(0, index).concat(controllerArray);
|
||||
controller.config.breadcrumbs = _getBreadcrumbs(nextStack.slice(0, -1));
|
||||
if (controller.Component.isLegacy) {
|
||||
controller.props.breadcrumbs = controller.config.breadcrumbs;
|
||||
}
|
||||
controller.config.breadcrumbs = _getBreadcrumbs(nextStack);
|
||||
controller.config.getDisplayName = () => controller.displayName;
|
||||
controller.config.setDisplayName = (displayName) => {
|
||||
controller.displayName = displayName;
|
||||
if (controller === _getCurrentController()) {
|
||||
// if not mounted yet, will be done in "mounted"
|
||||
env.services.title.setParts({ action: controller.displayName });
|
||||
}
|
||||
};
|
||||
|
||||
class ControllerComponent extends Component {
|
||||
setup() {
|
||||
@@ -632,9 +637,7 @@ function makeActionManager(env) {
|
||||
// END LEGACY CODE COMPATIBILITY
|
||||
controllerStack = nextStack; // the controller is mounted, commit the new stack
|
||||
pushState(controller);
|
||||
this.titleService.setParts({
|
||||
action: controller.title || this.env.config.displayName,
|
||||
});
|
||||
this.titleService.setParts({ action: controller.displayName });
|
||||
browser.sessionStorage.setItem("current_action", action._originalAction);
|
||||
}
|
||||
resolve();
|
||||
@@ -654,13 +657,6 @@ function makeActionManager(env) {
|
||||
_executeCloseAction();
|
||||
}
|
||||
}
|
||||
onTitleUpdated(ev) {
|
||||
controller.title = ev.detail;
|
||||
if (this.isMounted) {
|
||||
// if not mounted yet, will be done in "mounted"
|
||||
this.titleService.setParts({ action: controller.title });
|
||||
}
|
||||
}
|
||||
}
|
||||
ControllerComponent.template = ControllerComponentTemplate;
|
||||
ControllerComponent.Component = controller.Component;
|
||||
@@ -1369,7 +1365,16 @@ function makeActionManager(env) {
|
||||
}
|
||||
|
||||
export const actionService = {
|
||||
dependencies: ["effect", "localization", "notification", "router", "rpc", "ui", "user"],
|
||||
dependencies: [
|
||||
"effect",
|
||||
"localization",
|
||||
"notification",
|
||||
"router",
|
||||
"rpc",
|
||||
"title",
|
||||
"ui",
|
||||
"user",
|
||||
],
|
||||
start(env) {
|
||||
return makeActionManager(env);
|
||||
},
|
||||
|
||||
@@ -45,21 +45,18 @@ QUnit.module("Search", (hooks) => {
|
||||
assert.containsNone(controlPanel, ".o_cp_switch_buttons");
|
||||
|
||||
assert.containsOnce(controlPanel, ".breadcrumb");
|
||||
assert.containsOnce(controlPanel, ".breadcrumb li.breadcrumb-item");
|
||||
assert.strictEqual(
|
||||
controlPanel.el.querySelector("li.breadcrumb-item").innerText,
|
||||
"Unnamed"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("breadcrumbs prop", async (assert) => {
|
||||
QUnit.test("breadcrumbs", async (assert) => {
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: ControlPanel,
|
||||
config: {
|
||||
breadcrumbs: [{ jsId: "controller_7", name: "Previous" }],
|
||||
displayName: "Current",
|
||||
breadcrumbs: [
|
||||
{ jsId: "controller_7", name: "Previous" },
|
||||
{ jsId: "controller_9", name: "Current" },
|
||||
],
|
||||
},
|
||||
searchMenuTypes: [],
|
||||
});
|
||||
@@ -78,7 +75,7 @@ QUnit.module("Search", (hooks) => {
|
||||
assert.verifySteps(["controller_7"]);
|
||||
});
|
||||
|
||||
QUnit.test("viewSwitcherEntries prop", async (assert) => {
|
||||
QUnit.test("view switcher", async (assert) => {
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
|
||||
@@ -81,7 +81,7 @@ QUnit.module("Search", (hooks) => {
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
displayName: "Action Name",
|
||||
getDisplayName: () => "Action Name",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -296,7 +296,7 @@ QUnit.module("Search", (hooks) => {
|
||||
if (args.model === "ir.filters" && args.method === "create_or_replace") {
|
||||
const irFilter = args.args[0];
|
||||
assert.deepEqual(irFilter, {
|
||||
action_id: undefined,
|
||||
action_id: false,
|
||||
context: { group_by: [] },
|
||||
domain: "[]",
|
||||
is_default: false,
|
||||
|
||||
@@ -68,7 +68,7 @@ QUnit.module("Search", (hooks) => {
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
displayName: "Action Name",
|
||||
getDisplayName: () => "Action Name",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ QUnit.module("Search", (hooks) => {
|
||||
searchMenuTypes: ["favorite"],
|
||||
searchViewId: false,
|
||||
config: {
|
||||
displayName: "Action Name",
|
||||
getDisplayName: () => "Action Name",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ import { ormService } from "@web/core/orm_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { CustomFavoriteItem } from "@web/search/favorite_menu/custom_favorite_item";
|
||||
import { WithSearch } from "@web/search/with_search/with_search";
|
||||
import { getDefaultConfig } from "@web/views/view";
|
||||
import { viewService } from "@web/views/view_service";
|
||||
import { actionService } from "@web/webclient/actions/action_service";
|
||||
import { registerCleanup } from "../helpers/cleanup";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import { click, getFixture, mouseEnter, triggerEvent, triggerEvents } from "../helpers/utils";
|
||||
import { click, getFixture, mouseEnter, triggerEvent } from "../helpers/utils";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
const favoriteMenuRegistry = registry.category("favoriteMenu");
|
||||
@@ -38,7 +39,10 @@ export const makeWithSearch = async (params) => {
|
||||
|
||||
const serverData = props.serverData || undefined;
|
||||
const mockRPC = props.mockRPC || undefined;
|
||||
const config = props.config || {};
|
||||
const config = {
|
||||
...getDefaultConfig(),
|
||||
...props.config,
|
||||
};
|
||||
|
||||
delete props.serverData;
|
||||
delete props.mockRPC;
|
||||
|
||||
@@ -1320,8 +1320,18 @@ QUnit.module("Views", (hooks) => {
|
||||
label: "",
|
||||
});
|
||||
checkLegend(assert, graph, ["true / xphone", "false / xphone", "false / xpad"]);
|
||||
checkTooltip(assert, graph, { lines: [{ label: "true / xphone", value: "3 (37.50%)" }] }, 0);
|
||||
checkTooltip(assert, graph, { lines: [{ label: "false / xphone", value: "1 (12.50%)" }] }, 1);
|
||||
checkTooltip(
|
||||
assert,
|
||||
graph,
|
||||
{ lines: [{ label: "true / xphone", value: "3 (37.50%)" }] },
|
||||
0
|
||||
);
|
||||
checkTooltip(
|
||||
assert,
|
||||
graph,
|
||||
{ lines: [{ label: "false / xphone", value: "1 (12.50%)" }] },
|
||||
1
|
||||
);
|
||||
checkTooltip(assert, graph, { lines: [{ label: "false / xpad", value: "4 (50.00%)" }] }, 2);
|
||||
});
|
||||
|
||||
@@ -2732,17 +2742,15 @@ QUnit.module("Views", (hooks) => {
|
||||
);
|
||||
|
||||
QUnit.test("action name is displayed in breadcrumbs", async function (assert) {
|
||||
assert.expect(1);
|
||||
const graph = await makeView({
|
||||
serverData,
|
||||
type: "graph",
|
||||
resModel: "foo",
|
||||
config: {
|
||||
displayName: "Glou glou",
|
||||
},
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, {
|
||||
name: "Glou glou",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "graph"]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
graph.el.querySelector(".o_control_panel .breadcrumb-item.active").innerText,
|
||||
webClient.el.querySelector(".o_control_panel .breadcrumb-item.active").innerText,
|
||||
"Glou glou"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { getFixture } from "@web/../tests/helpers/utils";
|
||||
import { View } from "@web/views/view";
|
||||
import { getDefaultConfig, View } from "@web/views/view";
|
||||
import { _fieldsViewGet } from "../helpers/mock_server";
|
||||
import { addLegacyMockEnvironment } from "../webclient/helpers";
|
||||
|
||||
@@ -27,7 +27,10 @@ export const makeView = async (params) => {
|
||||
const props = { ...params };
|
||||
const serverData = props.serverData;
|
||||
const mockRPC = props.mockRPC;
|
||||
const config = props.config || {};
|
||||
const config = {
|
||||
...getDefaultConfig(),
|
||||
...props.config,
|
||||
};
|
||||
const legacyParams = props.legacyParams || {};
|
||||
|
||||
delete props.serverData;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { makeWithSearch, setupControlPanelServiceRegistry } from "@web/../tests/
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { Layout } from "@web/views/layout";
|
||||
import { getDefaultConfig } from "@web/views/view";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
|
||||
const { Component, hooks, mount, tags } = owl;
|
||||
@@ -126,7 +127,12 @@ QUnit.module("Views", (hooks) => {
|
||||
|
||||
class ToyB extends Component {
|
||||
setup() {
|
||||
useSubEnv({ config: { SearchPanel } });
|
||||
useSubEnv({
|
||||
config: {
|
||||
...getDefaultConfig(),
|
||||
SearchPanel,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
ToyB.template = xml`
|
||||
|
||||
@@ -333,15 +333,15 @@ QUnit.module("ActionManager", (hooks) => {
|
||||
setup() {
|
||||
this.breadcrumbTitle = "myOwlAction";
|
||||
const { breadcrumbs } = this.env.config;
|
||||
assert.strictEqual(breadcrumbs.length, 1);
|
||||
assert.strictEqual(breadcrumbs.length, 2);
|
||||
assert.strictEqual(breadcrumbs[0].name, "Favorite Ponies");
|
||||
}
|
||||
mounted() {
|
||||
this.trigger("controller-title-updated", this.breadcrumbTitle);
|
||||
this.env.config.setDisplayName(this.breadcrumbTitle);
|
||||
}
|
||||
onClick() {
|
||||
this.breadcrumbTitle = "newOwlTitle";
|
||||
this.trigger("controller-title-updated", this.breadcrumbTitle);
|
||||
this.env.config.setDisplayName(this.breadcrumbTitle);
|
||||
}
|
||||
}
|
||||
ClientAction.template = tags.xml`<div class="my_owl_action" t-on-click="onClick">owl client action</div>`;
|
||||
|
||||
Reference in New Issue
Block a user