diff --git a/addons/web/static/src/views/widgets/documentation_link/documentation_link.js b/addons/web/static/src/views/widgets/documentation_link/documentation_link.js
new file mode 100644
index 00000000000..199bbaea720
--- /dev/null
+++ b/addons/web/static/src/views/widgets/documentation_link/documentation_link.js
@@ -0,0 +1,36 @@
+/** @odoo-module **/
+
+import { session } from "@web/session";
+import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
+import { Component } from "@odoo/owl";
+import { registry } from "@web/core/registry";
+
+const LINK_REGEX = new RegExp("^https?://");
+
+export class DocumentationLink extends Component {
+ get url() {
+ if (LINK_REGEX.test(this.props.path)) {
+ return this.props.path;
+ } else {
+ const serverVersion = session.server_version.includes("alpha")
+ ? "master"
+ : session.server_version;
+ return "https://www.odoo.com/documentation/" + serverVersion + this.props.path;
+ }
+ }
+}
+DocumentationLink.template = "web.DocumentationLink";
+DocumentationLink.props = {
+ ...standardWidgetProps,
+ record: { type: Object, optional: 1 }, // The record is not needed in this widget
+ path: { type: String },
+};
+DocumentationLink.extractProps = ({ attrs }) => {
+ const { path } = attrs;
+ return {
+ path,
+ };
+};
+DocumentationLink.additionalClasses = ["d-inline"];
+
+registry.category("view_widgets").add("documentation_link", DocumentationLink);
diff --git a/addons/web/static/src/views/widgets/documentation_link/documentation_link.xml b/addons/web/static/src/views/widgets/documentation_link/documentation_link.xml
new file mode 100644
index 00000000000..b9a1754d3f9
--- /dev/null
+++ b/addons/web/static/src/views/widgets/documentation_link/documentation_link.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/addons/web/static/src/views/widgets/widget.js b/addons/web/static/src/views/widgets/widget.js
index a422ba6e32a..e2b62db5e06 100644
--- a/addons/web/static/src/views/widgets/widget.js
+++ b/addons/web/static/src/views/widgets/widget.js
@@ -25,11 +25,17 @@ export class Widget extends Component {
}
get classNames() {
- return {
+ const classNames = {
o_widget: true,
[`o_widget_${this.props.name}`]: true,
[this.props.className]: Boolean(this.props.className),
};
+ if (this.Widget.additionalClasses) {
+ for (const cls of this.Widget.additionalClasses) {
+ classNames[cls] = true;
+ }
+ }
+ return classNames;
}
get widgetProps() {
const { node: rawNode } = this.props;
diff --git a/addons/web/static/src/webclient/settings_form_view/settings/setting.js b/addons/web/static/src/webclient/settings_form_view/settings/setting.js
index 719443aabb6..19af10ba606 100644
--- a/addons/web/static/src/webclient/settings_form_view/settings/setting.js
+++ b/addons/web/static/src/webclient/settings_form_view/settings/setting.js
@@ -5,9 +5,8 @@ import { HighlightText } from "../highlight_text/highlight_text";
import { session } from "@web/session";
import { FormLabelHighlightText } from "../highlight_text/form_label_highlight_text";
import { Component, useState } from "@odoo/owl";
+import { DocumentationLink } from "@web/views/widgets/documentation_link/documentation_link";
-
-const LINK_REGEX = new RegExp("^https?://");
export class Setting extends Component {
setup() {
this.state = useState({
@@ -57,16 +56,6 @@ export class Setting extends Component {
return label || "";
}
- get url() {
- if (LINK_REGEX.test(this.props.documentation)) {
- return this.props.documentation;
- } else {
- const serverVersion = session.server_version.includes("alpha")
- ? "master"
- : session.server_version;
- return "https://www.odoo.com/documentation/" + serverVersion + this.props.documentation;
- }
- }
visible() {
if (!this.state.search.value) {
return true;
@@ -84,6 +73,7 @@ export class Setting extends Component {
Setting.components = {
FormLabelHighlightText,
HighlightText,
+ DocumentationLink,
};
Setting.template = "web.Setting";
Setting.props = {
diff --git a/addons/web/static/src/webclient/settings_form_view/settings/setting.xml b/addons/web/static/src/webclient/settings_form_view/settings/setting.xml
index 4c8ff64b877..224811cca22 100644
--- a/addons/web/static/src/webclient/settings_form_view/settings/setting.xml
+++ b/addons/web/static/src/webclient/settings_form_view/settings/setting.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/addons/web/static/tests/views/widgets/document_link_tests.js b/addons/web/static/tests/views/widgets/document_link_tests.js
new file mode 100644
index 00000000000..c50ebcdb45f
--- /dev/null
+++ b/addons/web/static/tests/views/widgets/document_link_tests.js
@@ -0,0 +1,72 @@
+/** @odoo-module **/
+import { getFixture } from "@web/../tests/helpers/utils";
+import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
+
+let target;
+let serverData;
+
+QUnit.module("Widgets", (hooks) => {
+ hooks.beforeEach(() => {
+ target = getFixture();
+ serverData = {
+ models: {
+ partner: {
+ fields: {
+ bar: { string: "Bar", type: "boolean" },
+ },
+ },
+ },
+ };
+
+ setupViewRegistries();
+ });
+
+ QUnit.module("DocumentationLink");
+
+ QUnit.test("documentation_link: relative path", async function (assert) {
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ serverData,
+ arch: `
+ `,
+ });
+
+ assert.hasAttrValue(
+ target.querySelector(".o_doc_link"),
+ "href",
+ "https://www.odoo.com/documentation/1.0/applications/technical/web/settings/this_is_a_test.html"
+ );
+ });
+ QUnit.test("documentation_link: absoluth path (http)", async function (assert) {
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ serverData,
+ arch: `
+ `,
+ });
+
+ assert.hasAttrValue(target.querySelector(".o_doc_link"), "href", "http://www.odoo.com/");
+ });
+ QUnit.test("documentation_link: absoluth path (https)", async function (assert) {
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ serverData,
+ arch: `
+ `,
+ });
+
+ assert.hasAttrValue(target.querySelector(".o_doc_link"), "href", "https://www.odoo.com/");
+ });
+});