git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Fix test
d0bb8baMark Eibes
src/Yoga/Block/Atom/Input/View/Container.pursnew file+130
@@ -0,0 +1,130 @@
1+module Yoga.Block.Atom.Input.View.Container where
2+
3+import Yoga.Prelude.View
4+import Data.Array as Array
5+import Data.Interpolate (i)
6+import Foreign.Object as Object
7+import Framer.Motion as M
8+import Partial.Unsafe (unsafeCrashWith)
9+import React.Basic.DOM (CSS, css)
10+import React.Basic.Emotion (Style)
11+import Yoga.Block.Atom.Input.Style as Style
12+
13+type PropsF f =
14+ ( css ∷ f Style
15+ , isInvalid ∷ f Boolean
16+ | Style.Props f (MandatoryProps ())
17+ )
18+
19+type MandatoryProps r =
20+ ( children ∷ Array JSX
21+ , hasFocus ∷ Boolean
22+ | r
23+ )
24+
25+type Props =
26+ PropsF Id
27+
28+type PropsOptional =
29+ PropsF OptionalProp
30+
31+component ∷ ∀ p q. Union p q Props => ReactComponent { | MandatoryProps p }
32+component = rawContainer
33+
34+type Propski =
35+ { css ∷ OptionalProp Style
36+ , hasFocus ∷ Boolean
37+ , isInvalid ∷ OptionalProp Boolean
38+ , children ∷ Array JSX
39+ }
40+
41+rawContainer ∷ ∀ p. ReactComponent { | p }
42+rawContainer =
43+ mkForwardRefComponent "InputContainer" do
44+ \(props ∷ { | PropsOptional }) ref -> React.do
45+ let
46+ result =
47+ M.div
48+ </* M.motion
49+ { variants: M.variants containerVariants
50+ , animate: M.animate if props.hasFocus then containerVariantLabels.focussed else containerVariantLabels.blurred
51+ }
52+ { className: "ry-input-container"
53+ , css: Style.inputContainer props
54+ , _data:
55+ props.isInvalid
56+ # foldMap \isInvalid ->
57+ Object.fromHomogeneous
58+ { "invalid": show isInvalid
59+ }
60+ , ref
61+ }
62+ /> props.children
63+ pure result
64+
65+drawPathUntil ∷ Int -> Array Point -> String
66+drawPathUntil idx thePath = do
67+ let fn { x, y } = i x "%" " " y "%"
68+ let firstFew = Array.take idx thePath
69+ let lastFew = Array.drop idx thePath $> (Array.last firstFew # fromMaybe' \_ -> unsafeCrashWith "ogod")
70+ let rendered = intercalate "," $ fn <$> (firstFew <> lastFew)
71+ i "polygon(" rendered ")"
72+
73+path ∷ Array Point
74+path = mkPath 4 8
75+
76+mkPath ∷ Int -> Int -> Array Point
77+mkPath borderX borderY = do
78+ let
79+ inside =
80+ [ {- ⌜ -} p borderX borderY
81+ , {- ⌞ -} p borderX (100 - borderY)
82+ , {- ⌟ -} p (100 - borderX) (100 - borderY)
83+ , {- ⌝ -} p (100 - borderX) borderY
84+ , {- ⌜ -} p borderX borderY
85+ ]
86+ outside =
87+ [ {-⌜ -} p 0 0
88+ , {-⌞ -} p 0 100
89+ , {- . -} p 25 100
90+ , {- . -} p 50 100
91+ , {- . -} p 75 100
92+ , {- ⌟-} p 100 100
93+ , {- ⌝-} p 100 0
94+ , {- ^ -} p 75 0
95+ , {- ^ -} p 50 0
96+ , {- ^ -} p 25 0
97+ , {-⌜ -} p 0 0
98+ ]
99+ inside <> outside <> (Array.reverse inside)
100+ where
101+ p ∷ Int -> Int -> Point
102+ p x y = { x, y }
103+
104+containerVariantLabels ∷
105+ { blurred ∷ M.VariantLabel
106+ , focussed ∷ M.VariantLabel
107+ }
108+containerVariantLabels = M.makeVariantLabels containerVariants
109+
110+containerVariants ∷
111+ { blurred ∷ CSS
112+ , focussed ∷ CSS
113+ }
114+containerVariants =
115+ { focussed:
116+ css
117+ { clipPath
118+ , transition: { duration: 0.6 }
119+ }
120+ , blurred:
121+ css
122+ { clipPath:
123+ drawPathUntil (Array.length path + 1) path
124+ }
125+ }
126+ where
127+ clipPath = 6 Array... (Array.length path) <#> \ln -> drawPathUntil ln path
128+
129+type Point =
130+ { x ∷ Int, y ∷ Int }
src/Yoga/Block/Atom/Input/View/Label.pursnew file+86
@@ -0,0 +1,86 @@
1+module Yoga.Block.Atom.Input.View.Label where
2+
3+import Yoga.Prelude.View
4+import Data.String.NonEmpty (NonEmptyString)
5+import Data.String.NonEmpty as NonEmptyString
6+import Effect.Unsafe (unsafePerformEffect)
7+import Foreign.Object as Object
8+import Framer.Motion as M
9+import React.Basic.DOM as R
10+import React.Basic.Hooks (reactComponent)
11+import React.Basic.Hooks as React
12+import Yoga.Block.Atom.Input.Style as Style
13+
14+type Props =
15+ { onClickLargeLabel ∷ EventHandler
16+ , isRequired ∷ Boolean
17+ , isInvalid ∷ Boolean
18+ , isFocussed ∷ Boolean
19+ , renderLargeLabel ∷ Boolean
20+ , leftIconRef ∷ NodeRef
21+ , inputRef ∷ NodeRef
22+ , labelId ∷ String
23+ , inputId ∷ String
24+ , labelText ∷ NonEmptyString
25+ }
26+
27+component ∷ ReactComponent Props
28+component =
29+ unsafePerformEffect
30+ $ reactComponent "Input Label" \props -> React.do
31+ -- Track input bounding box
32+ inputBbox /\ setInputBbox <- useState' (zero ∷ DOMRect)
33+ useEffectOnce do
34+ maybeBBox <- getBoundingBoxFromRef props.inputRef
35+ for_ maybeBBox setInputBbox
36+ mempty
37+ -- Left icon
38+ leftIconBbox /\ setLeftIconBbox <- useState' Nothing
39+ useEffectAlways do
40+ when (leftIconBbox == Nothing) do
41+ maybeBBox <- getBoundingBoxFromRef props.leftIconRef
42+ for_ maybeBBox (setLeftIconBbox <<< Just)
43+ mempty
44+ -- UI
45+ let
46+ result =
47+ container
48+ [ sharedLayout
49+ [ labelContainer
50+ [ labelSpan
51+ [ text ]
52+ ]
53+ ]
54+ ]
55+ text = R.text $ NonEmptyString.toString props.labelText
56+ sharedLayout = M.animateSharedLayout </ { type: M.switch }
57+ container = div </* { className: "ry-input-label-container", css: Style.labelContainer }
58+ labelContainer =
59+ guard (inputBbox /= zero)
60+ $ M.div
61+ </* { className: if props.renderLargeLabel then "ry-input-label-large" else "ry-input-label-small"
62+ , layoutId: M.layoutId "ry-input-label"
63+ , css:
64+ if props.renderLargeLabel then
65+ Style.labelLarge { leftIconWidth: leftIconBbox <#> _.width, inputWidth: inputBbox.width }
66+ else
67+ Style.labelSmall
68+ , layout: M.layout true
69+ , transition: M.transition { duration: 0.18, ease: "easeOut" }
70+ , _data:
71+ Object.fromHomogeneous
72+ { "has-focus": show props.isFocussed
73+ , "invalid": show props.isInvalid
74+ , "required": show props.isRequired
75+ }
76+ , initial: M.initial false
77+ }
78+ labelSpan =
79+ M.span
80+ </ { onClick: props.onClickLargeLabel
81+ , layout: M.layout true
82+ , layoutId: M.layoutId "ry-input-label-text"
83+ , htmlFor: props.inputId
84+ , id: props.labelId
85+ }
86+ pure result
src/Yoga/Block/Layout/Cluster/Spec.purs+6−4
@@ -1,8 +1,9 @@
1module Yoga.Block.Layout.Cluster.Spec where
1module Yoga.Block.Layout.Cluster.Spec where
2
2
3import Yoga.Prelude.Spec
3import Yoga.Prelude.Spec
4−import Yoga.Block.Layout.Cluster as Cluster
4+import Foreign.Object as Object
5import React.Basic.DOM as R
5import React.Basic.DOM as R
6+import Yoga.Block.Layout.Cluster as Cluster
6
7
7spec ∷ Spec Unit
8spec ∷ Spec Unit
8spec =
9spec =
@@ -12,10 +13,11 @@ spec =
12 void
13 void
13 $ renderComponent Cluster.component {}
14 $ renderComponent Cluster.component {}
14 it "accepts div props" do
15 it "accepts div props" do
15− { findByText } <-
16+ { findByTestId } <-
16 renderComponent Cluster.component
17 renderComponent Cluster.component
17 { role: "Heinz"
18 { role: "Heinz"
19+ , _data: Object.fromHomogeneous { testid: "cluster" }
18 , children: [ R.text "Find me!" ]
20 , children: [ R.text "Find me!" ]
19 }
21 }
20− elem <- findByText "Find me!"
22+ elem <- findByTestId "cluster"
21− elem `shouldHaveAttribute` "role"
23+ shouldHaveAttributeWithValue elem "role" "Heinz"