diff --git a/addons/web/static/src/core/utils/hooks.js b/addons/web/static/src/core/utils/hooks.js
index 10c339bdf3f..2fe6743a5c0 100644
--- a/addons/web/static/src/core/utils/hooks.js
+++ b/addons/web/static/src/core/utils/hooks.js
@@ -27,11 +27,14 @@ const { status, useComponent, useEffect, useRef, onWillUnmount } = owl;
* Focus an element referenced by a t-ref="autofocus" in the current component
* as soon as it appears in the DOM and if it was not displayed before.
* If it is an input/textarea, set the selection at the end.
+ * @param {Object} [params]
+ * @param {string} [params.refName] override the ref name "autofocus"
+ * @param {boolean} [params.selectAll] if true, will select the entire text value.
* @returns {Object} the element reference
*/
-export function useAutofocus() {
+export function useAutofocus({ refName, selectAll } = {}) {
const comp = useComponent();
- const ref = useRef("autofocus");
+ const ref = useRef(refName || "autofocus");
// Prevent autofocus in mobile
if (comp.env.isSmall) {
return ref;
@@ -45,8 +48,9 @@ export function useAutofocus() {
(el) => {
if (el) {
el.focus();
- if (["INPUT", "TEXTAREA"].includes(el.tagName) && el.type !== 'number') {
- el.selectionStart = el.selectionEnd = el.value.length;
+ if (["INPUT", "TEXTAREA"].includes(el.tagName) && el.type !== "number") {
+ el.selectionEnd = el.value.length;
+ el.selectionStart = selectAll ? 0 : el.value.length;
}
}
},
diff --git a/addons/web/static/tests/core/utils/hooks_tests.js b/addons/web/static/tests/core/utils/hooks_tests.js
index 7790f39d37e..4246df709f4 100644
--- a/addons/web/static/tests/core/utils/hooks_tests.js
+++ b/addons/web/static/tests/core/utils/hooks_tests.js
@@ -14,7 +14,7 @@ import {
} from "@web/../tests/helpers/utils";
import { LegacyComponent } from "@web/legacy/legacy_component";
-const { Component, onMounted, xml } = owl;
+const { Component, onMounted, useState, xml } = owl;
const serviceRegistry = registry.category("services");
QUnit.module("utils", () => {
@@ -47,30 +47,33 @@ QUnit.module("utils", () => {
assert.strictEqual(document.activeElement, comp.inputRef.el);
});
- QUnit.test("useAutofocus: simple usecase when input type is number", async function (assert) {
- class MyComponent extends Component {
- setup() {
- this.inputRef = useAutofocus();
+ QUnit.test(
+ "useAutofocus: simple usecase when input type is number",
+ async function (assert) {
+ class MyComponent extends Component {
+ setup() {
+ this.inputRef = useAutofocus();
+ }
}
- }
- MyComponent.template = xml`
+ MyComponent.template = xml`
`;
- registry.category("services").add("ui", uiService);
+ registry.category("services").add("ui", uiService);
- const env = await makeTestEnv();
- const target = getFixture();
- const comp = await mount(MyComponent, target, { env });
+ const env = await makeTestEnv();
+ const target = getFixture();
+ const comp = await mount(MyComponent, target, { env });
- assert.strictEqual(document.activeElement, comp.inputRef.el);
+ assert.strictEqual(document.activeElement, comp.inputRef.el);
- comp.render();
- await nextTick();
- assert.strictEqual(document.activeElement, comp.inputRef.el);
- });
+ comp.render();
+ await nextTick();
+ assert.strictEqual(document.activeElement, comp.inputRef.el);
+ }
+ );
QUnit.test("useAutofocus: conditional autofocus", async function (assert) {
class MyComponent extends Component {
@@ -124,7 +127,7 @@ QUnit.module("utils", () => {
const fakeUIService = {
start(env) {
- let ui = {};
+ const ui = {};
Object.defineProperty(env, "isSmall", {
get() {
return true;
@@ -142,6 +145,64 @@ QUnit.module("utils", () => {
await mount(MyComponent, target, { env });
});
+ QUnit.test("supports different ref names", async (assert) => {
+ class MyComponent extends Component {
+ setup() {
+ this.secondRef = useAutofocus({ refName: "second" });
+ this.firstRef = useAutofocus({ refName: "first" });
+
+ this.state = useState({ showSecond: true });
+ }
+ }
+ MyComponent.template = xml`
+
+
+
+
+ `;
+
+ registry.category("services").add("ui", uiService);
+
+ const env = await makeTestEnv();
+ const target = getFixture();
+ const comp = await mount(MyComponent, target, { env });
+ await nextTick();
+
+ // "first" is focused first since it has the last call to "useAutofocus"
+ assert.strictEqual(document.activeElement, comp.firstRef.el);
+
+ comp.state.showSecond = false;
+ await nextTick();
+ comp.state.showSecond = true;
+ await nextTick();
+
+ assert.strictEqual(document.activeElement, comp.secondRef.el);
+ });
+
+ QUnit.test("can select an entire text", async (assert) => {
+ class MyComponent extends Component {
+ setup() {
+ this.inputRef = useAutofocus({ selectAll: true });
+ }
+ }
+ MyComponent.template = xml`
+
+
+
+ `;
+
+ registry.category("services").add("ui", uiService);
+
+ const env = await makeTestEnv();
+ const target = getFixture();
+ const comp = await mount(MyComponent, target, { env });
+ await nextTick();
+
+ assert.strictEqual(document.activeElement, comp.inputRef.el);
+ assert.strictEqual(comp.inputRef.el.selectionStart, 0);
+ assert.strictEqual(comp.inputRef.el.selectionEnd, 10);
+ });
+
QUnit.module("useBus");
QUnit.test("useBus hook: simple usecase", async function (assert) {