Skip to content

Commit

Permalink
Change some validation styling utils; but also declare them as 'exper…
Browse files Browse the repository at this point in the history
…imental'.
  • Loading branch information
David Frese committed May 25, 2022
1 parent ad2afaf commit b0a950e
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 29 deletions.
57 changes: 36 additions & 21 deletions src/reacl_c_basics/forms/validation.cljs
@@ -1,5 +1,7 @@
(ns reacl-c-basics.forms.validation
"Utilities for a more customized display of form validity.
"EXPERIMENTAL
Utilities for a more customized display of form validity.
Note: the basic validity of input fields can be defined with the
`:invalid` and `:validate` attribtues
Expand Down Expand Up @@ -27,6 +29,8 @@
[active.clojure.lens :as lens]
[active.clojure.functions :as f]))

;; Note: this is still not 'perfect', esp. for the way to hide validation messages again; when to do that in general?

(c/defn-effect report-validity! "Like [[check-validity!]],
but validation errors are also shown to the user. The browser will
show them natively in a popup, or you can customize the visual
Expand Down Expand Up @@ -56,38 +60,48 @@
oninvalid (fn [state ev]
(.preventDefault ev) ;; do not show native validity popup of browser
(c/return :action [::msg (get-msg (.-target ev))]))
handle-reset (fn [state a]
(if (= ::reset a)
(c/return :state [(first state) nil])
(c/return :action a)))
dyn (fn [[_ validation-message] f]
(c/focus lens/first
(f validation-message
oninvalid)))
(-> (c/focus lens/first
;; apparently, there is no even when an input becomes valid again :-/
;; offer the user a ::reset action for that.
(f {:onInvalid oninvalid}
validation-message
::reset))
(c/handle-action handle-reset)))
set-msg (fn [[state _] a]
(if (and (vector? a) (= ::msg (first a)))
(c/return :state [state (second a)])
(c/return :action a)))]
(defn with-validity
"An item that will call `(f validation-msg on-invalid), where
`on-invalid` is an event handler that must be used for the
`:onInvalid` event in a form, input element or anything that
contains input elements. `validation-msg` will then be non-nil when
form validation fails and the problem should be described to the
user. This turns off the default behaviour of the browser to show
these messages."
"An item that will call `(f attrs validation-msg reset-action), where
attrs contains and `:onInvalid` event handlers that must be used in
a in a form, input element or anything that contains input
elements. `validation-msg` will then be non-nil when form validation
fails and the problem should be described to the user. You can emit
'reset-action' to reset the validation message to nil.
This turns off the default behaviour of the browser to show these
messages."
[f]
;; Note: message only 'appears' after form submit (or reportValidity); no initialization.
(c/local-state
nil
(-> (c/dynamic dyn f)
(c/handle-action set-msg)))))

(let [g (fn [item-f attrs f msg event]
(item-f (assoc attrs :onInvalid event)
(f msg)))]
(let [g (fn [item-f attrs1 f attrs2 msg reset-action]
(item-f (dom/merge-attributes attrs2 attrs1)
(f msg reset-action)))]
(defn- item-with-validity
[item-f attrs f]
(with-validity (f/partial g item-f attrs f))))

(defn form-with-validity
"A form item with contents `(f validation-msg)`, where
"A form item with contents `(f validation-msg reset-action)`, where
`validation-msg` is nil or the description of validation errors that
prevent the forms from being submitted."
([f]
Expand All @@ -96,9 +110,9 @@
(item-with-validity forms/form attrs f)))

(defn div-with-validity
"A div item with contents `(f validation-msg)`, where `validation-msg`
is nil or the description of validation errors in one of the
contained input elements."
"A div item with contents `(f validation-msg reset-action)`, where
`validation-msg` is nil or the description of validation errors in
one of the contained input elements."
([f]
(div-with-validity {} f))
([attrs f]
Expand All @@ -108,7 +122,7 @@
(c/fragment item (f msg)))]
(defn append-validity
"A div item with the given child item (which should usually be an
input element), followed by `(f validation-msg)` where
input element), followed by `(f validation-msg reset-action)` where
`validation-msg` is any validation error in that input element."
([item f]
(append-validity {} item f))
Expand All @@ -119,8 +133,9 @@
(c/fragment (f msg) item))]
(defn prepend-validity
"A div item with the given child item (which should usually be an
input element), prepended with `(f validation-msg)` where
`validation-msg` is any validation error in that input element."
input element), prepended with `(f validation-msg reset-action)`
where `validation-msg` is any validation error in that input
element."
([f item]
(prepend-validity {} f item))
([attrs f item]
Expand Down
23 changes: 15 additions & 8 deletions test/reacl_c_basics/forms/validation_test.cljs
Expand Up @@ -12,9 +12,9 @@
(deftest with-validity-test-1
(dt/rendering
(fval/with-validity
(fn [msg oninvalid]
;; Note: can put attrs in a div.
(dom/div {:onInvalid oninvalid}
(fn [attrs msg]
;; Note: attrs can put attrs in a div.
(dom/div attrs
(non-empty-input)
(when msg (dom/div (str "Msg: " msg))))))
:state "test"
Expand All @@ -27,11 +27,11 @@
(deftest with-validity-test-2
(dt/rendering
(fval/with-validity
(fn [msg oninvalid]
;; Note: can put attrs in an input directly.
(fn [attrs msg]
;; Note: attrs can put attrs in an input directly.
(c/with-state-as state
(dom/form {:data-testid "foo"}
(non-empty-input {:onInvalid oninvalid})
(non-empty-input attrs)
(when msg (dom/div (str "Msg: " msg)))))))
:state ""
(fn [env]
Expand All @@ -42,13 +42,20 @@
(deftest report-validity-test
(dt/rendering
(fval/form-with-validity {:report-validity true}
(fn [msg]
(fn [msg reset-action]
(c/fragment
(non-empty-input {})
(c/with-state-as txt
(when-not (empty? txt)
(c/init (c/return :action reset-action))))
(when msg (dom/div (str "Msg: " msg))))))
:state ""
(fn [env]
(is (some? (dt/query env (dt/by-text "Msg: Must not be empty")))))))
(is (some? (dt/query env (dt/by-text "Msg: Must not be empty"))))

(dt/set-state! env "foo")
(is (nil? (dt/query env (dt/by-text "Msg: Must not be empty"))))
)))

(deftest append-validity-test
(dt/rendering
Expand Down

0 comments on commit b0a950e

Please sign in to comment.