mirror of
https://github.com/penpot/penpot.git
synced 2026-09-14 16:09:01 +00:00
⏪ Backport form error management improvements from develop
This commit is contained in:
parent
01ecde3bfa
commit
13fd20f76f
@ -50,7 +50,8 @@
|
|||||||
touched? (and (contains? (:data @form) input-name)
|
touched? (and (contains? (:data @form) input-name)
|
||||||
(get-in @form [:touched input-name]))
|
(get-in @form [:touched input-name]))
|
||||||
|
|
||||||
error (get-in @form [:errors input-name])
|
error (or (get-in @form [:errors input-name])
|
||||||
|
(get-in @form [:extra-errors input-name]))
|
||||||
|
|
||||||
value (get-in @form [:data input-name] "")
|
value (get-in @form [:data input-name] "")
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,11 @@
|
|||||||
(let [props (m/properties schema)
|
(let [props (m/properties schema)
|
||||||
tprops (m/type-properties schema)
|
tprops (m/type-properties schema)
|
||||||
field (or (first in)
|
field (or (first in)
|
||||||
(:error/field props))]
|
(:error/field props))
|
||||||
|
|
||||||
|
field (if (vector? field)
|
||||||
|
field
|
||||||
|
[field])]
|
||||||
|
|
||||||
(if (contains? acc field)
|
(if (contains? acc field)
|
||||||
acc
|
acc
|
||||||
@ -58,30 +62,30 @@
|
|||||||
|
|
||||||
(or (= type :malli.core/missing-key)
|
(or (= type :malli.core/missing-key)
|
||||||
(nil? value))
|
(nil? value))
|
||||||
(assoc acc field {:message (tr "errors.field-missing")})
|
(assoc-in acc field {:message (tr "errors.field-missing")})
|
||||||
|
|
||||||
;; --- CHECK on schema props
|
;; --- CHECK on schema props
|
||||||
(contains? props :error/fn)
|
(contains? props :error/fn)
|
||||||
(assoc acc field (handle-error-fn props problem))
|
(assoc-in acc field (handle-error-fn props problem))
|
||||||
|
|
||||||
(contains? props :error/message)
|
(contains? props :error/message)
|
||||||
(assoc acc field (handle-error-message props))
|
(assoc-in acc field (handle-error-message props))
|
||||||
|
|
||||||
(contains? props :error/code)
|
(contains? props :error/code)
|
||||||
(assoc acc field (handle-error-code props))
|
(assoc-in acc field (handle-error-code props))
|
||||||
|
|
||||||
;; --- CHECK on type props
|
;; --- CHECK on type props
|
||||||
(contains? tprops :error/fn)
|
(contains? tprops :error/fn)
|
||||||
(assoc acc field (handle-error-fn tprops problem))
|
(assoc-in acc field (handle-error-fn tprops problem))
|
||||||
|
|
||||||
(contains? tprops :error/message)
|
(contains? tprops :error/message)
|
||||||
(assoc acc field (handle-error-message tprops))
|
(assoc-in acc field (handle-error-message tprops))
|
||||||
|
|
||||||
(contains? tprops :error/code)
|
(contains? tprops :error/code)
|
||||||
(assoc acc field (handle-error-code tprops))
|
(assoc-in acc field (handle-error-code tprops))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(assoc acc field {:message (tr "errors.invalid-data")})))))
|
(assoc-in acc field {:message (tr "errors.invalid-data")})))))
|
||||||
|
|
||||||
(defn- use-rerender-fn
|
(defn- use-rerender-fn
|
||||||
[]
|
[]
|
||||||
@ -114,20 +118,35 @@
|
|||||||
[f {:keys [schema validators]}]
|
[f {:keys [schema validators]}]
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(let [state (apply f args)
|
(let [state (apply f args)
|
||||||
cleaned (sm/decode schema (:data state) sm/string-transformer)
|
cleaned (sm/decode schema (:data state) sm/json-transformer)
|
||||||
valid? (sm/validate schema cleaned)
|
valid? (sm/validate schema cleaned)
|
||||||
errors (when-not valid?
|
|
||||||
(collect-schema-errors schema validators state))]
|
errors
|
||||||
|
(when-not valid?
|
||||||
|
(collect-schema-errors schema validators state))
|
||||||
|
|
||||||
|
extra-errors
|
||||||
|
(not-empty (:extra-errors state))]
|
||||||
|
|
||||||
(assoc state
|
(assoc state
|
||||||
:errors errors
|
:errors errors
|
||||||
:clean-data (when valid? cleaned)
|
:clean-data (when valid? cleaned)
|
||||||
:valid (and (not errors) valid?)))))
|
:valid (and (not errors)
|
||||||
|
(not extra-errors)
|
||||||
|
valid?)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- make-initial-state
|
||||||
|
[initial-data]
|
||||||
|
(let [initial (if (fn? initial-data) (initial-data) initial-data)
|
||||||
|
initial (d/nilv initial {})]
|
||||||
|
{:initial initial
|
||||||
|
:data initial
|
||||||
|
:errors {}
|
||||||
|
:touched {}}))
|
||||||
|
|
||||||
(defn- create-form-mutator
|
(defn- create-form-mutator
|
||||||
[internal-state rerender-fn wrap-update-fn initial opts]
|
[internal-state rerender-fn wrap-update-fn opts]
|
||||||
(mf/set-ref-val! internal-state initial)
|
|
||||||
|
|
||||||
(reify
|
(reify
|
||||||
IDeref
|
IDeref
|
||||||
(-deref [_]
|
(-deref [_]
|
||||||
@ -136,7 +155,10 @@
|
|||||||
IReset
|
IReset
|
||||||
(-reset! [_ new-value]
|
(-reset! [_ new-value]
|
||||||
(if (nil? new-value)
|
(if (nil? new-value)
|
||||||
(mf/set-ref-val! internal-state (if (fn? initial) (initial) initial))
|
(let [initial (-> (mf/ref-val internal-state)
|
||||||
|
(get :initial)
|
||||||
|
(make-initial-state))]
|
||||||
|
(mf/set-ref-val! internal-state initial))
|
||||||
(mf/set-ref-val! internal-state new-value))
|
(mf/set-ref-val! internal-state new-value))
|
||||||
(rerender-fn))
|
(rerender-fn))
|
||||||
|
|
||||||
@ -162,24 +184,25 @@
|
|||||||
(rerender-fn)))))
|
(rerender-fn)))))
|
||||||
|
|
||||||
(defn use-form
|
(defn use-form
|
||||||
[& {:keys [initial] :as opts}]
|
[& {:keys [initial schema validators] :as opts}]
|
||||||
(let [rerender-fn (use-rerender-fn)
|
(let [rerender-fn (use-rerender-fn)
|
||||||
|
|
||||||
initial
|
initial
|
||||||
(mf/with-memo [initial]
|
(mf/with-memo [initial]
|
||||||
{:data (if (fn? initial) (initial) initial)
|
(make-initial-state initial))
|
||||||
:errors {}
|
|
||||||
:touched {}})
|
|
||||||
|
|
||||||
internal-state
|
internal-state
|
||||||
(mf/use-ref nil)
|
(mf/use-ref initial)
|
||||||
|
|
||||||
form-mutator
|
form-mutator
|
||||||
(mf/with-memo [initial]
|
(mf/with-memo [schema validators]
|
||||||
(create-form-mutator internal-state rerender-fn wrap-update-schema-fn initial opts))]
|
(let [mutator (create-form-mutator internal-state rerender-fn wrap-update-schema-fn
|
||||||
|
(select-keys opts [:schema :validators]))]
|
||||||
|
(swap! mutator identity)
|
||||||
|
mutator))]
|
||||||
|
|
||||||
;; Initialize internal state once
|
;; Initialize internal state once
|
||||||
(mf/with-layout-effect []
|
(mf/with-effect []
|
||||||
(mf/set-ref-val! internal-state initial))
|
(mf/set-ref-val! internal-state initial))
|
||||||
|
|
||||||
(mf/with-effect [initial]
|
(mf/with-effect [initial]
|
||||||
@ -191,11 +214,16 @@
|
|||||||
([form field value]
|
([form field value]
|
||||||
(on-input-change form field value false))
|
(on-input-change form field value false))
|
||||||
([form field value trim?]
|
([form field value trim?]
|
||||||
(swap! form (fn [state]
|
(letfn [(clean-errors [errors]
|
||||||
(-> state
|
(-> errors
|
||||||
(assoc-in [:touched field] true)
|
(dissoc field)
|
||||||
(assoc-in [:data field] (if trim? (str/trim value) value))
|
(not-empty)))]
|
||||||
(update :errors dissoc field))))))
|
(swap! form (fn [state]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:touched field] true)
|
||||||
|
(assoc-in [:data field] (if trim? (str/trim value) value))
|
||||||
|
(update :errors clean-errors)
|
||||||
|
(update :extra-errors clean-errors)))))))
|
||||||
|
|
||||||
(defn update-input-value!
|
(defn update-input-value!
|
||||||
[form field value]
|
[form field value]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user