mirror of https://github.com/status-im/reagent.git
Make functional Reagent components optional, using new opts parameter
Render/as-element and other functions now take additional parameter for setting options. Currently only option is :functional-reag-elements?, if set to true, by default Reagent components are created as Functions, which behave similarly to Classes, but allow using Hooks.
This commit is contained in:
parent
63e118d2a0
commit
22a2841b45
|
@ -46,8 +46,8 @@
|
|||
(defn as-element
|
||||
"Turns a vector of Hiccup syntax into a React element. Returns form
|
||||
unchanged if it is not a vector."
|
||||
[form]
|
||||
(tmpl/as-element form))
|
||||
([form] (tmpl/as-element form nil))
|
||||
([form opts] (tmpl/as-element form opts)))
|
||||
|
||||
(defn adapt-react-class
|
||||
"Returns an adapter for a native React class, that may be used
|
||||
|
@ -60,9 +60,10 @@
|
|||
"Returns an adapter for a Reagent component, that may be used from
|
||||
React, for example in JSX. A single argument, props, is passed to
|
||||
the component, converted to a map."
|
||||
[c]
|
||||
([c] (reactify-component c nil))
|
||||
([c opts]
|
||||
(assert-some c "Component")
|
||||
(comp/reactify-component c))
|
||||
(comp/reactify-component c opts)))
|
||||
|
||||
(defn render
|
||||
"Render a Reagent component into the DOM. The first argument may be
|
||||
|
|
|
@ -34,10 +34,13 @@
|
|||
Returns the mounted component instance."
|
||||
([comp container]
|
||||
(render comp container nil))
|
||||
([comp container callback]
|
||||
([comp container callback-or-opts]
|
||||
(ratom/flush!)
|
||||
(let [f (fn []
|
||||
(tmpl/as-element (if (fn? comp) (comp) comp)))]
|
||||
(let [[opts callback] (if (fn? callback-or-opts)
|
||||
[nil callback-or-opts]
|
||||
[callback-or-opts (:callback callback-or-opts)])
|
||||
f (fn []
|
||||
(tmpl/as-element (if (fn? comp) (comp) comp) opts))]
|
||||
(render-comp f container callback))))
|
||||
|
||||
(defn unmount-component-at-node
|
||||
|
|
|
@ -6,14 +6,18 @@
|
|||
|
||||
(defn render-to-string
|
||||
"Turns a component into an HTML string."
|
||||
[component]
|
||||
([component]
|
||||
(render-to-string component nil))
|
||||
([component opts]
|
||||
(ratom/flush!)
|
||||
(binding [util/*non-reactive* true]
|
||||
(dom-server/renderToString (tmpl/as-element component))))
|
||||
(dom-server/renderToString (tmpl/as-element component opts)))))
|
||||
|
||||
(defn render-to-static-markup
|
||||
"Turns a component into an HTML string, without data-react-id attributes, etc."
|
||||
[component]
|
||||
([component]
|
||||
(render-to-static-markup component nil))
|
||||
([component opts]
|
||||
(ratom/flush!)
|
||||
(binding [util/*non-reactive* true]
|
||||
(dom-server/renderToStaticMarkup (tmpl/as-element component))))
|
||||
(dom-server/renderToStaticMarkup (tmpl/as-element component opts)))))
|
||||
|
|
|
@ -111,10 +111,11 @@
|
|||
5 (.call f c (nth v 1) (nth v 2) (nth v 3) (nth v 4))
|
||||
(.apply f c (.slice (into-array v) 1)))))]
|
||||
(cond
|
||||
(vector? res) (as-element res)
|
||||
;; FIXME: Opts
|
||||
(vector? res) (as-element res nil)
|
||||
(ifn? res) (let [f (if (reagent-class? res)
|
||||
(fn [& args]
|
||||
(as-element (apply vector res args)))
|
||||
(as-element (apply vector res args) nil))
|
||||
res)]
|
||||
(set! (.-reagentRender c) f)
|
||||
(recur c))
|
||||
|
@ -373,7 +374,7 @@
|
|||
|
||||
cmp))
|
||||
|
||||
(defn fn-to-class [f]
|
||||
(defn fn-to-class [f opts]
|
||||
(assert-callable f)
|
||||
(warn-unless (not (and (react-class? f)
|
||||
(not (reagent-class? f))))
|
||||
|
@ -389,15 +390,16 @@
|
|||
res (create-class withrender)]
|
||||
(cache-react-class f res))))
|
||||
|
||||
(defn as-class [tag]
|
||||
(defn as-class [tag opts]
|
||||
;; TODO: Cache per opts
|
||||
(if-some [cached-class (cached-react-class tag)]
|
||||
cached-class
|
||||
(fn-to-class tag)))
|
||||
(fn-to-class tag opts)))
|
||||
|
||||
(defn reactify-component [comp]
|
||||
(defn reactify-component [comp opts]
|
||||
(if (react-class? comp)
|
||||
comp
|
||||
(as-class comp)))
|
||||
(as-class comp opts)))
|
||||
|
||||
(defn functional-wrap-render
|
||||
[c]
|
||||
|
@ -406,10 +408,10 @@
|
|||
argv (.-argv c)
|
||||
res (apply f argv)]
|
||||
(cond
|
||||
(vector? res) (as-element res)
|
||||
(vector? res) (as-element res (.-opts c))
|
||||
(ifn? res) (let [f (if (reagent-class? res)
|
||||
(fn [& args]
|
||||
(as-element (apply vector res args)))
|
||||
(as-element (apply vector res args) (.-opts c)))
|
||||
res)]
|
||||
(set! (.-reagentRender c) f)
|
||||
(recur c))
|
||||
|
@ -501,6 +503,7 @@
|
|||
original Reagent component."
|
||||
[tag]
|
||||
;; TODO: Could be disabled for optimized builds?
|
||||
;; TODO: Need to cache per opts?
|
||||
(or (gobj/get fun-components tag)
|
||||
(let [f (fn [jsprops] (functional-render jsprops))
|
||||
_ (set! (.-displayName f) (util/fun-name tag))
|
||||
|
|
|
@ -246,10 +246,10 @@
|
|||
:component-did-update input-component-set-value
|
||||
:component-will-unmount input-unmount
|
||||
:reagent-render
|
||||
(fn [argv component jsprops first-child]
|
||||
(fn [argv component jsprops first-child opts]
|
||||
(let [this comp/*current-component*]
|
||||
(input-render-setup this jsprops)
|
||||
(make-element argv component jsprops first-child)))})
|
||||
(make-element argv component jsprops first-child opts)))})
|
||||
|
||||
(defn reagent-input
|
||||
[]
|
||||
|
@ -293,13 +293,21 @@
|
|||
(if (= :> (nth v 0 nil))
|
||||
(get-key (nth v 2 nil))))))
|
||||
|
||||
(defn reag-element [tag v]
|
||||
(defn reag-element [tag v opts]
|
||||
(let [c (comp/as-class tag opts)
|
||||
jsprops #js {}]
|
||||
(set! (.-argv jsprops) v)
|
||||
(when-some [key (key-from-vec v)]
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement c jsprops)))
|
||||
|
||||
(defn functional-reag-element [tag v opts]
|
||||
(if (or (comp/react-class? tag)
|
||||
;; TODO: Should check others for real comptibility, this fixes tests
|
||||
;; TODO: Drop support for fn + meta for Class component methods?
|
||||
(:should-component-update (meta tag)))
|
||||
;; as-class unncessary later as tag is always class
|
||||
(let [c (comp/as-class tag)
|
||||
(let [c (comp/as-class tag opts)
|
||||
jsprops #js {}]
|
||||
(set! (.-argv jsprops) v)
|
||||
(when-some [key (key-from-vec v)]
|
||||
|
@ -308,6 +316,7 @@
|
|||
(let [jsprops #js {}]
|
||||
(set! (.-reagentRender jsprops) tag)
|
||||
(set! (.-argv jsprops) (subvec v 1))
|
||||
(set! (.-opts jsprops) opts)
|
||||
(when-some [key (key-from-vec v)]
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement (comp/funtional-render-fn tag) jsprops))))
|
||||
|
@ -335,7 +344,7 @@
|
|||
(gobj/set tag-name-cache x v)
|
||||
v)))
|
||||
|
||||
(defn native-element [parsed argv first]
|
||||
(defn native-element [parsed argv first opts]
|
||||
(let [component (.-tag parsed)
|
||||
props (nth argv first nil)
|
||||
hasprops (or (nil? props) (map? props))
|
||||
|
@ -343,13 +352,13 @@
|
|||
#js {})
|
||||
first-child (+ first (if hasprops 1 0))]
|
||||
(if (input-component? component)
|
||||
(-> [(reagent-input) argv component jsprops first-child]
|
||||
(-> [(reagent-input) argv component jsprops first-child opts]
|
||||
(with-meta (meta argv))
|
||||
as-element)
|
||||
(as-element opts))
|
||||
(do
|
||||
(when-some [key (-> (meta argv) get-key)]
|
||||
(set! (.-key jsprops) key))
|
||||
(make-element argv component jsprops first-child)))))
|
||||
(make-element argv component jsprops first-child opts)))))
|
||||
|
||||
(defn str-coll [coll]
|
||||
(if (dev?)
|
||||
|
@ -365,7 +374,7 @@
|
|||
(defn hiccup-err [v & msg]
|
||||
(str (apply str msg) ": " (str-coll v) "\n" (comp/comp-name)))
|
||||
|
||||
(defn vec-to-elem [v]
|
||||
(defn vec-to-elem [v opts]
|
||||
(assert (pos? (count v)) (hiccup-err v "Hiccup form should not be empty"))
|
||||
(let [tag (nth v 0 nil)]
|
||||
(assert (valid-tag? tag) (hiccup-err v "Invalid Hiccup form"))
|
||||
|
@ -377,53 +386,56 @@
|
|||
(let [n (name tag)
|
||||
pos (.indexOf n ">")]
|
||||
(case pos
|
||||
-1 (native-element (cached-parse n) v 1)
|
||||
-1 (native-element (cached-parse n) v 1 opts)
|
||||
0 (let [component (nth v 1 nil)]
|
||||
;; Support [:> component ...]
|
||||
(assert (= ">" n) (hiccup-err v "Invalid Hiccup tag"))
|
||||
(native-element (->HiccupTag component nil nil nil) v 2))
|
||||
(native-element (->HiccupTag component nil nil nil) v 2 opts))
|
||||
;; Support extended hiccup syntax, i.e :div.bar>a.foo
|
||||
;; Apply metadata (e.g. :key) to the outermost element.
|
||||
;; Metadata is probably used only with sequeneces, and in that case
|
||||
;; only the key of the outermost element matters.
|
||||
(recur (with-meta [(subs n 0 pos)
|
||||
(assoc (with-meta v nil) 0 (subs n (inc pos)))]
|
||||
(meta v)))))
|
||||
(meta v))
|
||||
opts)))
|
||||
|
||||
(instance? NativeWrapper tag)
|
||||
(native-element tag v 1)
|
||||
(native-element tag v 1 opts)
|
||||
|
||||
:else (reag-element tag v))))
|
||||
:else (if (:functional-reag-elements? opts)
|
||||
(functional-reag-element tag v opts)
|
||||
(reag-element tag v opts)))))
|
||||
|
||||
(declare expand-seq)
|
||||
(declare expand-seq-check)
|
||||
|
||||
(defn as-element [x]
|
||||
(defn as-element [x opts]
|
||||
(cond (js-val? x) x
|
||||
(vector? x) (vec-to-elem x)
|
||||
(vector? x) (vec-to-elem x opts)
|
||||
(seq? x) (if (dev?)
|
||||
(expand-seq-check x)
|
||||
(expand-seq x))
|
||||
(expand-seq-check x opts)
|
||||
(expand-seq x opts))
|
||||
(named? x) (name x)
|
||||
(satisfies? IPrintWithWriter x) (pr-str x)
|
||||
:else x))
|
||||
|
||||
(set! comp/as-element as-element)
|
||||
|
||||
(defn expand-seq [s]
|
||||
(defn expand-seq [s opts]
|
||||
(into-array (map as-element s)))
|
||||
|
||||
(defn expand-seq-dev [s ^clj o]
|
||||
(defn expand-seq-dev [s ^clj o opts]
|
||||
(into-array (map (fn [val]
|
||||
(when (and (vector? val)
|
||||
(nil? (key-from-vec val)))
|
||||
(set! (.-no-key o) true))
|
||||
(as-element val))
|
||||
(as-element val opts))
|
||||
s)))
|
||||
|
||||
(defn expand-seq-check [x]
|
||||
(defn expand-seq-check [x opts]
|
||||
(let [ctx #js{}
|
||||
[res derefed] (ratom/check-derefs #(expand-seq-dev x ctx))]
|
||||
[res derefed] (ratom/check-derefs #(expand-seq-dev x ctx opts))]
|
||||
(when derefed
|
||||
(warn (hiccup-err x "Reactive deref not supported in lazy seq, "
|
||||
"it should be wrapped in doall")))
|
||||
|
@ -431,17 +443,17 @@
|
|||
(warn (hiccup-err x "Every element in a seq should have a unique :key")))
|
||||
res))
|
||||
|
||||
(defn make-element [argv component jsprops first-child]
|
||||
(defn make-element [argv component jsprops first-child opts]
|
||||
(case (- (count argv) first-child)
|
||||
;; Optimize cases of zero or one child
|
||||
0 (react/createElement component jsprops)
|
||||
|
||||
1 (react/createElement component jsprops
|
||||
(as-element (nth argv first-child nil)))
|
||||
(as-element (nth argv first-child nil) opts))
|
||||
|
||||
(.apply react/createElement nil
|
||||
(reduce-kv (fn [a k v]
|
||||
(when (>= k first-child)
|
||||
(.push a (as-element v)))
|
||||
(.push a (as-element v opts)))
|
||||
a)
|
||||
#js[component jsprops] argv))))
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
(js/performance.mark "functional-start")
|
||||
; (simple-benchmark [x [hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
||||
(dotimes [i 100000]
|
||||
(tmpl/vec-to-elem [hello-world-component]))
|
||||
(tmpl/vec-to-elem [hello-world-component] nil))
|
||||
(js/performance.mark "functional-end")
|
||||
(js/performance.measure "functional" "functional-start" "functional-end")
|
||||
|
||||
(js/performance.mark "class-start")
|
||||
; (simple-benchmark [x [^:class-component hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
||||
(dotimes [i 100000]
|
||||
(tmpl/vec-to-elem [^:class-component hello-world-component]))
|
||||
(tmpl/vec-to-elem [^:class-component hello-world-component] nil))
|
||||
(js/performance.mark "class-end")
|
||||
(js/performance.measure "class" "class-start" "class-end")
|
||||
)
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
:after (fn []
|
||||
(set! rv/debug false))})
|
||||
|
||||
(defn rstr [react-elem]
|
||||
(server/render-to-static-markup react-elem))
|
||||
(defn rstr [react-elem opts]
|
||||
(server/render-to-static-markup react-elem opts))
|
||||
|
||||
(defn log-error [& f]
|
||||
(debug/error (apply str f)))
|
||||
|
@ -34,13 +34,21 @@
|
|||
(finally
|
||||
(set! js/console.error org))))))
|
||||
|
||||
;; Different set of options to try for most test cases
|
||||
|
||||
(def test-options
|
||||
[nil
|
||||
{:functional-reag-elements? true}] )
|
||||
|
||||
(deftest really-simple-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
really-simple (fn []
|
||||
(swap! ran inc)
|
||||
[:div "div in really-simple"])]
|
||||
(with-mounted-component [really-simple nil nil]
|
||||
opts
|
||||
(fn [c div]
|
||||
(swap! ran inc)
|
||||
(is (= "div in really-simple" (.-innerText div)))
|
||||
|
@ -48,10 +56,11 @@
|
|||
(is (= 2 @ran))
|
||||
(rdom/force-update-all)
|
||||
(is (= 3 @ran))))
|
||||
(is (= 3 @ran)))))
|
||||
(is (= 3 @ran))))))
|
||||
|
||||
(deftest test-simple-callback
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
comp (r/create-class
|
||||
{:component-did-mount #(swap! ran inc)
|
||||
|
@ -65,13 +74,15 @@
|
|||
(swap! ran inc)
|
||||
[:div (str "hi " (:foo props) ".")]))})]
|
||||
(with-mounted-component [comp {:foo "you"} 1]
|
||||
opts
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
(is (= "hi you." (.-innerText div)))))
|
||||
(is (= 3 @ran)))))
|
||||
(is (= 3 @ran))))))
|
||||
|
||||
(deftest test-state-change
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
self (r/atom nil)
|
||||
comp (r/create-class
|
||||
|
@ -83,6 +94,7 @@
|
|||
(swap! ran inc)
|
||||
[:div (str "hi " (:foo (r/state this)))]))})]
|
||||
(with-mounted-component [comp]
|
||||
opts
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
(is (= "hi initial" (.-innerText div)))
|
||||
|
@ -96,10 +108,11 @@
|
|||
(r/set-state @self {:foo "you"})
|
||||
(r/flush)
|
||||
(is (= "hi you" (.-innerText div)))))
|
||||
(is (= 4 @ran)))))
|
||||
(is (= 4 @ran))))))
|
||||
|
||||
(deftest test-ratom-change
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
runs (rv/running)
|
||||
val (r/atom 0)
|
||||
|
@ -110,6 +123,7 @@
|
|||
(swap! ran inc)
|
||||
[:div (str "val " @v1 " " @val " " @secval)])]
|
||||
(with-mounted-component [comp]
|
||||
opts
|
||||
(fn [C div]
|
||||
(r/flush)
|
||||
(is (not= runs (rv/running)))
|
||||
|
@ -136,10 +150,11 @@
|
|||
(is (= "val 1 1 0" (.-innerText div)))
|
||||
(is (= 2 @ran) "did not run")))
|
||||
(is (= runs (rv/running)))
|
||||
(is (= 2 @ran)))))
|
||||
(is (= 2 @ran))))))
|
||||
|
||||
(deftest batched-update-test []
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
v1 (r/atom 0)
|
||||
v2 (r/atom 0)
|
||||
|
@ -152,6 +167,7 @@
|
|||
[:div @v1
|
||||
[c2 {:val @v1}]])]
|
||||
(with-mounted-component [c1]
|
||||
opts
|
||||
(fn [c div]
|
||||
(r/flush)
|
||||
(is (= 2 @ran))
|
||||
|
@ -172,10 +188,11 @@
|
|||
; (swap! v2 inc)
|
||||
; (r/flush)
|
||||
; (is (= 9 @ran))
|
||||
)))))
|
||||
))))))
|
||||
|
||||
(deftest init-state-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
really-simple (fn []
|
||||
(let [this (r/current-component)]
|
||||
|
@ -185,13 +202,15 @@
|
|||
[:div (str "this is "
|
||||
(:foo (r/state this)))])))]
|
||||
(with-mounted-component [really-simple nil nil]
|
||||
opts
|
||||
(fn [c div]
|
||||
(swap! ran inc)
|
||||
(is (= "this is foobar" (.-innerText div)))))
|
||||
(is (= 2 @ran)))))
|
||||
(is (= 2 @ran))))))
|
||||
|
||||
(deftest should-update-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [parent-ran (r/atom 0)
|
||||
child-ran (r/atom 0)
|
||||
child-props (r/atom nil)
|
||||
|
@ -204,6 +223,7 @@
|
|||
(swap! parent-ran inc)
|
||||
[:div "child-foo" [child @child-props]])]
|
||||
(with-mounted-component [parent nil nil]
|
||||
opts
|
||||
(fn [c div]
|
||||
(r/flush)
|
||||
(is (= 1 @child-ran))
|
||||
|
@ -250,10 +270,11 @@
|
|||
(is (= 7 @child-ran))
|
||||
|
||||
(rdom/force-update-all)
|
||||
(is (= 8 @child-ran)))))))
|
||||
(is (= 8 @child-ran))))))))
|
||||
|
||||
(deftest dirty-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
state (r/atom 0)
|
||||
really-simple (fn []
|
||||
|
@ -262,6 +283,7 @@
|
|||
(reset! state 3))
|
||||
[:div (str "state=" @state)])]
|
||||
(with-mounted-component [really-simple nil nil]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= 1 @ran))
|
||||
(is (= "state=0" (.-innerText div)))
|
||||
|
@ -269,21 +291,23 @@
|
|||
(r/flush)
|
||||
(is (= 2 @ran))
|
||||
(is (= "state=3" (.-innerText div)))))
|
||||
(is (= 2 @ran)))))
|
||||
(is (= 2 @ran))))))
|
||||
|
||||
(defn as-string [comp]
|
||||
(server/render-to-static-markup comp))
|
||||
(defn as-string [comp opts]
|
||||
(server/render-to-static-markup comp opts))
|
||||
|
||||
(deftest to-string-test []
|
||||
(doseq [opts test-options]
|
||||
(let [comp (fn [props]
|
||||
[:div (str "i am " (:foo props))])]
|
||||
(is (= "<div>i am foobar</div>" (as-string [comp {:foo "foobar"}])))))
|
||||
(is (= "<div>i am foobar</div>" (as-string [comp {:foo "foobar"}] opts))))))
|
||||
|
||||
(deftest data-aria-test []
|
||||
(doseq [opts test-options]
|
||||
(is (= "<div data-foo=\"x\"></div>"
|
||||
(as-string [:div {:data-foo "x"}])))
|
||||
(as-string [:div {:data-foo "x"}] opts)))
|
||||
(is (= "<div aria-labelledby=\"x\"></div>"
|
||||
(as-string [:div {:aria-labelledby "x"}])))
|
||||
(as-string [:div {:aria-labelledby "x"}] opts)))
|
||||
;; Skip test: produces warning in new React
|
||||
;; (is (not (re-find #"enctype"
|
||||
;; (as-string [:div {"enc-type" "x"}])))
|
||||
|
@ -291,58 +315,61 @@
|
|||
;; FIXME: For some reason UMD module returns everything in
|
||||
;; lowercase, and CommonJS with upper T
|
||||
(is (re-find #"enc[tT]ype"
|
||||
(as-string [:div {"encType" "x"}]))
|
||||
(as-string [:div {"encType" "x"}] opts))
|
||||
"Strings are passed through to React, and have to be camelcase.")
|
||||
(is (re-find #"enc[tT]ype"
|
||||
(as-string [:div {:enc-type "x"}]))
|
||||
"Strings are passed through to React, and have to be camelcase."))
|
||||
(as-string [:div {:enc-type "x"}] opts))
|
||||
"Strings are passed through to React, and have to be camelcase.")))
|
||||
|
||||
(deftest dynamic-id-class []
|
||||
(doseq [opts test-options]
|
||||
(is (re-find #"id=.foo"
|
||||
(as-string [:div#foo {:class "bar"}])))
|
||||
(as-string [:div#foo {:class "bar"}] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>"
|
||||
(as-string [:div.foo {:class "bar"}])))
|
||||
(as-string [:div.foo {:class "bar"}] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>"
|
||||
(as-string [:div.foo.bar])))
|
||||
(as-string [:div.foo.bar] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>"
|
||||
(as-string [:div.foo {:className "bar"}])))
|
||||
(as-string [:div.foo {:className "bar"}] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>"
|
||||
(as-string [:div {:className "foo bar"}])))
|
||||
(as-string [:div {:className "foo bar"}] opts)))
|
||||
(is (re-find #"id=.foo"
|
||||
(as-string [:div#foo.foo.bar])))
|
||||
(as-string [:div#foo.foo.bar] opts)))
|
||||
(is (re-find #"class=.xxx bar"
|
||||
(as-string [:div#foo.xxx.bar])))
|
||||
(as-string [:div#foo.xxx.bar] opts)))
|
||||
(is (re-find #"id=.foo"
|
||||
(as-string [:div.bar {:id "foo"}])))
|
||||
(as-string [:div.bar {:id "foo"}] opts)))
|
||||
(is (re-find #"id=.foo"
|
||||
(as-string [:div.bar.xxx {:id "foo"}])))
|
||||
(as-string [:div.bar.xxx {:id "foo"}] opts)))
|
||||
(is (= "<div id=\"foo\"></div>"
|
||||
(as-string [:div#bar {:id "foo"}]))
|
||||
"Dynamic id overwrites static"))
|
||||
(as-string [:div#bar {:id "foo"}] opts))
|
||||
"Dynamic id overwrites static")))
|
||||
|
||||
(defmulti my-div :type)
|
||||
(defmethod my-div :fooish [child] [:div.foo (:content child)])
|
||||
(defmethod my-div :barish [child] [:div.bar (:content child)])
|
||||
|
||||
(deftest ifn-component []
|
||||
(doseq [opts test-options]
|
||||
(let [comp {:foo [:div "foodiv"]
|
||||
:bar [:div "bardiv"]}]
|
||||
(is (= "<div><div>foodiv</div></div>"
|
||||
(as-string [:div [comp :foo]])))
|
||||
(as-string [:div [comp :foo]] opts)))
|
||||
(is (= "<div><div>bardiv</div></div>"
|
||||
(as-string [:div [comp :bar]])))
|
||||
(as-string [:div [comp :bar]] opts)))
|
||||
(is (= "<div class=\"foo\">inner</div>"
|
||||
(as-string [my-div {:type :fooish :content "inner"}])))))
|
||||
(as-string [my-div {:type :fooish :content "inner"}] opts))))))
|
||||
|
||||
(deftest symbol-string-tag []
|
||||
(is (= "<div>foobar</div>" (as-string ['div "foobar"])))
|
||||
(is (= "<div>foobar</div>" (as-string ["div" "foobar"])))
|
||||
(is (= "<div id=\"foo\">x</div>" (as-string ['div#foo "x"])))
|
||||
(is (= "<div id=\"foo\">x</div>" (as-string ["div#foo" "x"])))
|
||||
(is (= "<div class=\"foo bar\"></div>" (as-string ['div.foo {:class "bar"}])))
|
||||
(is (= "<div class=\"foo bar\"></div>" (as-string ["div.foo.bar"])))
|
||||
(doseq [opts test-options]
|
||||
(is (= "<div>foobar</div>" (as-string ['div "foobar"] opts)))
|
||||
(is (= "<div>foobar</div>" (as-string ["div" "foobar"] opts)))
|
||||
(is (= "<div id=\"foo\">x</div>" (as-string ['div#foo "x"] opts)))
|
||||
(is (= "<div id=\"foo\">x</div>" (as-string ["div#foo" "x"] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>" (as-string ['div.foo {:class "bar"}] opts)))
|
||||
(is (= "<div class=\"foo bar\"></div>" (as-string ["div.foo.bar"] opts)))
|
||||
(is (re-find #"id=.foo"
|
||||
(as-string ['div#foo.foo.bar]))))
|
||||
(as-string ['div#foo.foo.bar] opts)))))
|
||||
|
||||
(deftest partial-test []
|
||||
(let [p1 (r/partial vector 1 2)]
|
||||
|
@ -354,32 +381,36 @@
|
|||
(is (= (hash p1) (hash (r/partial vector 1 2))))))
|
||||
|
||||
(deftest test-null-component
|
||||
(doseq [opts test-options]
|
||||
(let [null-comp (fn [do-show]
|
||||
(when do-show
|
||||
[:div "div in test-null-component"]))]
|
||||
(is (= ""
|
||||
(as-string [null-comp false])))
|
||||
(as-string [null-comp false] opts)))
|
||||
(is (= "<div>div in test-null-component</div>"
|
||||
(as-string [null-comp true])))))
|
||||
(as-string [null-comp true] opts))))))
|
||||
|
||||
(deftest test-string
|
||||
(doseq [opts test-options]
|
||||
(is (= "<div data-reactroot=\"\">foo</div>"
|
||||
(server/render-to-string [:div "foo"])))
|
||||
(server/render-to-string [:div "foo"] opts)))
|
||||
|
||||
(is (= "<div data-reactroot=\"\"><p>foo</p></div>"
|
||||
(server/render-to-string [:div [:p "foo"]]))))
|
||||
(server/render-to-string [:div [:p "foo"]] opts)))))
|
||||
|
||||
(deftest test-static-markup
|
||||
(doseq [opts test-options]
|
||||
(is (= "<div>foo</div>"
|
||||
(rstr [:div "foo"])))
|
||||
(rstr [:div "foo"] opts)))
|
||||
(is (= "<div class=\"bar\"><p>foo</p></div>"
|
||||
(rstr [:div.bar [:p "foo"]])))
|
||||
(rstr [:div.bar [:p "foo"]] opts)))
|
||||
(is (= "<div class=\"bar\"><p>foobar</p></div>"
|
||||
(rstr [:div.bar {:dangerously-set-inner-HTML
|
||||
{:__html "<p>foobar</p>"}} ]))))
|
||||
{:__html "<p>foobar</p>"}}] opts)))))
|
||||
|
||||
(deftest test-return-class
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
top-ran (r/atom 0)
|
||||
comp (fn []
|
||||
|
@ -398,6 +429,7 @@
|
|||
prop (r/atom {:foo "you"})
|
||||
parent (fn [] [comp @prop 1])]
|
||||
(with-mounted-component [parent]
|
||||
opts
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
(is (= "hi you." (.-innerText div)))
|
||||
|
@ -408,10 +440,11 @@
|
|||
(r/flush)
|
||||
(is (= "hi me." (.-innerText div)))
|
||||
(is (= 1 @top-ran))
|
||||
(is (= 4 @ran)))))))
|
||||
(is (= 4 @ran))))))))
|
||||
|
||||
(deftest test-return-class-fn
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ran (r/atom 0)
|
||||
top-ran (r/atom 0)
|
||||
comp (fn []
|
||||
|
@ -426,6 +459,7 @@
|
|||
prop (r/atom {:foo "you"})
|
||||
parent (fn [] [comp @prop 1])]
|
||||
(with-mounted-component [parent]
|
||||
opts
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
(is (= "hi you." (.-innerText div)))
|
||||
|
@ -436,11 +470,13 @@
|
|||
(r/flush)
|
||||
(is (= "hi me." (.-innerText div)))
|
||||
(is (= 1 @top-ran))
|
||||
(is (= 4 @ran)))))))
|
||||
(is (= 4 @ran))))))))
|
||||
|
||||
(deftest test-create-element
|
||||
(doseq [opts test-options]
|
||||
(let [ae r/as-element
|
||||
ce r/create-element]
|
||||
ce r/create-element
|
||||
rstr #(rstr % opts)]
|
||||
(is (= (rstr (ce "div"))
|
||||
(rstr (ae [:div]))))
|
||||
(is (= (rstr (ce "div" nil))
|
||||
|
@ -460,7 +496,7 @@
|
|||
(is (= (rstr (ce "div" nil (ae [:div "foo"])))
|
||||
(rstr (ae [:div [:div "foo"]]))))
|
||||
(is (= (rstr (ae [:div (ce "div" nil "foo")]))
|
||||
(rstr (ae [:div [:div "foo"]]))))))
|
||||
(rstr (ae [:div [:div "foo"]])))))))
|
||||
|
||||
(def ndiv (let [cmp (fn [])]
|
||||
(gobj/extend
|
||||
|
@ -476,8 +512,10 @@
|
|||
cmp))
|
||||
|
||||
(deftest test-adapt-class
|
||||
(doseq [opts test-options]
|
||||
(let [d1 (r/adapt-react-class ndiv)
|
||||
d2 (r/adapt-react-class "div")]
|
||||
d2 (r/adapt-react-class "div")
|
||||
rstr #(rstr % opts)]
|
||||
(is (= (rstr [:div])
|
||||
(rstr [d1])))
|
||||
(is (= (rstr [:div "a"])
|
||||
|
@ -498,11 +536,13 @@
|
|||
(is (= (rstr [:div.foo "a"])
|
||||
(rstr [d2 {:class "foo"} "a"])))
|
||||
(is (= (rstr [:div "a" "b" [:div "c"]])
|
||||
(rstr [d2 "a" "b" [:div "c"]])))))
|
||||
(rstr [d2 "a" "b" [:div "c"]]))))))
|
||||
|
||||
(deftest test-adapt-class-2
|
||||
(doseq [opts test-options]
|
||||
(let [d1 ndiv
|
||||
d2 "div"]
|
||||
d2 "div"
|
||||
rstr #(rstr % opts)]
|
||||
(is (= (rstr [:div])
|
||||
(rstr [:> d1])))
|
||||
(is (= (rstr [:div "a"])
|
||||
|
@ -523,7 +563,7 @@
|
|||
(is (= (rstr [:div.foo "a"])
|
||||
(rstr [:> d2 {:class "foo"} "a"])))
|
||||
(is (= (rstr [:div "a" "b" [:div "c"]])
|
||||
(rstr [:> d2 "a" "b" [:div "c"]])))))
|
||||
(rstr [:> d2 "a" "b" [:div "c"]]))))))
|
||||
|
||||
(deftest adapt-react-class-shortcut-key-warning
|
||||
(let [w (debug/track-warnings
|
||||
|
@ -535,8 +575,10 @@
|
|||
(is (empty? (:warn w)))))
|
||||
|
||||
(deftest test-reactize-component
|
||||
(doseq [opts test-options]
|
||||
(let [ae r/as-element
|
||||
ce r/create-element
|
||||
rstr #(rstr % opts)
|
||||
a (atom nil)
|
||||
c1r (fn reactize [p & args]
|
||||
(reset! a args)
|
||||
|
@ -558,9 +600,10 @@
|
|||
(ae [:b "b"])
|
||||
(ae [:i "i"])))
|
||||
(rstr [:p "p:a" [:b "b"] [:i "i"]])))
|
||||
(is (= nil @a))))
|
||||
(is (= nil @a)))))
|
||||
|
||||
(deftest test-keys
|
||||
(doseq [opts test-options]
|
||||
(let [a nil ;; (r/atom "a")
|
||||
c (fn key-tester []
|
||||
[:div
|
||||
|
@ -570,6 +613,7 @@
|
|||
[:p {:key i} i (some-> a deref)])])
|
||||
w (debug/track-warnings
|
||||
#(with-mounted-component [c]
|
||||
opts
|
||||
(fn [c div])))]
|
||||
(is (empty? (:warn w))))
|
||||
|
||||
|
@ -583,12 +627,15 @@
|
|||
w (debug/track-warnings
|
||||
(wrap-capture-console-error
|
||||
#(with-mounted-component [c]
|
||||
opts
|
||||
(fn [c div]))))]
|
||||
(if (dev?)
|
||||
(is (re-find #"Warning: Every element in a seq should have a unique :key: \(\[:button \{:on-click #object\[Function\]\}\] \[:button \{:on-click #object\[Function\]\}\] \[:button \{:on-click #object\[Function\]\}\]\)\n \(in reagenttest.testreagent.key_tester\)"
|
||||
(first (:warn w)))))))))
|
||||
(first (:warn w))))))))))
|
||||
|
||||
(deftest test-extended-syntax
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<p><b>foo</b></p>"
|
||||
(rstr [:p>b "foo"])))
|
||||
(is (= (rstr [:p.foo [:b "x"]])
|
||||
|
@ -598,7 +645,7 @@
|
|||
(is (= (rstr [:div.foo [:p.bar.foo [:b.foobar "xy"]]])
|
||||
(rstr [:div.foo>p.bar.foo>b.foobar {} "xy"])))
|
||||
(is (= (rstr [:div [:p.bar.foo [:a.foobar {:href "href"} "xy"]]])
|
||||
(rstr [:div>p.bar.foo>a.foobar {:href "href"} "xy"]))))
|
||||
(rstr [:div>p.bar.foo>a.foobar {:href "href"} "xy"])))))
|
||||
|
||||
(deftest extended-syntax-metadata
|
||||
(when r/is-client
|
||||
|
@ -612,6 +659,8 @@
|
|||
)))))
|
||||
|
||||
(deftest test-class-from-collection
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= (rstr [:p {:class "a b c d"}])
|
||||
(rstr [:p {:class ["a" "b" "c" "d"]}])))
|
||||
(is (= (rstr [:p {:class "a b c"}])
|
||||
|
@ -619,9 +668,11 @@
|
|||
(is (= (rstr [:p {:class "a b c"}])
|
||||
(rstr [:p {:class '("a" "b" "c")}])))
|
||||
(is (= (rstr [:p {:class "a b c"}])
|
||||
(rstr [:p {:class #{"a" "b" "c"}}]))))
|
||||
(rstr [:p {:class #{"a" "b" "c"}}])))))
|
||||
|
||||
(deftest class-different-types
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(testing "named values are supported"
|
||||
(is (= (rstr [:p {:class "a"}])
|
||||
(rstr [:p {:class :a}])))
|
||||
|
@ -640,7 +691,7 @@
|
|||
|
||||
(testing "falsey values are filtered from collections"
|
||||
(is (= (rstr [:p {:class "a b"}])
|
||||
(rstr [:p {:class [:a :b false nil]}])))) )
|
||||
(rstr [:p {:class [:a :b false nil]}]))))))
|
||||
|
||||
(deftest test-force-update
|
||||
(let [v (atom {:v1 0
|
||||
|
@ -693,13 +744,16 @@
|
|||
(is (re-find #"atestcomponent" @a) "component-path should work")))))
|
||||
|
||||
(deftest test-sorted-map-key
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(let [c1 (fn [map]
|
||||
[:div (map 1)])
|
||||
c2 (fn []
|
||||
[c1 (sorted-map 1 "foo" 2 "bar")])]
|
||||
(is (= "<div>foo</div>" (rstr [c2])))))
|
||||
(is (= "<div>foo</div>" (rstr [c2]))))))
|
||||
|
||||
(deftest basic-with-let
|
||||
(doseq [opts test-options]
|
||||
(when r/is-client
|
||||
(let [n1 (atom 0)
|
||||
n2 (atom 0)
|
||||
|
@ -712,16 +766,18 @@
|
|||
(finally
|
||||
(swap! n3 inc))))]
|
||||
(with-mounted-component [c]
|
||||
opts
|
||||
(fn [_ div]
|
||||
(is (= [1 1 0] [@n1 @n2 @n3]))
|
||||
(swap! val inc)
|
||||
(is (= [1 1 0] [@n1 @n2 @n3]))
|
||||
(r/flush)
|
||||
(is (= [1 2 0] [@n1 @n2 @n3]))))
|
||||
(is (= [1 2 1] [@n1 @n2 @n3])))))
|
||||
(is (= [1 2 1] [@n1 @n2 @n3]))))))
|
||||
|
||||
(deftest with-let-destroy-only
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [n1 (atom 0)
|
||||
n2 (atom 0)
|
||||
c (fn []
|
||||
|
@ -731,12 +787,14 @@
|
|||
(finally
|
||||
(swap! n2 inc))))]
|
||||
(with-mounted-component [c]
|
||||
opts
|
||||
(fn [_ div]
|
||||
(is (= [1 0] [@n1 @n2]))))
|
||||
(is (= [1 1] [@n1 @n2])))))
|
||||
(is (= [1 1] [@n1 @n2]))))))
|
||||
|
||||
(deftest with-let-arg
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [a (atom 0)
|
||||
s (r/atom "foo")
|
||||
f (fn [x]
|
||||
|
@ -747,16 +805,19 @@
|
|||
(r/with-let []
|
||||
[f @s]))]
|
||||
(with-mounted-component [c]
|
||||
opts
|
||||
(fn [_ div]
|
||||
(is (= "foo" @a))
|
||||
(reset! s "bar")
|
||||
(r/flush)
|
||||
(is (= "bar" @a)))))))
|
||||
(is (= "bar" @a))))))))
|
||||
|
||||
(deftest with-let-non-reactive
|
||||
(doseq [opts test-options]
|
||||
(let [n1 (atom 0)
|
||||
n2 (atom 0)
|
||||
n3 (atom 0)
|
||||
rstr #(rstr % opts)
|
||||
c (fn []
|
||||
(r/with-let [a (swap! n1 inc)]
|
||||
(swap! n2 inc)
|
||||
|
@ -764,9 +825,10 @@
|
|||
(finally
|
||||
(swap! n3 inc))))]
|
||||
(is (= (rstr [c]) (rstr [:div 1])))
|
||||
(is (= [1 1 1] [@n1 @n2 @n3]))))
|
||||
(is (= [1 1 1] [@n1 @n2 @n3])))))
|
||||
|
||||
(deftest lifecycle
|
||||
(doseq [opts test-options]
|
||||
(let [n1 (atom 0)
|
||||
t (atom 0)
|
||||
res (atom {})
|
||||
|
@ -846,7 +908,7 @@
|
|||
(is (= {:at 9 :args [@t [@comp "a" "b"] {:foo "bar"} nil]}
|
||||
(:did-update @res))))]
|
||||
(when r/is-client
|
||||
(with-mounted-component [c2] check)
|
||||
(with-mounted-component [c2] opts check)
|
||||
(is (= {:at 10 :args [@t]}
|
||||
(:will-unmount @res)))
|
||||
|
||||
|
@ -855,10 +917,11 @@
|
|||
(reset! n1 0)
|
||||
(with-mounted-component [c2] check)
|
||||
(is (= {:at 10 :args [@t]}
|
||||
(:will-unmount @res))))))
|
||||
(:will-unmount @res)))))))
|
||||
|
||||
|
||||
(deftest lifecycle-native
|
||||
(doseq [opts test-options]
|
||||
(let [n1 (atom 0)
|
||||
t (atom 0)
|
||||
res (atom {})
|
||||
|
@ -958,9 +1021,9 @@
|
|||
(is (= 9 at))
|
||||
(is (= [@comp @oldprops] oldv))))]
|
||||
(when r/is-client
|
||||
(with-mounted-component [cnative] check)
|
||||
(with-mounted-component [cnative] opts check)
|
||||
(is (= {:at 10 :args [@t]}
|
||||
(:will-unmount @res))))))
|
||||
(:will-unmount @res)))))))
|
||||
|
||||
(defn foo []
|
||||
[:div])
|
||||
|
@ -988,6 +1051,8 @@
|
|||
|
||||
(deftest test-err-messages
|
||||
(when (dev?)
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (thrown-with-msg?
|
||||
:default #"Hiccup form should not be empty: \[]"
|
||||
(rstr [])))
|
||||
|
@ -1035,7 +1100,7 @@
|
|||
pkg "reagenttest.testreagent."
|
||||
stack1 (str "in " pkg "comp1")
|
||||
rend (fn [x]
|
||||
(with-mounted-component x identity))]
|
||||
(with-mounted-component x opts identity))]
|
||||
|
||||
;; Error is orginally caused by comp1, so only that is shown in the error
|
||||
(let [e (debug/track-warnings
|
||||
|
@ -1056,7 +1121,7 @@
|
|||
(is (re-find #"Error rendering component \(in reagenttest.testreagent.comp1\)"
|
||||
(last (:error e)))))
|
||||
|
||||
(let [e (debug/track-warnings #(r/as-element [nat]))]
|
||||
(let [e (debug/track-warnings #(r/as-element [nat] opts))]
|
||||
(is (re-find #"Using native React classes directly"
|
||||
(-> e :warn first))))
|
||||
|
||||
|
@ -1066,11 +1131,12 @@
|
|||
(-> e :warn first))))
|
||||
|
||||
(let [e (debug/track-warnings
|
||||
#(r/as-element (comp4)))]
|
||||
#(r/as-element (comp4) opts))]
|
||||
(is (re-find #"Every element in a seq should have a unique :key"
|
||||
(-> e :warn first))))))))
|
||||
(-> e :warn first)))))))))
|
||||
|
||||
(deftest test-error-boundary
|
||||
(doseq [opts test-options]
|
||||
(let [error (r/atom nil)
|
||||
info (r/atom nil)
|
||||
error-boundary (fn error-boundary [comp]
|
||||
|
@ -1092,6 +1158,7 @@
|
|||
(wrap-capture-window-error
|
||||
(wrap-capture-console-error
|
||||
#(with-mounted-component [error-boundary [comp2]]
|
||||
opts
|
||||
(fn [c div]
|
||||
(r/flush)
|
||||
(is (= "Test error" (.-message @error)))
|
||||
|
@ -1102,9 +1169,10 @@
|
|||
(is (re-find #"\n in reagenttest.testreagent.comp1 \(created by reagenttest.testreagent.comp2\)\n in reagenttest.testreagent.comp2 \(created by reagent[0-9]+\)\n in reagent[0-9]+ \(created by reagenttest.testreagent.error_boundary\)\n in reagenttest.testreagent.error_boundary"
|
||||
(.-componentStack ^js @info)))
|
||||
(is (re-find #"\n in .+\n in .+\n in reagent[0-9]+\n in .+"
|
||||
(.-componentStack ^js @info))) ))))))))
|
||||
(.-componentStack ^js @info))))))))))))
|
||||
|
||||
(deftest test-dom-node
|
||||
(doseq [opts test-options]
|
||||
(let [node (atom nil)
|
||||
ref (atom nil)
|
||||
comp (r/create-class
|
||||
|
@ -1114,22 +1182,28 @@
|
|||
(fn [this]
|
||||
(reset! node (rdom/dom-node this)))})]
|
||||
(with-mounted-component [comp]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= "foobar" (.-innerHTML @ref)))
|
||||
(is (= "foobar" (.-innerHTML @node)))
|
||||
(is (identical? @ref @node))))))
|
||||
(is (identical? @ref @node)))))))
|
||||
|
||||
(deftest test-empty-input
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<div><input/></div>"
|
||||
(rstr [:div [:input]]))))
|
||||
(rstr [:div [:input]])))))
|
||||
|
||||
(deftest test-object-children
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<p>foo bar1</p>"
|
||||
(rstr [:p 'foo " " :bar nil 1])))
|
||||
(is (= "<p>#object[reagent.ratom.RAtom {:val 1}]</p>"
|
||||
(rstr [:p (r/atom 1)]))))
|
||||
(rstr [:p (r/atom 1)])))))
|
||||
|
||||
(deftest test-after-render
|
||||
(doseq [opts test-options]
|
||||
(let [spy (atom 0)
|
||||
val (atom 0)
|
||||
exp (atom 0)
|
||||
|
@ -1145,6 +1219,7 @@
|
|||
(is (= @exp @val))
|
||||
[:div {:ref #(reset! node %)} @state]))]
|
||||
(with-mounted-component [comp]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= 1 @spy))
|
||||
(swap! state inc)
|
||||
|
@ -1163,20 +1238,26 @@
|
|||
; (r/flush)
|
||||
; (is (= 0 @spy))
|
||||
))
|
||||
(is (= nil @node))))
|
||||
(is (= nil @node)))))
|
||||
|
||||
(deftest style-property-names-are-camel-cased
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<div style=\"text-align:center\">foo</div>"
|
||||
(rstr [:div {:style {:text-align "center"}} "foo"]))))
|
||||
(rstr [:div {:style {:text-align "center"}} "foo"])))))
|
||||
|
||||
(deftest custom-element-class-prop
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<custom-element class=\"foobar\">foo</custom-element>"
|
||||
(rstr [:custom-element {:class "foobar"} "foo"])))
|
||||
|
||||
(is (= "<custom-element class=\"foobar\">foo</custom-element>"
|
||||
(rstr [:custom-element.foobar "foo"]))))
|
||||
(rstr [:custom-element.foobar "foo"])))))
|
||||
|
||||
(deftest html-entities
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(testing "entity numbers can be unescaped always"
|
||||
(is (= "<i> </i>"
|
||||
(rstr [:i (gstr/unescapeEntities " ")]))))
|
||||
|
@ -1184,7 +1265,7 @@
|
|||
(when r/is-client
|
||||
(testing "When DOM is available, all named entities can be unescaped"
|
||||
(is (= "<i> </i>"
|
||||
(rstr [:i (gstr/unescapeEntities " ")]))))))
|
||||
(rstr [:i (gstr/unescapeEntities " ")])))))))
|
||||
|
||||
(defn context-wrapper []
|
||||
(r/create-class
|
||||
|
@ -1214,12 +1295,13 @@
|
|||
|
||||
|
||||
(deftest test-fragments
|
||||
(doseq [opts test-options]
|
||||
(testing "Fragment as array"
|
||||
(let [comp (fn comp1 []
|
||||
#js [(r/as-element [:div "hello"])
|
||||
(r/as-element [:div "world"])])]
|
||||
#js [(r/as-element [:div "hello"] opts)
|
||||
(r/as-element [:div "world"] opts)])]
|
||||
(is (= "<div>hello</div><div>world</div>"
|
||||
(as-string [comp])))))
|
||||
(as-string [comp] opts)))))
|
||||
|
||||
(testing "Fragment element, :<>"
|
||||
(let [comp (fn comp2 []
|
||||
|
@ -1228,7 +1310,7 @@
|
|||
[:div "world"]
|
||||
[:div "foo"] ])]
|
||||
(is (= "<div>hello</div><div>world</div><div>foo</div>"
|
||||
(as-string [comp])))))
|
||||
(as-string [comp] opts)))))
|
||||
|
||||
(testing "Fragment key"
|
||||
;; This would cause React warning if both fragements didn't have key set
|
||||
|
@ -1250,7 +1332,7 @@
|
|||
[:div "1"]
|
||||
[:div "2"]])])]
|
||||
(is (= "<div><div>hello</div><div>world</div><div>foo</div><div>1</div><div>2</div></div>"
|
||||
(as-string [comp]))))))
|
||||
(as-string [comp] opts)))))))
|
||||
|
||||
;; In bundle version, the names aren't optimized.
|
||||
;; In node module processed versions, names probably are optimized.
|
||||
|
@ -1260,6 +1342,8 @@
|
|||
(def Consumer (.-Consumer my-context))
|
||||
|
||||
(deftest new-context-test
|
||||
(doseq [opts test-options
|
||||
:let [rstr #(rstr % opts)]]
|
||||
(is (= "<div>Context: foo</div>"
|
||||
(rstr (r/create-element
|
||||
Provider #js {:value "foo"}
|
||||
|
@ -1298,9 +1382,10 @@
|
|||
(this-as this
|
||||
(r/as-element [:div "Context: " (.-context this)])))})]
|
||||
(is (= "<div>Context: default</div>"
|
||||
(rstr [comp]))))))
|
||||
(rstr [comp])))))))
|
||||
|
||||
(deftest on-failed-prop-comparison-in-should-update-swallow-exception-and-do-not-update-component
|
||||
(doseq [opts test-options]
|
||||
(let [prop (r/atom {:todos 1})
|
||||
component-was-updated (atom false)
|
||||
error-thrown-after-updating-props (atom false)
|
||||
|
@ -1314,6 +1399,7 @@
|
|||
(when (and r/is-client (dev?))
|
||||
(let [e (debug/track-warnings
|
||||
#(with-mounted-component [component]
|
||||
opts
|
||||
(fn [c div]
|
||||
(reset! prop (sorted-map 1 2))
|
||||
(try
|
||||
|
@ -1324,10 +1410,11 @@
|
|||
(is (not @component-was-updated))
|
||||
(is (not @error-thrown-after-updating-props)))))]
|
||||
(is (re-find #"Warning: Exception thrown while comparing argv's in shouldComponentUpdate:"
|
||||
(first (:warn e))))))))
|
||||
(first (:warn e)))))))))
|
||||
|
||||
(deftest get-derived-state-from-props-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [prop (r/atom 0)
|
||||
;; Usually one can use Cljs object as React state. However,
|
||||
;; getDerivedStateFromProps implementation in React uses
|
||||
|
@ -1344,14 +1431,16 @@
|
|||
component (fn []
|
||||
[pure-component {:value @prop}])]
|
||||
(with-mounted-component [component]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= "Value foo" (.-innerText div)))
|
||||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (= "Value foo foo" (.-innerText div))))))))
|
||||
(is (= "Value foo foo" (.-innerText div)))))))))
|
||||
|
||||
(deftest get-derived-state-from-error-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [prop (r/atom 0)
|
||||
component (r/create-class
|
||||
{:constructor (fn [this props]
|
||||
|
@ -1370,14 +1459,16 @@
|
|||
(wrap-capture-window-error
|
||||
(wrap-capture-console-error
|
||||
#(with-mounted-component [component [bad-component]]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= "Ok" (.-innerText div)))
|
||||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (= "Error" (.-innerText div))))))))))
|
||||
(is (= "Error" (.-innerText div)))))))))))
|
||||
|
||||
(deftest get-snapshot-before-update-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [ref (react/createRef)
|
||||
prop (r/atom 0)
|
||||
did-update (atom nil)
|
||||
|
@ -1395,6 +1486,7 @@
|
|||
component-2 (fn []
|
||||
[component {:value @prop}])]
|
||||
(with-mounted-component [component-2]
|
||||
opts
|
||||
(fn [c div]
|
||||
;; Attach to DOM to get real height value
|
||||
(.appendChild js/document.body div)
|
||||
|
@ -1402,10 +1494,11 @@
|
|||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (= {:height 20} @did-update))
|
||||
(.removeChild js/document.body div))))))
|
||||
(.removeChild js/document.body div)))))))
|
||||
|
||||
(deftest issue-462-test
|
||||
(when r/is-client
|
||||
(doseq [opts test-options]
|
||||
(let [val (r/atom 0)
|
||||
render (atom 0)
|
||||
a (fn issue-462-a [nr]
|
||||
|
@ -1419,6 +1512,7 @@
|
|||
^{:key @val}
|
||||
[b])]
|
||||
(with-mounted-component [c]
|
||||
opts
|
||||
(fn [c div]
|
||||
(is (= 1 @render))
|
||||
(reset! val 1)
|
||||
|
@ -1426,13 +1520,14 @@
|
|||
(is (= 2 @render))
|
||||
(reset! val 0)
|
||||
(r/flush)
|
||||
(is (= 3 @render)))))))
|
||||
(is (= 3 @render))))))))
|
||||
|
||||
(deftest functional-component-poc-simple
|
||||
(when r/is-client
|
||||
(let [c (fn [x]
|
||||
[:span "Hello " x])]
|
||||
(with-mounted-component [c "foo"]
|
||||
{:functional-reag-elements? true}
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Hello foo" (.-innerText div))))))))
|
||||
|
@ -1448,6 +1543,7 @@
|
|||
(reset! set-count! set-count)
|
||||
[:span "Count " c]))]
|
||||
(with-mounted-component [c 5]
|
||||
{:functional-reag-elements? true}
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Count 5" (.-innerText div)))
|
||||
|
@ -1460,6 +1556,7 @@
|
|||
c (fn [x]
|
||||
[:span "Count " @count])]
|
||||
(with-mounted-component [c 5]
|
||||
{:functional-reag-elements? true}
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Count 5" (.-innerText div)))
|
||||
|
@ -1479,6 +1576,7 @@
|
|||
(reset! set-count! set-count)
|
||||
[:span "Counts " @r-count " " c]))]
|
||||
(with-mounted-component [c 15]
|
||||
{:functional-reag-elements? true}
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Counts 3 15" (.-innerText div)))
|
||||
|
|
|
@ -2,15 +2,18 @@
|
|||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rdom]))
|
||||
|
||||
(defn with-mounted-component [comp f]
|
||||
(defn with-mounted-component
|
||||
([comp f]
|
||||
(with-mounted-component comp nil f))
|
||||
([comp opts f]
|
||||
(when r/is-client
|
||||
(let [div (.createElement js/document "div")]
|
||||
(try
|
||||
(let [c (rdom/render comp div)]
|
||||
(let [c (rdom/render comp div opts)]
|
||||
(f c div))
|
||||
(finally
|
||||
(rdom/unmount-component-at-node div)
|
||||
(r/flush))))))
|
||||
(r/flush)))))))
|
||||
|
||||
(defn with-mounted-component-async [comp done f]
|
||||
(when r/is-client
|
||||
|
|
Loading…
Reference in New Issue