mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
add different view types
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { makeEnv } from "./env";
|
import { makeEnv } from "./env";
|
||||||
import { loadMenus, loadTemplates } from "./loaders";
|
import { loadMenus, loadTemplates } from "./loaders";
|
||||||
import { actionRegistry } from "./registries";
|
import { actionRegistry, viewRegistry } from "./registries";
|
||||||
import { rpc } from "./services/ajax";
|
import { rpc } from "./services/ajax";
|
||||||
import { Router } from "./services/router";
|
import { Router } from "./services/router";
|
||||||
import { Store } from "./store/store";
|
import { Store } from "./store/store";
|
||||||
@@ -20,7 +20,7 @@ document.addEventListener("DOMContentLoaded", async function() {
|
|||||||
|
|
||||||
const templates = await loadTemplates();
|
const templates = await loadTemplates();
|
||||||
const menuInfo = loadMenus();
|
const menuInfo = loadMenus();
|
||||||
const store = new Store(services, menuInfo, actionRegistry);
|
const store = new Store(services, menuInfo, actionRegistry, viewRegistry);
|
||||||
const env = makeEnv(store, templates);
|
const env = makeEnv(store, templates);
|
||||||
|
|
||||||
// Creating root widget
|
// Creating root widget
|
||||||
|
|||||||
@@ -1,6 +1,24 @@
|
|||||||
import { Registry } from "./core/registry";
|
import { Registry } from "./core/registry";
|
||||||
import { ControllerWidget } from "./store/store";
|
import { ControllerWidget } from "./store/store";
|
||||||
import { Discuss } from "./discuss/discuss";
|
import { Discuss } from "./discuss/discuss";
|
||||||
|
import { ListView } from "./views/list_view";
|
||||||
|
import { KanbanView } from "./views/kanban_view";
|
||||||
|
import { FormView } from "./views/form_view";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Views
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const viewRegistry: Registry<ControllerWidget> = new Registry();
|
||||||
|
|
||||||
|
viewRegistry
|
||||||
|
.add("list", ListView)
|
||||||
|
.add("kanban", KanbanView)
|
||||||
|
.add("form", FormView);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Client Actions
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export const actionRegistry: Registry<ControllerWidget> = new Registry();
|
export const actionRegistry: Registry<ControllerWidget> = new Registry();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Type } from "../core/component";
|
import { Type } from "../core/component";
|
||||||
import { rpcMixin } from "./rpc_mixin";
|
import { rpcMixin } from "./rpc_mixin";
|
||||||
import { Widget } from "../widget";
|
import { Widget } from "../widget";
|
||||||
import { View } from "../views/view";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
@@ -82,11 +81,21 @@ export function actionManagerMixin<T extends ReturnType<typeof rpcMixin>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
doActWindowAction(descr: ActWindowActionDescription) {
|
doActWindowAction(descr: ActWindowActionDescription) {
|
||||||
|
const tag = descr.views[0][1];
|
||||||
|
let View = this.viewRegistry.get(tag);
|
||||||
|
if (!View) {
|
||||||
|
this.addNotification({
|
||||||
|
title: "Invalid View type",
|
||||||
|
type: "warning",
|
||||||
|
message: `Cannot find view of type '${tag}' in the view registry`
|
||||||
|
});
|
||||||
|
View = Widget;
|
||||||
|
}
|
||||||
return async function executor(
|
return async function executor(
|
||||||
this: Controller,
|
this: Controller,
|
||||||
parent: Widget<any, any>
|
parent: Widget<any, any>
|
||||||
) {
|
) {
|
||||||
const widget = new View(parent, { info: descr.views[0][1] });
|
const widget = new View!(parent, { info: descr.views[0][1] });
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
await widget.mount(div);
|
await widget.mount(div);
|
||||||
this.widget = widget;
|
this.widget = widget;
|
||||||
|
|||||||
@@ -65,18 +65,21 @@ export class BaseStore extends EventBus {
|
|||||||
menuInfo: MenuInfo;
|
menuInfo: MenuInfo;
|
||||||
services: Services;
|
services: Services;
|
||||||
actionRegistry: Registry<ControllerWidget>;
|
actionRegistry: Registry<ControllerWidget>;
|
||||||
|
viewRegistry: Registry<ControllerWidget>;
|
||||||
currentQuery: Query;
|
currentQuery: Query;
|
||||||
generateID = idGenerator();
|
generateID = idGenerator();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
services: Services,
|
services: Services,
|
||||||
menuInfo: MenuInfo,
|
menuInfo: MenuInfo,
|
||||||
actionRegistry: Registry<ControllerWidget>
|
actionRegistry: Registry<ControllerWidget>,
|
||||||
|
viewRegistry: Registry<ControllerWidget>
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.services = services;
|
this.services = services;
|
||||||
this.menuInfo = menuInfo;
|
this.menuInfo = menuInfo;
|
||||||
this.actionRegistry = actionRegistry;
|
this.actionRegistry = actionRegistry;
|
||||||
|
this.viewRegistry = viewRegistry;
|
||||||
this.currentQuery = {};
|
this.currentQuery = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +132,10 @@ export class Store extends actionManagerMixin(rpcMixin(BaseStore)) {
|
|||||||
constructor(
|
constructor(
|
||||||
services: Services,
|
services: Services,
|
||||||
menuInfo: MenuInfo,
|
menuInfo: MenuInfo,
|
||||||
actionRegistry: Registry<ControllerWidget>
|
actionRegistry: Registry<ControllerWidget>,
|
||||||
|
viewRegistry: Registry<ControllerWidget>
|
||||||
) {
|
) {
|
||||||
super(services, menuInfo, actionRegistry);
|
super(services, menuInfo, actionRegistry, viewRegistry);
|
||||||
const query = this.services.router.getQuery();
|
const query = this.services.router.getQuery();
|
||||||
let { app, actionId } = this.getAppAndAction(query);
|
let { app, actionId } = this.getAppAndAction(query);
|
||||||
this.state.currentApp = app;
|
this.state.currentApp = app;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Widget } from "../widget";
|
||||||
|
|
||||||
|
export class FormView extends Widget<{}, { info: any }> {
|
||||||
|
inlineTemplate = `<div>form view: <span t-esc="props.info"/></div>`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Widget } from "../widget";
|
||||||
|
|
||||||
|
export class KanbanView extends Widget<{}, { info: any }> {
|
||||||
|
inlineTemplate = `<div>kanban view: <span t-esc="props.info"/></div>`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Widget } from "../widget";
|
||||||
|
|
||||||
|
export class ListView extends Widget<{}, { info: any }> {
|
||||||
|
inlineTemplate = `<div>list view: <span t-esc="props.info"/></div>`;
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { Widget } from "../widget";
|
|
||||||
|
|
||||||
export class View extends Widget<{}, { info: any }> {
|
|
||||||
inlineTemplate = `<div>some view: <span t-esc="props.info"/></div>`;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { readFile } from "fs";
|
import { readFile } from "fs";
|
||||||
import { BaseMenuItem, getMenuInfo } from "../../src/ts/loaders";
|
import { BaseMenuItem, getMenuInfo } from "../../src/ts/loaders";
|
||||||
import { actionRegistry } from "../../src/ts/registries";
|
import { actionRegistry, viewRegistry } from "../../src/ts/registries";
|
||||||
import { ActionDescription } from "../../src/ts/store/action_manager_mixin";
|
import { ActionDescription } from "../../src/ts/store/action_manager_mixin";
|
||||||
import { MenuInfo } from "../../src/ts/store/store";
|
import { MenuInfo } from "../../src/ts/store/store";
|
||||||
import { Registry } from "../../src/ts/core/registry";
|
import { Registry } from "../../src/ts/core/registry";
|
||||||
@@ -9,6 +9,7 @@ export interface TestData {
|
|||||||
menuInfo: MenuInfo;
|
menuInfo: MenuInfo;
|
||||||
actions: ActionDescription[];
|
actions: ActionDescription[];
|
||||||
actionRegistry: typeof actionRegistry;
|
actionRegistry: typeof actionRegistry;
|
||||||
|
viewRegistry: typeof viewRegistry;
|
||||||
templates: string;
|
templates: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,13 +19,16 @@ export async function makeTestData(): Promise<TestData> {
|
|||||||
if (!templates) {
|
if (!templates) {
|
||||||
templates = await loadTemplates();
|
templates = await loadTemplates();
|
||||||
}
|
}
|
||||||
const registry: typeof actionRegistry = new Registry();
|
const _actionRegistry: typeof actionRegistry = new Registry();
|
||||||
(<any>registry).map = Object.assign({}, (<any>actionRegistry).map);
|
(<any>_actionRegistry).map = Object.assign({}, (<any>actionRegistry).map);
|
||||||
|
const _viewRegistry: typeof actionRegistry = new Registry();
|
||||||
|
(<any>_viewRegistry).map = Object.assign({}, (<any>actionRegistry).map);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuInfo: makeMenuInfo(),
|
menuInfo: makeMenuInfo(),
|
||||||
actions: makeActionData(),
|
actions: makeActionData(),
|
||||||
actionRegistry: registry,
|
actionRegistry: _actionRegistry,
|
||||||
|
viewRegistry: _viewRegistry,
|
||||||
templates
|
templates
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,14 @@ export function makeTestEnv(info: TestInfo = {}): TestEnv {
|
|||||||
roots: []
|
roots: []
|
||||||
};
|
};
|
||||||
const actionRegistry = info.actionRegistry || new Registry();
|
const actionRegistry = info.actionRegistry || new Registry();
|
||||||
|
const viewRegistry = info.viewRegistry || new Registry();
|
||||||
const actions = info.actions || [];
|
const actions = info.actions || [];
|
||||||
|
|
||||||
const data: TestData = {
|
const data: TestData = {
|
||||||
menuInfo,
|
menuInfo,
|
||||||
actions,
|
actions,
|
||||||
actionRegistry,
|
actionRegistry,
|
||||||
|
viewRegistry,
|
||||||
templates
|
templates
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ export function makeTestEnv(info: TestInfo = {}): TestEnv {
|
|||||||
router: info.router || new MockRouter()
|
router: info.router || new MockRouter()
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = new Store(services, menuInfo, actionRegistry);
|
const store = new Store(services, menuInfo, actionRegistry, viewRegistry);
|
||||||
const env = makeEnv(store, templates);
|
const env = makeEnv(store, templates);
|
||||||
const testEnv = Object.assign({ store }, env);
|
const testEnv = Object.assign({ store }, env);
|
||||||
return testEnv;
|
return testEnv;
|
||||||
|
|||||||
@@ -38,3 +38,15 @@ test("display a warning if client action is not in registry", async () => {
|
|||||||
expect(notifs.length).toBe(1);
|
expect(notifs.length).toBe(1);
|
||||||
expect(notifs[0].type).toBe("warning");
|
expect(notifs[0].type).toBe("warning");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("display a warning if view is not in registry", async () => {
|
||||||
|
const data = await makeTestData();
|
||||||
|
data.viewRegistry = new Registry();
|
||||||
|
const testEnv = makeTestEnv(data);
|
||||||
|
|
||||||
|
await testEnv.store.doAction(250);
|
||||||
|
|
||||||
|
const notifs = testEnv.store.state.notifications;
|
||||||
|
expect(notifs.length).toBe(1);
|
||||||
|
expect(notifs[0].type).toBe("warning");
|
||||||
|
});
|
||||||
|
|||||||
@@ -118,6 +118,40 @@ exports[`if url has action_id, will render action and navigate to proper menu_id
|
|||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`open act window action with invalid viewtype => empty widget is rendered + warning 1`] = `
|
||||||
|
"<div class=\\"o_web_client\\">
|
||||||
|
<div class=\\"o_navbar\\">
|
||||||
|
<a aria-label=\\"Applications\\" class=\\"o_title fa fa-th\\" href=\\"#\\" title=\\"Applications\\" accesskey=\\"h\\"></a>
|
||||||
|
|
||||||
|
<a class=\\"o_menu_brand\\" href=\\"\\" role=\\"button\\">
|
||||||
|
Notes
|
||||||
|
</a>
|
||||||
|
<ul class=\\"o_menu_sections\\">
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=\\"o_content\\"><div></div></div>
|
||||||
|
<div class=\\"o_notification_container\\">
|
||||||
|
|
||||||
|
<div class=\\"o_notification o_error\\">
|
||||||
|
|
||||||
|
<div class=\\"o_notification_title\\">
|
||||||
|
<span class=\\"o_icon fa fa-3x fa-exclamation\\" role=\\"img\\" aria-label=\\"Notification Invalid View type\\" title=\\"Notification Invalid View type\\"></span>
|
||||||
|
Invalid View type
|
||||||
|
</div>
|
||||||
|
<div class=\\"o_notification_content\\">
|
||||||
|
Cannot find view of type 'kanban' in the view registry
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class=\\"o_loading d-none\\">Loading</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`start with no action => clicks on client action => discuss is rendered 1`] = `
|
exports[`start with no action => clicks on client action => discuss is rendered 1`] = `
|
||||||
"<div class=\\"o_web_client\\">
|
"<div class=\\"o_web_client\\">
|
||||||
<div class=\\"o_navbar\\">
|
<div class=\\"o_navbar\\">
|
||||||
|
|||||||
@@ -76,3 +76,17 @@ test("clicks on client action with invalid key => empty widget is rendered + war
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toMatchSnapshot();
|
expect(fixture.innerHTML).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("open act window action with invalid viewtype => empty widget is rendered + warning", async () => {
|
||||||
|
const data = await makeTestData();
|
||||||
|
data.viewRegistry = new Registry();
|
||||||
|
const testEnv = makeTestEnv(data);
|
||||||
|
const root = new Root(testEnv, testEnv.store);
|
||||||
|
await root.mount(fixture);
|
||||||
|
|
||||||
|
// note menu item
|
||||||
|
await (<any>document.querySelector('[data-menu="205"]')).click();
|
||||||
|
await helpers.nextTick();
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user