feat(11-02): auto-scroll to first errored field on validation failure

- BackendSelectionStep: add onInvalidSubmit scrolling to remote-name input
- BackendSelectionStep: pass onInvalidSubmit to form onSubmit and handleCardClick
- RemoteConfigStep: add FieldErrors import from react-hook-form
- RemoteConfigStep: add onInvalidSubmit scrolling to first errored field by key
- RemoteConfigStep: pass onInvalidSubmit to form onSubmit
This commit is contained in:
2026-04-01 13:30:39 +02:00
parent 8b7daa860c
commit 90cdeda142
2 changed files with 16 additions and 4 deletions
@@ -50,9 +50,14 @@ export function BackendSelectionStep() {
dispatch({ type: 'SET_STEP', payload: 1 });
}
function onInvalidSubmit() {
// Only one validatable field in this step: remote-name
document.getElementById('remote-name')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function handleCardClick(backendType: BackendType) {
pendingBackend.current = backendType;
void handleSubmit(onValidSubmit)();
void handleSubmit(onValidSubmit, onInvalidSubmit)();
}
return (
@@ -62,7 +67,7 @@ export function BackendSelectionStep() {
Name your remote and pick the cloud storage provider you want to connect to.
Your choice determines which credentials are required in the next step.
</p>
<form onSubmit={handleSubmit(onValidSubmit)}>
<form onSubmit={handleSubmit(onValidSubmit, onInvalidSubmit)}>
<TextFieldMD3
id="remote-name"
label="Remote name"
+9 -2
View File
@@ -10,7 +10,7 @@ import { BACKEND_SCHEMAS } from '../../schemas';
import { FieldRenderer } from '../ui/FieldRenderer';
import { AzureAuthToggle } from './AzureAuthToggle';
import { SftpAuthToggle } from './SftpAuthToggle';
import type { FieldError } from 'react-hook-form';
import type { FieldError, FieldErrors } from 'react-hook-form';
import { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
export function RemoteConfigStep() {
@@ -43,6 +43,13 @@ export function RemoteConfigStep() {
dispatch({ type: 'SET_STEP', payload: 2 });
};
function onInvalidSubmit(errors: FieldErrors) {
const firstKey = Object.keys(errors)[0];
if (firstKey) {
document.getElementById(firstKey)?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
const backendLabel: Record<NonNullable<typeof backendType>, string> = {
azureblob: 'Azure Blob Storage',
s3: 'Amazon S3',
@@ -60,7 +67,7 @@ export function RemoteConfigStep() {
Enter the credentials for your selected backend. These values are written directly into the
generated rclone.conf and are never sent to any server.
</p>
<form onSubmit={handleSubmit(onNext)}>
<form onSubmit={handleSubmit(onNext, onInvalidSubmit)}>
{backendType === 'azureblob' ? (
<>
{/* Account field via FieldRenderer */}