Skip to content

Commit 3dcc01a

Browse files
briansztamfateribrkhalil
authored andcommitted
feat: implement account list item component (#17303)
Signed-off-by: Brian Sztamfater <[email protected]>
1 parent 9df6a14 commit 3dcc01a

File tree

7 files changed

+474
-55
lines changed

7 files changed

+474
-55
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
(ns quo2.components.list-items.account.component-spec
2+
(:require [test-helpers.component :as h]
3+
[quo2.components.list-items.account.view :as account]
4+
[quo2.foundations.colors :as colors]))
5+
6+
(h/describe "List items: account"
7+
(h/test "default render'"
8+
(h/render [account/view])
9+
(h/is-truthy (h/query-by-label-text :container)))
10+
11+
(h/test "on-press-in changes state to :pressed"
12+
(h/render [account/view])
13+
(h/fire-event :on-press-in (h/get-by-label-text :container))
14+
(h/wait-for #(h/has-style (h/query-by-label-text :container)
15+
{:backgroundColor (colors/custom-color :blue 50 5)})))
16+
17+
(h/test "on-press-in changes state to :pressed with blur? enabled"
18+
(h/render [account/view {:blur? true}])
19+
(h/fire-event :on-press-in (h/get-by-label-text :container))
20+
(h/wait-for #(h/has-style (h/query-by-label-text :container)
21+
{:backgroundColor colors/white-opa-5})))
22+
23+
(h/test "on-press-out changes state to :active"
24+
(h/render [account/view])
25+
(h/fire-event :on-press-in (h/get-by-label-text :container))
26+
(h/fire-event :on-press-out (h/get-by-label-text :container))
27+
(h/wait-for #(h/has-style (h/query-by-label-text :container)
28+
{:backgroundColor (colors/custom-color :blue 50 10)})))
29+
30+
(h/test "on-press-out changes state to :active with blur? enabled"
31+
(h/render [account/view {:blur? true}])
32+
(h/fire-event :on-press-in (h/get-by-label-text :container))
33+
(h/fire-event :on-press-out (h/get-by-label-text :container))
34+
(h/wait-for #(h/has-style (h/query-by-label-text :container)
35+
{:backgroundColor colors/white-opa-10})))
36+
37+
(h/test "on-press-out changes state to :selected"
38+
(h/render [account/view {:selectable? true}])
39+
(h/fire-event :on-press-in (h/get-by-label-text :container))
40+
(h/fire-event :on-press-out (h/get-by-label-text :container))
41+
(h/wait-for #(h/is-truthy (h/query-by-label-text :check-icon))))
42+
43+
(h/test "on-press-out calls on-press"
44+
(let [on-press (h/mock-fn)]
45+
(h/render [account/view {:on-press on-press}])
46+
(h/fire-event :on-press-in (h/get-by-label-text :container))
47+
(h/fire-event :on-press-out (h/get-by-label-text :container))
48+
(h/was-called on-press)))
49+
50+
(h/test "renders token props if type :tag"
51+
(h/render [account/view {:type :tag}])
52+
(h/is-truthy (h/query-by-label-text :tag-container)))
53+
54+
(h/test "renders keycard icon if title-icon? is true"
55+
(h/render [account/view {:title-icon? true}])
56+
(h/is-truthy (h/query-by-label-text :keycard-icon)))
57+
58+
(h/test "doesn't render keycard icon if title-icon? is false"
59+
(h/render [account/view])
60+
(h/is-falsy (h/query-by-label-text :keycard-icon)))
61+
62+
(h/test "renders balance container but not arrow icon if type :balance-neutral"
63+
(h/render [account/view {:type :balance-neutral}])
64+
(h/is-truthy (h/query-by-label-text :balance-container))
65+
(h/is-falsy (h/query-by-label-text :arrow-icon)))
66+
67+
(h/test "renders balance container and negative arrow icon if type :balance-negative"
68+
(h/render [account/view {:type :balance-negative}])
69+
(h/is-truthy (h/query-by-label-text :balance-container))
70+
(h/is-truthy (h/query-by-label-text :icon-negative))
71+
(h/is-falsy (h/query-by-label-text :icon-positive)))
72+
73+
(h/test "renders balance container and positive arrow icon if type :balance-positive"
74+
(h/render [account/view {:type :balance-positive}])
75+
(h/is-truthy (h/query-by-label-text :balance-container))
76+
(h/is-falsy (h/query-by-label-text :icon-negative))
77+
(h/is-truthy (h/query-by-label-text :icon-positive)))
78+
79+
(h/test "renders options button if type :action"
80+
(let [on-options-press (h/mock-fn)]
81+
(h/render [account/view
82+
{:type :action
83+
:on-options-press on-options-press}])
84+
(h/is-truthy (h/query-by-label-text :options-button))
85+
(h/fire-event :on-press (h/get-by-label-text :options-button))
86+
(h/was-called on-options-press))))
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
(ns quo2.components.list-items.account.style
2+
(:require [quo2.foundations.colors :as colors]))
3+
4+
(defn- background-color
5+
[{:keys [state blur? customization-color]}]
6+
(cond (or (= state :pressed) (= state :selected))
7+
(if blur? colors/white-opa-5 (colors/custom-color customization-color 50 5))
8+
(= state :active)
9+
(if blur? colors/white-opa-10 (colors/custom-color customization-color 50 10))
10+
(and (= state :pressed) blur?) colors/white-opa-10
11+
:else :transparent))
12+
13+
(defn container
14+
[props]
15+
{:height 56
16+
:border-radius 12
17+
:background-color (background-color props)
18+
:flex-direction :row
19+
:align-items :center
20+
:padding-horizontal 12
21+
:padding-vertical 6
22+
:justify-content :space-between})
23+
24+
(def left-container
25+
{:flex-direction :row
26+
:align-items :center})
27+
28+
(defn metric-text
29+
[type theme]
30+
{:color (case type
31+
:balance-positive (colors/theme-colors colors/success-50 colors/success-60 theme)
32+
:balance-negative (colors/theme-colors colors/danger-50 colors/danger-60 theme)
33+
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
34+
35+
(defn dot-divider
36+
[type theme]
37+
{:width 2
38+
:height 2
39+
:border-radius 2
40+
:margin-horizontal 4
41+
:background-color (case type
42+
:balance-positive (colors/theme-colors colors/success-50-opa-40
43+
colors/success-60-opa-40
44+
theme)
45+
:balance-negative (colors/theme-colors colors/danger-50-opa-40
46+
colors/danger-50-opa-40
47+
theme)
48+
(colors/theme-colors colors/neutral-80-opa-40 colors/neutral-50-opa-40 theme))})
49+
50+
(defn arrow-icon
51+
[type theme]
52+
{:size 16
53+
:color (if (= type :balance-positive)
54+
(colors/theme-colors colors/success-50 colors/success-60 theme)
55+
(colors/theme-colors colors/danger-50 colors/danger-60 theme))})
56+
57+
(def arrow-icon-container
58+
{:margin-left 4})
59+
60+
(def account-container
61+
{:margin-left 8})
62+
63+
(def account-title-container
64+
{:flex-direction :row
65+
:height 22
66+
:align-items :center})
67+
68+
(defn account-address
69+
[blur? theme]
70+
{:height 18
71+
:color (if blur?
72+
colors/white-opa-40
73+
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
74+
75+
(def keycard-icon-container
76+
{:margin-left 4})
77+
78+
(def token-tag-container
79+
{:height 40
80+
:padding-top 4})
81+
82+
(defn token-tag-text-container
83+
[blur? theme]
84+
{:flex-direction :row
85+
:align-items :center
86+
:height 16
87+
:padding-horizontal 3
88+
:border-width 1
89+
:border-radius 6
90+
:border-color (if blur?
91+
colors/white-opa-10
92+
(colors/theme-colors colors/neutral-20
93+
colors/neutral-80
94+
theme))})
95+
96+
(defn token-tag-text
97+
[blur? theme]
98+
{:margin-top -1
99+
:color (if blur?
100+
colors/white-opa-70
101+
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
102+
103+
(def balance-container
104+
{:align-items :flex-end
105+
:justify-content :space-between})
106+
107+
(def metrics-container
108+
{:flex-direction :row
109+
:align-items :center
110+
:margin-top 2})
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
(ns quo2.components.list-items.account.view
2+
(:require [quo2.components.avatars.account-avatar.view :as account-avatar]
3+
[quo2.components.markdown.text :as text]
4+
[quo2.foundations.colors :as colors]
5+
[quo2.theme :as quo.theme]
6+
[react-native.core :as rn]
7+
[quo2.components.list-items.account.style :as style]
8+
[reagent.core :as reagent]
9+
[quo2.components.icon :as icon]))
10+
11+
(defn- account-view
12+
[{:keys [account-props title-icon? blur? theme]
13+
:or {title-icon? false}}]
14+
[rn/view {:style style/left-container}
15+
[account-avatar/view (assoc account-props :size 32)]
16+
[rn/view {:style style/account-container}
17+
[rn/view
18+
{:style style/account-title-container}
19+
[text/text
20+
{:weight :semi-bold
21+
:size :paragraph-1}
22+
(:name account-props)]
23+
(when title-icon?
24+
[rn/view
25+
{:style style/keycard-icon-container
26+
:accessibility-label :keycard-icon}
27+
[icon/icon :i/keycard
28+
{:size 20
29+
:color (if blur?
30+
colors/white-opa-40
31+
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]])]
32+
[text/text {:size :paragraph-2}
33+
[text/text
34+
{:size :paragraph-2
35+
:weight :monospace
36+
:style (style/account-address blur? theme)}
37+
(:address account-props)]]]])
38+
39+
(defn- balance-view
40+
[{:keys [balance-props type theme]}]
41+
[rn/view
42+
{:style style/balance-container
43+
:accessibility-label :balance-container}
44+
[text/text
45+
{:weight :medium
46+
:size :paragraph-2}
47+
(:fiat-value balance-props)]
48+
[rn/view
49+
{:style style/metrics-container}
50+
[text/text
51+
{:size :paragraph-2
52+
:style (style/metric-text type theme)}
53+
(str (:percentage-change balance-props) "%")]
54+
[rn/view {:style (style/dot-divider type theme)}]
55+
[text/text
56+
{:size :paragraph-2
57+
:style (style/metric-text type theme)}
58+
(:fiat-change balance-props)]
59+
(when (not= type :balance-neutral)
60+
[rn/view
61+
{:style style/arrow-icon-container
62+
:accessibility-label :arrow-icon}
63+
[icon/icon (if (= type :balance-positive) :i/positive :i/negative)
64+
(assoc (style/arrow-icon type theme)
65+
:accessibility-label
66+
(if (= type :balance-positive) :icon-positive :icon-negative))]])]])
67+
68+
(defn- token-tag
69+
[{:keys [token-props blur? theme]}]
70+
;; TODO: Use Tiny tag component when available (issue #17341)
71+
[rn/view
72+
{:style (style/token-tag-text-container blur? theme)
73+
:accessibility-label :tag-container}
74+
[text/text
75+
{:size :label
76+
:weight :medium
77+
:style (style/token-tag-text blur? theme)}
78+
(str (:value token-props) " " (:symbol token-props))]])
79+
80+
(defn- options-button
81+
[{:keys [on-options-press blur? theme]}]
82+
[rn/pressable
83+
{:accessibility-label :options-button
84+
:on-press #(when on-options-press
85+
(on-options-press))}
86+
[icon/icon :i/options
87+
{:color (if blur?
88+
colors/white-opa-70
89+
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]])
90+
91+
(defn- check-icon
92+
[{:keys [blur? customization-color theme]}]
93+
[rn/view {:accessibility-label :check-icon}
94+
[icon/icon :i/check
95+
{:color (if blur?
96+
colors/white
97+
(colors/theme-colors (colors/custom-color customization-color 50)
98+
(colors/custom-color customization-color 60)
99+
theme))}]])
100+
101+
(defn- f-internal-view
102+
[]
103+
(let [state (reagent/atom :default)
104+
active-or-selected? (atom false)
105+
timer (atom nil)
106+
on-press-in (fn []
107+
(when-not (= @state :selected)
108+
(reset! timer (js/setTimeout #(reset! state :pressed) 100))))]
109+
(fn [{:keys [type selectable? blur? customization-color on-press]
110+
:or {customization-color :blue
111+
type :default
112+
blur? false}
113+
:as props}]
114+
(let [on-press-out (fn []
115+
(let [new-state (if @active-or-selected?
116+
:default
117+
(if (and (= type :default) selectable?)
118+
:selected
119+
:active))]
120+
(when @timer (js/clearTimeout @timer))
121+
(reset! timer nil)
122+
(reset! active-or-selected? (or (= new-state :active)
123+
(= new-state :selected)))
124+
(reset! state new-state)
125+
(when on-press
126+
(on-press))))]
127+
(rn/use-effect
128+
#(cond (and selectable? (= type :default) (= @state :active)) (reset! state :selected)
129+
(and (not selectable?) (= type :default) (= @state :selected)) (reset! state :active))
130+
[selectable?])
131+
[rn/pressable
132+
{:style (style/container
133+
{:state @state :blur? blur? :customization-color customization-color})
134+
:on-press-in on-press-in
135+
:on-press-out on-press-out
136+
:accessibility-label :container}
137+
[account-view props]
138+
[rn/view {:style (when (= type :tag) style/token-tag-container)}
139+
(when (or (= type :balance-neutral)
140+
(= type :balance-negative)
141+
(= type :balance-positive))
142+
[balance-view props])
143+
(when (= type :tag)
144+
[token-tag props])
145+
(when (= type :action)
146+
[options-button props])
147+
(when (and (= type :default)
148+
(= @state :selected))
149+
[check-icon props])]]))))
150+
151+
(defn- internal-view
152+
[props]
153+
[:f> f-internal-view props])
154+
155+
(def view (quo.theme/with-theme internal-view))

src/quo2/core.cljs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
quo2.components.links.link-preview.view
6565
quo2.components.links.url-preview-list.view
6666
quo2.components.links.url-preview.view
67+
quo2.components.list-items.account.view
6768
quo2.components.list-items.account-list-card.view
6869
quo2.components.list-items.channel.view
6970
quo2.components.list-items.community.view
@@ -255,6 +256,7 @@
255256
(def url-preview-list quo2.components.links.url-preview-list.view/view)
256257

257258
;;;; List items
259+
(def account-item quo2.components.list-items.account.view/view)
258260
(def account-list-card quo2.components.list-items.account-list-card.view/view)
259261
(def channel quo2.components.list-items.channel.view/view)
260262
(def dapp quo2.components.list-items.dapp.view/view)

src/quo2/core_spec.cljs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
[quo2.components.links.link-preview.component-spec]
3838
[quo2.components.links.url-preview-list.component-spec]
3939
[quo2.components.links.url-preview.component-spec]
40+
[quo2.components.list-items.account.component-spec]
4041
[quo2.components.list-items.channel.component-spec]
4142
[quo2.components.list-items.community.component-spec]
4243
[quo2.components.list-items.dapp.component-spec]

0 commit comments

Comments
 (0)