diff --git a/addons/web/static/src/core/notebook/notebook.js b/addons/web/static/src/core/notebook/notebook.js index 1721cc6b553..32660271057 100644 --- a/addons/web/static/src/core/notebook/notebook.js +++ b/addons/web/static/src/core/notebook/notebook.js @@ -2,7 +2,14 @@ import { scrollTo } from "@web/core/utils/scrolling"; -import { Component, onWillDestroy, onWillUpdateProps, useEffect, useRef, useState } from "@odoo/owl"; +import { + Component, + onWillDestroy, + onWillUpdateProps, + useEffect, + useRef, + useState, +} from "@odoo/owl"; /** * A notebook component that will render only the current page and allow @@ -109,7 +116,9 @@ export class Notebook extends Component { } activatePage(pageIndex) { - this.state.currentPage = pageIndex; + if (!this.disabledPages.includes(pageIndex)) { + this.state.currentPage = pageIndex; + } } computePages(props) { @@ -121,6 +130,7 @@ export class Notebook extends Component { page.isVisible = true; } } + this.disabledPages = []; const pages = []; const pagesWithIndex = []; for (const [k, v] of Object.entries({ ...props.slots, ...props.pages })) { @@ -130,6 +140,9 @@ export class Notebook extends Component { } else { pages.push([id, v]); } + if (v.isDisabled) { + this.disabledPages.push(k); + } } for (const page of pagesWithIndex) { pages.splice(page[1].index, 0, page); @@ -176,5 +189,6 @@ Notebook.props = { anchors: { type: Object, optional: true }, defaultPage: { type: String, optional: true }, orientation: { type: String, optional: true }, + icons: { type: Object, optional: true }, onPageUpdate: { type: Function, optional: true }, }; diff --git a/addons/web/static/src/core/notebook/notebook.scss b/addons/web/static/src/core/notebook/notebook.scss index f6df01d3254..ae0fb423fc9 100644 --- a/addons/web/static/src/core/notebook/notebook.scss +++ b/addons/web/static/src/core/notebook/notebook.scss @@ -26,6 +26,13 @@ .nav-item { white-space: nowrap; margin: 0 -1px 0 0; + + &.disabled { + .nav-link { + cursor: not-allowed; + opacity: .3; + } + } } .nav-link { diff --git a/addons/web/static/src/core/notebook/notebook.xml b/addons/web/static/src/core/notebook/notebook.xml index 755e13e0a1d..cea82b93b3b 100644 --- a/addons/web/static/src/core/notebook/notebook.xml +++ b/addons/web/static/src/core/notebook/notebook.xml @@ -5,8 +5,11 @@
diff --git a/addons/web/static/tests/core/notebook_tests.js b/addons/web/static/tests/core/notebook_tests.js index 061a7679059..4f19b302620 100644 --- a/addons/web/static/tests/core/notebook_tests.js +++ b/addons/web/static/tests/core/notebook_tests.js @@ -334,4 +334,93 @@ QUnit.module("Components", (hooks) => { assert.containsOnce(target, ".page2"); assert.strictEqual(target.querySelector(".nav-link.active").textContent, "page2"); }); + + QUnit.test("disabled pages are greyed out and can't be toggled", async (assert) => { + class Parent extends Component {} + Parent.components = { Notebook }; + Parent.template = xml` + + +
+ + +
+ + +
+ + `; + + const env = await makeTestEnv(); + await mount(Parent, target, { env }); + assert.containsOnce(target, ".page1", "the default page is displayed"); + assert.hasClass( + target.querySelector(".nav-item:nth-child(2)"), + "disabled", + "tab of the disabled page is greyed out" + ); + + await click(target.querySelector(".nav-item:nth-child(2) .nav-link")); + assert.containsOnce(target, ".page1", "the same page is still displayed"); + + await click(target.querySelector(".nav-item:nth-child(3) .nav-link")); + assert.containsOnce(target, ".page3", "the third page is now displayed"); + }); + + QUnit.test("icons can be given for each page tab", async (assert) => { + class Parent extends Component { + get icons() { + return { + 1: "fa-trash", + 3: "fa-pencil", + }; + } + } + Parent.components = { Notebook }; + Parent.template = xml` + + +
+ + +
+ + +
+ + `; + + const env = await makeTestEnv(); + await mount(Parent, target, { env }); + assert.hasClass( + target.querySelector(".nav-item:nth-child(1) i"), + "fa-trash", + "tab of the first page has the given icon" + ); + assert.strictEqual( + target.querySelector(".nav-item:nth-child(1)").textContent, + "page1", + "tab of the second page has the right text" + ); + assert.containsNone( + target.querySelector(".nav-item:nth-child(2)"), + "i", + "tab of the second page doesn't have an icon" + ); + assert.strictEqual( + target.querySelector(".nav-item:nth-child(2)").textContent, + "page2", + "tab of the second page has the right text" + ); + assert.hasClass( + target.querySelector(".nav-item:nth-child(3) i"), + "fa-pencil", + "tab of the third page has the given icon" + ); + assert.strictEqual( + target.querySelector(".nav-item:nth-child(3)").textContent, + "page3", + "tab of the second page has the right text" + ); + }); });