diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 2db67597bd..7fa87eb692 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -1087,7 +1087,7 @@ ;; === Operations -(def decode-shape-attrs +(def decode-shape-attrs (sm/decoder cts/schema:shape-attrs sm/json-transformer)) (defmethod process-operation :assign diff --git a/common/src/app/common/files/tokens.cljc b/common/src/app/common/files/tokens.cljc index 7d6cbec6bd..c09e06a53d 100644 --- a/common/src/app/common/files/tokens.cljc +++ b/common/src/app/common/files/tokens.cljc @@ -11,6 +11,7 @@ [app.common.i18n :refer [tr]] [app.common.schema :as sm] [app.common.types.token :as cto] + [app.common.types.token-status :as ctos] [app.common.types.tokens-lib :as ctob] [clojure.set :as set] [cuerdas.core :as str] @@ -302,6 +303,8 @@ ;; HELPERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Token + (def parseable-token-value-regexp "Regexp that can be used to parse a number value out of resolved token value. This regexp also trims whitespace around the value." @@ -373,3 +376,15 @@ ;; FIXME: this should be precalculated ? (defn is-reference? [token] (str/includes? (:value token) "{")) + +;; Tokens status + +(defn set-theme-status + [token-status tokens-lib theme-id status] + (assert (ctos/token-status? token-status) "expected valid token-status") + (assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib") + (assert (uuid? theme-id) "expected valid theme-id") + (assert (boolean? status) "expected boolean status") + (if (ctob/get-theme tokens-lib theme-id) + (ctos/set-theme-status token-status tokens-lib theme-id status) + token-status)) \ No newline at end of file diff --git a/common/src/app/common/test_helpers/tokens.cljc b/common/src/app/common/test_helpers/tokens.cljc index ddaf97a1cf..e0a1733167 100644 --- a/common/src/app/common/test_helpers/tokens.cljc +++ b/common/src/app/common/test_helpers/tokens.cljc @@ -29,6 +29,7 @@ (ctf/update-file-data file ctf/ensure-tokens-lib)) (defn update-tokens-lib + "Modify the tokens-lib of a file " [file f] (ctf/update-file-data file #(ctf/update-tokens-lib % f))) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 777e23e5ea..48c48ae531 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -330,10 +330,14 @@ (:token-status file-data))) (defn update-tokens-lib + "Update the tokens-lib inside file-data through a callback function. + The function will receive the tokens lib and the rest of args." [file-data f & args] (d/update-when file-data :tokens-lib #(apply f % args))) (defn update-token-status + "Update the token-status inside file-data through a callback function. + The function will receive the tokens status, the tokens lib and the rest of args." [file-data f & args] (d/update-when file-data :token-status #(apply f % (get-tokens-lib file-data) args))) diff --git a/common/src/app/common/types/token_status.cljc b/common/src/app/common/types/token_status.cljc index 0bc77edd80..09a5461325 100644 --- a/common/src/app/common/types/token_status.cljc +++ b/common/src/app/common/types/token_status.cljc @@ -24,7 +24,7 @@ (deactivate-theme [_ tokens-lib theme-id] "Remove a theme uuid from active themes") (set-theme-status [_ tokens-lib theme-id status] "Add or remove a theme uuid to active themes") (theme-active? [_ theme-id] "Check if a theme uuid is active") - (active-theme-count [_] "Return the number of active themes") + (active-themes-count [_] "Return the number of active themes") (activate-set [_ set-id] "Add a set uuid to active sets") (deactivate-set [_ set-id] "Remove a set uuid from active sets") (toggle-set-active [_ set-id] "Toggle a set uuid in active sets") @@ -44,39 +44,50 @@ ITokenStatus (activate-theme [this tokens-lib theme-id] + (assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib") + (assert (uuid? theme-id) "expected valid theme-id") (if (ctob/get-theme tokens-lib theme-id) (TokenStatus. (conj active-themes theme-id) active-sets) this)) (deactivate-theme [this tokens-lib theme-id] + (assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib") + (assert (uuid? theme-id) "expected valid theme-id") (if (ctob/get-theme tokens-lib theme-id) (TokenStatus. (disj active-themes theme-id) active-sets) this)) (set-theme-status [this tokens-lib theme-id status] + (assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib") + (assert (uuid? theme-id) "expected valid theme-id") + (assert (boolean? status) "expected boolean status") (if status (activate-theme this tokens-lib theme-id) (deactivate-theme this tokens-lib theme-id))) (theme-active? [_ theme-id] + (assert (uuid? theme-id) "expected valid theme-id") (contains? active-themes theme-id)) - (active-theme-count [_] - (prn active-themes) + (active-themes-count [_] (count active-themes)) (activate-set [_ set-id] + (assert (uuid? set-id) "expected valid set-id") (TokenStatus. active-themes (conj active-sets set-id))) (deactivate-set [_ set-id] + (assert (uuid? set-id) "expected valid set-id") (TokenStatus. active-themes (disj active-sets set-id))) (toggle-set-active [this set-id] + (assert (uuid? set-id) "expected valid set-id") (if (contains? active-sets set-id) (deactivate-set this set-id) (activate-set this set-id))) (set-active? [_ set-id] + (assert (uuid? set-id) "expected valid set-id") (contains? active-sets set-id)) (active-set-count [_] @@ -126,6 +137,7 @@ "Make a TokenStatus from a TokensLib, activating the themes and sets marked as active in the library (to migrate from legacy files)." [tokens-lib] + (assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib") (let [active-themes (into #{} (comp (map :id) (filter #(not= % ctob/hidden-theme-id))) @@ -135,7 +147,7 @@ (map ctob/get-id)) (ctob/get-active-themes-set-names tokens-lib))] (make-token-status :active-themes active-themes - :active-sets active-sets))) + :active-sets active-sets))) ;; === Pretty-print for debugging === diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 9b5beaf72a..d30e83e6be 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -778,6 +778,7 @@ (delete-theme [_ id] "delete a theme in the library") (theme-count [_] "get the total number if themes in the library") (get-theme-tree [_] "get a nested tree of all themes in the library") + (get-theme-tree-no-hidden [_] "get a nested tree of all themes in the library except the hidden theme") (get-themes [_] "get an ordered sequence of all themes in the library") (get-theme [_ id] "get one theme looking for id") (get-theme-by-name [_ group name] "get one theme looking for group and name") @@ -1192,6 +1193,9 @@ Will return a value that matches this schema: (get-theme-tree [_] themes) + (get-theme-tree-no-hidden [_] + (dissoc themes hidden-theme-group)) + (get-theme-groups [_] (into [] (comp (map key) diff --git a/common/test/common_tests/files/tokens_test.cljc b/common/test/common_tests/files/tokens_test.cljc index 8052cbc981..3bf0ce8189 100644 --- a/common/test/common_tests/files/tokens_test.cljc +++ b/common/test/common_tests/files/tokens_test.cljc @@ -7,6 +7,11 @@ (ns common-tests.files.tokens-test (:require [app.common.files.tokens :as cfo] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.tokens :as tht] + [app.common.types.token-status :as ctos] + [app.common.types.tokens-lib :as ctob] [clojure.test :as t])) (t/deftest test-parse-token-value @@ -80,3 +85,11 @@ (t/is (nil? (cfo/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}} {:applied-tokens {:x "a"}}] #{:y}))))) + +(t/deftest set-theme-status + (t/testing "setting the status of a theme gets it activated or deactivated" + (let [tokens-lib (-> (ctob/make-tokens-lib) + (ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme1) :name "theme"))) + token-status (ctos/make-token-status) + token-status' (cfo/set-theme-status token-status tokens-lib (thi/id :theme1) true)] + (t/is (ctos/theme-active? token-status' (thi/id :theme1)))))) diff --git a/common/test/common_tests/logic/token_test.cljc b/common/test/common_tests/logic/token_test.cljc index 6d54874ae4..7499f8af89 100644 --- a/common/test/common_tests/logic/token_test.cljc +++ b/common/test/common_tests/logic/token_test.cljc @@ -13,7 +13,6 @@ [app.common.test-helpers.tokens :as tht] [app.common.types.tokens-lib :as ctob] [app.common.uuid :as uuid] - [clojure.datafy :refer [datafy]] [clojure.test :as t])) (t/use-fixtures :each thi/test-fixture) diff --git a/common/test/common_tests/types/file_test.cljc b/common/test/common_tests/types/file_test.cljc index 05e34e4d72..106355883f 100644 --- a/common/test/common_tests/types/file_test.cljc +++ b/common/test/common_tests/types/file_test.cljc @@ -8,9 +8,10 @@ (:require [app.common.test-helpers.files :as thf] [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.tokens :as tht] [app.common.types.file :as ctf] - [app.common.types.tokens-lib :as ctob] [app.common.types.token-status :as ctos] + [app.common.types.tokens-lib :as ctob] [clojure.test :as t])) (t/deftest test-ensure-tokens-lib @@ -23,36 +24,77 @@ (t/is (contains? file-data' :token-status)) (t/is (ctos/token-status? (:token-status file-data'))))) - (t/testing "ensure-tokens-lib should create a token-status from an existing tokens-lib") - (let [file (thf/sample-file :file1) - file-data (-> (ctf/file-data file) - (assoc :tokens-lib (-> (ctob/make-tokens-lib) - (ctob/add-set (ctob/make-token-set - :id (thi/new-id! :set1) - :name "set1")) - (ctob/add-set (ctob/make-token-set - :id (thi/new-id! :set2) - :name "set2")) - (ctob/add-theme (ctob/make-token-theme - :id (thi/new-id! :theme1) - :name "theme1" - :sets #{"set1"})) - (ctob/add-theme (ctob/make-token-theme - :id ctob/hidden-theme-id - :name "HIDDEN_THEME" - :sets #{"set2"})) - (ctob/activate-theme ctob/hidden-theme-id) - (ctob/activate-theme (thi/id :theme1))))) - file-data' (ctf/ensure-tokens-lib file-data) - token-status' (:token-status file-data')] + (t/testing "ensure-tokens-lib should create a token-status from an existing tokens-lib" + (let [file (thf/sample-file :file1) + file-data (-> (ctf/file-data file) + (assoc :tokens-lib (-> (ctob/make-tokens-lib) + (ctob/add-set (ctob/make-token-set + :id (thi/new-id! :set1) + :name "set1")) + (ctob/add-set (ctob/make-token-set + :id (thi/new-id! :set2) + :name "set2")) + (ctob/add-theme (ctob/make-token-theme + :id (thi/new-id! :theme1) + :name "theme1" + :sets #{"set1"})) + (ctob/add-theme (ctob/make-token-theme + :id ctob/hidden-theme-id + :name "HIDDEN_THEME" + :sets #{"set2"})) + (ctob/activate-theme ctob/hidden-theme-id) + (ctob/activate-theme (thi/id :theme1))))) + file-data' (ctf/ensure-tokens-lib file-data) + token-status' (:token-status file-data')] - (t/is (contains? file-data' :tokens-lib)) - (t/is (ctob/tokens-lib? (:tokens-lib file-data'))) - (t/is (contains? file-data' :token-status)) - (t/is (ctos/token-status? token-status')) - (t/is (ctos/check-token-status token-status')) - (t/is (= 1 (ctos/active-theme-count token-status'))) - (t/is (true? (ctos/theme-active? token-status' (thi/id :theme1)))) - (t/is (= 1 (ctos/active-set-count token-status') 1)) - (t/is (ctos/set-active? token-status' (thi/id :set1))))) + (t/is (contains? file-data' :tokens-lib)) + (t/is (ctob/tokens-lib? (:tokens-lib file-data'))) + (t/is (contains? file-data' :token-status)) + (t/is (ctos/token-status? token-status')) + (t/is (ctos/check-token-status token-status')) + (t/is (= 1 (ctos/active-themes-count token-status'))) + (t/is (true? (ctos/theme-active? token-status' (thi/id :theme1)))) + (t/is (= 1 (ctos/active-set-count token-status') 1)) + (t/is (ctos/set-active? token-status' (thi/id :set1)))))) +(t/deftest test-update-tokens-lib + (t/testing "update when there is no tokens-lib has no effect" + (let [file (thf/sample-file :file1) + file' (ctf/update-tokens-lib file #(t/is false "This should not be called"))] + (t/is (= file file')))) + + (t/testing "update a tokens-lib applies the changes correctly" + (let [file (-> (thf/sample-file :file1) + (tht/add-tokens-lib)) + file' (ctf/update-file-data file + #(ctf/update-tokens-lib + % + ctob/add-theme + (ctob/make-token-theme :id (thi/new-id! :theme1) + :name "theme 1"))) + + tokens-lib' (tht/get-tokens-lib file')] + (t/is (= 2 (ctob/theme-count tokens-lib'))) ;; Count the hidden theme + (t/is (ctob/token-theme? (ctob/get-theme tokens-lib' (thi/id :theme1))))))) + +(t/deftest test-update-token-status + (t/testing "update when there is no token-status has no effect" + (let [file (thf/sample-file :file1) + file' (ctf/update-token-status file #(t/is false "This should not be called"))] + (t/is (= file file')))) + + (t/testing "update a token-status applies the changes correctly" + (let [file (-> (thf/sample-file :file1) + (tht/add-tokens-lib) + (tht/update-tokens-lib + (fn [tokens-lib] + (-> tokens-lib + (ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme1) :name "theme")))))) + file' (ctf/update-file-data file + #(ctf/update-token-status + % + ctos/activate-theme + (thi/id :theme1))) + token-status' (tht/get-token-status file')] + (t/is (= 1 (ctos/active-themes-count token-status'))) + (t/is (ctos/theme-active? token-status' (thi/id :theme1)))))) \ No newline at end of file diff --git a/common/test/common_tests/types/token_status_test.cljc b/common/test/common_tests/types/token_status_test.cljc index 2252cfb2dd..d59e687c05 100644 --- a/common/test/common_tests/types/token_status_test.cljc +++ b/common/test/common_tests/types/token_status_test.cljc @@ -10,8 +10,8 @@ #?(:clj [clojure.data.json :as json]) [app.common.test-helpers.ids-map :as thi] [app.common.transit :as tr] - [app.common.types.tokens-lib :as ctob] [app.common.types.token-status :as ctos] + [app.common.types.tokens-lib :as ctob] [app.common.uuid :as uuid] [clojure.datafy :refer [datafy]] [clojure.test :as t])) @@ -20,10 +20,10 @@ (let [theme-id (uuid/next) set-id (uuid/next) status (ctos/make-token-status :active-themes #{theme-id} - :active-sets #{set-id})] + :active-sets #{set-id})] (t/is (ctos/token-status? status)) (t/is (ctos/check-token-status status)) - (t/is (= (ctos/active-theme-count status) 1)) + (t/is (= (ctos/active-themes-count status) 1)) (t/is (ctos/theme-active? status theme-id)) (t/is (= (ctos/active-set-count status) 1)) (t/is (ctos/set-active? status set-id)))) @@ -32,7 +32,7 @@ (let [status (ctos/make-token-status)] (t/is (ctos/token-status? status)) (t/is (ctos/check-token-status status)) - (t/is (= (ctos/active-theme-count status) 0)) + (t/is (= (ctos/active-themes-count status) 0)) (t/is (= (ctos/active-set-count status) 0)))) (t/deftest make-invalid-token-status @@ -47,29 +47,35 @@ (t/deftest activate-theme (let [theme-id (uuid/next) + tokens-lib (-> (ctob/make-tokens-lib) + (ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme"))) status (ctos/make-token-status) - status' (ctos/activate-theme status theme-id)] + status' (ctos/activate-theme status tokens-lib theme-id)] (t/is (not (ctos/theme-active? status theme-id))) (t/is (ctos/theme-active? status' theme-id)) - (t/is (= (ctos/active-theme-count status') 1)))) + (t/is (= (ctos/active-themes-count status') 1)))) (t/deftest deactivate-theme (let [theme-id (uuid/next) + tokens-lib (-> (ctob/make-tokens-lib) + (ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme"))) status (ctos/make-token-status :active-themes #{theme-id}) - status' (ctos/deactivate-theme status theme-id)] + status' (ctos/deactivate-theme status tokens-lib theme-id)] (t/is (ctos/theme-active? status theme-id)) (t/is (not (ctos/theme-active? status' theme-id))) - (t/is (= (ctos/active-theme-count status') 0)))) + (t/is (= (ctos/active-themes-count status') 0)))) -(t/deftest toggle-theme-active +(t/deftest set-theme-status (let [theme-id (uuid/next) + tokens-lib (-> (ctob/make-tokens-lib) + (ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme"))) status (ctos/make-token-status) - status' (ctos/toggle-theme-active status theme-id) - status'' (ctos/toggle-theme-active status' theme-id)] + status' (ctos/set-theme-status status tokens-lib theme-id true) + status'' (ctos/set-theme-status status' tokens-lib theme-id false)] (t/is (ctos/theme-active? status' theme-id)) (t/is (not (ctos/theme-active? status'' theme-id))) - (t/is (= (ctos/active-theme-count status') 1)) - (t/is (= (ctos/active-theme-count status'') 0)))) + (t/is (= (ctos/active-themes-count status') 1)) + (t/is (= (ctos/active-themes-count status'') 0)))) (t/deftest activate-set (let [set-id (uuid/next) @@ -165,7 +171,7 @@ token-status (ctos/make-token-status-from-lib tokens-lib)] (t/is (ctos/token-status? token-status)) (t/is (ctos/check-token-status token-status)) - (t/is (= (ctos/active-theme-count token-status) 2)) + (t/is (= (ctos/active-themes-count token-status) 2)) (t/is (ctos/theme-active? token-status (thi/id :theme-1))) (t/is (ctos/theme-active? token-status (thi/id :theme-2))) (t/is (= (ctos/active-set-count token-status) 2)) diff --git a/frontend/src/app/main/ui/workspace/tokens/themes/theme_selector.cljs b/frontend/src/app/main/ui/workspace/tokens/themes/theme_selector.cljs index 92f5f6ad03..cc02d29a44 100644 --- a/frontend/src/app/main/ui/workspace/tokens/themes/theme_selector.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/themes/theme_selector.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.common.types.token-status :as ctos] [app.common.types.tokens-lib :as ctob] [app.common.uuid :as uuid] [app.main.data.modal :as modal] @@ -15,6 +16,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.context :as ctx] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.hooks :as hooks] @@ -24,17 +26,16 @@ [rumext.v2 :as mf])) (mf/defc themes-list* - [{:keys [themes active-theme-paths on-close is-grouped]}] + [{:keys [themes token-status on-close is-grouped]}] (when (seq themes) [:ul {:class (stl/css :theme-options)} (for [[_ {:keys [id name] :as theme}] themes - :let [theme-id (ctob/get-theme-path theme) - selected? (get active-theme-paths theme-id) + :let [selected? (ctos/theme-active? token-status id) select-theme (fn [e] (dom/stop-propagation e) (st/emit! (dwtl/toggle-token-theme-active id)) (on-close))]] - [:li {:key theme-id + [:li {:key (str "theme-" id) :role "option" :aria-selected selected? :class (stl/css-case @@ -53,35 +54,42 @@ (modal/show! :tokens/themes {})) (mf/defc theme-options* - [{:keys [active-theme-paths themes on-close]}] - [:ul {:class (stl/css :theme-options :custom-select-dropdown) - :role "listbox"} - (for [[group themes] themes] - [:li {:key group - :aria-labelledby (dm/str group "-label") - :role "group"} - (when (seq group) - [:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str (str/kebab group) "-label") :title group} group]) - [:> themes-list* {:themes themes - :active-theme-paths active-theme-paths - :on-close on-close - :is-grouped true}]]) - [:li {:class (stl/css :separator) - :aria-hidden true}] - [:li {:class (stl/css-case :checked-element true - :checked-element-button true) - :role "option" - :on-click open-tokens-theme-modal} - [:> text* {:as "span" :typography "body-small"} (tr "workspace.tokens.edit-themes")] - [:> icon* {:icon-id i/arrow-right :aria-hidden true}]]]) + [{:keys [tokens-lib token-status on-close]}] + (let [themes (ctob/get-theme-tree-no-hidden tokens-lib)] + [:ul {:class (stl/css :theme-options :custom-select-dropdown) + :role "listbox"} + (for [[group themes] themes] + [:li {:key group + :aria-labelledby (dm/str group "-label") + :role "group"} + (when (seq group) + [:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str (str/kebab group) "-label") :title group} group]) + [:> themes-list* {:themes themes + :token-status token-status + ;; :active-theme-paths active-theme-paths + :on-close on-close + :is-grouped true}]]) + [:li {:class (stl/css :separator) + :aria-hidden true}] + [:li {:class (stl/css-case :checked-element true + :checked-element-button true) + :role "option" + :on-click open-tokens-theme-modal} + [:> text* {:as "span" :typography "body-small"} (tr "workspace.tokens.edit-themes")] + [:> icon* {:icon-id i/arrow-right :aria-hidden true}]]])) (mf/defc theme-selector* [{:keys []}] (let [;; Store - active-theme-paths (mf/deref refs/workspace-active-theme-paths-no-hidden) - active-themes-count (count active-theme-paths) - themes (mf/deref refs/workspace-token-theme-tree-no-hidden) + tokens-lib (mf/use-ctx ctx/tokens-lib) + token-status (mf/use-ctx ctx/token-status) + + active-themes-count (ctos/active-themes-count token-status) + active-theme-paths (-> (ctob/get-active-theme-paths tokens-lib) ;; TODO replace by a call to ctos + (disj ctob/hidden-theme-path)) + can-edit? (:can-edit (deref refs/permissions)) + ;; Data current-label (cond (> active-themes-count 1) (tr "workspace.tokens.active-themes" active-themes-count) @@ -140,7 +148,9 @@ [:& dropdown {:show is-open? :on-close on-close-dropdown} - [:> theme-options* {:active-theme-paths active-theme-paths - :themes themes + [:> theme-options* {;;:active-theme-paths active-theme-paths + ;;:themes themes + :tokens-lib tokens-lib + :token-status token-status :on-close on-close-dropdown}]]]) container))]))