[#16377] feat: add calendar component in quo2 preview (#16783)

This commit is contained in:
Mohsen Ghafouri 2023-07-28 21:56:15 +03:00 committed by GitHub
parent b9890a9d44
commit 3b8f66d2ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 937 additions and 0 deletions

View File

@ -0,0 +1,64 @@
(ns quo2.components.calendar.calendar.component-spec
(:require [quo2.components.calendar.calendar.view :as calendar]
[test-helpers.component :as h]
[cljs-time.core :as time]))
(def start-date (time/date-time (time/year (time/now)) (time/month (time/now)) 5))
(def end-date (time/date-time (time/plus start-date (time/days 2))))
(h/describe "calendar component"
(h/test "default render of calendar component"
(h/render
[calendar/view
{:start-date start-date
:end-date end-date}])
(-> (h/expect (h/query-by-translation-text "Mo"))
(h/is-truthy)))
(h/test "should call on-change with selected date on first click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/view
{:start-date nil
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (time/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date nil})))
(h/test "should call on-change with start and end date on second click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/view
{:start-date start-date :end-date nil :on-change on-change}])
(h/fire-event :press (h/query-by-text (str (time/day end-date))))
(h/was-called-with on-change {:start-date start-date :end-date end-date})))
(h/test "should reset the dates on third click"
(let [on-change (h/mock-fn)]
(h/render
[calendar/view
{:start-date start-date
:end-date end-date
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (time/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date nil})))
(h/test "should reset dates when start date is clicked again"
(let [on-change (h/mock-fn)]
(h/render
[calendar/view
{:start-date start-date
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (time/day start-date))))
(h/was-called-with on-change {:start-date nil :end-date nil})))
(h/test "should assign start and end date correctly when upper range selected first"
(let [on-change (h/mock-fn)]
(h/render
[calendar/view
{:start-date end-date
:end-date nil
:on-change on-change}])
(h/fire-event :press (h/query-by-text (str (time/day start-date))))
(h/was-called-with on-change {:start-date start-date :end-date end-date}))))

View File

@ -0,0 +1,7 @@
(ns quo2.components.calendar.calendar.days-grid.style)
(def container-days
{:flex-grow 1
:margin-top 4
:margin-horizontal 8
:overflow :hidden})

View File

@ -0,0 +1,65 @@
(ns quo2.components.calendar.calendar.days-grid.utils
(:require
[utils.number :as utils.number]
[cljs-time.core :as time]))
(defn- day-of-week
[date]
(let [day (time/day-of-week date)]
(mod day 7)))
(defn- add-days
[date days]
(time/plus date (time/days days)))
(defn day-grid
[year month]
(let [year (utils.number/parse-int year)
month (utils.number/parse-int month)
first-day (time/date-time year month 1)
start-day (add-days first-day (- 0 (day-of-week first-day)))
end-day (add-days start-day 34)]
(loop [days []
current-day start-day]
(if (time/after? current-day end-day)
days
(recur (conj days current-day) (add-days current-day 1))))))
(defn get-day-state
[day today year month start-date end-date]
(cond
(and start-date (time/equal? day start-date)) :selected
(and end-date (time/equal? day end-date)) :selected
(and (= (time/year day) (time/year today))
(= (time/month day) (time/month today))
(= (time/day day) (time/day today))) :today
(and (= (time/year day) year) (= (time/month day) month)) :default
:else :disabled))
(defn update-range
[day start-date end-date]
(let [new-state (cond
(and start-date end-date) {:start-date day :end-date nil}
(and start-date (time/equal? day start-date)) {:start-date nil :end-date nil}
(and end-date (time/equal? day end-date)) {:start-date nil :end-date nil}
(nil? start-date) {:start-date day :end-date nil}
(nil? end-date) {:start-date start-date :end-date day}
:else {:start-date start-date
:end-date end-date})]
(if (and (:start-date new-state)
(:end-date new-state)
(time/after? (:start-date new-state) (:end-date new-state)))
{:start-date (:end-date new-state) :end-date (:start-date new-state)}
new-state)))
(defn in-range?
[day start-date end-date]
(and start-date end-date (time/after? day start-date) (time/before? day end-date)))
(defn get-in-range-pos
[day start-date end-date]
(cond
(or (nil? start-date) (nil? end-date)) nil
(and start-date (time/equal? day start-date)) :start
(and end-date (time/equal? day end-date)) :end
(in-range? day start-date end-date) :middle))

View File

@ -0,0 +1,51 @@
(ns quo2.components.calendar.calendar.days-grid.utils-test
(:require [cljs.test :refer-macros [deftest is testing]]
[quo2.components.calendar.calendar.days-grid.utils :as utils]
[cljs-time.core :as time]))
(deftest day-grid-test
(let [day-grid-result (utils/day-grid "2023" "7")]
(testing "it returns correct days grid"
(is (= 35 (count day-grid-result)))
(is (time/equal? (time/date-time 2023 6 25) (first day-grid-result)))
(is (time/equal? (time/date-time 2023 7 29) (last day-grid-result))))))
(deftest get-day-state-test
(let [today (time/date-time 2023 7 27)
year 2023
month 7
start-date (time/date-time 2023 7 20)
end-date (time/date-time 2023 7 30)]
(testing "it returns :today when day equals today"
(is (= :today (utils/get-day-state today today year month start-date end-date))))
(testing "it returns :selected when day equals start-date and not today"
(is
(= :selected (utils/get-day-state start-date today year month start-date end-date))))
(testing "it returns :selected when day equals end-date and not today"
(is
(= :selected (utils/get-day-state end-date today year month start-date end-date))))))
(deftest update-range-test
(let [start-date (time/date-time 2023 7 20)
end-date (time/date-time 2023 7 30)
day (time/date-time 2023 7 27)]
(testing "it returns updated range"
(is
(= {:start-date day :end-date nil} (utils/update-range day start-date end-date))))))
(deftest in-range-test
(let [start-date (time/date-time 2023 7 20)
end-date (time/date-time 2023 7 30)
day (time/date-time 2023 7 27)]
(testing "it returns true when day is within range"
(is (utils/in-range? day start-date end-date))
(is (not (utils/in-range? (time/date-time 2023 7 19) start-date end-date))))))
(deftest get-in-range-pos-test
(let [start-date (time/date-time 2023 7 20)
end-date (time/date-time 2023 7 30)
day (time/date-time 2023 7 27)]
(testing "it returns correct position within range"
(is (= :start (utils/get-in-range-pos start-date start-date end-date)))
(is (= :end (utils/get-in-range-pos end-date start-date end-date)))
(is (= :middle (utils/get-in-range-pos day start-date end-date))))))

View File

@ -0,0 +1,41 @@
(ns quo2.components.calendar.calendar.days-grid.view
(:require [react-native.core :as rn]
[cljs-time.core :as time]
[quo2.components.calendar.calendar.days-grid.utils :as utils]
[quo2.components.calendar.calendar-day.view :as calendar-day]
[quo2.components.calendar.calendar.days-grid.style :as style]))
(defn- day-view
[day _ _ {:keys [year month selection-range on-press customization-color]}]
(let [today (time/now)
start-date (:start-date selection-range)
end-date (:end-date selection-range)
state (utils/get-day-state day today year month start-date end-date)
in-range (utils/get-in-range-pos day start-date end-date)
on-press #(on-press (time/date-time day))]
[calendar-day/view
{:customization-color customization-color
:state state
:in-range in-range
:on-press on-press}
(str (time/day day))]))
(defn view
[{:keys [year month on-change start-date end-date customization-color]}]
(let [on-day-press (fn [day]
(let [new-selection (utils/update-range day start-date end-date)]
(on-change new-selection)))]
[rn/view
{:style style/container-days}
[rn/flat-list
{:data (utils/day-grid year month)
:key-fn str
:num-columns 7
:content-container-style {:margin-horizontal -2}
:render-fn day-view
:render-data {:customization-color customization-color
:year year
:month month
:on-press on-day-press
:selection-range {:start-date start-date
:end-date end-date}}}]]))

View File

@ -0,0 +1,25 @@
(ns quo2.components.calendar.calendar.month-picker.component-spec
(:require [quo2.components.calendar.calendar.month-picker.view :as month-picker]
[test-helpers.component :as h]))
(h/describe "month-picker component"
(h/test "default render of month-picker component"
(h/render
[month-picker/view
{:year "2023" :month "7"}])
(-> (h/expect (h/query-by-translation-text "July 2023"))
(h/is-truthy)))
(h/test "should call on-change with next month when right button pressed"
(let [on-change (h/mock-fn)]
(h/render
[month-picker/view {:year "2023" :month "7" :on-change on-change}])
(h/fire-event :press (h/query-by-label-text :next-month-button))
(h/was-called on-change)))
(h/test "should call on-change with previous month when left button pressed"
(let [on-change (h/mock-fn)]
(h/render
[month-picker/view {:year "2023" :month "1" :on-change on-change}])
(h/fire-event :press (h/query-by-label-text :previous-month-button))
(h/was-called on-change))))

View File

@ -0,0 +1,14 @@
(ns quo2.components.calendar.calendar.month-picker.style
(:require [quo2.foundations.colors :as colors]))
(def container
{:align-items :center
:flex-direction :row
:flex-grow 1
:padding-horizontal 12
:padding-vertical 9
:justify-content :space-between})
(defn text
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})

View File

@ -0,0 +1,22 @@
(ns quo2.components.calendar.calendar.month-picker.utils
(:require [utils.datetime :as datetime]))
(defn format-month-year
[year month]
(let [month (cond
(or (nil? month) (zero? month)) 1
(> month 12) 12
:else month)]
(str (datetime/format-long-month month) " " year)))
(defn next-month
[year month]
(let [new-month (if (= month 12) 1 (inc month))
new-year (if (= month 12) (inc year) year)]
{:year (str new-year) :month (str new-month)}))
(defn previous-month
[year month]
(let [new-month (if (= month 1) 12 (dec month))
new-year (if (= month 1) (dec year) year)]
{:year (str new-year) :month (str new-month)}))

View File

@ -0,0 +1,20 @@
(ns quo2.components.calendar.calendar.month-picker.utils-test
(:require [cljs.test :refer-macros [deftest is testing]]
[quo2.components.calendar.calendar.month-picker.utils :as utils]))
(deftest format-month-year-test
(testing "returns correct format for given year and month"
(is (= (utils/format-month-year 2023 1) "January 2023"))
(is (= (utils/format-month-year 2023 12) "December 2023"))
(is (= (utils/format-month-year 2023 0) "January 2023"))
(is (= (utils/format-month-year 2023 13) "December 2023"))))
(deftest next-month-test
(testing "returns the next month and year"
(is (= (utils/next-month 2023 1) {:year "2023" :month "2"}))
(is (= (utils/next-month 2023 12) {:year "2024" :month "1"}))))
(deftest previous-month-test
(testing "returns the previous month and year"
(is (= (utils/previous-month 2023 1) {:year "2022" :month "12"}))
(is (= (utils/previous-month 2023 12) {:year "2023" :month "11"}))))

View File

@ -0,0 +1,36 @@
(ns quo2.components.calendar.calendar.month-picker.view
(:require [react-native.core :as rn]
[utils.number :as utils.number]
[quo2.theme :as theme]
[quo2.components.buttons.button.view :as button]
[quo2.components.markdown.text :as text]
[quo2.components.calendar.calendar.month-picker.style :as style]
[quo2.components.calendar.calendar.month-picker.utils :as utils]))
(defn- view-internal
[{:keys [year month on-change theme]}]
(let [year (utils.number/parse-int year)
month (utils.number/parse-int month)]
[rn/view
{:style style/container}
[button/button
{:icon true
:type :outline
:accessibility-label :previous-month-button
:size 24
:on-press #(on-change (utils/previous-month year month))}
:i/chevron-left]
[text/text
{:weight :semi-bold
:size :paragraph-1
:style (style/text theme)}
(utils/format-month-year year month)]
[button/button
{:icon true
:accessibility-label :next-month-button
:size 24
:type :outline
:on-press #(on-change (utils/next-month year month))}
:i/chevron-right]]))
(def view (theme/with-theme view-internal))

View File

@ -0,0 +1,14 @@
(ns quo2.components.calendar.calendar.style
(:require [quo2.foundations.colors :as colors]))
(defn container
[theme]
{:flex-direction :row
:height 270
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
:border-radius 12
:border-width 1
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)})
(def container-main
{:flex-grow 1})

View File

@ -0,0 +1,25 @@
(ns quo2.components.calendar.calendar.utils
(:require [utils.datetime :as datetime]
[utils.number :as utils.number]
[clojure.string :as string]))
(defn generate-years
[current-year]
(let [current-year current-year]
(reverse (vec (range (- current-year 100) (+ current-year 1))))))
(defn current-year
[]
(-> (datetime/now)
datetime/timestamp->year-month-day-date
(string/split #"-")
first
utils.number/parse-int))
(defn current-month
[]
(-> (datetime/now)
datetime/timestamp->year-month-day-date
(string/split #"-")
second
utils.number/parse-int))

View File

@ -0,0 +1,30 @@
(ns quo2.components.calendar.calendar.utils-test
(:require [cljs.test :refer-macros [deftest is testing]]
[quo2.components.calendar.calendar.utils :as utils]
[utils.datetime :as datetime]
[clojure.string :as string]
[utils.number :as utils.number]))
(deftest generate-years-test
(testing "returns correct years range"
(let [current-year (utils/current-year)]
(is (= (last (utils/generate-years current-year)) (- current-year 100)))
(is (= (first (utils/generate-years current-year)) current-year)))))
(deftest current-year-test
(testing "returns the current year"
(let [current-year (-> (datetime/now)
datetime/timestamp->year-month-day-date
(string/split #"-")
first
utils.number/parse-int)]
(is (= (utils/current-year) current-year)))))
(deftest current-month-test
(testing "returns the current month"
(let [current-month (-> (datetime/now)
datetime/timestamp->year-month-day-date
(string/split #"-")
second
utils.number/parse-int)]
(is (= (utils/current-month) current-month)))))

View File

@ -0,0 +1,42 @@
(ns quo2.components.calendar.calendar.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[reagent.core :as reagent]
[utils.number :as utils.number]
[quo2.components.calendar.calendar.utils :as utils]
[quo2.components.calendar.calendar.style :as style]
[quo2.components.calendar.calendar.years-list.view :as years-list]
[quo2.components.calendar.calendar.days-grid.view :as days-grid]
[quo2.components.calendar.calendar.weekdays-header.view :as weekdays-header]
[quo2.components.calendar.calendar.month-picker.view :as month-picker]))
(defn- view-internal
[]
(let [selected-year (reagent/atom (utils/current-year))
selected-month (reagent/atom (utils/current-month))
on-change-year #(reset! selected-year %)
on-change-month (fn [new-date]
(reset! selected-year (utils.number/parse-int (:year new-date)))
(reset! selected-month (utils.number/parse-int (:month new-date))))]
(fn [{:keys [on-change start-date end-date theme]}]
[rn/view
{:style (style/container theme)}
[years-list/view
{:on-change-year on-change-year
:year @selected-year}]
[rn/view
{:style style/container-main}
[month-picker/view
{:year @selected-year
:month @selected-month
:on-change on-change-month}]
[weekdays-header/view]
[days-grid/view
{:year @selected-year
:month @selected-month
:start-date start-date
:end-date end-date
:on-change on-change
:customization-color :blue}]]])))
(def view (theme/with-theme view-internal))

View File

@ -0,0 +1,18 @@
(ns quo2.components.calendar.calendar.weekdays-header.style
(:require [quo2.foundations.colors :as colors]))
(def container-weekday-row
{:flex-direction :row
:justify-content :space-between
:padding-horizontal 8})
(def container-weekday
{:width 32
:height 30
:padding-top 2
:justify-content :center
:align-items :center})
(defn text-weekdays
[theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})

View File

@ -0,0 +1,23 @@
(ns quo2.components.calendar.calendar.weekdays-header.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[utils.datetime :as datetime]
[utils.i18n :as i18n]
[quo2.components.markdown.text :as text]
[quo2.components.calendar.calendar.weekdays-header.style :as style]))
(defn- view-internal
[theme]
[rn/view
{:style style/container-weekday-row}
(for [weekday datetime/weekday-names]
[rn/view
{:style style/container-weekday
:key weekday}
[text/text
{:weight :medium
:size :paragraph-2
:style (style/text-weekdays theme)}
(str (i18n/label weekday))]])])
(def view (theme/with-theme view-internal))

View File

@ -0,0 +1,33 @@
(ns quo2.components.calendar.calendar.years-list.style
(:require [quo2.foundations.colors :as colors]))
(defn gradient-start-color
[theme]
(colors/theme-colors colors/white colors/neutral-90 theme))
(defn gradient-end-color
[theme]
(colors/theme-colors colors/white-opa-0 colors/neutral-100-opa-0 theme))
(def gradient-view
{:position :absolute
:height 50
:border-top-left-radius 12
:top 0
:left 0
:right 0})
(defn container-years
[theme]
{:border-width 1
:overflow :hidden
:padding-left 8
:padding-right 7
:padding-vertical 8
:margin-left -1
:margin-top -1
:margin-bottom -1
:border-style :dashed
:border-top-left-radius 12
:border-bottom-left-radius 12
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)})

View File

@ -0,0 +1,49 @@
(ns quo2.components.calendar.calendar.years-list.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[react-native.linear-gradient :as linear-gradient]
[quo2.components.calendar.calendar.utils :as utils]
[quo2.components.calendar.calendar-year.view :as calendar-year]
[quo2.components.calendar.calendar.years-list.style :as style]))
(defn- year-view
[year _ _ {:keys [selected-year on-press]}]
[calendar-year/view
{:selected? (= year selected-year)
:on-press #(on-press year)}
(str year)])
(defn- separator
[]
[rn/view {:style {:height 4}}])
(defn- footer
[]
[rn/view {:style {:height 32}}])
(defn- gradiant-overview
[theme]
[linear-gradient/linear-gradient
{:colors [(style/gradient-start-color theme) (style/gradient-end-color theme)]
:style style/gradient-view
:start {:x 0 :y 0}
:end {:x 0 :y 1}}])
(defn view-internal
[{:keys [on-change-year year theme]}]
[rn/view
{:style (style/container-years theme)}
[rn/flat-list
{:data (utils/generate-years (utils/current-year))
:key-fn str
:list-key :years-list
:inverted true
:shows-vertical-scroll-indicator false
:footer [footer]
:separator [separator]
:render-fn year-view
:render-data {:selected-year year
:on-press #(on-change-year %)}}]
[gradiant-overview theme]])
(def view (theme/with-theme view-internal))

View File

@ -0,0 +1,14 @@
(ns quo2.components.calendar.calendar-day.component-spec
(:require [quo2.components.calendar.calendar-day.view :as calendar-day]
[test-helpers.component :as h]))
(h/describe "calendar-day component"
(h/test "default render of calendar-day component"
(h/render [calendar-day/view {} "25"])
(h/is-truthy (h/query-by-text "25")))
(h/test "should not call on-press when state is :disabled"
(let [on-press (h/mock-fn)]
(h/render [calendar-day/view {:on-press on-press :state :disabled} "25"])
(h/fire-event :press (h/query-by-text "25"))
(h/was-not-called on-press))))

View File

@ -0,0 +1,69 @@
(ns quo2.components.calendar.calendar-day.style
(:require [quo2.foundations.colors :as colors]))
(def wrapper
{:flex 1
:margin-vertical 2
:justify-content :center
:align-items :center})
(def container-base
{:align-items :center
:justify-content :center
:border-radius 10
:height 32
:width 32})
(defn text-base
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)
:text-align :center})
(defn in-range-background
[{:keys [in-range theme]}]
(cond-> {:position :absolute
:top 0
:right 0
:left 0
:bottom 0}
(= in-range :start)
(assoc :background-color
(colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
:left 20)
(= in-range :middle)
(assoc :background-color
(colors/theme-colors colors/neutral-5 colors/neutral-80 theme))
(= in-range :end)
(assoc :background-color
(colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
:right 20)))
(defn container
[{:keys [state theme customization-color]}]
(cond-> container-base
(= state :default)
(assoc :background-color colors/neutral-100-opa-0)
(= state :disabled)
(assoc :opacity 0.3)
(= state :selected)
(assoc :background-color (colors/custom-color-by-theme customization-color 50 60 nil nil theme))))
(defn text
[{:keys [state theme]}]
(cond-> (text-base theme)
(= state :selected) (assoc :color colors/white)))
(defn indicator
[{:keys [state theme customization-color]}]
{:width 4
:position :absolute
:bottom 3
:height 2
:border-radius 8
:background-color (if (= state :today)
(colors/custom-color-by-theme customization-color 50 60 nil nil theme)
colors/neutral-100-opa-0)})

View File

@ -0,0 +1,31 @@
(ns quo2.components.calendar.calendar-day.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.markdown.text :as text]
[quo2.components.calendar.calendar-day.style :as style]))
(defn- view-internal
[{:keys [state in-range on-press customization-color theme]
:or {state :default}}
day]
[rn/view {:style style/wrapper}
[rn/view {:style (style/in-range-background {:in-range in-range :theme theme})}]
[rn/touchable-opacity
{:on-press on-press
:style (style/container
{:state state
:theme theme
:customization-color customization-color})
:disabled (= state :disabled)}
[text/text
{:weight :medium
:size :paragraph-2
:style (style/text {:state state :theme theme})}
day]
[rn/view
{:style (style/indicator
{:state state
:theme theme
:customization-color customization-color})}]]])
(def view (theme/with-theme view-internal))

View File

@ -0,0 +1,14 @@
(ns quo2.components.calendar.calendar-year.component-spec
(:require [quo2.components.calendar.calendar-year.view :as calendar-year]
[test-helpers.component :as h]))
(h/describe "calendar-year component"
(h/test "default render of calendar-year component"
(h/render [calendar-year/view {} "2023"])
(h/is-truthy (h/query-by-text "2023")))
(h/test "should not call on-press when disabled"
(let [on-press (h/mock-fn)]
(h/render [calendar-year/view {:on-press on-press :disabled? true} "2023"])
(h/fire-event :press (h/query-by-text "2023"))
(h/was-not-called on-press))))

View File

@ -0,0 +1,25 @@
(ns quo2.components.calendar.calendar-year.style
(:require [quo2.foundations.colors :as colors]))
(def container-base
{:align-items :center
:justify-content :center
:border-radius 10
:height 32
:width 48})
(defn text-base
[theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(defn container
[{:keys [selected? disabled? theme]}]
(cond-> container-base
disabled? (assoc :opacity 0.3)
selected? (assoc :background-color
(colors/theme-colors colors/neutral-10 colors/neutral-70 theme))))
(defn text
[{:keys [selected? theme]}]
(cond-> (text-base theme)
selected? (assoc :color (colors/theme-colors colors/neutral-100 colors/white theme))))

View File

@ -0,0 +1,24 @@
(ns quo2.components.calendar.calendar-year.view
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.markdown.text :as text]
[quo2.components.calendar.calendar-year.style :as style]))
(defn- view-internal
[{:keys [selected? disabled? on-press theme]} year]
[rn/touchable-opacity
{:on-press on-press
:style (style/container
{:selected? selected?
:disabled? disabled?
:theme theme})
:disabled disabled?}
[text/text
{:weight :medium
:size :paragraph-2
:style (style/text
{:selected? selected?
:theme theme})}
year]])
(def view (theme/with-theme view-internal))

View File

@ -13,6 +13,9 @@
quo2.components.buttons.predictive-keyboard.view
quo2.components.buttons.slide-button.view
quo2.components.browser.browser-input.view
quo2.components.calendar.calendar.view
quo2.components.calendar.calendar-day.view
quo2.components.calendar.calendar-year.view
quo2.components.code.snippet
quo2.components.colors.color-picker.view
quo2.components.common.separator.view
@ -120,6 +123,11 @@
;;;; BROWSER
(def browser-input quo2.components.browser.browser-input.view/browser-input)
;;;; CALENDAR
(def calendar quo2.components.calendar.calendar.view/view)
(def calendar-day quo2.components.calendar.calendar-day.view/view)
(def calendar-year quo2.components.calendar.calendar-year.view/view)
;;;; CODE
(def snippet quo2.components.code.snippet/snippet)

View File

@ -5,6 +5,10 @@
[quo2.components.buttons.button.component-spec]
[quo2.components.buttons.predictive-keyboard.component-spec]
[quo2.components.buttons.slide-button.component-spec]
[quo2.components.calendar.calendar.component-spec]
[quo2.components.calendar.calendar-day.component-spec]
[quo2.components.calendar.calendar.month-picker.component-spec]
[quo2.components.calendar.calendar-year.component-spec]
[quo2.components.browser.browser-input.component-spec]
[quo2.components.colors.color-picker.component-spec]
[quo2.components.counter.component-spec]

View File

@ -0,0 +1,42 @@
(ns status-im2.contexts.quo-preview.calendar.calendar
(:require [status-im2.contexts.quo-preview.preview :as preview]
[react-native.core :as rn]
[utils.datetime :as dt]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[quo2.core :as quo]))
(def descriptor
[{:label "Start Date"
:key :start-date
:type :text}
{:label "End Date"
:key :end-date
:type :text}])
(defn cool-preview
[]
(let [state (reagent/atom {:start-date nil :end-date nil})
range (reagent/atom {:start-date nil :end-date nil})]
(fn
[]
[rn/touchable-without-feedback
{:on-press rn/dismiss-keyboard!}
[rn/view {:style {:flex 1}}
[preview/customizer state descriptor]
[rn/view {:style {:padding 19 :flex-grow 2}}
[quo/calendar
{:start-date (:start-date @range)
:end-date (:end-date @range)
:on-change (fn [new-range]
(reset! state
{:start-date (dt/format-date (:start-date new-range))
:end-date (dt/format-date (:end-date new-range))})
(reset! range new-range))}]]]])))
(defn preview-calendar
[]
[rn/view
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
:flex 1}}
[cool-preview]])

View File

@ -0,0 +1,45 @@
(ns status-im2.contexts.quo-preview.calendar.calendar-day
(:require [status-im2.contexts.quo-preview.preview :as preview]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[quo2.core :as quo]))
(def descriptor
[{:label "State:"
:key :state
:type :select
:options [{:key :default
:value "Default"}
{:key :selected
:value "Selected"}
{:key :disabled
:value "Disabled"}
{:key :today
:value "Today"}]}])
(defn cool-preview
[]
(let [state (reagent/atom
{:state :default})]
(fn
[]
[rn/touchable-without-feedback
{:on-press rn/dismiss-keyboard!}
[rn/view
[preview/customizer state descriptor]
[rn/view
{:padding-vertical 60
:align-items :center}
[quo/calendar-day (assoc @state :customization-color :blue) 12]]]])))
(defn preview-calendar-day
[]
[rn/view
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
:flex 1}}
[rn/flat-list
{:style {:flex 1}
:keyboard-should-persist-taps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -0,0 +1,39 @@
(ns status-im2.contexts.quo-preview.calendar.calendar-year
(:require [status-im2.contexts.quo-preview.preview :as preview]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[quo2.core :as quo]))
(def descriptor
[{:label "Selected?"
:key :selected?
:type :boolean}
{:label "Disabled?"
:key :disabled?
:type :boolean}])
(defn cool-preview
[]
(let [state (reagent/atom {:selected? false :disabled? false})]
(fn
[]
[rn/touchable-without-feedback
{:on-press rn/dismiss-keyboard!}
[rn/view
[preview/customizer state descriptor]
[rn/view
{:padding-vertical 60
:align-items :center}
[quo/calendar-year @state "2023"]]]])))
(defn preview-calendar-year
[]
[rn/view
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
:flex 1}}
[rn/flat-list
{:style {:flex 1}
:keyboard-should-persist-taps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -19,6 +19,9 @@
[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.predictive-keyboard :as predictive-keyboard]
[status-im2.contexts.quo-preview.calendar.calendar :as calendar]
[status-im2.contexts.quo-preview.calendar.calendar-day :as calendar-day]
[status-im2.contexts.quo-preview.calendar.calendar-year :as calendar-year]
[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.colors.color-picker :as color-picker]
@ -145,6 +148,15 @@
:browser [{:name :browser-input
:options {:topBar {:visible false}}
:component browser-input/preview-browser-input}]
:calendar [{:name :calendar
:options {:topBar {:visible true}}
:component calendar/preview-calendar}
{:name :calendar-day
:options {:topBar {:visible true}}
:component calendar-day/preview-calendar-day}
{:name :calendar-year
:options {:topBar {:visible true}}
:component calendar-year/preview-calendar-year}]
:code [{:name :snippet
:options {:topBar {:visible true}}
:component code-snippet/preview-code-snippet}]

View File

@ -204,6 +204,10 @@
[mock]
(.toHaveBeenCalled (js/expect mock)))
(defn was-called-with
[mock expected-arg]
(.toHaveBeenCalledWith (js/expect mock) expected-arg))
(defn was-called-times
[^js mock number-of-times]
(.toHaveBeenCalledTimes (js/expect mock) number-of-times))

View File

@ -9,6 +9,8 @@
(defn now [] (t/now))
(def weekday-names ["su" "mo" "tu" "we" "th" "fr" "sa"])
(def ^:const int->weekday
"Maps the corresponding string representation of a weekday
By it's numeric index as in cljs-time"
@ -93,6 +95,12 @@
[^js locsym]
(nth (.-DATEFORMATS locsym) 2))
(defn format-date
[date]
(if date
(t.format/unparse (t.format/formatter "dd/MM/yyyy") date)
""))
;;;; Datetime formats
(defn- medium-date-time-format
[locsym]
@ -113,6 +121,10 @@
(def short-date-with-time-fmt (get-formatter-fn short-date-format-with-time))
(def datetime-within-one-week-fmt (get-formatter-fn datetime-within-one-week-format))
(def format-long-month
(memoize (fn [month]
(.format ^js ((get-formatter-fn (constantly "MMMM")))
(t/date-time 1970 month)))))
;;;; Utilities
(defn previous-years?
[datetime]

View File

@ -190,3 +190,11 @@
"it"
#'utils.datetime/medium-date-time-format)]
(is (= (datetime/day-relative epoch) "01 gen 1970, 12:00:00 AM")))))
(deftest format-long-month-test
(testing "returns correct month string"
(is (= "January" (datetime/format-long-month 1)))
(is (= "February" (datetime/format-long-month 2)))
(is (= "March" (datetime/format-long-month 3)))
(is (= "April" (datetime/format-long-month 4)))
(is (= "December" (datetime/format-long-month 12)))))

View File

@ -2221,6 +2221,13 @@
"all-messages": "All messages",
"muted-until": "Muted until {{duration}}",
"until-you-turn-it-back-on": "you turn it back on",
"mo": "Mo",
"tu": "Tu",
"we": "We",
"th": "Th",
"fr": "Fr",
"sa": "Sa",
"su": "Su",
"mon": "Mon",
"tue": "Tue",
"wed": "Wed",