Quo2 Wallet: Settings Item (#17179)

* feat quo2: settings-item
This commit is contained in:
Omar Basem 2023-09-07 15:36:28 +04:00 committed by GitHub
parent c1c8c210e8
commit 36c87d3857
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 399 additions and 233 deletions

View File

@ -85,14 +85,14 @@
56 0
40 (if border-color 8 9)
32 (if border-color 4 5)
24 (if border-color 0 1)
24 0
(if border-color 8 9)))
:padding-bottom (when-not (or icon-only? icon-left icon-right)
(case size
56 0
40 9
32 5
24 4
24 0
9))
:overflow :hidden
:background-color background-color

View File

@ -1,7 +1,7 @@
(ns quo2.components.settings.category.settings.view
(:require
[quo2.components.markdown.text :as text]
[quo2.components.settings.settings-list.view :as settings-list]
[quo2.components.settings.settings-item.view :as settings-item]
[quo2.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]
@ -21,7 +21,7 @@
[rn/flat-list
{:data data
:style (style/settings-items theme blur?)
:render-fn (fn [item] [settings-list/settings-list item])
:render-fn (fn [item] [settings-item/view item])
:separator [rn/view {:style (style/settings-separator theme blur?)}]}]])
(def settings-category (quo.theme/with-theme category-internal))

View File

@ -0,0 +1,57 @@
(ns quo2.components.settings.settings-item.component-spec
(:require [quo2.components.settings.settings-item.view :as settings-item]
[test-helpers.component :as h]))
(def props
{:title "Account"
:accessibility-label :settings-item
:action :arrow
:image :icon
:image-props :i/browser})
(h/describe "Settings list tests"
(h/test "Default render of Setting list component"
(h/render [settings-item/view props])
(h/is-truthy (h/get-by-label-text :settings-item)))
(h/test "It renders a title"
(h/render [settings-item/view props])
(h/is-truthy (h/get-by-text "Account")))
(h/test "its gets passed an on press event"
(let [event (h/mock-fn)]
(h/render [settings-item/view
(merge props {:on-press event})])
(h/fire-event :press (h/get-by-text "Account"))
(h/was-called event)))
(h/test "on change event gets fired for toggle"
(let [on-change (h/mock-fn)]
(h/render [settings-item/view
(merge props
{:action :selector
:action-props {:on-change on-change}})])
(h/fire-event :press (h/get-by-label-text :toggle-off))
(h/was-called on-change)))
(h/test "It renders a label"
(h/render [settings-item/view (merge props {:label :color})])
(h/is-truthy (h/get-by-label-text :label-component)))
(h/test "It renders a status tag component"
(h/render [settings-item/view
(merge props
{:tag :context
:tag-props {:context "Test Tag"
:icon :i/placeholder}})])
(h/is-truthy (h/get-by-text "Test Tag")))
(h/test "on press event gets fired for button"
(let [event (h/mock-fn)]
(h/render [settings-item/view
(merge props
{:action :button
:action-props {:button-text "test button"
:on-press event}})])
(h/fire-event :press (h/get-by-text "test button"))
(h/was-called event))))

View File

