From 122def0a1460c7d22f5f865c0ffd3167a66a0daf Mon Sep 17 00:00:00 2001 From: Rad0van Date: Sun, 29 Mar 2026 15:17:50 +0200 Subject: [PATCH] [web_widget_dropdown_dynamic][FIX]: value disappearing after losing focus when used in list view, propagate required prop --- web_widget_dropdown_dynamic/README.rst | 4 ++ .../readme/CONTRIBUTORS.md | 4 ++ .../src/js/field_dynamic_dropdown.esm.js | 17 +++++++- .../web_widget_dropdown_dynamic_tests.esm.js | 42 +++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/web_widget_dropdown_dynamic/README.rst b/web_widget_dropdown_dynamic/README.rst index 2597238a93c2..cab37a4aa3ac 100644 --- a/web_widget_dropdown_dynamic/README.rst +++ b/web_widget_dropdown_dynamic/README.rst @@ -123,6 +123,10 @@ Contributors - ``Heliconia Solutions Pvt. Ltd. ``\ \_ +- `Data Dance s.r.o. `__ + + - Radovan Skolnik + Other credits ------------- diff --git a/web_widget_dropdown_dynamic/readme/CONTRIBUTORS.md b/web_widget_dropdown_dynamic/readme/CONTRIBUTORS.md index 49740cb77480..b9b99766ac8e 100644 --- a/web_widget_dropdown_dynamic/readme/CONTRIBUTORS.md +++ b/web_widget_dropdown_dynamic/readme/CONTRIBUTORS.md @@ -15,3 +15,7 @@ - [Tecnativa](https://www.tecnativa.com): - Carlos Roca - `Heliconia Solutions Pvt. Ltd. `_ + +- [Data Dance s.r.o.](https://www.datadance.eu/) + + - Radovan Skolnik \<\> diff --git a/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js b/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js index 4e83058f95c1..f64fa1b4203f 100644 --- a/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js +++ b/web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.esm.js @@ -11,6 +11,7 @@ export class FieldDynamicDropdown extends Component { ...standardFieldProps, method: {type: String}, context: {type: Object}, + required: {type: Boolean, optional: true}, }; setup() { super.setup(); @@ -36,6 +37,19 @@ export class FieldDynamicDropdown extends Component { } return specialDataCaches[key]; } + get string() { + const value = this.props.record.data[this.props.name]; + if (!value || !this.specialData) { + return ""; + } + if (!this.specialData) { + return ""; + } + const option = this.specialData.find( + (o) => o[0] === value || String(o[0]) === String(value) + ); + return option ? String(option[1]) : String(value); + } get options() { const fieldType = this.type || ""; if (["char", "integer", "selection"].includes(fieldType)) { @@ -84,9 +98,10 @@ export const dynamicDropdownField = { component: FieldDynamicDropdown, displayName: _t("Dynamic Dropdown"), supportedTypes: ["char", "integer", "selection"], - extractProps: ({options}, {context}) => ({ + extractProps: ({options}, {context, required}) => ({ method: options?.values, context, + required, }), }; registry.category("fields").add("dynamic_dropdown", dynamicDropdownField); diff --git a/web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.esm.js b/web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.esm.js index 0c84b40e3171..8412591b43bb 100644 --- a/web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.esm.js +++ b/web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.esm.js @@ -146,6 +146,48 @@ QUnit.module("web_widget_dropdown_dynamic", (hooks) => { assert.containsOnce(field_target, 'option[value="\\"10\\""]'); }); + QUnit.test("displays label for selected value in readonly mode", async (assert) => { + // Bug: FieldDynamicDropdown extended Component directly instead of SelectionField, + // so it never inherited get string(). The web.SelectionField template uses `this.string` + // in its readonly branch (). Without the getter, this.string is + // undefined and the span renders empty — the selected value visually "disappears" + // whenever the field switches to readonly display (e.g. row loses focus in a list). + serverData.models["sale.order"].records[0].content_string = "value a"; + + await makeView({ + type: "form", + resModel: "sale.order", + serverData, + arch: ` +
+ + `, + resId: 1, + mockRPC(route, args) { + if (args.method === "method_name") { + return [["value a", "Value A"]]; + } + }, + }); + + const fieldEl = target.querySelector(".o_field_widget[name='content_string']"); + assert.ok( + fieldEl.querySelector("span"), + "field renders a element in readonly mode" + ); + assert.strictEqual( + fieldEl.querySelector("span").textContent.trim(), + "Value A", + "the option label is shown in readonly mode, not blank" + ); + }); + QUnit.test("values are fetched w/o context (selection)", async (assert) => { assert.expect(6); await makeView({