Closed nireno closed 1 year ago
You can create a hook that stores an array of tuple (or map) of (Field, React.domRef)
and check for fields with errors to focus/scroll to the input.
Here it is an example of how to do something similar:
// FormWrapper.res
module Make = (Config: ReForm.Config) => {
include ReForm.Make(Config)
module ComparableField = Belt.Id.MakeComparable({
type t = field
let cmp = Pervasives.compare
})
type fields = Belt.MutableMap.t<ComparableField.t, Dom.element, ComparableField.identity>
type registerFieldFn = (field, Js.Nullable.t<Dom.element>) => unit
let useScrollToField = (~offsetTop=0, ()) => {
let fieldsRef: React.ref<fields> = React.useRef(
Belt.MutableMap.make(~id=module(ComparableField)),
)
let register = (fieldName, fieldRef) => {
switch fieldRef->Js.Nullable.toOption {
| Some(el) => fieldsRef.current->Belt.MutableMap.set(fieldName, el)
| None => ()
}
}
let scrollToFieldWithError = ({state}: onSubmitAPI) => {
state.fieldsState
->Belt.Array.get(0)
->Belt.Option.map(((field, _)) => field)
->Belt.Option.flatMap(field => fieldsRef.current->Belt.MutableMap.get(field))
->Belt.Option.map(el => DomApiExtra.Window.scrollTo(0, el->DomApiExtra.offsetTop - offsetTop))
->ignore
}
(register, scrollToFieldWithError)
}
}
// MyApp.res
module Lenses = %lenses(
type state = {
name: string,
}
)
module MyForm = FormWrapper.Make(Lenses)
@react.component
let make = () => {
// reform stuff here (useForm, etc)
let (registerField, scrollWhenFail) = MyForm.useScrollToField()
<input ref={Field(Name)->registerField->ReactDOM.Ref.callbackDomRef} />
}
Thanks I'll give your example a shot. I thought that's what it would boil down to but was hoping there might be some magic to help out.
Let me know if you need help to change the example from scrolling to a focusing behaviour.
Any progress here? Can I close this issue? Let me know if you need help.
Had some friction getting things wired up but finally got it:
scrollWhenFail
up to onSubmitFail
when configuring ReForm use
.state.fieldsState->Belt.Array.get(0)
.el.focus({preventScroll: true})
. Without preventScroll
the focused element would just pop into view. Having it lets me follow up with el.scrollIntoView({"behavior": smooth, "block": center})
to smoothly scroll the invalid input into view.Thanks!
On submit validation failure, is there any feature that would help to focus on the first invalid input element?