Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions web_widget_dropdown_dynamic/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ Contributors

- ``Heliconia Solutions Pvt. Ltd. <https://www.heliconia.io>``\ \_

- `Data Dance s.r.o. <https://www.datadance.eu/>`__

- Radovan Skolnik <radovan@skolnik.info>

Other credits
-------------

Expand Down
4 changes: 4 additions & 0 deletions web_widget_dropdown_dynamic/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@
- [Tecnativa](https://www.tecnativa.com):
- Carlos Roca
- `Heliconia Solutions Pvt. Ltd. <https://www.heliconia.io>`_

- [Data Dance s.r.o.](https://www.datadance.eu/)

- Radovan Skolnik \<<radovan@skolnik.info>\>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class FieldDynamicDropdown extends Component {
...standardFieldProps,
method: {type: String},
context: {type: Object},
required: {type: Boolean, optional: true},
};
setup() {
super.setup();
Expand All @@ -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)) {
Expand Down Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<span t-esc="string">). 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: `
<form>
<field
name="content_string"
widget="dynamic_dropdown"
options="{'values': 'method_name'}"
context="{'depending_on': id}"
readonly="1"
/>
</form>`,
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 <span> 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({
Expand Down
Loading