From 8792d912bc8cbc36e717d94daf45bbdd23a29750 Mon Sep 17 00:00:00 2001 From: James Teh Date: Thu, 24 Nov 2022 02:21:42 +1000 Subject: [PATCH] fix: focus the textarea in the Compose dialog (#2227) ComposeBox already specified autoFocus for the ComposeInput. However, this didn't work because the dialog was disabled when the ComposeInput code tried to focus the textarea. To fix this, tweak A11yDialog to look for the autofocus attribute when determining what to focus. This is consistent with the behavior for native HTML dialogs. Then, have ComposeInput set this attribute if it's in a dialog. Fixes #1839. --- src/routes/_components/compose/ComposeInput.html | 10 +++++++++- src/routes/_thirdparty/a11y-dialog/a11y-dialog.js | 14 +++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/routes/_components/compose/ComposeInput.html b/src/routes/_components/compose/ComposeInput.html index 544717cb..47172d3e 100644 --- a/src/routes/_components/compose/ComposeInput.html +++ b/src/routes/_components/compose/ComposeInput.html @@ -117,7 +117,15 @@ firstTime = false const { autoFocus } = this.get() if (autoFocus) { - requestAnimationFrame(() => textarea.focus({ preventScroll: true })) + const { realm } = this.get() + if (realm === 'dialog') { + // If we're in a dialog, the dialog will be hidden at this + // point. Also, the dialog has its own initial focus behavior. + // Tell the dialog to focus the textarea. + textarea.setAttribute('autofocus', true) + } else { + requestAnimationFrame(() => textarea.focus({ preventScroll: true })) + } } } }) diff --git a/src/routes/_thirdparty/a11y-dialog/a11y-dialog.js b/src/routes/_thirdparty/a11y-dialog/a11y-dialog.js index fdf265b1..0ddbd5c4 100644 --- a/src/routes/_thirdparty/a11y-dialog/a11y-dialog.js +++ b/src/routes/_thirdparty/a11y-dialog/a11y-dialog.js @@ -108,7 +108,7 @@ A11yDialog.prototype.show = function (event) { // it later, then set the focus to the first focusable child of the dialog // element focusedBeforeDialog = document.activeElement - setFocusToFirstItem(this.node) + setInitialFocus(this.node) // Bind a focus event listener to the body element to make sure the focus // stays trapped inside the dialog while open, and start listening for some @@ -281,7 +281,7 @@ A11yDialog.prototype._maintainFocus = function (event) { // If the dialog is shown and the focus is not within the dialog element, // move it back to its first focusable child if (this.shown && !this.node.contains(event.target)) { - setFocusToFirstItem(this.node) + setInitialFocus(this.node) } } @@ -333,9 +333,17 @@ function collect (target) { * * @param {Element} node */ -function setFocusToFirstItem (node) { +function setInitialFocus (node) { const focusableChildren = getFocusableChildren(node) + // If there's an element with an autofocus attribute, focus that. + for (const child of focusableChildren) { + if (child.getAttribute('autofocus')) { + child.focus() + return + } + } + // Otherwise, focus the first focusable element. if (focusableChildren.length) { focusableChildren[0].focus() }