From 679739a63d84fefd91d531e6a82761f8d416dffb Mon Sep 17 00:00:00 2001 From: luvi Date: Thu, 25 Aug 2022 11:17:33 +0000 Subject: [PATCH] [FIX] web, *: fix ProgressBarField behaviors *: survey This commit brings back the correct behavior on blur to the progressbar field. It turns as a text value as soon as the user has focused out of the input. It also removes the wrong usage of the max_value option. The behavior was wrongly adapted from the legacy progressbar implementation. Tests have been adapted to assert the correct behavior. closes odoo/odoo#98902 Signed-off-by: Aaron Bohy (aab) --- addons/survey/views/survey_survey_views.xml | 5 +-- .../fields/progress_bar/progress_bar_field.js | 24 ++++++++----- .../progress_bar/progress_bar_field.xml | 23 ++++++------ .../views/fields/progress_bar_field_tests.js | 36 ++++++++++++++++--- 4 files changed, 59 insertions(+), 29 deletions(-) diff --git a/addons/survey/views/survey_survey_views.xml b/addons/survey/views/survey_survey_views.xml index 120d15e07d3..499ae4120ca 100644 --- a/addons/survey/views/survey_survey_views.xml +++ b/addons/survey/views/survey_survey_views.xml @@ -263,10 +263,7 @@ type="object" name="action_survey_user_input_certified" class="fw-bold"> - + Passed Certified diff --git a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js index 287061489f0..bba216f92b9 100644 --- a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js +++ b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.js @@ -3,24 +3,27 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useAutofocus } from "@web/core/utils/hooks"; +import { useDebounced } from "@web/core/utils/timing"; import { useNumpadDecimal } from "../numpad_decimal_hook"; import { parseFloat } from "../parsers"; import { standardFieldProps } from "../standard_field_props"; -const { Component, onWillUpdateProps, useState } = owl; +const { Component, onWillUpdateProps, useRef, useState } = owl; const formatters = registry.category("formatters"); const parsers = registry.category("parsers"); export class ProgressBarField extends Component { setup() { useNumpadDecimal(); - useAutofocus({ refName: "max-value", selectAll: true }); - useAutofocus({ refName: "current-value", selectAll: true }); + useAutofocus({ refName: "maxValue", selectAll: true }); + useAutofocus({ refName: "currentValue", selectAll: true }); + this.root = useRef("numpadDecimal"); this.state = useState({ currentValue: this.getCurrentValue(this.props), maxValue: this.getMaxValue(this.props), isEditing: false, }); + this.onBlurDebounced = useDebounced(this.onBlur); onWillUpdateProps((nextProps) => { Object.assign(this.state, { currentValue: this.getCurrentValue(nextProps), @@ -38,6 +41,9 @@ export class ProgressBarField extends Component { get isMaxValueInteger() { return this.state.maxValue % 1 === 0; } + get isPercentage() { + return !this.props.maxValueField || !isNaN(this.props.maxValueField); + } getCurrentValueField(p) { return typeof p.currentValueField === "string" ? p.currentValueField : p.name; @@ -80,7 +86,6 @@ export class ProgressBarField extends Component { this.state.currentValue = parsedValue; this.props.record.update({ [this.getCurrentValueField(this.props)]: parsedValue }); if (this.props.readonly) { - this.state.isEditing = false; this.props.record.save(); } } @@ -99,7 +104,6 @@ export class ProgressBarField extends Component { this.state.maxValue = parsedValue; this.props.record.update({ [this.getMaxValueField(this.props)]: parsedValue }); if (this.props.readonly) { - this.state.isEditing = false; this.props.record.save(); } } @@ -109,10 +113,14 @@ export class ProgressBarField extends Component { this.state.isEditing = true; } } + // When both max and current value are editable, as one input is blurred when + // switching to the other, the state would revert to isEditing = false. We need + // to stay in edition mode if the focus is still in the field. onBlur() { - if (this.props.readonly) { - this.state.isEditing = false; + if (this.root.el && this.root.el.contains(document.activeElement)) { + return; } + this.state.isEditing = false; } onCurrentValueInput(ev) { @@ -138,7 +146,6 @@ ProgressBarField.props = { ...standardFieldProps, maxValueField: { type: [String, Number], optional: true }, currentValueField: { type: String, optional: true }, - isPercentage: { type: Boolean, optional: true }, isEditable: { type: Boolean, optional: true }, isEditableInReadonly: { type: Boolean, optional: true }, isCurrentValueEditable: { type: Boolean, optional: true }, @@ -153,7 +160,6 @@ ProgressBarField.extractProps = ({ attrs }) => { return { maxValueField: attrs.options.max_value, currentValueField: attrs.options.current_value, - isPercentage: !attrs.options.max_value, isEditable: !attrs.options.readonly && attrs.options.editable, isEditableInReadonly: attrs.options.editable_readonly, isCurrentValueEditable: diff --git a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml index 351fa5235fe..0c782b1a2f6 100644 --- a/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml +++ b/addons/web/static/src/views/fields/progress_bar/progress_bar_field.xml @@ -3,12 +3,12 @@ -
+
- +
@@ -17,15 +17,16 @@
-
+
- +
diff --git a/addons/web/static/tests/views/fields/progress_bar_field_tests.js b/addons/web/static/tests/views/fields/progress_bar_field_tests.js index 9eb8d205239..dcfaf650eb0 100644 --- a/addons/web/static/tests/views/fields/progress_bar_field_tests.js +++ b/addons/web/static/tests/views/fields/progress_bar_field_tests.js @@ -4,7 +4,12 @@ import { makeFakeLocalizationService, makeFakeNotificationService, } from "@web/../tests/helpers/mock_services"; -import { click, editInput, getFixture } from "@web/../tests/helpers/utils"; +import { + click, + editInput, + getFixture, + nextTick +} from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { registry } from "@web/core/registry"; @@ -54,7 +59,6 @@ QUnit.module("Fields", (hooks) => { QUnit.test("ProgressBarField: max_value should update", async function (assert) { assert.expect(3); - serverData.models.partner.records = serverData.models.partner.records.slice(0, 1); serverData.models.partner.records[0].float_field = 2; serverData.models.partner.onchanges = { @@ -107,7 +111,7 @@ QUnit.module("Fields", (hooks) => { QUnit.test( "ProgressBarField: value should update in edit mode when typing in input", async function (assert) { - assert.expect(5); + assert.expect(6); serverData.models.partner.records[0].int_field = 99; await makeView({ @@ -151,13 +155,21 @@ QUnit.module("Fields", (hooks) => { ); await editInput(target, ".o_progressbar_value.o_input", "69"); + target.querySelector(".o_progressbar_value.o_input").blur(); + + await nextTick(); + assert.strictEqual( + target.querySelector(".o_progressbar_value").textContent, + "69%", + "New value should be different after focusing out of the field" + ); await click(target.querySelector(".o_form_button_save")); assert.strictEqual( target.querySelector(".o_progressbar_value").textContent, "69%", - "New value should be different than initial after click" + "New value is still displayed after save" ); } ); @@ -271,7 +283,7 @@ QUnit.module("Fields", (hooks) => { QUnit.test( "ProgressBarField: update both max value and current value in edit mode when both options are given", async function (assert) { - assert.expect(7); + assert.expect(10); serverData.models.partner.records[0].int_field = 99; await makeView({ @@ -310,9 +322,23 @@ QUnit.module("Fields", (hooks) => { const currentVal = target.querySelectorAll(".o_progressbar_value.o_input")[0]; const maxVal = target.querySelectorAll(".o_progressbar_value.o_input")[1]; + assert.strictEqual(document.activeElement, currentVal, "First input is focused"); assert.strictEqual(currentVal.value, "99", "Initial value in input is correct"); assert.strictEqual(maxVal.value, "0.44", "Initial value in input is correct"); + maxVal.focus(); + await nextTick(); + assert.strictEqual(document.activeElement, maxVal, "Second input is focused"); + + maxVal.blur(); + await nextTick(); + assert.containsNone( + target, + ".o_progressbar input", + "Inputs are no longer visible when focusing out" + ); + + await click(target.querySelector(".o_progress")); await editInput(target, ".o_progressbar input:nth-of-type(1)", "2000"); await editInput(target, ".o_progressbar input:nth-of-type(2)", "69");