[#18083] Implement Page top component (#18163)

* Add container style and fix `:auto-complete` parameter in address-input

* Add container style to recovery-phrase input

* Add container style and accessibility-label to search-input

* Add page-top component and tests

* Add page top preview
This commit is contained in:
Ulises Manuel 2023-12-20 13:17:39 -06:00 committed by GitHub
parent 04184b41e5
commit 7cf31651b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 553 additions and 28 deletions

View File

@ -4,13 +4,15 @@
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(def container
{:padding-horizontal 20
:padding-top 8
:padding-bottom 16
:height 48
:flex-direction :row
:align-items :flex-start})
(defn container
[container-style]
(merge {:padding-horizontal 20
:padding-top 8
:padding-bottom 16
:height 48
:flex-direction :row
:align-items :flex-start}
container-style))
(def buttons-container
{:flex-direction :row

View File

@ -60,8 +60,9 @@
(let [status (reagent/atom :default)
value (reagent/atom "")
focused? (atom false)]
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan on-detect-ens
on-detect-address address-regex valid-ens-or-address?]}]
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan
on-detect-ens on-detect-address address-regex valid-ens-or-address?
container-style]}]
(let [on-change (fn [text]
(when (not= @value text)
(let [address? (when address-regex
@ -108,14 +109,14 @@
(when-not (empty? scanned-value)
(on-change scanned-value)))
[scanned-value])
[rn/view {:style style/container}
[rn/view {:style (style/container container-style)}
[rn/text-input
{:accessibility-label :address-text-input
:style (style/input-text theme)
:placeholder (i18n/label :t/name-ens-or-address)
:placeholder-text-color placeholder-text-color
:default-value @value
:auto-complete (when platform/ios? :none)
:auto-complete (when platform/ios? :off)
:auto-capitalize :none
:auto-correct false
:spell-check false

View File

@ -3,11 +3,13 @@
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]))
(def container
{:min-height 40
:flex 1
:padding-vertical 4
:padding-horizontal 20})
(defn container
[container-style]
(merge {:min-height 40
:flex 1
:padding-vertical 4
:padding-horizontal 20}
container-style))
(defn input
[]

View File

@ -44,7 +44,8 @@
set-focused #(reset! state :focused)
set-default #(reset! state :default)]
(fn [{:keys [customization-color theme blur? on-focus on-blur mark-errors?
error-pred-current-word error-pred-written-words word-limit]
error-pred-current-word error-pred-written-words word-limit
container-style]
:or {customization-color :blue
word-limit ##Inf
error-pred-current-word (constantly false)
@ -52,7 +53,7 @@
:as props}
text]
(let [extra-props (apply dissoc props custom-props)]
[rn/view {:style style/container}
[rn/view {:style (style/container container-style)}
[rn/text-input
(merge {:accessibility-label :recovery-phrase-input
:style (style/input)

View File

@ -43,10 +43,12 @@
{:flex-direction :row
:margin-right 8})
(def container
{:flex 1
:flex-direction :row
:align-items :center})
(defn container
[container-style]
(merge {:flex 1
:flex-direction :row
:align-items :center}
container-style))
(def scroll-container
{:flex-direction :row

View File

@ -48,12 +48,14 @@
scroll-view-ref (atom nil)
use-value? (boolean value)]
(fn [{:keys [value tags disabled? blur? on-change-text customization-color
on-clear on-focus on-blur override-theme]
on-clear on-focus on-blur override-theme container-style]
:or {customization-color :blue}
:as props}
& children]
(let [clean-props (apply dissoc props props-to-remove)]
[rn/view {:style style/container}
[rn/view
{:accessibility-label :search-input
:style (style/container container-style)}
[rn/scroll-view
{:ref #(reset! scroll-view-ref %)
:style style/scroll-container

View File

@ -0,0 +1,122 @@
(ns quo.components.text-combinations.page-top.component-spec
(:require [quo.components.text-combinations.page-top.view :as page-top]
[test-helpers.component :as h]))
(defonce mock-picture {:uri (js/require "../resources/images/mock2/user_picture_male4.png")})
(def context-tag-data
{:type :community
:state :default
:customization-color :army
:community-logo mock-picture
:community-name "Coinbase"
:emoji "😝"})
(h/describe "Page Top"
(h/test "Default render"
(h/render [page-top/view {:title "Title"}])
(h/is-truthy (h/get-by-text "Title")))
(h/test "Avatar and Title"
(h/render [page-top/view
{:title "Title"
:avatar {:emoji "🥨"
:customization-color :army}}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :channel-avatar)))
(h/describe "Description"
(h/test "Text"
(h/render [page-top/view
{:title "Title"
:description :text
:description-text "This is a textual description"}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "This is a textual description")))
(h/test "Context tag"
(h/render [page-top/view
{:title "Title"
:description :context-tag
:context-tag context-tag-data}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :context-tag)))
(h/test "Summary"
(h/render [page-top/view
{:title "Title"
:description :summary
:summary {:row-1 {:text-1 "Send"
:text-2 "from"
:context-tag-1 context-tag-data
:context-tag-2 context-tag-data}
:row-2 {:text-1 "to"
:text-2 "via"
:context-tag-1 context-tag-data
:context-tag-2 context-tag-data}}}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Send"))
(h/is-truthy (h/get-by-text "from"))
(h/is-truthy (h/get-by-text "to"))
(h/is-truthy (h/get-by-text "via"))
(h/is-equal (count (js->clj (h/get-all-by-label-text :context-tag))) 4))
(h/test "Collection"
(h/render [page-top/view
{:title "Title"
:description :collection
:collection-text "Collectible Collection"
:collection-image mock-picture}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Collectible Collection"))
(h/is-truthy (h/get-by-label-text :collection-avatar)))
(h/test "Community"
(h/render [page-top/view
{:title "Title"
:description :community
:community-text "Doodles"
:community-image mock-picture}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Doodles"))
(h/is-truthy (h/get-by-label-text :community-logo))))
(h/test "Emoji dash"
(let [emoji-dash ["❤️" "✏️" "💬" "😋" "📱" "🚓" "💹" "😝" "👊" "👤" "😚" "🚉" "👻" "\uD83D\uDC6F"]]
(h/render [page-top/view
{:title "Title"
:emoji-dash emoji-dash}])
(doseq [emoji emoji-dash]
(h/is-truthy (h/get-by-text emoji)))
(h/is-truthy (h/get-by-text "Title"))))
(h/test "Search input"
(h/render [page-top/view
{:title "Title"
:input :search}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :search-input)))
(h/test "Address input"
(h/render [page-top/view
{:title "Title"
:input :address}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :address-text-input)))
(h/test "Address input"
(h/render [page-top/view
{:title "Title"
:input :recovery-phrase}])
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :recovery-phrase-input))))

View File

@ -0,0 +1,79 @@
(ns quo.components.text-combinations.page-top.style
(:require [quo.foundations.colors :as colors]))
(def top-container
{:padding-vertical 12
:padding-horizontal 20
:row-gap 8})
(def header
{:flex-direction :row
:justify-content :space-between
:height 32})
(def header-title
{:flex-direction :row
:column-gap 8})
(def header-counter
{:margin-left 20
:margin-bottom 2
:justify-content :flex-end})
(def context-tag-description
{:align-items :flex-start})
(def summary-description
{:row-gap 4})
(def summary-description-row
{:flex-direction :row
:align-items :center
:column-gap 4})
(def image-text-description
{:flex-direction :row
:column-gap 8
:align-items :center})
(def community-logo
{:width 24
:height 24
:border-radius 12})
(def community-logo-ring
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:border-radius 12
:border-width 1
:border-color colors/neutral-80-opa-5})
(def emoji-dash
{:flex-direction :row})
(def emoji
{:width 20
:height 20})
(def header-counter-text {:color colors/neutral-40})
(defn input-container
[theme input blur?]
(when-not (= input :recovery-phrase)
{:border-bottom-width 1
:border-color (if blur?
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme)
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))}))
(def search-input-container
{:margin-top 8
:margin-bottom 12
:margin-horizontal 20})
(def recovery-phrase-container
{:padding-vertical nil
:padding-top 4
:padding-bottom 12})

View File

@ -0,0 +1,144 @@
(ns quo.components.text-combinations.page-top.view
(:require [clojure.string :as string]
[quo.components.avatars.channel-avatar.view :as channel-avatar]
[quo.components.avatars.collection-avatar.view :as collection-avatar]
[quo.components.inputs.address-input.view :as address-input]
[quo.components.inputs.recovery-phrase.view :as recovery-phrase]
[quo.components.inputs.search-input.view :as search-input]
[quo.components.markdown.text :as text]
[quo.components.tags.context-tag.view :as context-tag]
[quo.components.text-combinations.page-top.style :as style]
[quo.theme]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[utils.number]))
(defn- format-counter
[n]
(let [num (utils.number/parse-int n)]
(if (<= num 9)
(str "0" num)
(str num))))
(defn- header-counter
[counter-top counter-bottom]
[rn/view {:style style/header-counter}
[text/text
{:style style/header-counter-text
:weight :regular
:size :paragraph-2}
(str (format-counter counter-top)
"/"
(format-counter counter-bottom))]])
(defn- header
[{:keys [title input counter-top counter-bottom]
avatar-props :avatar}]
[rn/view {:style style/header}
[rn/view {:style style/header-title}
(when avatar-props
[channel-avatar/view (assoc avatar-props :size :size-32)])
[text/text
{:weight :semi-bold
:size :heading-1}
title]]
(when (= input :recovery-phrase)
[header-counter counter-top counter-bottom])])
(defn- summary-description
[{:keys [row-1 row-2] :as _summary-props} blur?]
(let [text-props {:size :paragraph-2
:weight :medium}]
[rn/view {:style style/summary-description}
(when-let [{:keys [text-1 text-2 context-tag-1 context-tag-2]} row-1]
[rn/view {:style style/summary-description-row}
[text/text text-props text-1]
[context-tag/view (assoc context-tag-1 :size 24 :blur? blur?)]
[text/text text-props text-2]
[context-tag/view (assoc context-tag-2 :size 24 :blur? blur?)]])
(when-let [{:keys [text-1 text-2 context-tag-1 context-tag-2]} row-2]
[rn/view {:style style/summary-description-row}
[text/text text-props text-1]
[context-tag/view (assoc context-tag-1 :size 24 :blur? blur?)]
[text/text text-props text-2]
[context-tag/view (assoc context-tag-2 :size 24 :blur? blur?)]])]))
(defn- community-logo
[image]
[rn/view {:accessibility-label :community-logo}
[fast-image/fast-image
{:source image
:style style/community-logo}]
[rn/view {:style style/community-logo-ring}]])
(defn- description-container
[{:keys [description description-text collection-text community-text
collection-image community-image blur?]
context-tag-props :context-tag
summary-props :summary}]
[rn/view
(cond
(and (= description :text) (not (string/blank? description-text)))
[text/text
{:weight :regular
:size :paragraph-1}
description-text]
(and (= description :context-tag) context-tag-props)
[rn/view {:style style/context-tag-description}
[context-tag/view (assoc context-tag-props :size 24 :blur? blur?)]]
(and (= description :summary) summary-props)
[summary-description summary-props blur?]
(= description :collection)
[rn/view {:style style/image-text-description}
[collection-avatar/view {:image collection-image}]
[text/text {:weight :semi-bold :size :paragraph-1}
collection-text]]
(= description :community)
[rn/view {:style style/image-text-description}
[community-logo community-image]
[text/text {:weight :semi-bold :size :paragraph-1}
community-text]])])
(defn- emoji-dash
[emojis]
(into [rn/view {:style style/emoji-dash}]
(map (fn [emoji]
[rn/view {:style style/emoji}
[rn/text {:adjusts-font-size-to-fit true} emoji]]))
emojis))
(defn- view-internal
[{:keys [theme description input blur? input-props]
emojis :emoji-dash
:as props}]
[rn/view
[rn/view {:style style/top-container}
[header props]
(when description
[description-container props])
(when emojis
[emoji-dash emojis])]
(when input
[rn/view {:style (style/input-container theme input blur?)}
(case input
:search
[search-input/search-input
(assoc input-props
:container-style style/search-input-container
:blur? blur?)]
:address
[address-input/address-input (assoc input-props :blur? blur?)]
:recovery-phrase
[recovery-phrase/recovery-phrase-input
(assoc input-props
:container-style style/recovery-phrase-container
:blur? blur?)]
nil)])])
(def view (quo.theme/with-theme view-internal))

View File

@ -142,6 +142,7 @@
quo.components.tags.tiny-tag.view
quo.components.tags.token-tag.view
quo.components.text-combinations.channel-name.view
quo.components.text-combinations.page-top.view
quo.components.text-combinations.standard-title.view
quo.components.text-combinations.username.view
quo.components.text-combinations.view
@ -391,6 +392,7 @@
;;;; Text combinations
(def channel-name quo.components.text-combinations.channel-name.view/view)
(def page-top quo.components.text-combinations.page-top.view/view)
(def standard-title quo.components.text-combinations.standard-title.view/view)
(def text-combinations quo.components.text-combinations.view/view)
(def username quo.components.text-combinations.username.view/view)

View File

@ -80,6 +80,7 @@
[quo.components.tags.summary-tag.component-spec]
[quo.components.tags.tiny-tag.component-spec]
[quo.components.text-combinations.channel-name.component-spec]
[quo.components.text-combinations.page-top.component-spec]
[quo.components.text-combinations.username.component-spec]
[quo.components.wallet.account-card.component-spec]
[quo.components.wallet.account-origin.component-spec]

View File

@ -162,10 +162,9 @@
[status-im.contexts.quo-preview.tags.tags :as tags]
[status-im.contexts.quo-preview.tags.tiny-tag :as tiny-tag]
[status-im.contexts.quo-preview.tags.token-tag :as token-tag]
[status-im.contexts.quo-preview.text-combinations.channel-name :as
channel-name]
[status-im.contexts.quo-preview.text-combinations.preview :as
text-combinations]
[status-im.contexts.quo-preview.text-combinations.channel-name :as channel-name]
[status-im.contexts.quo-preview.text-combinations.page-top :as page-top]
[status-im.contexts.quo-preview.text-combinations.preview :as text-combinations]
[status-im.contexts.quo-preview.text-combinations.standard-title :as standard-title]
[status-im.contexts.quo-preview.text-combinations.username :as username]
[status-im.contexts.quo-preview.wallet.account-card :as account-card]
@ -466,6 +465,8 @@
:component text-combinations/view}
{:name :channel-name
:component channel-name/view}
{:name :page-top
:component page-top/view}
{:name :standard-title
:component standard-title/view}
{:name :username

View File

@ -0,0 +1,166 @@
(ns status-im.contexts.quo-preview.text-combinations.page-top
(:require [quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[reagent.core :as reagent]
[status-im.common.resources :as resources]
[status-im.contexts.quo-preview.preview :as preview]))
(def avatar-1
{:emoji "🥨"
:customization-color :army})
(def avatar-2
{:emoji "🍑"
:customization-color :blue})
(def context-tag-1
{:type :community
:state :default
:customization-color :army
:community-logo (resources/mock-images :coinbase)
:community-name "Coinbase"
:emoji "😝"})
(def context-tag-2
{:type :collectible
:state :default
:customization-color :army
:collectible (resources/mock-images :collectible)
:collectible-name "Collectible"
:collectible-number "123"})
(def context-tag-3
{:type :token
:state :default
:token "SNT"
:amount "250,000"})
(def context-tag-4
{:type :account
:blur? false
:state :default
:customization-color :sky
:account-name "Trip to vegas"
:emoji "⚡"})
(def context-tag-5
{:type :default
:customization-color :army
:profile-picture nil
:full-name "Random user"})
(def context-tag-6
{:type :network
:customization-color :army
:network-logo (quo.resources/get-network :optimism)
:network-name "Optimism"})
(def emoji-dash-1
["❤️" "✏️" "💬" "😋" "📱" "🚓" "💹" "😝" "👊" "👤" "😚" "🚉" "👻" "\uD83D\uDC6F"])
(def emoji-dash-2
["🍩" "🤖" "🏀" "🔥" "🌂" "💎" "🚨" "😍" "🐷" "🌶" "🍑" "😈" "🦄" "🕵️‍♀️"])
(def main-descriptor
[{:key :blur?
:type :boolean}
{:key :title
:type :text}
{:key :avatar
:type :select
:options [{:key nil
:value "(No avatar)"}
{:key avatar-1
:value "Avatar variation 1"}
{:key avatar-2
:value "Avatar variation 2"}]}
{:key :description
:type :select
:options [{:key nil
:value "(No description)"}
{:key :text}
{:key :context-tag}
{:key :summary}
{:key :collection}
{:key :community}]}
{:key :emoji-dash
:type :select
:options [{:key nil
:value "(No emoji dash)"}
{:key emoji-dash-1
:value "Emoji dash variation 1"}
{:key emoji-dash-2
:value "Emoji dash variation 2"}]}
{:key :input
:type :select
:options [{:key nil
:value "(No input)"}
{:key :search}
{:key :address}
{:key :recovery-phrase}]}])
(def description-text-descriptor
[{:key :description-text
:type :text}])
(def context-tag-descriptor
[{:key :context-tag
:type :select
:options [{:key context-tag-1
:value "Context tag variation 1"}
{:key context-tag-2
:value "Context tag variation 2"}
{:key context-tag-3
:value "Context tag variation 3"}
{:key context-tag-4
:value "Context tag variation 4"}
{:key context-tag-5
:value "Context tag variation 5"}
{:key context-tag-6
:value "Context tag variation 6"}]}])
(def recovery-phrase-descriptor
[{:key :counter-top
:type :text}
{:key :counter-bottom
:type :text}])
(defn view
[]
(let [state (reagent/atom
{:blur? true
:title "Title"
:description nil
:description-text (str "Share random funny stuff with the community "
"and then something more here.")
:context-tag context-tag-1
:summary {:row-1 {:text-1 "Send"
:text-2 "from"
:context-tag-1 context-tag-3
:context-tag-2 context-tag-4}
:row-2 {:text-1 "to"
:text-2 "via"
:context-tag-1 context-tag-5
:context-tag-2 context-tag-6}}
:emoji-dash nil
:input nil
:collection-text "Collectible Collection"
:collection-image (resources/get-mock-image :collectible-monkey)
:community-image (resources/get-mock-image :community-logo)
:community-text "Doodles"
:input-props {:placeholder "Input placeholder"}
:counter-top "50"
:counter-bottom "100"})]
(fn []
(let [descriptor (concat main-descriptor
(case (:description @state)
:text description-text-descriptor
:context-tag context-tag-descriptor
nil)
(when (= (:input @state) :recovery-phrase)
recovery-phrase-descriptor))]
[preview/preview-container
{:state state
:descriptor descriptor
:show-blur-background? true
:blur? (:blur? @state)
:component-container-style {:padding-horizontal 10}}
[quo/page-top @state]]))))