Skip to content

Commit e57fb32

Browse files
fix(dashboard): focus the offending field when a submit is blocked
The app form validates on submit with the error list hidden, so a required field left empty made Save look like a no-op: the error rendered somewhere off screen. Pass focusOnFirstError. RJSF's built-in handler resolves the field through form.elements, which the tagName="div" form does not have, so resolve it by generated id instead. Fixes #3135 Signed-off-by: Myasnikov Daniil <[email protected]>
1 parent 01a3bff commit e57fb32

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

‎packages/system/dashboard/images/console/apps/console/src/components/SchemaForm.test.tsx‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,16 @@ describe("SchemaForm validate handle", () => {
198198
expect(validate(ref)).toBe(false)
199199
})
200200

201+
it("focuses the first offending field so a blocked submit is not silent", () => {
202+
const ref = createRef<SchemaFormHandle>()
203+
render(
204+
<SchemaForm ref={ref} openAPISchema={requiredSchema} formData={{}} onChange={noop} />,
205+
)
206+
207+
validate(ref)
208+
expect(document.activeElement).toBe(document.getElementById("root_name"))
209+
})
210+
201211
it("reports valid through the ref once the required field is populated", () => {
202212
const ref = createRef<SchemaFormHandle>()
203213
render(

‎packages/system/dashboard/images/console/apps/console/src/components/SchemaForm.tsx‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useMemo, useEffect, useRef, forwardRef, useImperativeHandle } from "rea
22
import Form from "@rjsf/core"
33
import validator from "@rjsf/validator-ajv8"
44
import { getDefaultFormState } from "@rjsf/utils"
5-
import type { RJSFSchema, UiSchema, TemplatesType } from "@rjsf/utils"
5+
import type { RJSFSchema, UiSchema, TemplatesType, RJSFValidationError } from "@rjsf/utils"
66
import { keysOrderToUiSchema, sanitizeSchema } from "../lib/keys-order.ts"
77
import { addSensitiveStringWidgets } from "../lib/sensitive-fields.ts"
88
import {
@@ -206,6 +206,22 @@ interface SchemaFormProps {
206206
immutableMode?: "enforce" | "off"
207207
}
208208

209+
/**
210+
* Errors render inline with the error list hidden, so a blocked submit is
211+
* invisible unless the offending field is brought into view. RJSF's built-in
212+
* focus resolves the field through `form.elements`, which the `tagName="div"`
213+
* form does not have — resolve it by generated id instead.
214+
*/
215+
function focusFirstError(error: RJSFValidationError) {
216+
const segments = (error.property ?? "")
217+
.replace(/\['?([^'\]]+)'?\]/g, ".$1")
218+
.split(".")
219+
.filter(Boolean)
220+
const field = document.getElementById(["root", ...segments].join("_"))
221+
field?.scrollIntoView?.({ block: "center" })
222+
field?.focus?.({ preventScroll: true })
223+
}
224+
209225
export interface SchemaFormHandle {
210226
/**
211227
* Run RJSF validation against the current form data and render any errors
@@ -345,6 +361,7 @@ export const SchemaForm = forwardRef<SchemaFormHandle, SchemaFormProps>(function
345361
onChange={(e) => onChange(e.formData)}
346362
liveValidate={false}
347363
showErrorList={false}
364+
focusOnFirstError={focusFirstError}
348365
>
349366
{children}
350367
</Form>

0 commit comments

Comments
 (0)