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 }); 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) { function handleCardClick(backendType: BackendType) {
pendingBackend.current = backendType; pendingBackend.current = backendType;
void handleSubmit(onValidSubmit)(); void handleSubmit(onValidSubmit, onInvalidSubmit)();
} }
return ( return (
@@ -62,7 +67,7 @@ export function BackendSelectionStep() {
Name your remote and pick the cloud storage provider you want to connect to. 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. Your choice determines which credentials are required in the next step.
</p> </p>
<form onSubmit={handleSubmit(onValidSubmit)}> <form onSubmit={handleSubmit(onValidSubmit, onInvalidSubmit)}>
<TextFieldMD3 <TextFieldMD3
id="remote-name" id="remote-name"
label="Remote name" label="Remote name"
+9 -2
View File
@@ -10,7 +10,7 @@ import { BACKEND_SCHEMAS } from '../../schemas';
import { FieldRenderer } from '../ui/FieldRenderer'; import { FieldRenderer } from '../ui/FieldRenderer';
import { AzureAuthToggle } from './AzureAuthToggle'; import { AzureAuthToggle } from './AzureAuthToggle';
import { SftpAuthToggle } from './SftpAuthToggle'; 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'; import { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
export function RemoteConfigStep() { export function RemoteConfigStep() {
@@ -43,6 +43,13 @@ export function RemoteConfigStep() {
dispatch({ type: 'SET_STEP', payload: 2 }); 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> = { const backendLabel: Record<NonNullable<typeof backendType>, string> = {
azureblob: 'Azure Blob Storage', azureblob: 'Azure Blob Storage',
s3: 'Amazon S3', s3: 'Amazon S3',
@@ -60,7 +67,7 @@ export function RemoteConfigStep() {
Enter the credentials for your selected backend. These values are written directly into the Enter the credentials for your selected backend. These values are written directly into the
generated rclone.conf and are never sent to any server. generated rclone.conf and are never sent to any server.
</p> </p>
<form onSubmit={handleSubmit(onNext)}> <form onSubmit={handleSubmit(onNext, onInvalidSubmit)}>
{backendType === 'azureblob' ? ( {backendType === 'azureblob' ? (
<> <>
{/* Account field via FieldRenderer */} {/* Account field via FieldRenderer */}