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"
+ );
+ });
});