Commit 9e872a96 authored by James Laver's avatar James Laver

v0.1.0

parent 953a4679
...@@ -20,13 +20,12 @@ ...@@ -20,13 +20,12 @@
"pulp": "^12.4.0", "pulp": "^12.4.0",
"purescript": "^0.12.5", "purescript": "^0.12.5",
"purs-loader": "^3.2.0", "purs-loader": "^3.2.0",
"react-testing-library": "^5.9.0", "react-testing-library": "^6.1.2",
"spago": "^0.7.5" "spago": "^0.7.5"
}, },
"dependencies": { "dependencies": {
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6"
"react-testing-library": "^6.1.2"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
......
...@@ -123,6 +123,11 @@ let additions = ...@@ -123,6 +123,11 @@ let additions =
, "spec", "spec-mocha", "unsafe-coerce" ] , "spec", "spec-mocha", "unsafe-coerce" ]
"https://github.com/irresponsible/purescript-dom-simple" "https://github.com/irresponsible/purescript-dom-simple"
"master" "master"
, ffi-simple =
mkPackage
[ "functions", "nullable", "prelude" ]
"https://github.com/irresponsible/purescript-ffi-simple"
"v0.1.2"
, spec-mocha = , spec-mocha =
mkPackage mkPackage
[ "console", "foldable-traversable", "exceptions", "spec" ] [ "console", "foldable-traversable", "exceptions", "spec" ]
......
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
[ "console" [ "console"
, "dom-simple" , "dom-simple"
, "effect" , "effect"
, "ffi-simple"
, "functions" , "functions"
, "newtype" , "newtype"
, "nullable" , "nullable"
, "prelude" , "prelude"
, "refs"
, "spec" , "spec"
, "spec-mocha" , "spec-mocha"
, "unsafe-coerce" ] , "unsafe-coerce" ]
......
module Reactix.DOM.Raw module Reactix.DOM.Raw
(LeafFactory, TreeFactory (LeafFactory, ElementFactory
, button, div, div', i, i', p, p', span, span' , button, div, div', hr
, i, i', p, p', span, span'
, text) where , text) where
import Reactix.React (Element, createDOMElement) import Reactix.React (Element, createElement)
import Unsafe.Coerce (unsafeCoerce) import Unsafe.Coerce (unsafeCoerce)
createLeafDOMElement :: forall props. String -> Record props -> Element createLeafElement :: forall props. String -> Record props -> Element
createLeafDOMElement e p = createDOMElement e p [] createLeafElement e p = createElement e p []
-- A factory function for a DOM element with no children -- A factory function for a DOM element with no children
type LeafFactory = forall props. Record props -> Element type LeafFactory = forall props. Record props -> Element
-- A factory function for a DOM element with children -- A factory function for a DOM element with children
type TreeFactory = forall props. Record props -> Array Element -> Element type ElementFactory = forall props. Record props -> Array Element -> Element
text :: String -> Element text :: String -> Element
text = unsafeCoerce text = unsafeCoerce
button :: TreeFactory button :: ElementFactory
button = createDOMElement "button" button = createElement "button"
div :: TreeFactory div :: ElementFactory
div = createDOMElement "div" div = createElement "div"
div' :: LeafFactory div' :: LeafFactory
div' = createLeafDOMElement "div" div' = createLeafElement "div"
i :: TreeFactory hr :: LeafFactory
i = createDOMElement "i" hr = createLeafElement "hr"
i :: ElementFactory
i = createElement "i"
i' :: LeafFactory i' :: LeafFactory
i' = createLeafDOMElement "i" i' = createLeafElement "i"
p :: TreeFactory p :: ElementFactory
p = createDOMElement "p" p = createElement "p"
p' :: LeafFactory p' :: LeafFactory
p' = createLeafDOMElement "p" p' = createLeafElement "p"
span :: TreeFactory span :: ElementFactory
span = createDOMElement "span" span = createElement "span"
span' :: LeafFactory span' :: LeafFactory
span' = createLeafDOMElement "span" span' = createLeafElement "span"
...@@ -4,12 +4,6 @@ var React = require("react"); ...@@ -4,12 +4,6 @@ var React = require("react");
function _simple(prop) { function _simple(prop) {
return function() { return React[prop].apply(React, arguments); }; return function() { return React[prop].apply(React, arguments); };
} }
function _tuple(prop) {
return function(ctor) {
const r = React[prop].apply(React, Array.prototype.slice.call(arguments, 1));
return ctor(r[0])(r[1]);
}
}
function _memo(prop) { function _memo(prop) {
return function() { return function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
...@@ -17,16 +11,13 @@ function _memo(prop) { ...@@ -17,16 +11,13 @@ function _memo(prop) {
return React[prop].apply(React, args); return React[prop].apply(React, args);
} }
} }
exports._tuple = function tuple(ctor, v) { return ctor(v[0])(v[1]); };
exports._useContext = _simple('useContext'); exports._useContext = _simple('useContext');
exports._useDebugValue = _simple('useDebugValue'); exports._useDebugValue = _simple('useDebugValue');
exports._useDebugValuePrime = _simple('useDebugValue'); exports._useDebugValuePrime = _simple('useDebugValue');
// exports._useImperativeHandle = _simple('useImperativeHandle'); // exports._useImperativeHandle = _simple('useImperativeHandle');
exports._useState = _tuple('useState');
exports._useReducer = _tuple('useReducer');
exports._useRef = function(ctor, value) { exports._useRef = function(ctor, value) {
const r = React.useRef(value); const r = React.useRef(value);
const set = function(v) { r.current = v; }; const set = function(v) { r.current = v; };
...@@ -40,16 +31,3 @@ exports._useMemo3 = _memo('useMemo'); ...@@ -40,16 +31,3 @@ exports._useMemo3 = _memo('useMemo');
exports._useMemo4 = _memo('useMemo'); exports._useMemo4 = _memo('useMemo');
exports._useMemo5 = _memo('useMemo'); exports._useMemo5 = _memo('useMemo');
exports._useEffect = _simple('useEffect');
exports._useEffect1 = _memo('useEffect');
exports._useEffect2 = _memo('useEffect');
exports._useEffect3 = _memo('useEffect');
exports._useEffect4 = _memo('useEffect');
exports._useEffect5 = _memo('useEffect');
exports._useLayoutEffect = _simple('useLayoutEffect');
exports._useLayoutEffect1 = _memo('useLayoutEffect');
exports._useLayoutEffect2 = _memo('useLayoutEffect');
exports._useLayoutEffect3 = _memo('useLayoutEffect');
exports._useLayoutEffect4 = _memo('useLayoutEffect');
exports._useLayoutEffect5 = _memo('useLayoutEffect');
module Reactix.Hooks module Reactix.Hooks
( State, useState ( State, useState
, HookEffect
, useEffect, useEffect1, useEffect2, useEffect3, useEffect4, useEffect5 , useEffect, useEffect1, useEffect2, useEffect3, useEffect4, useEffect5
, useLayoutEffect, useLayoutEffect1, useLayoutEffect2 , useLayoutEffect, useLayoutEffect1, useLayoutEffect2
, useLayoutEffect3, useLayoutEffect4, useLayoutEffect5 , useLayoutEffect3, useLayoutEffect4, useLayoutEffect5
, useMemo, useMemo1, useMemo2, useMemo3, useMemo4, useMemo5 -- , Reducer
, Reducer, useReducer, useReducer' -- , useReducer, useReducer'
, Ref, useRef -- , useContext
, useContext -- , useMemo, useMemo1, useMemo2 --, useMemo3, useMemo4, useMemo5
, useDebugValue, useDebugValue' -- , Ref, useRef
-- , useDebugValue, useDebugValue'
-- , useImperativeHandle -- , useImperativeHandle
) )
where where
import Prelude import Prelude
import Data.Function.Uncurried (Fn2, mkFn2) import Data.Function.Uncurried ( Fn2, mkFn2, runFn2 )
import Data.Tuple (Tuple(..)) import Data.Tuple ( Tuple(..) )
import Effect (Effect) import Effect ( Effect )
import Effect.Uncurried ( EffectFn1, runEffectFn1, EffectFn2, runEffectFn2, EffectFn3, runEffectFn3, EffectFn4, runEffectFn4, EffectFn5, runEffectFn5, EffectFn6, runEffectFn6 ) import Effect.Uncurried ( EffectFn1, runEffectFn1, EffectFn2, runEffectFn2, EffectFn3, runEffectFn3, EffectFn4, runEffectFn4, EffectFn5, runEffectFn5, EffectFn6, runEffectFn6 )
import FFI.Simple ( (...), delay, args2, args3, args4, args5 )
import DOM.Simple.Console
import Reactix.React ( Hooks, react, unsafeHooksEffect )
import Reactix.React ( class MonadHooks, unsafeHooksEffect, Context )
--- useState --- useState
...@@ -26,113 +32,111 @@ import Reactix.React ( class MonadHooks, unsafeHooksEffect, Context ) ...@@ -26,113 +32,111 @@ import Reactix.React ( class MonadHooks, unsafeHooksEffect, Context )
-- | A state hook is a tuple of value and setter -- | A state hook is a tuple of value and setter
type State state = Tuple state (EffectFn1 state Unit) type State state = Tuple state (EffectFn1 state Unit)
-- | Given an Effect function returning an initial value, returns a UseState hook -- | Given an Effect function returning an initial value, returns a State
useState :: forall s m. MonadHooks m => Effect s -> m (State s) useState :: forall s. (Unit -> Effect s) -> Hooks (State s)
useState = unsafeHooksEffect <<< runEffectFn2 _useState Tuple useState s = hook $ \_ -> pure $ tuple $ react ... "useState" $ [ delay s ]
-- -- useReducer
-- type Reducer state action = Tuple state (EffectFn1 action Unit)
-- useReducer :: forall s a i. (s -> a -> s) -> (i -> s) -> i -> Hooks (Reducer s a)
-- useReducer f i j = pure $ tuple $ react ... "useReducer" $ args3 f i j
-- useReducer' :: forall s a. (s -> a -> s) -> s -> Hooks (Reducer s a)
-- useReducer' r = useReducer r identity
-- instance readReducer :: Read (Reducer s a) s where
-- read = _read0
-- instance writeReducer :: Write (Reducer s a) a where
-- write = runEffectFn2 _call1
-- | Takes an effect function which calculates the initial state
foreign import _useState :: forall s t. EffectFn2 (s -> t -> Tuple s t) (Effect s) (State s)
-- useEffect -- useEffect
type HookEffect = Unit -> Effect (Unit -> Effect Unit)
wrapEffect :: HookEffect -> Effect (Effect Unit)
wrapEffect f = do
cleanup <- f unit
pure $ delay cleanup
_useEffect :: forall a. HookEffect -> a -> Hooks Unit
_useEffect e a = hook $ \_ -> pure $ react ... "useEffect" $ args2 (wrapEffect e) a
-- | Given an Effect function which returns a cleanup Effect function, -- | Given an Effect function which returns a cleanup Effect function,
-- | register an effect to be called after rendering -- | register an effect to be called after rendering
useEffect :: forall m. MonadHooks m => Effect (Effect Unit) -> m Unit useEffect :: HookEffect -> Hooks Unit
useEffect = unsafeHooksEffect <<< runEffectFn1 _useEffect useEffect e = hook $ \_ -> pure $ react ... "useEffect" $ [ wrapEffect e ]
-- | Like useEffect, but with a memo value -- | Like useEffect, but with a memo value
useEffect1 :: forall a m. MonadHooks m => a -> Effect (Effect Unit) -> m Unit useEffect1 :: forall a. a -> HookEffect -> Hooks Unit
useEffect1 a = unsafeHooksEffect <<< runEffectFn2 _useEffect1 a useEffect1 a e = _useEffect e [a]
-- | Like useEffect, but with 2 memo values -- | Like useEffect, but with 2 memo values
useEffect2 :: forall a b m. MonadHooks m => a -> b -> Effect (Effect Unit) -> m Unit useEffect2 :: forall a b. a -> b -> HookEffect -> Hooks Unit
useEffect2 a b = unsafeHooksEffect <<< runEffectFn3 _useEffect2 a b useEffect2 a b e = _useEffect e $ args2 a b
-- | Like useEffect, but with 3 memo values -- | Like useEffect, but with 3 memo values
useEffect3 :: forall a b c m. MonadHooks m => a -> b -> c -> Effect (Effect Unit) -> m Unit useEffect3 :: forall a b c. a -> b -> c -> HookEffect -> Hooks Unit
useEffect3 a b c = unsafeHooksEffect <<< runEffectFn4 _useEffect3 a b c useEffect3 a b c e = _useEffect e $ args3 a b c
-- | Like useEffect, but with 4 memo values -- | Like useEffect, but with 4 memo values
useEffect4 :: forall a b c d m. MonadHooks m => a -> b -> c -> d -> Effect (Effect Unit) -> m Unit useEffect4 :: forall a b c d. a -> b -> c -> d -> HookEffect -> Hooks Unit
useEffect4 a b c d = unsafeHooksEffect <<< runEffectFn5 _useEffect4 a b c d useEffect4 a b c d e = _useEffect e $ args4 a b c d
-- | Like useEffect, but with 5 memo values -- | Like useEffect, but with 5 memo values
useEffect5 :: forall a b c d e m. MonadHooks m => a -> b -> c -> d -> e -> Effect (Effect Unit) -> m Unit useEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> HookEffect -> Hooks Unit
useEffect5 a b c d e = unsafeHooksEffect <<< runEffectFn6 _useEffect5 a b c d e useEffect5 a b c d f e = _useEffect e $ args5 a b c d f
foreign import _useEffect :: EffectFn1 (Effect (Effect Unit)) Unit
foreign import _useEffect1 :: forall a. EffectFn2 a (Effect (Effect Unit)) Unit
foreign import _useEffect2 :: forall a b. EffectFn3 a b (Effect (Effect Unit)) Unit
foreign import _useEffect3 :: forall a b c. EffectFn4 a b c(Effect (Effect Unit)) Unit
foreign import _useEffect4 :: forall a b c d. EffectFn5 a b c d (Effect (Effect Unit)) Unit
foreign import _useEffect5 :: forall a b c d e. EffectFn6 a b c d e (Effect (Effect Unit)) Unit
-- useLayoutEffect -- useLayoutEffect
-- | Given an Effect function which returns a cleanup Effect function, -- | Given an Effect function which returns a cleanup Effect function,
-- | register an effect to be called in the same phase as -- | register an effect to be called in the same phase as
-- | `componentDidMount` and `componentDidUpdate` used to be. -- | `componentDidMount` and `componentDidUpdate` used to be.
useLayoutEffect :: forall m. MonadHooks m => Effect (Effect Unit) -> m Unit useLayoutEffect :: HookEffect -> Hooks Unit
useLayoutEffect = unsafeHooksEffect <<< runEffectFn1 _useLayoutEffect useLayoutEffect e = hook $ \_ -> pure $ react ... "useLayoutEffect" $ [ wrapEffect e ]
_useLayoutEffect :: forall a. HookEffect -> a -> Hooks Unit
_useLayoutEffect e a = hook $ \_ -> pure $ react ... "useLayoutEffect" $ args2 (wrapEffect e) a
-- | Like useLayoutEffect, but with a memo value -- | Like useLayoutEffect, but with a memo value
useLayoutEffect1 :: forall a m. MonadHooks m => a -> Effect (Effect Unit) -> m Unit useLayoutEffect1 :: forall a. a -> HookEffect -> Hooks Unit
useLayoutEffect1 a = unsafeHooksEffect <<< runEffectFn2 _useLayoutEffect1 a useLayoutEffect1 a e = _useLayoutEffect e [a]
-- | Like useLayoutEffect, but with 2 memo values -- | Like useLayoutEffect, but with 2 memo values
useLayoutEffect2 :: forall a b m. MonadHooks m => a -> b -> Effect (Effect Unit) -> m Unit useLayoutEffect2 :: forall a b. a -> b -> HookEffect -> Hooks Unit
useLayoutEffect2 a b = unsafeHooksEffect <<< runEffectFn3 _useLayoutEffect2 a b useLayoutEffect2 a b e = _useLayoutEffect e $ args2 a b
-- | Like useLayoutEffect, but with 3 memo values -- | Like useLayoutEffect, but with 3 memo values
useLayoutEffect3 :: forall a b c m. MonadHooks m => a -> b -> c -> Effect (Effect Unit) -> m Unit useLayoutEffect3 :: forall a b c. a -> b -> c -> HookEffect -> Hooks Unit
useLayoutEffect3 a b c = unsafeHooksEffect <<< runEffectFn4 _useLayoutEffect3 a b c useLayoutEffect3 a b c e = _useLayoutEffect e $ args3 a b c
-- | Like useLayoutEffect, but with 4 memo values -- | Like useLayoutEffect, but with 4 memo values
useLayoutEffect4 :: forall a b c d m. MonadHooks m => a -> b -> c -> d -> Effect (Effect Unit) -> m Unit useLayoutEffect4 :: forall a b c d. a -> b -> c -> d -> HookEffect -> Hooks Unit
useLayoutEffect4 a b c d = unsafeHooksEffect <<< runEffectFn5 _useLayoutEffect4 a b c d useLayoutEffect4 a b c d e = _useLayoutEffect e $ args4 a b c d
-- | Like useLayoutEffect, but with 5 memo values -- | Like useLayoutEffect, but with 5 memo values
useLayoutEffect5 :: forall a b c d e m. MonadHooks m => a -> b -> c -> d -> e -> Effect (Effect Unit) -> m Unit useLayoutEffect5 :: forall a b c d e. a -> b -> c -> d -> e -> HookEffect -> Hooks Unit
useLayoutEffect5 a b c d e= unsafeHooksEffect <<< runEffectFn6 _useLayoutEffect5 a b c d e useLayoutEffect5 a b c d f e = _useLayoutEffect e $ args5 a b c d f
foreign import _useLayoutEffect :: EffectFn1 (Effect (Effect Unit)) Unit
foreign import _useLayoutEffect1 :: forall a. EffectFn2 a (Effect (Effect Unit)) Unit
foreign import _useLayoutEffect2 :: forall a b. EffectFn3 a b (Effect (Effect Unit)) Unit
foreign import _useLayoutEffect3 :: forall a b c. EffectFn4 a b c(Effect (Effect Unit)) Unit
foreign import _useLayoutEffect4 :: forall a b c d. EffectFn5 a b c d (Effect (Effect Unit)) Unit
foreign import _useLayoutEffect5 :: forall a b c d e. EffectFn6 a b c d e (Effect (Effect Unit)) Unit
-- useMemo -- useMemo
useMemo :: forall a m. MonadHooks m => Effect a -> m a -- useMemo :: forall a. Effect a -> Hooks a
useMemo = unsafeHooksEffect <<< runEffectFn1 _useMemo -- useMemo = unsafeHooksEffect <<< runEffectFn1 _useMemo
useMemo1 :: forall a b m. MonadHooks m => b -> Effect a -> m a -- useMemo1 :: forall a b. b -> Effect a -> Hooks a
useMemo1 a = unsafeHooksEffect <<< runEffectFn2 _useMemo1 a -- useMemo1 a = unsafeHooksEffect <<< runEffectFn2 _useMemo1 a
useMemo2 :: forall a b c m. MonadHooks m => b -> c -> Effect a -> m a -- useMemo2 :: forall a b c. b -> c -> Effect a -> Hooks a
useMemo2 a b = unsafeHooksEffect <<< runEffectFn3 _useMemo2 a b -- useMemo2 a b = unsafeHooksEffect <<< runEffectFn3 _useMemo2 a b
useMemo3 :: forall a b c d m. MonadHooks m => b -> c -> d -> Effect a -> m a
useMemo3 a b c = unsafeHooksEffect <<< runEffectFn4 _useMemo3 a b c
useMemo4 :: forall a b c d e m. MonadHooks m => b -> c -> d -> e -> Effect a -> m a
useMemo4 a b c d = unsafeHooksEffect <<< runEffectFn5 _useMemo4 a b c d
useMemo5 :: forall a b c d e f m. MonadHooks m => b -> c -> d -> e -> f -> Effect a -> m a
useMemo5 a b c d e = unsafeHooksEffect <<< runEffectFn6 _useMemo5 a b c d e
foreign import _useMemo :: forall a. EffectFn1 (Effect a) a -- foreign import _useMemo :: forall a. EffectFn1 (Effect a) a
foreign import _useMemo1 :: forall a b. EffectFn2 b (Effect a) a -- foreign import _useMemo1 :: forall a b. EffectFn2 b (Effect a) a
foreign import _useMemo2 :: forall a b c. EffectFn3 b c (Effect a) a -- foreign import _useMemo2 :: forall a b c. EffectFn3 b c (Effect a) a
foreign import _useMemo3 :: forall a b c d. EffectFn4 b c d (Effect a) a
foreign import _useMemo4 :: forall a b c d e. EffectFn5 b c d e (Effect a) a
foreign import _useMemo5 :: forall a b c d e f. EffectFn6 b c d e f (Effect a) a
...@@ -143,47 +147,41 @@ type Ref state = Tuple state (EffectFn1 state Unit) ...@@ -143,47 +147,41 @@ type Ref state = Tuple state (EffectFn1 state Unit)
foreign import _useRef :: forall r s. EffectFn2 (r -> s -> Tuple r s) r (Ref r) foreign import _useRef :: forall r s. EffectFn2 (r -> s -> Tuple r s) r (Ref r)
useRef :: forall r m. MonadHooks m => r -> m (Ref r) useRef :: forall r. r -> Hooks (Ref r)
useRef r = unsafeHooksEffect $ runEffectFn2 _useRef Tuple r useRef r = unsafeHooksEffect $ runEffectFn2 _useRef Tuple r
-- useReducer
type Reducer state action = Tuple state (EffectFn1 action Unit)
useReducer :: forall s a i m. MonadHooks m => (s -> a -> s) -> (i -> s) -> i -> m (Reducer s a)
useReducer f i j = unsafeHooksEffect $ runEffectFn4 _useReducer Tuple (mkFn2 f) j i
useReducer' :: forall s a m. MonadHooks m => (s -> a -> s) -> s -> m (Reducer s a)
useReducer' r = useReducer r identity
-- instance readReducer :: Read (Reducer s a) s where
-- read = _read0
-- instance writeReducer :: Write (Reducer s a) a where
-- write = runEffectFn2 _call1
foreign import _useReducer :: forall s a i x y. EffectFn4 (x -> y -> Tuple x y) (Fn2 s a s) i (i -> s) (Reducer s a)
-- useContext -- useContext
foreign import _useContext :: forall a m. MonadHooks m => Context a -> m a -- foreign import _useContext :: forall a. Context a -> Hooks a
useContext :: forall a m. MonadHooks m => Context a -> m a -- useContext :: forall a. Context a -> Hooks a
useContext = _useContext -- useContext = _useContext
-- useDebugValue -- useDebugValue
useDebugValue :: forall v v' m. MonadHooks m => v -> (v -> v') -> m Unit useDebugValue :: forall v v'. v -> (v -> v') -> Hooks Unit
useDebugValue v = unsafeHooksEffect <<< runEffectFn2 _useDebugValue v useDebugValue v = unsafeHooksEffect <<< runEffectFn2 _useDebugValue v
useDebugValue' :: forall v m. MonadHooks m => v -> m Unit useDebugValue' :: forall v. v -> Hooks Unit
useDebugValue' = unsafeHooksEffect <<< runEffectFn1 _useDebugValuePrime useDebugValue' = unsafeHooksEffect <<< runEffectFn1 _useDebugValuePrime
foreign import _useDebugValue :: forall v v'. EffectFn2 v (v -> v') Unit foreign import _useDebugValue :: forall v v'. EffectFn2 v (v -> v') Unit
foreign import _useDebugValuePrime :: forall v. EffectFn1 v Unit foreign import _useDebugValuePrime :: forall v. EffectFn1 v Unit
-- foreign import _useImperativeHandle ::
-- ffi utilities
tuple :: forall a b c. a -> Tuple b c
tuple = runFn2 _tuple Tuple
foreign import _tuple :: forall a b c. Fn2 (a -> b -> Tuple a b) c (Tuple a b)
hook :: forall v. (Unit -> Effect v) -> Hooks v
hook = unsafeHooksEffect <<< delay
-- foreign import _useImperativeHandle ::
'use strict'; 'use strict';
exports.react = require("react");
exports.reactDOM = require('react-dom');
var React = require("react");
var react_dom = require('react-dom');
function _simple(prop) {
return function() { return React[prop].apply(React, arguments); };
}
exports._isValid = _simple('isValidElement');
exports._children = function(c) { return React.Children.toArray(c); };
exports._memo = _simple('memo');
exports._memoPrime = _simple('memo');
exports._createRef = function() { return React.createRef(); };
exports._deref = function(r) { return r.current; };
// https://reactjs.org/docs/react-api.html#reactforwardref
// exports._forwardRef = _simple('forwardRef');
// creating and cloning elements
exports._cloneElement = _simple('cloneElement');
function _createElement(elem, props, children) {
var c = children.slice();
c.unshift(props);
c.unshift(elem);
return React.createElement.apply(React, c);
}
exports._createElement = _createElement;
exports._createFragment = function(children) {
return _createElement(React.Fragment, {}, children);
};
exports._contextProvider = function(c) { return c.Provider; };
exports._contextConsumer = function(c) { return c.Consumer; };
exports._createContext = function(ctor, val) {
var c = React.createContext(val);
return {provider: c.Provider, consumer: c.Consumer, context: c};
};
exports._render = function(a,b) { return react_dom.render(a,b); };
exports._named = function(name, f) { Object.defineProperty(f, 'name', {value: name, writable: false}); return f; };
module Reactix.React module Reactix.React
( Element, cloneElement, createDOMElement ( React, react
, Children, children , ReactDOM, reactDOM
, class Childless , Element
, class MonadHooks, unsafeHooksEffect , Hooks, unsafeHooksEffect, runHooks
, Context, ContextProvider, ContextConsumer, createContext, provider, consumer
, Context, Provider, Consumer, createContext, provider, consumer, consume
, render , render
, Component , class IsComponent
, pureLeaf, pureTree, hooksLeaf, hooksTree , Component, createElement
, class Componentesque , staticComponent, hooksComponent
, createLeaf, createTree
, fragment , fragment
, NullableRef, createRef , NullableRef, createRef, readNullableRef
, isValid , isValid
, Memo , Memo, memo, memo'
, memo, memo'
) )
where where
import Prelude import Prelude
import Data.Function.Uncurried ( Fn2, runFn2, mkFn2, Fn3, runFn3 ) import Data.Function.Uncurried (mkFn2)
import Data.Maybe ( Maybe ) import Data.Maybe ( Maybe )
import Data.Nullable ( Nullable, toMaybe ) import Data.Nullable ( Nullable, toMaybe )
import Effect ( Effect ) import Effect ( Effect )
import Effect.Class ( class MonadEffect, liftEffect ) import Effect.Class ( class MonadEffect, liftEffect )
import Effect.Uncurried (EffectFn1, mkEffectFn1, EffectFn2, runEffectFn2) import Effect.Uncurried (EffectFn1, mkEffectFn1, EffectFn2)
import Unsafe.Coerce (unsafeCoerce) import Unsafe.Coerce (unsafeCoerce)
import Prim.Row (class Lacks) import Prim.Row (class Lacks)
import DOM.Simple as DOM import DOM.Simple as DOM
import FFI.Simple.PseudoArray as PA
import FFI.Simple ( (..), (...), args2, args3, delay, defineProperty )
foreign import data React :: Type
foreign import data ReactDOM :: Type
foreign import react :: React
foreign import reactDOM :: ReactDOM
-- basic types -- basic types
newtype Component props = Component (EffectFn1 (Record props) Element)
-- | A React Element node -- | A React Element node
foreign import data Element :: Type foreign import data Element :: Type
-- | A wrapper over an Array of Elements -- | The Hooks monad
foreign import data Children :: Type newtype Hooks a = Hooks (Effect a)
-- | A convenience for adding `children` to a list of props runHooks :: forall a. Hooks a -> Effect a
type WithChildren p = ( children :: Children | p ) runHooks (Hooks a) = a
-- | This is to hide that it's actually implemented with Effect instance functorHooks :: Functor Hooks where
class Monad m <= MonadHooks m where map f (Hooks a) = Hooks (map f a)
unsafeHooksEffect :: forall a. Effect a -> m a
instance monadHooksEffect :: MonadHooks Effect where instance applyHooks :: Apply Hooks where
unsafeHooksEffect = unsafeCoerce apply (Hooks f) (Hooks a) = Hooks (apply f a)
class Childless (props :: # Type) instance applicativeHooks :: Applicative Hooks where
pure = Hooks <<< pure
instance childlessLacksChildren :: Lacks "children" props => Childless props instance bindHooks :: Bind Hooks where
bind (Hooks a) f = Hooks (a >>= (runHooks <<< f))
class Componentesque (c :: # Type -> Type) instance monadHooks :: Monad Hooks
newtype Component p = Component (EffectFn1 (Record p) Element) unsafeHooksEffect :: forall a. Effect a -> Hooks a
unsafeHooksEffect = Hooks
instance componentesqueComponent :: Componentesque Component class IsComponent component (props :: # Type) children
| component -> props
, component -> children
foreign import data Memo :: # Type -> Type instance componentIsComponent :: IsComponent (Component props) props (Array Element)
instance memoIsComponent :: IsComponent (Memo props) props (Array Element)
instance stringIsComponent :: IsComponent String props (Array Element)
instance providerIsComponent :: IsComponent (Provider v) (value :: v) (Array Element)
instance consumerIsComponent :: IsComponent (Consumer v) () (v -> Element)
instance componentesqueMemo :: Componentesque Memo createElement
:: forall component props
. IsComponent component props (Array Element)
=> component -> Record props -> Array Element -> Element
createElement = rawCreateElement
-- Component building -- Component building
-- | Creates a pure leaf component from a function -- | The type of a function that can be turned into a component with
pureLeaf :: -- | `staticComponent`. Will not have access to the `Hooks` Monad.
forall props. Childless props
=> String
-> (Record props -> Element)
-> Component props
pureLeaf name f = named name $ Component (mkEffectFn1 $ pure <<< f)
-- | Creates a pure tree component from a function
pureTree ::
forall props. Childless props
=> String
-> (Record props -> Array Element -> Element)
-> Component (WithChildren props)
pureTree name c = named name $ Component $ mkEffectFn1 c'
where
c' :: Record (WithChildren props) -> Effect Element
c' props = pure $ c (unsafeCoerce props) (children props.children)
-- | Creates a hooks leaf component from a function
hooksLeaf ::
forall props. Childless props
=> String
-> (forall m. MonadHooks m => Record props -> m Element)
-> Component props
hooksLeaf name c = named name $ Component (mkEffectFn1 c)
hooksTree ::
forall props. Childless props
=> String
-> (forall m. MonadHooks m
=> Record props
-> Array Element
-> m Element)
-> Component (WithChildren props)
hooksTree name c = named name $ Component $ mkEffectFn1 c'
where
c' :: Record (WithChildren props) -> Effect Element
c' props = c (unsafeCoerce props) (children props.children)
-- element creation type StaticComponent props = Record props -> Array Element -> Element
-- | Creates a DOM element of the given tag -- | Turns a `StaticComponent` function into a Component
createDOMElement :: forall props. String -> Record props -> Array Element -> Element staticComponent :: forall props. String -> StaticComponent props -> Component props
createDOMElement = runFn3 _createElement staticComponent name c = Component $ named name $ mkEffectFn1 c'
where
-- | Creates a leaf component from a props Record c' :: Record props -> Effect Element
createLeaf :: c' props = pure $ c props (children props)
forall props cpt.
Childless props
=> Componentesque cpt
=> cpt props
-> Record props
-> Element
createLeaf c p = runFn3 _createElement c p []
-- | Creates a tree component from a props Record and an Array of children
createTree ::
forall props cpt.
Childless props
=> Componentesque cpt
=> cpt (WithChildren props)
-> Record props
-> Array Element
-> Element
createTree = runFn3 _createElement
-- createElement :: forall c p. CreateElement c p => c -> p -> Array Element ->
foreign import _createElement :: forall c p cs e. Fn3 c p cs e -- | The type of a function that can be turned into a component with
-- | `hooksComponent`. Will have access to the `Hooks` Monad.
type HooksComponent props = Record props -> Array Element -> Hooks Element
-- | Turns a `HooksComponent` function into a Component
hooksComponent :: forall props. String -> HooksComponent props -> Component props
hooksComponent name c = Component $ named name $ mkEffectFn1 c'
where
c' :: Record props -> Effect Element
c' props = runHooks $ c props (children props)
rawCreateElement :: forall c p cs. c -> p -> Array cs -> Element
rawCreateElement c p cs = react ... "createElement" $ args
where args = PA.unshift c $ PA.unshift p cs
-- Element cloning -- Element cloning
-- | Clones an element, -- | Clones an element. Quite unsafe because tripping through Element
-- | loses the type of the props. Be careful.
cloneElement :: forall props. Element -> Record props -> Element
cloneElement = runFn2 _cloneElement
foreign import _cloneElement :: forall p. Fn2 Element p Element -- cloneElement :: forall props. Element -> Record props -> Element
-- cloneElement e p = react ... "cloneElement" $ args2 e p
-- Fragment creation -- Fragment creation
-- TODO: add key support
-- | Combines several elements together -- | Combines several elements together
fragment :: Array Element -> Element fragment :: Array Element -> Element
fragment = _createFragment fragment es = rawCreateElement (react .. "Fragment") {} es
foreign import _createFragment :: Array Element -> Element
instance semigroupElement :: Semigroup Element where instance semigroupElement :: Semigroup Element where
append a b = fragment [a, b] append a b = fragment [a, b]
-- | Renders a React Element to a real Element -- | Renders a React Element to a real Element
render :: forall m. MonadEffect m => MonadHooks m => Element -> DOM.Element -> m Unit render :: Element -> DOM.Element -> Effect Unit
render e d = liftEffect (runEffectFn2 _render e d) render e d = delay \_ -> react ... "render" $ args2 e d
foreign import _render :: EffectFn2 Element DOM.Element Unit -- -- Memoisation
foreign import data Memo :: # Type -> Type
-- Memoisation
memo :: memo ::
forall props. forall props.
Component props Component props
-> (Record props -> Record props -> Boolean) -> (Record props -> Record props -> Boolean)
-> Memo props -> Memo props
memo c f = runFn2 _memo c (mkFn2 f) memo c f = react ... "memo" $ args2 c (mkFn2 f)
memo' :: forall props. Component props -> Memo props memo' :: forall props. Component props -> Memo props
memo' = _memoPrime memo' c = react ... "memo" $ [ c ]
foreign import _memo :: forall c f r. Fn2 c f r
foreign import _memoPrime :: forall c r. c -> r
-- Children
foreign import _children :: Children -> Array Element
children :: Children -> Array Element
children = _children
...@@ -201,40 +158,34 @@ children = _children ...@@ -201,40 +158,34 @@ children = _children
foreign import data Context :: Type -> Type foreign import data Context :: Type -> Type
-- | The Provider for a React Context -- | The Provider for a React Context
foreign import data ContextProvider :: Type -> Type foreign import data Provider :: Type -> Type
-- | The Consumer for a React Context -- | The Consumer for a React Context
foreign import data ContextConsumer :: Type -> Type foreign import data Consumer :: Type -> Type
foreign import _createContext :: forall v. v -> Context v
foreign import _contextProvider :: forall v. Context v -> ContextProvider v
foreign import _contextConsumer :: forall v. Context v -> ContextConsumer v
-- | Creates a `Context` from a given value
createContext :: forall v. v -> Context v createContext :: forall v. v -> Context v
createContext = _createContext createContext v = react ... "createContext" $ [v]
provider :: forall v. Context v -> ContextProvider v provider :: forall v. Context v -> Provider v
provider = _contextProvider provider c = c .. "Provider"
consumer :: forall v. Context v -> ContextConsumer v consumer :: forall v. Context v -> Consumer v
consumer = _contextConsumer consumer c = c .. "Consumer"
consume :: forall v. Context v -> (v -> Element) -> Element
consume c f = rawCreateElement c {} [f]
-- Ref creation -- Ref creation
foreign import data NullableRef :: Type -> Type foreign import data NullableRef :: Type -> Type
foreign import _createRef :: forall r. Unit -> NullableRef r
createRef :: forall r. Unit -> NullableRef r createRef :: forall r. Unit -> NullableRef r
createRef = _createRef createRef _ = react ... "createRef" $ []
foreign import _deref :: forall r. NullableRef r -> Nullable r
readNullableRef :: forall r. NullableRef r -> Maybe r readNullableRef :: forall r. NullableRef r -> Maybe r
readNullableRef = toMaybe <<< _deref readNullableRef r = toMaybe $ r .. "current"
-- Ref Forwarding -- Ref Forwarding
...@@ -244,12 +195,18 @@ readNullableRef = toMaybe <<< _deref ...@@ -244,12 +195,18 @@ readNullableRef = toMaybe <<< _deref
-- foreign import _forwardRef :: forall r p. (Fn2 p r Element) -> Forwarded p -- foreign import _forwardRef :: forall r p. (Fn2 p r Element) -> Forwarded p
named :: forall c. String -> c -> c named
named = runFn2 _named :: forall props
. String
-> EffectFn1 (Record props) Element
-> EffectFn1 (Record props) Element
named = flip $ defineProperty "name"
foreign import _named :: forall c. Fn2 String c c isValid :: forall a. a -> Boolean
isValid a = react ... "isValidElement" $ [ a ]
foreign import _isValid :: forall a. a -> Boolean -- Utils
children :: forall a. a -> Array Element
children a = react .. "Children" ... "toArray" $ [ (a .. "children") ]
isValid :: forall a. a -> Boolean
isValid = _isValid
-- | https://reactjs.org/docs/events.html
module Reactix.SyntheticEvent where
import Prelude
import DOM.Simple as DOM
import Effect ( Effect )
import Effect.Uncurried ( EffectFn1, runEffectFn1 )
import FFI.Simple ( (..), (...) )
class IsSyntheticEvent e
foreign import data NativeEvent :: Type
foreign import data MouseEvent :: Type
foreign import data KeyboardEvent :: Type
instance keyboardEventIsSyntheticEvent :: IsSyntheticEvent KeyboardEvent
instance mouseEventIsSyntheticEvent :: IsSyntheticEvent MouseEvent
bubbles :: forall e. IsSyntheticEvent e => e -> Boolean
bubbles e = e .. "bubbles"
cancelable :: forall e. IsSyntheticEvent e => e -> Boolean
cancelable e = e .. "cancelable"
isTrusted :: forall e. IsSyntheticEvent e => e -> Boolean
isTrusted e = e .. "isTrusted"
defaultPrevented :: forall e. IsSyntheticEvent e => e -> Boolean
defaultPrevented e = e .. "defaultPrevented"
eventPhase :: forall e. IsSyntheticEvent e => e -> Number
eventPhase e = e .. "eventPhase"
timestamp :: forall e. IsSyntheticEvent e => e -> Number
timestamp e = e .. "timeStamp"
type' :: forall e. IsSyntheticEvent e => e -> String
type' e = e .. "type"
-- target :: forall e. IsSyntheticEvent e => e -> NativeEventTarget
-- target e = e .. "target"
-- currentTarget :: forall e. IsSyntheticEvent e => e -> NativeEventTarget
-- currentTarget e = e .. "currentTarget"
-- nativeEvent :: forall e. IsSyntheticEvent e => e -> NativeEvent
-- nativeEvent e = e .. "nativeEvent"
stopPropagation :: forall e. IsSyntheticEvent e => e -> Effect Unit
stopPropagation e = e ... "stopPropagation" $ []
preventDefault :: forall e. IsSyntheticEvent e => e -> Effect Unit
preventDefault e = e ... "preventDefault" $ []
isPropagationStopped :: forall e. IsSyntheticEvent e => e -> Effect Unit
isPropagationStopped e = e ... "isPropagationStopped" $ []
isDefaultPrevented :: forall e. IsSyntheticEvent e => e -> Effect Unit
isDefaultPrevented e = e ... "isDefaultPrevented" $ []
-- Events with Modifier keys
-- | This class is used to access information about modifier keys for
-- | supported events
class HasModifierKeys e
instance mouseEventHasModifierKeys :: HasModifierKeys MouseEvent
instance keyboardEventHasModifierKeys :: HasModifierKeys KeyboardEvent
-- instance touchEventHasModifierKeys :: HasModifierKeys TouchEvent
altKey :: forall e. HasModifierKeys e => e -> Boolean
altKey e = e .. "altKey"
ctrlKey :: forall e. HasModifierKeys e => e -> Boolean
ctrlKey e = e .. "ctrlKey"
shiftKey :: forall e. HasModifierKeys e => e -> Boolean
shiftKey e = e .. "shiftKey"
metaKey :: forall e. HasModifierKeys e => e -> Boolean
metaKey e = e .. "metaKey"
getModifierState :: forall e. HasModifierKeys e => e -> String -> Boolean
getModifierState e s = e ... "getModifierState" $ [ s ]
-- Keyboard Events
key :: KeyboardEvent -> String
key e = e .. "key"
which :: KeyboardEvent -> Number
which e = e .. "which"
charCode :: KeyboardEvent -> Int
charCode e = e .. "charCode"
keyCode :: KeyboardEvent -> Number
keyCode e = e .. "keyCode"
locale :: KeyboardEvent -> String
locale e = e .. "locale"
location :: KeyboardEvent -> Number
location e = e .. "location"
repeat :: KeyboardEvent -> Boolean
repeat e = e .. "repeat"
button :: MouseEvent -> Number
button e = e .. "button"
buttons :: MouseEvent -> Number
buttons e = e .. "buttons"
-- relatedTarget :: MouseEvent -> NativeEventTarget
-- relatedTarget e = e .. "relatedTarget"
clientX :: MouseEvent -> Number
clientX e = e .. "clientX"
clientY :: MouseEvent -> Number
clientY e = e .. "clientY"
pageX :: MouseEvent -> Number
pageX e = e .. "pageX"
pageY :: MouseEvent -> Number
pageY e = e .. "pageY"
screenX :: MouseEvent -> Number
screenX e = e .. "screenX"
screenY :: MouseEvent -> Number
screenY e = e .. "screenY"
-- foreign import data TouchEvent :: Type
-- changedTouches :: TouchEvent -> NativeTouchList
-- targetTouches :: TouchEvent -> NativeTouchList
-- touches :: TouchEvent -> NativeTouchList
-- foreign import data NativeDataTransfer :: Type
-- foreign import data NativeAbstractView :: Type
-- foreign import data NativeTouchList :: Type
-- foreign import data AnimationEvent :: Type
-- animationName :: AnimationEvent -> String
-- pseudoElement :: AnimationEvent -> String
-- elapsedTime :: AnimationEvent -> Number
-- foreign import data ClipboardEvent :: Type
-- clipboardData :: ClipboardEvent -> NativeDataTransfer
-- foreign import data CompositionEvent :: Type
-- data' :: SyntheticCompositionEvent -> String
-- foreign import data FocusEvent :: Type
-- relatedTarget :: FocusEvent -> NativeEventTarget
-- foreign import data TransitionEvent :: Type
-- propertyName :: TransitionEvent -> String
-- pseudoElement :: TransitionEvent -> String
-- elapsedTime :: TransitionEvent -> Number
-- foreign import data UIEvent :: Type
-- detail :: UIEvent -> Number
-- view :: UIEvent -> NativeAbstractView
-- foreign import data WheelEvent :: Type
-- deltaMode :: WheelEvent -> Number
-- deltaX :: WheelEvent -> Number
-- deltaY :: WheelEvent -> Number
-- deltaZ :: WheelEvent -> Number
'use strict'; 'use strict';
const utils = require('react-dom/test-utils'); 'use strict';
const test = require('react-testing-library'); exports.testUtils = require('react-dom/test-utils');
exports.testingLibrary = require('react-testing-library');
function spread(obj,prop) {
return function() {
return obj[prop].apply(obj, Array.from(arguments));
};
}
exports._act = function(e) {
var ret;
utils.act(function() { ret = e(); });
return ret;
};
exports._render = spread(test,'render');
exports._fireEvent = function(name, obj) {
test.fireEvent[name].call(test.fireEvent, obj);
};
...@@ -3,36 +3,48 @@ module Reactix.Test ...@@ -3,36 +3,48 @@ module Reactix.Test
, render , render
, Rendered , Rendered
, fireEvent, fireClick , fireEvent, fireClick
, cleanup
) where ) where
import Prelude ( Unit ) import Prelude
import Effect ( Effect ) import Effect ( Effect )
import Effect.Uncurried ( EffectFn1, runEffectFn1, EffectFn2, runEffectFn2 ) import Effect.Uncurried ( EffectFn1, runEffectFn1, EffectFn2, runEffectFn2 )
import Data.Function.Uncurried ( Fn2, runFn2 ) import Data.Function.Uncurried ( Fn2, runFn2 )
import DOM.Simple as DOM import DOM.Simple as DOM
import Reactix.React ( Element ) import Reactix.React ( react, Element )
import FFI.Simple ( (..), (...), delay )
import DOM.Simple.Console
foreign import data TestUtils :: Type
foreign import testUtils :: TestUtils
foreign import data Testing :: Type
foreign import testingLibrary :: Testing
type Rendered = type Rendered =
{ getByText :: EffectFn1 String Element { getByText :: String -> Effect Element
, getByTestId :: EffectFn1 String Element , getByTestId :: String -> Effect Element
, container :: DOM.Element , container :: DOM.Element
, asFragment :: Effect DOM.Fragment } , asFragment :: Effect DOM.Fragment }
render :: Element -> Effect Rendered render :: Element -> Effect Rendered
render = runEffectFn1 _render render e = pure $ raw { getByText=getByText, getByTestId=getByTestId }
where getByText = runEffectFn1 raw.getByText
foreign import _render :: EffectFn1 Element Rendered getByTestId = runEffectFn1 raw.getByTestId
raw = testingLibrary ... "render" $ [e]
-- | Make react behave more predictably in tests -- | Make react behave more predictably in tests
act :: forall t. Effect t -> Effect t act :: forall t. (Unit -> Effect t) -> Effect t
act = runEffectFn1 _act act f = testUtils ... "act" $ [ delay f ]
foreign import _act :: forall t. EffectFn1 (Effect t) t
fireClick :: DOM.Element -> Effect Unit fireClick :: DOM.Element -> Effect Unit
fireClick = fireEvent "click" fireClick = fireEvent "click"
fireEvent :: String -> DOM.Element -> Effect Unit fireEvent :: String -> DOM.Element -> Effect Unit
fireEvent = runEffectFn2 _fireEvent fireEvent ev el = pure $ (testingLibrary .. "fireEvent") ... ev $ [el]
cleanup :: Effect Unit
cleanup = delay $ \_ -> pure $ testingLibrary ... "cleanup" $ []
foreign import _fireEvent :: EffectFn2 String DOM.Element Unit
...@@ -8,35 +8,35 @@ import Data.Traversable ( traverse, traverse_, sequence_ ) ...@@ -8,35 +8,35 @@ import Data.Traversable ( traverse, traverse_, sequence_ )
import Data.Tuple ( Tuple(..) ) import Data.Tuple ( Tuple(..) )
import Data.Tuple.Nested ( (/\) ) import Data.Tuple.Nested ( (/\) )
import Effect ( Effect ) import Effect ( Effect )
import Effect.Aff ( Aff )
import Effect.Class ( liftEffect ) import Effect.Class ( liftEffect )
import Effect.Ref as Ref
import Effect.Uncurried ( EffectFn1, mkEffectFn1, runEffectFn1 ) import Effect.Uncurried ( EffectFn1, mkEffectFn1, runEffectFn1 )
-- import Effect.Aff (launchAff_) -- import Effect.Aff (launchAff_)
import Test.Spec ( Spec, describe, it ) import Test.Spec ( Spec, describe, it )
import Test.Spec.Assertions ( shouldEqual ) import Test.Spec.Assertions ( shouldEqual )
-- import Test.Spec.QuickCheck (quickCheck') -- import Test.Spec.QuickCheck (quickCheck')
import DOM.Simple.Console ( log2 )
import DOM.Simple as DOM import DOM.Simple as DOM
import DOM.Simple.Document as Document import DOM.Simple.Document as Document
import DOM.Simple.Element as Element import DOM.Simple.Element as Element
import DOM.Simple.Event as Event import DOM.Simple.Event as Event
import Reactix as R import Reactix as R
import Reactix.Test as RT import Reactix.Test as RT
import Reactix.DOM.Raw ( button, div, i, text ) import Reactix.DOM.Raw ( button, div, i, text )
import Reactix.Hooks ( useState ) import Reactix.Hooks ( useState, useEffect, useLayoutEffect )
staticTest :: Spec Unit staticTest :: Spec Unit
staticTest = staticTest =
describe "Basic DOM rendering" do describe "Basic DOM rendering" $ do
it "Simple elements" do it "Simple elements" $ do
root <- liftEffect $ RT.render elem root <- liftEffect $ RT.render elem
(Element.name <$> Element.children root.container) `shouldEqual` ["I"] let children = Element.children root.container
it "Fragments" do (Element.name <$> children) `shouldEqual` ["I"]
children /\ count <- liftEffect $ (Element.innerHTML <$> children) `shouldEqual` ["hello world"]
do let root = Document.createElement "div" it "Fragments" $ do
RT.act $ R.render frag root root <- liftEffect $ RT.render $ frag
pure $ Tuple (Element.children root) (Element.childCount root) Element.childCount root.container `shouldEqual` 2
count `shouldEqual` 2 let children = Element.children root.container
A.length children `shouldEqual` 2 A.length children `shouldEqual` 2
(Element.name <$> children) `shouldEqual` ["I", "I"] (Element.name <$> children) `shouldEqual` ["I", "I"]
(Element.innerHTML <$> children) `shouldEqual` ["hello","world"] (Element.innerHTML <$> children) `shouldEqual` ["hello","world"]
...@@ -46,28 +46,26 @@ staticTest = ...@@ -46,28 +46,26 @@ staticTest =
type CounterProps = ( count :: Int ) type CounterProps = ( count :: Int )
counterCpt :: R.Component CounterProps counterCpt :: R.Component CounterProps
counterCpt = R.hooksLeaf "Counter" cpt counterCpt = R.hooksComponent "Counter" cpt
where where
cpt :: forall m. R.MonadHooks m => Record CounterProps -> m R.Element cpt {count} _ = do
cpt {count} = do y /\ setY <- useState $ \_ -> pure count
y /\ setY <- useState $ pure count
pure $ div { className: "counter" } pure $ div { className: "counter" }
[ button { type: "button", onClick: onclick setY (y + 1) } [ text "++" ] [ button { type: "button", onClick: onclick setY (y + 1) } [ text "++" ]
, div {} [ text (show y) ] ] , div {} [ text (show y) ] ]
onclick set to = mkEffectFn1 $ \e -> do onclick set to = mkEffectFn1 $ \e -> runEffectFn1 set to
runEffectFn1 set to
counterTest :: Spec Unit counterTest :: Spec Unit
counterTest = counterTest =
describe "Counter" do describe "Counter" do
it "Works for plain components" $ do it "Works for plain components" $ do
let counter = R.createLeaf counterCpt {count: 0} let counter = R.createElement counterCpt {count: 0} []
liftEffect (RT.render counter) >>= test liftEffect (RT.render counter) >>= test
it "Works for memoised components" $ do it "Works for memoised components" $ do
let counter = R.createLeaf (R.memo counterCpt (==)) {count: 0} let counter = R.createElement (R.memo counterCpt (==)) {count: 0} []
liftEffect (RT.render counter) >>= test liftEffect (RT.render counter) >>= test
it "works for memo'ised components" $ do it "works for memo'ised components" $ do
let counter = R.createLeaf (R.memo' counterCpt) {count: 0} let counter = R.createElement (R.memo' counterCpt) {count: 0} []
liftEffect (RT.render counter) >>= test liftEffect (RT.render counter) >>= test
where where
test root = do test root = do
...@@ -86,12 +84,105 @@ counterTest = ...@@ -86,12 +84,105 @@ counterTest =
A.length children4 `shouldEqual` 2 A.length children4 `shouldEqual` 2
(Element.innerHTML <$> children4) `shouldEqual` ["++", "2"] (Element.innerHTML <$> children4) `shouldEqual` ["++", "2"]
listTest :: Spec Unit data EffectorState = Fresh | Initialised | Done
listTest = pure unit
derive instance eqEffectorState :: Eq EffectorState
instance showEffectorState :: Show EffectorState where
show Fresh = "fresh"
show Initialised = "initialised"
show Done = "done"
type EffectorProps = ( stateRef :: Ref.Ref EffectorState )
effectorCpt :: R.Component EffectorProps
effectorCpt = R.hooksComponent "Effector" cpt
where cpt {stateRef} _ = do
useEffect $ \_ -> do
Ref.write Initialised stateRef
pure $ \_ -> Ref.write Done stateRef
pure $ div {} []
-- TODO: test it's firing at the right time
effectorTest :: Spec Unit
effectorTest =
describe "Effector" do
it "Works for plain components" $
test $ effectorCpt
it "works for memo'ised components" $
test $ R.memo' effectorCpt
where
test :: forall cpt. R.IsComponent cpt EffectorProps (Array R.Element) => cpt -> Aff Unit
test cpt = do
ref <- liftEffect $ Ref.new Fresh
let effector = R.createElement cpt {stateRef: ref} []
root <- liftEffect (RT.render effector)
state <- liftEffect $ Ref.read ref
state `shouldEqual` Initialised
liftEffect $ RT.cleanup
state' <- liftEffect $ Ref.read ref
state' `shouldEqual` Done
layoutEffectorCpt :: R.Component EffectorProps
layoutEffectorCpt = R.hooksComponent "LayoutEffector" cpt
where cpt {stateRef} _ = do
useLayoutEffect $ \_ -> do
Ref.write Initialised stateRef
pure $ \_ -> Ref.write Done stateRef
pure $ div {} []
-- TODO: test it's firing at the right time
layoutEffectorTest :: Spec Unit
layoutEffectorTest =
describe "LayoutEffector" do
it "Works for plain components" $
test $ layoutEffectorCpt
it "works for memo'ised components" $
test $ R.memo' layoutEffectorCpt
where
test :: forall cpt. R.IsComponent cpt EffectorProps (Array R.Element) => cpt -> Aff Unit
test cpt = do
ref <- liftEffect $ Ref.new Fresh
let effector = R.createElement cpt {stateRef: ref} []
root <- liftEffect (RT.render effector)
state <- liftEffect $ Ref.read ref
state `shouldEqual` Initialised
liftEffect $ RT.cleanup
state' <- liftEffect $ Ref.read ref
state' `shouldEqual` Done
type ContextProps = ()
-- contextualCpt :: R.Component ContextProps
-- contextualCpt = R.hooksComponent "Contextual" cpt
-- where cpt {stateRef} _ = do
-- useEffect $ \_ -> do
-- Ref.write Initialised stateRef
-- pure $ \_ -> Ref.write Done stateRef
-- pure $ div {} []
-- contextTest :: Spec Unit
-- contextTest =
-- describe "Context" do
-- it "Works for plain components" $
-- test $ contextualCpt
-- where test cpt = pure unit
-- reducerTest :: Spec Unit
-- memoTest :: Spec Unit
-- refTest :: Spec Unit
-- imperativeHandleTest :: Spec Unit
-- debugValueTest :: Spec Unit
-- listTest :: Spec Unit
-- listTest = pure unit
spec :: Spec Unit spec :: Spec Unit
spec = sequence_ spec = sequence_
[ staticTest [ staticTest
, counterTest , counterTest
, listTest , effectorTest
, layoutEffectorTest
] ]
-- , listTest
-- ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment