[FIX] web: Fix behaviors of ProgressBarField
This commit fixes behavior issues in the progress bar field: - the progressbar would not write on a field if it was declared as 'readonly' on the server (it sounds counter-intuitive but this widget was supposed to write on records unless given the 'readonly' flag in the field options); - the "isEditable" prop was incorrectly computed, resulting in the field not being editable in kanban views when it should be; - when editing a value in the field, the focus should be automatically given to the first input. These issues have been fixed in this commit. closes odoo/odoo#97411 Signed-off-by: Bruno Boi (boi) <boi@odoo.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { ProgressBarField } from "./progress_bar_field";
|
||||
|
||||
export class KanbanProgressBarField extends ProgressBarField {
|
||||
onClick() {
|
||||
if (this.props.isEditable && !this.props.record.isReadonly(this.props.name)) {
|
||||
if (this.props.isEditable) {
|
||||
this.state.isEditing = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { _lt } from "@web/core/l10n/translation";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useAutofocus } from "@web/core/utils/hooks";
|
||||
import { useNumpadDecimal } from "../numpad_decimal_hook";
|
||||
import { parseFloat } from "../parsers";
|
||||
import { standardFieldProps } from "../standard_field_props";
|
||||
import { useNumpadDecimal } from "../numpad_decimal_hook";
|
||||
|
||||
const { Component, onWillUpdateProps, useState } = owl;
|
||||
const formatters = registry.category("formatters");
|
||||
@@ -13,6 +14,8 @@ const parsers = registry.category("parsers");
|
||||
export class ProgressBarField extends Component {
|
||||
setup() {
|
||||
useNumpadDecimal();
|
||||
useAutofocus({ refName: "max-value", selectAll: true });
|
||||
useAutofocus({ refName: "current-value", selectAll: true });
|
||||
this.state = useState({
|
||||
currentValue: this.getCurrentValue(this.props),
|
||||
maxValue: this.getMaxValue(this.props),
|
||||
@@ -62,10 +65,10 @@ export class ProgressBarField extends Component {
|
||||
return formatter(this.state.maxValue, { humanReadable });
|
||||
}
|
||||
|
||||
onCurrentValueChange(value) {
|
||||
onCurrentValueChange(ev) {
|
||||
let parsedValue;
|
||||
try {
|
||||
parsedValue = parseFloat(value);
|
||||
parsedValue = parseFloat(ev.target.value);
|
||||
} catch {
|
||||
this.props.record.setInvalidField(this.props.name);
|
||||
return;
|
||||
@@ -81,10 +84,10 @@ export class ProgressBarField extends Component {
|
||||
this.props.record.save();
|
||||
}
|
||||
}
|
||||
onMaxValueChange(value) {
|
||||
onMaxValueChange(ev) {
|
||||
let parsedValue;
|
||||
try {
|
||||
parsedValue = parseFloat(value);
|
||||
parsedValue = parseFloat(ev.target.value);
|
||||
} catch {
|
||||
this.props.record.setInvalidField(this.props.name);
|
||||
return;
|
||||
@@ -151,7 +154,7 @@ ProgressBarField.extractProps = ({ attrs }) => {
|
||||
maxValueField: attrs.options.max_value,
|
||||
currentValueField: attrs.options.current_value,
|
||||
isPercentage: !attrs.options.max_value,
|
||||
isEditable: attrs.options.editable,
|
||||
isEditable: !attrs.options.readonly && attrs.options.editable,
|
||||
isEditableInReadonly: attrs.options.editable_readonly,
|
||||
isCurrentValueEditable:
|
||||
attrs.options.editable &&
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
inputmode="numeric"
|
||||
t-att-value="formatCurrentValue() or ''"
|
||||
t-att-required="props.required"
|
||||
t-on-change="(ev) => this.onCurrentValueChange(ev.target.value)"
|
||||
t-on-input="(ev) => this.onCurrentValueInput(ev)"
|
||||
t-on-change="onCurrentValueChange"
|
||||
t-on-input="onCurrentValueInput"
|
||||
t-on-blur="onBlur"
|
||||
/>
|
||||
<span>%</span>
|
||||
@@ -40,23 +40,25 @@
|
||||
<t t-else="">
|
||||
<input
|
||||
t-if="props.isCurrentValueEditable"
|
||||
t-ref="current-value"
|
||||
class="o_progressbar_value o_input"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
t-att-value="formatCurrentValue()"
|
||||
t-on-change="(ev) => this.onCurrentValueChange(ev.target.value)"
|
||||
t-on-input="(ev) => this.onCurrentValueInput(ev)"
|
||||
t-on-change="onCurrentValueChange"
|
||||
t-on-input="onCurrentValueInput"
|
||||
t-on-blur="onBlur"
|
||||
/>
|
||||
<span t-if="props.isCurrentValueEditable and props.isMaxValueEditable" t-esc="'/'" />
|
||||
<input
|
||||
t-if="props.isMaxValueEditable"
|
||||
t-ref="max-value"
|
||||
class="o_progressbar_value o_input"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
t-att-value="formatMaxValue()"
|
||||
t-on-change="(ev) => this.onMaxValueChange(ev.target.value)"
|
||||
t-on-input="(ev) => this.onMaxValueInput(ev)"
|
||||
t-on-change="onMaxValueChange"
|
||||
t-on-input="onMaxValueInput"
|
||||
t-on-blur="onBlur"
|
||||
/>
|
||||
</t>
|
||||
@@ -65,4 +67,3 @@
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
|
||||
@@ -72,6 +72,11 @@ export class KanbanArchParser extends XMLParser {
|
||||
node.setAttribute("widget", "many2many_tags");
|
||||
}
|
||||
const fieldInfo = Field.parseFieldNode(node, models, modelName, "kanban", jsClass);
|
||||
if (!node.hasAttribute("force_save")) {
|
||||
// Force save is true by default on kanban views:
|
||||
// this allows to write on any field regardless of its modifiers.
|
||||
fieldInfo.forceSave = true;
|
||||
}
|
||||
const name = fieldInfo.name;
|
||||
fieldNodes[name] = fieldInfo;
|
||||
node.setAttribute("field_id", name);
|
||||
|
||||
@@ -364,6 +364,8 @@ QUnit.module("Fields", (hooks) => {
|
||||
|
||||
QUnit.test("ProgressBarField: field is editable in kanban", async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
serverData.models.partner.fields.int_field.readonly = true;
|
||||
serverData.models.partner.records[0].int_field = 99;
|
||||
|
||||
await makeView({
|
||||
@@ -412,6 +414,7 @@ QUnit.module("Fields", (hooks) => {
|
||||
);
|
||||
|
||||
await editInput(target, ".o_progressbar_value.o_input", "69");
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_progressbar_value").textContent,
|
||||
"69 / 100",
|
||||
@@ -423,6 +426,42 @@ QUnit.module("Fields", (hooks) => {
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("force readonly in kanban", async (assert) => {
|
||||
assert.expect(3);
|
||||
|
||||
serverData.models.partner.records[0].int_field = 99;
|
||||
|
||||
await makeView({
|
||||
serverData,
|
||||
type: "kanban",
|
||||
resModel: "partner",
|
||||
arch: /* xml */ `
|
||||
<kanban>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div>
|
||||
<field name="int_field" widget="progressbar" options="{'editable': true, 'max_value': 'float_field', 'readonly': True}" />
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
resId: 1,
|
||||
mockRPC(route, { method, args }) {
|
||||
if (method === "write") {
|
||||
throw new Error("Not supposed to write");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(target.querySelector(".o_progressbar_value").textContent, "99 / 100");
|
||||
|
||||
// Clicking on the progress bar should not change the value
|
||||
await click(target.querySelector(".o_progress"));
|
||||
|
||||
assert.strictEqual(target.querySelector(".o_progressbar_value").textContent, "99 / 100");
|
||||
assert.containsNone(target, ".o_progressbar_value.o_input");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"ProgressBarField: readonly and editable attrs/options in kanban",
|
||||
async function (assert) {
|
||||
|
||||
Reference in New Issue
Block a user