@ -0,0 +1,57 @@
(ns quo2.components.settings.settings-item.style
(:require [quo2.foundations.colors :as colors]))
(defn find-icon-height
[description tag image]
(let [icon-height (if (= image :icon-avatar) 32 20)
icon-height (if description 40 icon-height)]
(if tag 72 icon-height)))
(defn container
[{:keys [in-card? tag]}]
{:padding-horizontal 12
:padding-vertical (if in-card? 12 13)
:flex-direction :row
:justify-content :space-between
:height (if tag 96 48)})
(def sub-container
{:flex-direction :row
:align-items :center})
(def left-container
{:margin-left 12
:height "100%"
:justify-content :center})
(defn image-container
[description tag image]
{:height (find-icon-height description tag image)
:justify-content :flex-start})
(def status-container
{:flex-direction :row
:align-items :center})
(defn status-dot
[online? theme]
{:width 8
:height 8
:border-radius 8
:margin-right 6
:background-color (if online?
(colors/theme-colors colors/success-50 colors/success-60 theme)
(colors/theme-colors colors/danger-50 colors/danger-60 theme))})
(defn color
[blur? theme]
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
(defn label-dot
[background-color]
{:width 15
:height 15
:border-radius 12
:background-color background-color})

View File

@ -0,0 +1,117 @@
(ns quo2.components.settings.settings-item.view
(:require
[quo2.components.icon :as icon]
[quo2.components.list-items.preview-list.view :as preview-list]
[quo2.components.selectors.selectors.view :as selectors]
[quo2.components.buttons.button.view :as button]
[quo2.components.markdown.text :as text]
[quo2.theme :as quo.theme]
[react-native.core :as rn]
[quo2.components.avatars.icon-avatar :as icon-avatar]
[quo2.components.tags.status-tags :as status-tags]
[quo2.components.tags.context-tag.view :as context-tag]
[quo2.components.settings.settings-item.style :as style]
[quo2.components.avatars.user-avatar.view :as user-avatar]
[utils.i18n :as i18n]))
(defn status-description
[{:keys [description-props blur? theme]}]
(let [{:keys [online? text]} description-props]
[rn/view {:style style/status-container}
[rn/view {:style (style/status-dot online? blur?)}]
[text/text
{:size :paragraph-2
:style (style/color blur? theme)}
(if online? (i18n/label :t/online-now) text)]]))
(defn text-description
[{:keys [description-props blur? theme]}]
(let [{:keys [text icon]} description-props]
[rn/view
{:style style/sub-container}
[text/text
{:size :paragraph-2
:style (style/color blur? theme)}
text]
(when icon
[icon/icon icon
(merge (style/color blur? theme)
{:size 16
:container-style {:margin-left 4}})])]))
(defn description-component
[{:keys [description] :as props}]
(case description
:text [text-description props]
:text-plus-icon [text-description props]
:status [status-description props]
nil))
(defn image-component
[{:keys [image image-props description tag blur? theme]}]
[rn/view
{:style (style/image-container description tag image)}
(case image
:icon [icon/icon image-props (style/color blur? theme)]
:avatar [user-avatar/user-avatar image-props]
:icon-avatar [icon-avatar/icon-avatar image-props]
nil)])
(defn tag-component
[{:keys [tag tag-props]}]
(case tag
:positive [status-tags/status-tag
{:status {:type :positive}
:label (i18n/label :t/positive)
:size :small
:container-style {:margin-top 8}}]
:context [context-tag/view
(merge tag-props
{:type :icon
:size 24
:container-style {:margin-top 8
:align-self :flex-start}})]
nil))
(defn label-component
[{:keys [label label-props blur? theme]}]
[rn/view {:accessibility-label :label-component}
(case label
:text [text/text
{:style (style/color blur? theme)}
label-props]
:color [rn/view
{:style (style/label-dot label-props)}]
:preview [preview-list/view {:type (:type label-props)} (:data label-props)]
nil)])
(defn action-component
[{:keys [action action-props blur? theme]}]
[rn/view {:style {:margin-left 12}}
(case action
:arrow [icon/icon :i/chevron-right (style/color blur? theme)]
:button [button/button
{:type :outline
:size 24
:on-press (:on-press action-props)}
(:button-text action-props)]
:selector [selectors/toggle action-props]
nil)])
(defn- internal-view
[{:keys [title on-press accessibility-label] :as props}]
[rn/pressable
{:style (style/container props)
:on-press on-press
:accessibility-label accessibility-label}
[rn/view {:style style/sub-container}
[image-component props]
[rn/view {:style style/left-container}
[text/text {:weight :medium} title]
[description-component props]
[tag-component props]]]
[rn/view {:style style/sub-container}
[label-component props]
[action-component props]]])
(def view (quo.theme/with-theme internal-view))

View File

@ -1,58 +0,0 @@
(ns quo2.components.settings.settings-list.component-spec
(:require [quo2.components.settings.settings-list.view :as settings-list]
[test-helpers.component :as h]))
(h/describe "Settings list tests"
(h/test "Default render of Setting list component"
(h/render [settings-list/settings-list {:accessibility-label "test"}])
(h/is-truthy (h/get-by-label-text :test)))
(h/test "It renders a title"
(h/render [settings-list/settings-list {:title "test"}])
(h/is-truthy (h/get-by-text "test")))
(h/test "its gets passed an on press event"
(let [event (h/mock-fn)]
(h/render [settings-list/settings-list
{:title "test"
:on-press event}])
(h/fire-event :press (h/get-by-text "test"))
(h/was-called event)))
(h/test "on change event gets fired for toggle"
(let [on-change (h/mock-fn)]
(h/render [settings-list/settings-list
{:title "test"
:toggle-props {:on-change on-change}}])
(h/fire-event :press (h/get-by-label-text :toggle-off))
(h/was-called on-change)))
(h/test "It renders a badge"
(h/render [settings-list/settings-list {:badge? true}])
(h/is-truthy (h/get-by-label-text :setting-list-badge)))
(h/test "It renders a status tag component"
(h/render [settings-list/settings-list
{:status-tag-props
{:size :small
:status {:type :positive}
:label "test tag"}}])
(h/is-truthy (h/get-by-text "test tag")))
(h/test "on press event gets fired for button"
(let [event (h/mock-fn)]
(h/render [settings-list/settings-list
{:button-props {:title "test button"
:on-press event}}])
(h/fire-event :press (h/get-by-text "test button"))
(h/was-called event)))
(h/test "It renders a list of community icons"
(h/render [settings-list/settings-list
{:communities-props {:data
[{:source "1"
:accessibility-label :community-1}
{:source "2"
:accessibility-label :community-2}]}}])
(h/is-truthy (h/get-by-label-text :community-1))
(h/is-truthy (h/get-by-label-text :community-2))))

View File

@ -1,135 +0,0 @@
(ns quo2.components.settings.settings-list.view
(:require [quo2.components.settings.settings-list.style :as style]
[quo2.components.icon :as icons]
[quo2.components.selectors.selectors.view :as selectors]
[quo2.components.buttons.button.view :as button]
[quo2.components.markdown.text :as text]
[quo2.components.tags.status-tags :as status-tag]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn settings-title
[title status-tag-props override-theme]
[rn/view
{:style style/title-container}
(when title
[text/text
{:accessibility-label :setting-item-name-text
:ellipsize-mode :tail
:style (style/title override-theme)
:override-theme override-theme
:number-of-lines 1
:weight :medium
:size :paragraph-1}
title])
(when status-tag-props
[rn/view {:style style/tag-container}
[status-tag/status-tag
status-tag-props]])])
(defn left-icon-comp
[icon]
[rn/view {:style style/icon}
[icons/icon icon
{:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)}]])
(def chevron-icon
[rn/view
[icons/icon :chevron-right
{:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)}]])
(defn toggle-button
[{:keys [checked?
on-change]}]
[selectors/toggle
{:checked? checked?
:on-change (fn [new-value] (on-change new-value))}])
(defn badge-icon
[override-theme]
[rn/view
{:accessible :true
:accessibility-label :setting-list-badge
:style (style/dot override-theme)}])
(defn right-button
[{:keys [title
on-press]}]
[button/button
{:type :outline
:on-press on-press
:size 24}
title])
(defn communities-icons
[{:keys [data
icon-style]}
override-theme]
(let [communities-count (dec (count data))]
[rn/view
{:style style/communities-container}
(map-indexed
(fn [index {:keys [source accessibility-label]}]
[rn/image
{:key source
:source (if (string? source)
{:uri source}
source)
:accessibility-label accessibility-label
:style (merge (style/community-icon (- communities-count index) override-theme)
icon-style)}])
data)]))
(defn settings-list
"Options
- `title` String to show in the center of the component, right to the icon and left to optional gadgets.
- `on-press` Callback called when the component is pressed.
- `accessibility-label` String to use as accessibility-label for VoiceOver.
- `left-icon` icon keyword for icon on left.
- `chevron?` Boolean to show/hide chevron at the right border of the component.
- `toggle-prop` Map with the following keys:
`checked?` Boolean value to set check or unchecked toggle.
`on-change` Callback called when user toggles toggle. Will pass the new toggle value to the callback
- `badge?` Boolean to show/hide badge.
- `button-props` Map with the following keys:
`title` String to show as button text.
`on-press` Callback called when button is pressed.
- `communities-props` Map with the following keys:
`data` Array of maps containg source of the community asset.
- `style` Styles map to be merge with default container styles.
- `overide-theme` :dark or :light
- `status-tag-props see the spec for status-tag component
"
[{:keys [title
on-press
accessibility-label
left-icon
chevron?
toggle-props
badge?
button-props
communities-props
container-style
override-theme
status-tag-props]}]
[rn/touchable-without-feedback
{:on-press on-press
:accessibility-label accessibility-label}
[rn/view
{:style (merge style/item-container container-style)}
[rn/view {:style style/inner-container}
(when left-icon
[left-icon-comp left-icon])
[settings-title title status-tag-props override-theme]
(when toggle-props
[toggle-button toggle-props])
(when badge? [badge-icon override-theme])
(when button-props
[right-button button-props])
(when communities-props (communities-icons communities-props override-theme))
(when chevron? chevron-icon)]]])

View File

@ -73,18 +73,19 @@
(defn- view-internal
[{:keys [theme type size state blur? customization-color profile-picture full-name users
group-name token-logo amount token-name network-logo network-name networks
account-name emoji collectible collectible-name collectible-number duration]
account-name emoji collectible collectible-name collectible-number duration container-style]
:or {customization-color :blue
type :default
state :default}
:as props}]
[rn/view
{:style (style/container {:theme theme
{:style (merge (style/container {:theme theme
:type type
:size size
:state state
:blur? blur?
:customization-color customization-color})}
:customization-color customization-color})
container-style)}
(case type
:default
[tag-skeleton {:theme theme :size size :text full-name}

View File

@ -30,18 +30,21 @@
icon
text-color
label
accessibility-label]}]
accessibility-label
container-style]}]
(let [paragraph-size (if (= size :small) :paragraph-2 :paragraph-1)]
[rn/view
{:accessible true
:accessibility-label accessibility-label
:style (assoc (if (= size :small)
:style (merge
(assoc (if (= size :small)
small-container-style
large-container-style)
:align-self :flex-start
:border-width 1
:border-color border-color
:background-color background-color)}
:background-color background-color)
container-style)}
[rn/view
{:flex-direction :row
:align-items :center
@ -61,9 +64,10 @@
:color text-color}} label]]])))
(defn- positive
[size theme label _ no-icon?]
[size theme label _ no-icon? container-style]
[base-tag
{:accessibility-label :status-tag-positive
:container-style container-style
:size size
:icon (when-not no-icon? :i/positive-state)
:background-color colors/success-50-opa-10
@ -73,9 +77,10 @@
:text-color (if (= theme :dark) colors/success-60 colors/success-50)}])
(defn- negative
[size theme label _ no-icon?]
[size theme label _ no-icon? container-style]
[base-tag
{:accessibility-label :status-tag-negative
:container-style container-style
:size size
:icon (when-not no-icon? :i/negative-state)
:background-color colors/danger-50-opa-10
@ -85,9 +90,10 @@
:text-color (colors/theme-colors colors/danger-50 colors/danger-60 theme)}])
(defn- pending
[size theme label blur? no-icon?]
[size theme label blur? no-icon? container-style]
[base-tag
{:accessibility-label :status-tag-pending
:container-style container-style
:size size
:label label
:icon (when-not no-icon?
@ -106,7 +112,7 @@
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}])
(defn- status-tag-internal
[{:keys [status size theme label blur? no-icon?]}]
[{:keys [status size theme label blur? no-icon? container-style]}]
(when status
(when-let [status-component (case (:type status)
:positive positive
@ -118,6 +124,7 @@
theme
label
blur?
no-icon?])))
no-icon?
container-style])))
(def status-tag (quo.theme/with-theme status-tag-internal))

View File

@ -101,7 +101,7 @@
quo2.components.settings.data-item.view
quo2.components.settings.privacy-option
quo2.components.settings.reorder-item.view
quo2.components.settings.settings-list.view
quo2.components.settings.settings-item.view
quo2.components.settings.category.view
quo2.components.share.qr-code.view
quo2.components.share.share-qr-code.view
@ -305,7 +305,7 @@
;;;; Settings
(def privacy-option quo2.components.settings.privacy-option/card)
(def account quo2.components.settings.accounts.view/account)
(def settings-list quo2.components.settings.settings-list.view/settings-list)
(def settings-item quo2.components.settings.settings-item.view/view)
(def reorder-item quo2.components.settings.reorder-item.view/reorder-item)
(def category quo2.components.settings.category.view/category)
(def data-item quo2.components.settings.data-item.view/view)

View File

@ -56,7 +56,7 @@
[quo2.components.selectors.reactions.component-spec]
[quo2.components.selectors.selectors.component-spec]
[quo2.components.settings.reorder-item.component-spec]
[quo2.components.settings.settings-list.component-spec]
[quo2.components.settings.settings-item.component-spec]
[quo2.components.settings.category.component-spec]
[quo2.components.settings.data-item.component-spec]
[quo2.components.share.share-qr-code.component-spec]

View File

@ -66,6 +66,7 @@
[status-im2.contexts.quo-preview.list-items.channel :as channel]
[status-im2.contexts.quo-preview.list-items.dapp :as dapp]
[status-im2.contexts.quo-preview.list-items.preview-lists :as preview-lists]
[status-im2.contexts.quo-preview.list-items.token-value :as token-value]
[status-im2.contexts.quo-preview.list-items.user-list :as user-list]
[status-im2.contexts.quo-preview.list-items.community-list :as community-list]
[status-im2.contexts.quo-preview.markdown.text :as text]
@ -94,7 +95,7 @@
[status-im2.contexts.quo-preview.selectors.selectors :as selectors]
[status-im2.contexts.quo-preview.settings.accounts :as accounts]
[status-im2.contexts.quo-preview.settings.data-item :as data-item]
[status-im2.contexts.quo-preview.settings.settings-list :as settings-list]
[status-im2.contexts.quo-preview.settings.settings-item :as settings-item]
[status-im2.contexts.quo-preview.settings.privacy-option :as privacy-option]
[status-im2.contexts.quo-preview.settings.reorder-item :as reorder-item]
[status-im2.contexts.quo-preview.settings.category :as category]
@ -266,6 +267,8 @@
:component dapp/preview}
{:name :preview-lists
:component preview-lists/view}
{:name :token-value
:component token-value/preview}
{:name :user-list
:options {:topBar {:visible true}}
:component user-list/preview-user-list}]
@ -328,8 +331,8 @@
:component privacy-option/preview-options}
{:name :accounts
:component accounts/preview-accounts}
{:name :settings-list
:component settings-list/preview-settings-list}
{:name :settings-item
:component settings-item/preview}
{:name :reorder-item
:component reorder-item/preview-reorder-item}
{:name :category

View File

@ -10,15 +10,16 @@
[status-im2.contexts.quo-preview.preview :as preview]))
(defn create-item-array
[n {:keys [right-icon? image? subtitle?]}]
(vec (for [i (range n)]
[n {:keys [right-icon? image? subtitle? list-type]}]
(vec
(for [i (range n)]
{:title (str "Item " i)
:subtitle (when subtitle? "subtitle")
:chevron? true
:action :arrow
:right-icon (when right-icon? :i/globe)
:left-icon :i/browser
:image-size (if image? 32 0)
:image (when image? (resources/get-mock-image :diamond))})))
:image (if (= list-type :settings) :icon (when image? (resources/get-mock-image :diamond)))
:image-props :i/browser
:image-size (if image? 32 0)})))
(def reorder-descriptor
[{:label "Right icon:"

View File

@ -0,0 +1,116 @@
(ns status-im2.contexts.quo-preview.settings.settings-item
(:require
[quo2.core :as quo]
[react-native.core :as rn]
[status-im2.common.resources :as resources]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:key :title
:type :text}
{:key :action
:type :select
:options [{:key nil
:value :none}
{:key :arrow
:value :arrow}
{:key :button
:value :button}
{:key :selector
:value :selector}]}
{:key :label
:type :select
:options [{:key nil
:value :none}
{:key :color
:value :color}
{:key :preview
:value :preview}
{:key :text
:value :text}]}
{:key :image
:type :select
:options [{:key nil
:value :none}
{:key :icon
:value :icon}
{:key :avatar
:value :avatar}
{:key :icon-avatar
:value :icon-avatar}]}
{:key :description
:type :select
:options [{:key nil
:value :none}
{:key :text
:value :text}
{:key :text-plus-icon
:value :text-plus-icon}
{:key :status
:value :status}]}
{:key :tag
:type :select
:options [{:key nil
:value :none}
{:key :positive
:value :positive}
{:key :context
:value :context}]}])
(def communities-list
[{:source (resources/get-mock-image :coinbase)}
{:source (resources/get-mock-image :decentraland)}
{:source (resources/get-mock-image :rarible)}])
(defn get-props
[data]
(when (:toggle-props data) (js/console.warn data))
(merge
data
{:image-props (case (:image data)
:icon :i/browser
:avatar {:full-name "A Y"
:size :xxs
:customization-color :blue}
:icon-avatar {:size :medium
:icon :i/placeholder
:color :blue}
nil)
:description-props (case (:description data)
:text {:text "This is a description"}
:text-plus-icon {:text "This is a description"
:icon :i/placeholder}
:status {:online? true}
nil)
:action-props (case (:action data)
:button {:on-press #(js/alert "Button pressed!")
:button-text "Button"}
nil)
:label-props (case (:label data)
:text "Label"
:color :blue
:preview {:type :communities
:data communities-list}
nil)
:tag-props (case (:tag data)
:context {:icon :i/placeholder
:context "Context"}
nil)}))
(defn preview
[]
(let [state (reagent/atom {:title "Account"
:accessibility-label :settings-item
:action :arrow
:image :icon
:blur? false
:on-press (fn [] (js/alert "Settings list item pressed"))})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:blur? (:blur? @state)
:show-blur-background? true}
[rn/view {:style {:flex 1}}
[quo/settings-item (get-props @state)]]])))

View File

@ -12,7 +12,7 @@
show-button?]}]
(let [paired? (and (not this-device?) enabled?)
unpaired? (not enabled?)]
[quo/settings-list
[quo/settings-item
(cond->
{:container-style style/device-container
:title name