Merge pull request #13130 from overleaf/td-history-add-label-autofocus

History migration: Autofocus input in add label dialog

GitOrigin-RevId: b51187e735414cd4b027413dfe57a0ba332c7b1a
This commit is contained in:
Tim Down 2023-05-25 10:59:55 +01:00 committed by Copybot
parent 82d7264c32
commit 80a5f4594e
2 changed files with 39 additions and 7 deletions

View file

@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { Modal, FormGroup, FormControl } from 'react-bootstrap'
import ModalError from './modal-error'
import AccessibleModal from '../../../../shared/components/accessible-modal'
@ -9,6 +9,7 @@ import useAddOrRemoveLabels from '../../hooks/use-add-or-remove-labels'
import { useHistoryContext } from '../../context/history-context'
import { addLabel } from '../../services/api'
import { Label } from '../../services/types/label'
import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
type AddLabelModalProps = {
show: boolean
@ -32,6 +33,17 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
const { signal } = useAbortController()
const { addUpdateLabel } = useAddOrRemoveLabels()
const { autoFocusedRef, resetAutoFocus } =
useRefWithAutoFocus<HTMLInputElement>()
// Reset the autofocus when `show` changes so that autofocus still happens if
// the dialog is shown, hidden and then shown again
useEffect(() => {
if (show) {
resetAutoFocus()
}
}, [resetAutoFocus, show])
const handleModalExited = () => {
setComment('')
@ -71,7 +83,9 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
<Modal.Body>
{isError && <ModalError error={responseError} />}
<FormGroup>
<FormControl
<input
ref={autoFocusedRef}
className="form-control"
type="text"
placeholder={t('history_new_label_name')}
required
@ -79,7 +93,6 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
onChange={(
e: React.ChangeEvent<HTMLInputElement & FormControl>
) => setComment(e.target.value)}
autoFocus // eslint-disable-line jsx-a11y/no-autofocus
/>
</FormGroup>
</Modal.Body>

View file

@ -1,17 +1,36 @@
import { useRef, useEffect } from 'react'
import { useRef, useEffect, useCallback, useState } from 'react'
export function useRefWithAutoFocus<T extends HTMLElement = HTMLElement>() {
const autoFocusedRef = useRef<T>(null)
const [hasFocused, setHasFocused] = useState(false)
const resetAutoFocus = useCallback(() => setHasFocused(false), [])
// Run on every render but use hasFocused to ensure that the autofocus only
// happens once
useEffect(() => {
if (hasFocused) {
return
}
let request: number | null = null
if (autoFocusedRef.current) {
window.requestAnimationFrame(() => {
request = window.requestAnimationFrame(() => {
if (autoFocusedRef.current) {
autoFocusedRef.current.focus()
setHasFocused(true)
request = null
}
})
}
}, [])
return { autoFocusedRef }
// Cancel a pending autofocus prior to autofocus actually happening on
// render, and on unmount
return () => {
if (request !== null) {
window.cancelAnimationFrame(request)
}
}
})
return { autoFocusedRef, resetAutoFocus }
}