feat: browser input (#16487)

This commit is contained in:
BalogunofAfrica 2023-07-24 12:28:43 +01:00 committed by GitHub
parent 00f39225ea
commit c5a486622d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 331 additions and 0 deletions

View File

@ -0,0 +1,58 @@
(ns quo2.components.browser.browser-input.component-spec
(:require [quo2.components.browser.browser-input.view :as browser-input]
[test-helpers.component :as h]))
(h/describe "Browser input"
(h/test "Renders empty in default state"
(h/render [browser-input/browser-input
{:on-change-text (h/mock-fn)
:value ""}])
(h/is-truthy (h/get-by-label-text :browser-input)))
(h/test "On change text is fired"
(let [on-change-text (h/mock-fn)]
(h/render [browser-input/browser-input
{:on-change-text on-change-text
:value "mock text"}])
(h/fire-event :change-text (h/get-by-label-text :browser-input) "mock-text-new")
(h/was-called on-change-text)))
(h/describe "Input Label"
(h/test "Doesn't render label text when input is focused"
(h/render [browser-input/browser-input
{:auto-focus true}])
(h/is-null (h/query-by-label-text :browser-input-label)))
(h/test "Renders label text when input has a value and input is not focused"
(h/render [browser-input/browser-input
{:default-value "mock default"}])
(h/is-truthy (h/query-by-label-text :browser-input-label)))
(h/test "Renders site favicon when specified"
(h/render [browser-input/browser-input
{:default-value "mock default" :favicon :i/verified}])
(h/is-truthy (h/query-by-label-text :browser-input-favicon)))
(h/test "Renders lock icon when lock is enabled"
(h/render [browser-input/browser-input
{:default-value "mock default" :locked? true}])
(h/is-truthy (h/query-by-label-text :browser-input-locked-icon))))
(h/describe "Clear button"
(h/test "Doesn't render in default state"
(h/render [browser-input/browser-input])
(h/is-null (h/query-by-label-text :browser-input-clear-button)))
(h/test "Renders when there is a value"
(h/render [browser-input/browser-input
{:default-value "mock text"}])
(h/is-truthy (h/query-by-label-text :browser-input-clear-button)))
(h/test "Is pressable"
(let [on-clear (h/mock-fn)]
(h/render [browser-input/browser-input
{:default-value "mock text"
:on-clear on-clear}])
(h/is-truthy (h/query-by-label-text :browser-input-clear-button))
(h/fire-event :press (h/query-by-label-text :browser-input-clear-button))
(h/was-called on-clear)))))

View File

@ -0,0 +1,56 @@
(ns quo2.components.browser.browser-input.style
(:require [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(def clear-icon-container
{:align-items :center
:height 20
:justify-content :center
:margin-left 8
:top 4
:width 20})
(def favicon-icon-container
{:margin-right 2})
(defn input
[disabled?]
(assoc (text/text-style {:size :paragraph-1
:weight :regular})
:flex 1
:min-height 36
:min-width 120
:opacity (if disabled? 0.3 1)))
(def lock-icon-container
{:margin-left 2})
(defn active-container
[display?]
{:align-items :center
:flex-direction :row
:justify-content :center
:opacity (if display? 1 0)})
(def default-container
{:align-items :center
:bottom 0
:flex-direction :row
:left 0
:padding-horizontal 20
:position :absolute
:right 0
:top 0
:z-index 10})
(defn text
[]
(assoc (text/text-style {:size :paragraph-1
:weight :medium})
:color
(colors/theme-colors colors/neutral-100 colors/white)))
(def root-container
{:height 60
:padding-horizontal 20
:padding-vertical 8})

View File

@ -0,0 +1,144 @@
(ns quo2.components.browser.browser-input.view
(:require [quo2.components.icon :as icon]
[quo2.components.browser.browser-input.style :as style]
[react-native.core :as rn]
[reagent.core :as reagent]
[quo2.foundations.colors :as colors]
[quo2.theme :as quo2.theme]
[clojure.string :as string]
[react-native.platform :as platform]))
(defn remove-http-https-www
[value]
(string/replace value #"(https?://(www\.)?|http://)" ""))
(defn clear-icon-color
[blur? theme]
(if blur?
(colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 theme)
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)))
(defn lock-icon-color
[blur? theme]
(if blur?
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-40 theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(defn- clear-button
[{:keys [on-press blur? theme]}]
[rn/touchable-opacity
{:accessibility-label :browser-input-clear-button
:on-press on-press
:style style/clear-icon-container}
[icon/icon :i/clear
{:color (clear-icon-color blur? theme)}]])
(defn lock-icon
[{:keys [blur? theme]}]
[rn/view
[icon/icon :i/locked
{:accessibility-label :browser-input-locked-icon
:color (lock-icon-color blur? theme)
:container-style style/lock-icon-container
:size 16}]])
(defn cursor-color
[customization-color theme]
(colors/theme-colors (colors/custom-color customization-color 50)
(colors/custom-color customization-color 60)
theme))
(defn placeholder-color
[state blur? theme]
(cond
(and blur? (= state :active))
(colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 theme)
blur?
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-30 theme)
(= state :active)
(colors/theme-colors colors/neutral-30 colors/neutral-60 theme)
:else
(colors/theme-colors colors/neutral-40 colors/neutral-50 theme)))
(def ^:private props-to-remove
[:cursor-color :placeholder-text-color :editable :on-change-text :on-focus
:on-blur :on-clear :value :disabled? :blur? :customization-color :theme])
(defn browser-input-internal
[{:keys [default-value]
:or {default-value ""}}]
(let [state (reagent/atom :default)
value (reagent/atom default-value)
set-active #(reset! state :active)
set-default #(reset! state :default)
set-value #(reset! value %)
ref (atom nil)
clear-input (fn []
(.clear ^js @ref)
(reset! value ""))
focus-input (fn []
(set-active)
(.focus ^js @ref))]
(fn [{:keys [disabled? blur? on-change-text customization-color
on-clear on-focus on-blur theme get-ref locked?
favicon favicon-color favicon-size]
:as props}]
(let [clean-props (apply dissoc props props-to-remove)]
[rn/view {:style style/root-container}
(when (and (seq @value) (= @state :default))
[rn/touchable-opacity
{:style style/default-container
:on-press focus-input}
(when favicon
[icon/icon favicon
{:accessibility-label :browser-input-favicon
:color favicon-color
:container-style style/favicon-icon-container
:size favicon-size}])
[rn/text
{:accessibility-label :browser-input-label
:style (style/text)} (remove-http-https-www @value)]
(when locked?
[lock-icon
{:blur? blur?
:theme theme}])])
[rn/view {:style (style/active-container (or (empty? @value) (= @state :active)))}
[rn/text-input
(merge
clean-props
{:accessibility-label :browser-input
:auto-capitalize :none
:auto-correct false
:cursor-color (cursor-color customization-color theme)
:editable (not disabled?)
:keyboard-appearance (colors/theme-colors :light :dark theme)
:keyboard-type :web-search
:on-blur (fn []
(set-default)
(when on-blur (on-blur)))
:on-change-text (fn [new-text]
(set-value new-text)
(when on-change-text (on-change-text new-text)))
:on-focus (fn []
(set-active)
(when on-focus (on-focus)))
:placeholder-text-color (placeholder-color @state blur? theme)
:ref (fn [r]
(reset! ref r)
(when get-ref (get-ref r)))
:selection-color (when platform/ios?
(cursor-color customization-color theme))
:select-text-on-focus true
:style (style/input disabled?)})]
(when (seq @value)
[clear-button
{:blur? blur?
:on-press (fn []
(clear-input)
(when on-clear (on-clear)))
:theme theme}])]]))))
(def browser-input (quo2.theme/with-theme browser-input-internal))

View File

@ -12,6 +12,7 @@
quo2.components.buttons.dynamic-button quo2.components.buttons.dynamic-button
quo2.components.buttons.predictive-keyboard.view quo2.components.buttons.predictive-keyboard.view
quo2.components.buttons.slide-button.view quo2.components.buttons.slide-button.view
quo2.components.browser.browser-input.view
quo2.components.code.snippet quo2.components.code.snippet
quo2.components.colors.color-picker.view quo2.components.colors.color-picker.view
quo2.components.common.separator.view quo2.components.common.separator.view
@ -138,6 +139,9 @@
(def predictive-keyboard quo2.components.buttons.predictive-keyboard.view/view) (def predictive-keyboard quo2.components.buttons.predictive-keyboard.view/view)
(def slide-button quo2.components.buttons.slide-button.view/view) (def slide-button quo2.components.buttons.slide-button.view/view)
;;;; BROWSER
(def browser-input quo2.components.browser.browser-input.view/browser-input)
;;;; CODE ;;;; CODE
(def snippet quo2.components.code.snippet/snippet) (def snippet quo2.components.code.snippet/snippet)

View File

@ -5,6 +5,7 @@
[quo2.components.buttons.button.component-spec] [quo2.components.buttons.button.component-spec]
[quo2.components.buttons.predictive-keyboard.component-spec] [quo2.components.buttons.predictive-keyboard.component-spec]
[quo2.components.buttons.slide-button.component-spec] [quo2.components.buttons.slide-button.component-spec]
[quo2.components.browser.browser-input.component-spec]
[quo2.components.colors.color-picker.component-spec] [quo2.components.colors.color-picker.component-spec]
[quo2.components.counter.--tests--.counter-component-spec] [quo2.components.counter.--tests--.counter-component-spec]
[quo2.components.counter.step.component-spec] [quo2.components.counter.step.component-spec]

View File

@ -0,0 +1,64 @@
(ns status-im2.contexts.quo-preview.browser.browser-input
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[utils.re-frame :as rf]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:label "Show Favicon"
:key :favicon?
:type :boolean}
{:label "Locked"
:key :locked?
:type :boolean}
{:label "Disabled"
:key :disabled?
:type :boolean}])
(defn cool-preview
[]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardWillShow"
#(reset! keyboard-shown? true))
keyboard-hide-listener (.addListener rn/keyboard
"keyboardWillHide"
#(reset! keyboard-shown? false))
{:keys [bottom top]} (safe-area/get-insets)
state (reagent/atom {:blur? false
:disabled? false
:favicon? false
:placeholder "Search or enter dapp domain"
:locked? false})]
[rn/keyboard-avoiding-view
{:style {:flex 1 :padding-top top}}
[quo/page-nav
{:align-mid? true
:mid-section {:type :text-only :main-text ""}
:left-section {:icon :i/arrow-left
:on-press
#(rf/dispatch [:navigate-back])}}]
[rn/flat-list
{:header [preview/customizer state descriptor]
:key-fn str
:keyboard-should-persist-taps :always
:style {:flex 1}}]
[rn/view
[quo/browser-input
(assoc @state
:customization-color :blue
:favicon (when (:favicon? @state) :i/verified))]
[rn/view {:style {:height (if-not @keyboard-shown? bottom 0)}}]]]
(finally
(.remove keyboard-show-listener)
(.remove keyboard-hide-listener))))
(defn preview-browser-input
[]
[rn/view
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
:flex 1}}
[cool-preview]])

View File

@ -19,6 +19,7 @@
[status-im2.contexts.quo-preview.buttons.slide-button :as slide-button] [status-im2.contexts.quo-preview.buttons.slide-button :as slide-button]
[status-im2.contexts.quo-preview.buttons.dynamic-button :as dynamic-button] [status-im2.contexts.quo-preview.buttons.dynamic-button :as dynamic-button]
[status-im2.contexts.quo-preview.buttons.predictive-keyboard :as predictive-keyboard] [status-im2.contexts.quo-preview.buttons.predictive-keyboard :as predictive-keyboard]
[status-im2.contexts.quo-preview.browser.browser-input :as browser-input]
[status-im2.contexts.quo-preview.code.snippet :as code-snippet] [status-im2.contexts.quo-preview.code.snippet :as code-snippet]
[status-im2.contexts.quo-preview.colors.color-picker :as color-picker] [status-im2.contexts.quo-preview.colors.color-picker :as color-picker]
[status-im2.contexts.quo-preview.community.community-card-view :as community-card] [status-im2.contexts.quo-preview.community.community-card-view :as community-card]
@ -140,6 +141,9 @@
{:name :predictive-keyboard {:name :predictive-keyboard
:options {:topBar {:visible true}} :options {:topBar {:visible true}}
:component predictive-keyboard/preview-predictive-keyboard}] :component predictive-keyboard/preview-predictive-keyboard}]
:browser [{:name :browser-input
:options {:topBar {:visible false}}
:component browser-input/preview-browser-input}]
:code [{:name :snippet :code [{:name :snippet
:options {:topBar {:visible true}} :options {:topBar {:visible true}}
:component code-snippet/preview-code-snippet}] :component code-snippet/preview-code-snippet}]