diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index 2e8a4e2cce..104948105c 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -47,10 +47,12 @@ :string :typography}) +(def token-name-ref :string) + (sm/register! ::token [:map {:title "Token"} [:id ::sm/uuid] - [:name :string] + [:name token-name-ref] [:type [::sm/one-of token-types]] [:value :any] [:description {:optional true} :string] @@ -58,48 +60,48 @@ (sm/register! ::border-radius [:map - [:rx {:optional true} ::sm/uuid] - [:ry {:optional true} ::sm/uuid] - [:r1 {:optional true} ::sm/uuid] - [:r2 {:optional true} ::sm/uuid] - [:r3 {:optional true} ::sm/uuid] - [:r4 {:optional true} ::sm/uuid]]) + [:rx {:optional true} token-name-ref] + [:ry {:optional true} token-name-ref] + [:r1 {:optional true} token-name-ref] + [:r2 {:optional true} token-name-ref] + [:r3 {:optional true} token-name-ref] + [:r4 {:optional true} token-name-ref]]) (def border-radius-keys (schema-keys ::border-radius)) (sm/register! ::stroke-width [:map - [:stroke-width {:optional true} ::sm/uuid]]) + [:stroke-width {:optional true} token-name-ref]]) (def stroke-width-keys (schema-keys ::stroke-width)) (sm/register! ::sizing [:map - [:width {:optional true} ::sm/uuid] - [:height {:optional true} ::sm/uuid] - [:layout-item-min-w {:optional true} ::sm/uuid] - [:layout-item-max-w {:optional true} ::sm/uuid] - [:layout-item-min-h {:optional true} ::sm/uuid] - [:layout-item-max-h {:optional true} ::sm/uuid]]) + [:width {:optional true} token-name-ref] + [:height {:optional true} token-name-ref] + [:layout-item-min-w {:optional true} token-name-ref] + [:layout-item-max-w {:optional true} token-name-ref] + [:layout-item-min-h {:optional true} token-name-ref] + [:layout-item-max-h {:optional true} token-name-ref]]) (def sizing-keys (schema-keys ::sizing)) (sm/register! ::opacity [:map - [:opacity {:optional true} ::sm/uuid]]) + [:opacity {:optional true} token-name-ref]]) (def opacity-keys (schema-keys ::opacity)) (sm/register! ::spacing [:map - [:row-gap {:optional true} ::sm/uuid] - [:column-gap {:optional true} ::sm/uuid] - [:p1 {:optional true} ::sm/uuid] - [:p2 {:optional true} ::sm/uuid] - [:p3 {:optional true} ::sm/uuid] - [:p4 {:optional true} ::sm/uuid] - [:x {:optional true} ::sm/uuid] - [:y {:optional true} ::sm/uuid]]) + [:row-gap {:optional true} token-name-ref] + [:column-gap {:optional true} token-name-ref] + [:p1 {:optional true} token-name-ref] + [:p2 {:optional true} token-name-ref] + [:p3 {:optional true} token-name-ref] + [:p4 {:optional true} token-name-ref] + [:x {:optional true} token-name-ref] + [:y {:optional true} token-name-ref]]) (def spacing-keys (schema-keys ::spacing)) @@ -113,7 +115,7 @@ (sm/register! ::rotation [:map - [:rotation {:optional true} ::sm/uuid]]) + [:rotation {:optional true} token-name-ref]]) (def rotation-keys (schema-keys ::rotation)) diff --git a/frontend/src/app/main/data/tokens.cljs b/frontend/src/app/main/data/tokens.cljs index 84c83ef0ac..a27c7f30cb 100644 --- a/frontend/src/app/main/data/tokens.cljs +++ b/frontend/src/app/main/data/tokens.cljs @@ -16,6 +16,7 @@ [app.main.data.workspace.shapes :as dwsh] [app.main.refs :as refs] [app.main.ui.workspace.tokens.common :refer [workspace-shapes]] + [app.main.ui.workspace.tokens.token :as wtt] [beicon.v2.core :as rx] [clojure.data :as data] [cuerdas.core :as str] @@ -55,22 +56,22 @@ (first))] shape)) -(defn token-from-attributes [token-id attributes] - (->> (map (fn [attr] [attr token-id]) attributes) +(defn token-from-attributes [token attributes] + (->> (map (fn [attr] [attr (wtt/token-identifier token)]) attributes) (into {}))) (defn unapply-token-id [shape attributes] (update shape :applied-tokens d/without-keys attributes)) -(defn apply-token-id-to-attributes [{:keys [shape token-id attributes]}] - (let [token (token-from-attributes token-id attributes)] +(defn apply-token-to-attributes [{:keys [shape token attributes]}] + (let [token (token-from-attributes token attributes)] (toggle-or-apply-token shape token))) (defn apply-token-to-shape [{:keys [shape token attributes] :as _props}] - (let [applied-tokens (apply-token-id-to-attributes {:shape shape - :token-id (:id token) - :attributes attributes})] + (let [applied-tokens (apply-token-to-attributes {:shape shape + :token token + :attributes attributes})] (update shape :applied-tokens #(merge % applied-tokens)))) (defn maybe-apply-token-to-shape @@ -80,17 +81,6 @@ (apply-token-to-shape props) shape)) -(defn update-token-from-attributes - [{:keys [token-id shape-id attributes]}] - (ptk/reify ::update-token-from-attributes - ptk/WatchEvent - (watch [_ state _] - (let [shape (get-shape-from-state shape-id state) - applied-tokens (apply-token-id-to-attributes {:shape shape - :token-id token-id - :attributes attributes})] - (rx/of (update-shape shape-id {:applied-tokens applied-tokens})))))) - (defn get-token-data-from-token-id [id] (let [workspace-data (deref refs/workspace-data)] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 05efc28a48..e786b1141f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -300,7 +300,7 @@ (update-fn shape) shape)) {:reg-objects? true - :attrs [:rx :ry :r1 :r2 :r3 :r4]}))) + :attrs [:rx :ry :r1 :r2 :r3 :r4 :applied-tokens]}))) on-switch-to-radius-1 (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/tokens/changes.cljs b/frontend/src/app/main/ui/workspace/tokens/changes.cljs index af53ee46c1..45ff044df4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/changes.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/changes.cljs @@ -34,11 +34,10 @@ (watch [_ state _] (->> (rx/from (sd/resolve-tokens+ (get-in state [:workspace-data :tokens]))) (rx/mapcat - (fn [sd-tokens] + (fn [resolved-tokens] (let [undo-id (js/Symbol) - resolved-value (-> (get sd-tokens (:id token)) - (wtt/resolve-token-value)) - tokenized-attributes (wtt/attributes-map attributes (:id token))] + resolved-value (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value]) + tokenized-attributes (wtt/attributes-map attributes token)] (rx/of (dwu/start-undo-transaction undo-id) (dwsh/update-shapes shape-ids (fn [shape] @@ -58,7 +57,7 @@ ptk/WatchEvent (watch [_ _ _] (rx/of - (let [remove-token #(when % (wtt/remove-attributes-for-token-id attributes (:id token) %))] + (let [remove-token #(when % (wtt/remove-attributes-for-token attributes token %))] (dwsh/update-shapes shape-ids (fn [shape] diff --git a/frontend/src/app/main/ui/workspace/tokens/form.cljs b/frontend/src/app/main/ui/workspace/tokens/form.cljs index 5094f29eff..4521906289 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/form.cljs @@ -92,7 +92,7 @@ Token names should only contain letters and digits separated by . characters.")} ;; When creating a new token we dont have a token name yet, ;; so we use a temporary token name that hopefully doesn't clash with any of the users token names. token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value) - token-references (sd/find-token-references input) + token-references (wtt/find-token-references input) direct-self-reference? (get token-references token-name)] (cond empty-input? (p/rejected nil) @@ -104,7 +104,7 @@ Token names should only contain letters and digits separated by . characters.")} (-> (sd/resolve-tokens+ new-tokens #_ {:debug? true}) (p/then (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-id)] + (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)] (cond resolved-value (p/resolved resolved-token) (sd/missing-reference-error? errors) (p/rejected :error/token-missing-reference) diff --git a/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs b/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs index 738cdbc1e7..81881d8109 100644 --- a/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs @@ -2,13 +2,11 @@ (:require ["@tokens-studio/sd-transforms" :as sd-transforms] ["style-dictionary$default" :as sd] - [app.common.data :as d] [app.main.refs :as refs] [app.main.ui.workspace.tokens.token :as wtt] [cuerdas.core :as str] [promesa.core :as p] - [rumext.v2 :as mf] - [shadow.resource])) + [rumext.v2 :as mf])) (def StyleDictionary "The global StyleDictionary instance used as an external library for now, @@ -23,13 +21,6 @@ ;; Functions ------------------------------------------------------------------- -(defn find-token-references - "Finds token reference values in `value-string` and returns a set with all contained namespaces." - [value-string] - (some->> (re-seq #"\{([^}]*)\}" value-string) - (map second) - (into #{}))) - (defn tokens->style-dictionary+ "Resolves references and math expressions using StyleDictionary. Returns a promise with the resolved dictionary." @@ -88,16 +79,16 @@ (resolve-sd-tokens+ config))] (let [resolved-tokens (reduce (fn [acc ^js cur] - (let [value (.-value cur) - resolved-value (d/parse-double (.-value cur)) - original-value (-> cur .-original .-value) - id (uuid (.-uuid (.-id cur))) - missing-reference? (and (not resolved-value) - (re-find #"\{" value) - (= value original-value))] - (cond-> (assoc-in acc [id :resolved-value] resolved-value) - missing-reference? (update-in [id :errors] (fnil conj #{}) :style-dictionary/missing-reference)))) - tokens sd-tokens)] + (let [id (uuid (.-uuid (.-id cur))) + origin-token (get tokens id) + parsed-value (wtt/parse-token-value (.-value cur)) + resolved-token (if (not parsed-value) + (assoc origin-token :errors [:style-dictionary/missing-reference]) + (assoc origin-token + :resolved-value (:value parsed-value) + :resolved-unit (:unit parsed-value)))] + (assoc acc (wtt/token-identifier resolved-token) resolved-token))) + {} sd-tokens)] (when debug? (js/console.log "Resolved tokens" resolved-tokens)) resolved-tokens))) @@ -148,20 +139,31 @@ (comment (defonce !output (atom nil)) + (-> @refs/workspace-tokens + (resolve-tokens+ {:debug? false}) + (.then js/console.log)) + (-> (resolve-workspace-tokens+ {:debug? true}) (p/then #(reset! !output %))) + @!output + (->> @refs/workspace-tokens - (resolve-tokens+)) + (resolve-tokens+) + (#(doto % js/console.log))) (-> (clj->js {"a" {:name "a" :value "5"} "b" {:name "b" :value "{a} * 2"}}) + (#(resolve-sd-tokens+ % {:debug? true}))) + (defonce output (atom nil)) + (require '[shadow.resource]) (let [example (-> (shadow.resource/inline "./data/example-tokens-set.json") (js/JSON.parse) .-core)] - (resolve-sd-tokens+ example {:debug? true})) + (.then (resolve-sd-tokens+ example {:debug? true}) + #(reset! output %))) nil) diff --git a/frontend/src/app/main/ui/workspace/tokens/token.cljs b/frontend/src/app/main/ui/workspace/tokens/token.cljs index b0d12d2205..9c0efd356d 100644 --- a/frontend/src/app/main/ui/workspace/tokens/token.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/token.cljs @@ -4,6 +4,32 @@ [clojure.set :as set] [cuerdas.core :as str])) +(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." + #"^\s*(-?[0-9]+\.?[0-9]*)(px|%)?\s*$") + +(defn parse-token-value + "Parses a resolved value and separates the unit from the value. + Returns a map of {:value `number` :unit `string`}." + [value] + (cond + (number? value) {:value value} + (string? value) (when-let [[_ value unit] (re-find parseable-token-value-regexp value)] + (when-let [parsed-value (d/parse-double value)] + {:value parsed-value + :unit unit})))) + +(defn find-token-references + "Finds token reference values in `value-string` and returns a set with all contained namespaces." + [value-string] + (some->> (re-seq #"\{([^}]*)\}" value-string) + (map second) + (into #{}))) + +(defn token-identifier [{:keys [name] :as _token}] + name) + (defn resolve-token-value [{:keys [value resolved-value] :as _token}] (or resolved-value @@ -11,17 +37,17 @@ (defn attributes-map "Creats an attributes map using collection of `attributes` for `id`." - [attributes id] - (->> (map (fn [attr] {attr id}) attributes) + [attributes token] + (->> (map (fn [attr] [attr (token-identifier token)]) attributes) (into {}))) -(defn remove-attributes-for-token-id +(defn remove-attributes-for-token "Removes applied tokens with `token-id` for the given `attributes` set from `applied-tokens`." - [attributes token-id applied-tokens] + [attributes token applied-tokens] (let [attr? (set attributes)] (->> (remove (fn [[k v]] (and (attr? k) - (= v token-id))) + (= v (token-identifier token)))) applied-tokens) (into {})))) @@ -29,7 +55,7 @@ "Test if `token` is applied to a `shape` on single `token-attribute`." [token shape token-attribute] (when-let [id (get-in shape [:applied-tokens token-attribute])] - (= (:id token) id))) + (= (token-identifier token) id))) (defn token-applied? "Test if `token` is applied to a `shape` with at least one of the one of the given `token-attributes`." diff --git a/frontend/test/token_tests/helpers/tokens.cljs b/frontend/test/token_tests/helpers/tokens.cljs index 716dd07558..87db316fb9 100644 --- a/frontend/test/token_tests/helpers/tokens.cljs +++ b/frontend/test/token_tests/helpers/tokens.cljs @@ -15,8 +15,8 @@ (defn apply-token-to-shape [file shape-label token-label attributes] (let [first-page-id (get-in file [:data :pages 0]) shape-id (thi/id shape-label) - token-id (thi/id token-label) - applied-attributes (wtt/attributes-map attributes token-id)] + token (get-token file token-label) + applied-attributes (wtt/attributes-map attributes token)] (update-in file [:data :pages-index first-page-id :objects shape-id diff --git a/frontend/test/token_tests/logic/token_actions_test.cljs b/frontend/test/token_tests/logic/token_actions_test.cljs index beef098473..e0d3fac38d 100644 --- a/frontend/test/token_tests/logic/token_actions_test.cljs +++ b/frontend/test/token_tests/logic/token_actions_test.cljs @@ -5,6 +5,7 @@ [app.common.test-helpers.files :as cthf] [app.common.test-helpers.shapes :as cths] [app.main.ui.workspace.tokens.changes :as wtch] + [app.main.ui.workspace.tokens.token :as wtt] [cljs.test :as t :include-macros true] [frontend-tests.helpers.pages :as thp] [frontend-tests.helpers.state :as ths] @@ -31,31 +32,58 @@ :type :border-radius}))) (t/deftest test-apply-token - (t/testing "applying a token twice with the same attributes will override the previous applied token" + (t/testing "applies token to shape and updates shape attributes to resolved value" (t/async - done - (let [file (setup-file) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtch/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-1) - :on-update-shape wtch/update-shape-radius-all}) - (wtch/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtch/update-shape-radius-all})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-2' (toht/get-token file' :token-2) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:rx rect-1') 24)) - (t/is (= (:ry rect-1') 24))))))))) + done + (let [file (setup-file) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtch/update-shape-radius-all})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-2' (toht/get-token file' :token-2) + rect-1' (cths/get-shape file' :rect-1)] + (t/testing "shape `:applied-tokens` got updated" + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) + (t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) + (t/testing "shape radius got update to the resolved token value." + (t/is (= (:rx rect-1') 24)) + (t/is (= (:ry rect-1') 24)))))))))) + +(t/deftest test-apply-multiple-tokens + (t/testing "applying a token twice with the same attributes will override the previously applied tokens values" + (t/async + done + (let [file (setup-file) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-1) + :on-update-shape wtch/update-shape-radius-all}) + (wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:rx :ry} + :token (toht/get-token file :token-2) + :on-update-shape wtch/update-shape-radius-all})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-2' (toht/get-token file' :token-2) + rect-1' (cths/get-shape file' :rect-1)] + (t/testing "shape `:applied-tokens` got updated" + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) + (t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) + (t/testing "shape radius got update to the resolved token value." + (t/is (= (:rx rect-1') 24)) + (t/is (= (:ry rect-1') 24)))))))))) (t/deftest test-apply-token-overwrite (t/testing "removes old token attributes and applies only single attribute" @@ -87,58 +115,37 @@ (t/testing "other border-radius attributes got removed" (t/is (nil? (:rx (:applied-tokens rect-1'))))) (t/testing "r1 got applied with :token-2" - (t/is (= (:r1 (:applied-tokens rect-1')) (:id token-2')))) + (t/is (= (:r1 (:applied-tokens rect-1')) (wtt/token-identifier token-2')))) (t/testing "while :r4 was kept" - (t/is (= (:r4 (:applied-tokens rect-1')) (:id token-1')))))))))));))))))))))) - -(t/deftest test-apply-border-radius - (t/testing "applies radius token and updates the shapes radius" - (t/async - done - (let [file (setup-file) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtch/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:rx :ry} - :token (toht/get-token file :token-2) - :on-update-shape wtch/update-shape-radius-all})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-2' (toht/get-token file' :token-2) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:rx rect-1') 24)) - (t/is (= (:ry rect-1') 24))))))))) + (t/is (= (:r4 (:applied-tokens rect-1')) (wtt/token-identifier token-1')))))))))));))))))))))) (t/deftest test-apply-dimensions (t/testing "applies dimensions token and updates the shapes width and height" (t/async - done - (let [file (-> (setup-file) - (toht/add-token :token-target {:value "100" - :name "dimensions.sm" - :type :dimensions})) - store (ths/setup-store file) - rect-1 (cths/get-shape file :rect-1) - events [(wtch/apply-token {:shape-ids [(:id rect-1)] - :attributes #{:width :height} - :token (toht/get-token file :token-target) - :on-update-shape wtch/update-shape-dimensions})]] - (tohs/run-store-async - store done events - (fn [new-state] - (let [file' (ths/get-file-from-store new-state) - token-target' (toht/get-token file' :token-target) - rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100))))))))) + done + (let [file (-> (setup-file) + (toht/add-token :token-target {:value "100" + :name "dimensions.sm" + :type :dimensions})) + store (ths/setup-store file) + rect-1 (cths/get-shape file :rect-1) + events [(wtch/apply-token {:shape-ids [(:id rect-1)] + :attributes #{:width :height} + :token (toht/get-token file :token-target) + :on-update-shape wtch/update-shape-dimensions})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-store new-state) + token-target' (toht/get-token file' :token-target) + rect-1' (cths/get-shape file' :rect-1)] + (t/testing "shape `:applied-tokens` got updated" + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:width (:applied-tokens rect-1')) (wtt/token-identifier token-target'))) + (t/is (= (:height (:applied-tokens rect-1')) (wtt/token-identifier token-target')))) + (t/testing "shapes width and height got updated" + (t/is (= (:width rect-1') 100)) + (t/is (= (:height rect-1') 100)))))))))) (t/deftest test-apply-sizing (t/testing "applies sizing token and updates the shapes width and height" @@ -160,11 +167,13 @@ (let [file' (ths/get-file-from-store new-state) token-target' (toht/get-token file' :token-target) rect-1' (cths/get-shape file' :rect-1)] - (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:width (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:height (:applied-tokens rect-1')) (:id token-target'))) - (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100))))))))) + (t/testing "shape `:applied-tokens` got updated" + (t/is (some? (:applied-tokens rect-1'))) + (t/is (= (:width (:applied-tokens rect-1')) (wtt/token-identifier token-target'))) + (t/is (= (:height (:applied-tokens rect-1')) (wtt/token-identifier token-target')))) + (t/testing "shapes width and height got updated" + (t/is (= (:width rect-1') 100)) + (t/is (= (:height rect-1') 100)))))))))) (t/deftest test-apply-opacity (t/testing "applies opacity token and updates the shapes opacity" @@ -207,13 +216,13 @@ token-opacity-percent (toht/get-token file' :opacity-percent) token-opacity-invalid (toht/get-token file' :opacity-invalid)] (t/testing "float value got translated to float and applied to opacity" - (t/is (= (:opacity (:applied-tokens rect-1')) (:id token-opacity-float))) + (t/is (= (:opacity (:applied-tokens rect-1')) (wtt/token-identifier token-opacity-float))) (t/is (= (:opacity rect-1') 0.3))) (t/testing "percentage value got translated to float and applied to opacity" - (t/is (= (:opacity (:applied-tokens rect-2')) (:id token-opacity-percent))) + (t/is (= (:opacity (:applied-tokens rect-2')) (wtt/token-identifier token-opacity-percent))) (t/is (= (:opacity rect-2') 0.4))) (t/testing "invalid opacity value got applied but did not change shape" - (t/is (= (:opacity (:applied-tokens rect-3')) (:id token-opacity-invalid))) + (t/is (= (:opacity (:applied-tokens rect-3')) (wtt/token-identifier token-opacity-invalid))) (t/is (nil? (:opacity rect-3'))))))))))) (t/deftest test-apply-rotation @@ -237,7 +246,7 @@ token-target' (toht/get-token file' :token-target) rect-1' (cths/get-shape file' :rect-1)] (t/is (some? (:applied-tokens rect-1'))) - (t/is (= (:rotation (:applied-tokens rect-1')) (:id token-target'))) + (t/is (= (:rotation (:applied-tokens rect-1')) (wtt/token-identifier token-target'))) (t/is (= (:rotation rect-1') 120))))))))) (t/deftest test-apply-stroke-width @@ -267,10 +276,10 @@ rect-with-stroke' (cths/get-shape file' :rect-1) rect-without-stroke' (cths/get-shape file' :rect-2)] (t/testing "token got applied to rect with stroke and shape stroke got updated" - (t/is (= (:stroke-width (:applied-tokens rect-with-stroke')) (:id token-target'))) + (t/is (= (:stroke-width (:applied-tokens rect-with-stroke')) (wtt/token-identifier token-target'))) (t/is (= (get-in rect-with-stroke' [:strokes 0 :stroke-width]) 10))) (t/testing "token got applied to rect without stroke but shape didnt get updated" - (t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (:id token-target'))) + (t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (wtt/token-identifier token-target'))) (t/is (empty? (:strokes rect-without-stroke'))))))))))) (t/deftest test-toggle-token-none @@ -294,10 +303,10 @@ rect-2' (cths/get-shape file' :rect-2)] (t/is (some? (:applied-tokens rect-1'))) (t/is (some? (:applied-tokens rect-2'))) - (t/is (= (:rx (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:rx (:applied-tokens rect-2')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-1')) (:id token-2'))) - (t/is (= (:ry (:applied-tokens rect-2')) (:id token-2'))) + (t/is (= (:rx (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) + (t/is (= (:rx (:applied-tokens rect-2')) (wtt/token-identifier token-2'))) + (t/is (= (:ry (:applied-tokens rect-1')) (wtt/token-identifier token-2'))) + (t/is (= (:ry (:applied-tokens rect-2')) (wtt/token-identifier token-2'))) (t/is (= (:rx rect-1') 24)) (t/is (= (:rx rect-2') 24))))))))) @@ -361,10 +370,10 @@ rect-with-other-token-2' (cths/get-shape file' :rect-3)] (t/testing "token got applied to all shapes" - (t/is (= (:rx (:applied-tokens rect-with-other-token-1')) (:id target-token))) - (t/is (= (:rx (:applied-tokens rect-without-token')) (:id target-token))) - (t/is (= (:rx (:applied-tokens rect-with-other-token-2')) (:id target-token))) + (t/is (= (:rx (:applied-tokens rect-with-other-token-1')) (wtt/token-identifier target-token))) + (t/is (= (:rx (:applied-tokens rect-without-token')) (wtt/token-identifier target-token))) + (t/is (= (:rx (:applied-tokens rect-with-other-token-2')) (wtt/token-identifier target-token))) - (t/is (= (:ry (:applied-tokens rect-with-other-token-1')) (:id target-token))) - (t/is (= (:ry (:applied-tokens rect-without-token')) (:id target-token))) - (t/is (= (:ry (:applied-tokens rect-with-other-token-2')) (:id target-token))))))))))) + (t/is (= (:ry (:applied-tokens rect-with-other-token-1')) (wtt/token-identifier target-token))) + (t/is (= (:ry (:applied-tokens rect-without-token')) (wtt/token-identifier target-token))) + (t/is (= (:ry (:applied-tokens rect-with-other-token-2')) (wtt/token-identifier target-token))))))))))) diff --git a/frontend/test/token_tests/style_dictionary_test.cljs b/frontend/test/token_tests/style_dictionary_test.cljs deleted file mode 100644 index ff03ba16c8..0000000000 --- a/frontend/test/token_tests/style_dictionary_test.cljs +++ /dev/null @@ -1,20 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS INC - -(ns token-tests.style-dictionary-test - (:require - [app.main.ui.workspace.tokens.style-dictionary :as wtsd] - [cljs.test :as t :include-macros true])) - -(t/deftest test-find-token-references - ;; Return references - (t/is (= #{"foo" "bar"} (wtsd/find-token-references "{foo} + {bar}"))) - ;; Ignore non reference text - (t/is (= #{"foo.bar.baz"} (wtsd/find-token-references "{foo.bar.baz} + something"))) - ;; No references found - (t/is (nil? (wtsd/find-token-references "1 + 2"))) - ;; Edge-case: Ignore unmatched closing parens - (t/is (= #{"foo" "bar"} (wtsd/find-token-references "{foo}} + {bar}")))) diff --git a/frontend/test/token_tests/token_test.cljs b/frontend/test/token_tests/token_test.cljs index bcf86fcc99..4b38c889cc 100644 --- a/frontend/test/token_tests/token_test.cljs +++ b/frontend/test/token_tests/token_test.cljs @@ -9,67 +9,95 @@ [app.main.ui.workspace.tokens.token :as wtt] [cljs.test :as t :include-macros true])) +(t/deftest test-parse-token-value + (t/testing "parses double from a token value" + (t/is (= {:value 100.1 :unit nil} (wtt/parse-token-value "100.1"))) + (t/is (= {:value -9 :unit nil} (wtt/parse-token-value "-9")))) + (t/testing "trims white-space" + (t/is (= {:value -1.3 :unit nil} (wtt/parse-token-value " -1.3 ")))) + (t/testing "parses unit: px" + (t/is (= {:value 70.3 :unit "px"} (wtt/parse-token-value " 70.3px ")))) + (t/testing "parses unit: %" + (t/is (= {:value -10 :unit "%"} (wtt/parse-token-value "-10%")))) + (t/testing "parses unit: px") + (t/testing "returns nil for any invalid characters" + (t/is (nil? (wtt/parse-token-value " -1.3a ")))) + (t/testing "doesnt accept invalid double" + (t/is (nil? (wtt/parse-token-value ".3"))))) + +(t/deftest find-token-references + (t/testing "finds references inside curly braces in a string" + (t/is (= #{"foo" "bar"} (wtt/find-token-references "{foo} + {bar}"))) + (t/testing "ignores extra text" + (t/is (= #{"foo.bar.baz"} (wtt/find-token-references "{foo.bar.baz} + something")))) + (t/testing "ignores string without references" + (t/is (nil? (wtt/find-token-references "1 + 2")))) + (t/testing "handles edge-case for extra curly braces" + (t/is (= #{"foo" "bar"} (wtt/find-token-references "{foo}} + {bar}")))))) + (t/deftest remove-attributes-for-token-id - (t/testing "removes attributes matching the `token-id`, keeps other attributes" - (t/is (= {:ry :b} - (wtt/remove-attributes-for-token-id - #{:rx :ry} :a {:rx :a :ry :b}))))) + (t/testing "removes attributes matching the `token`, keeps other attributes" + (t/is (= {:ry "b"} + (wtt/remove-attributes-for-token #{:rx :ry} {:name "a"} {:rx "a" :ry "b"}))))) (t/deftest token-applied-test (t/testing "matches passed token with `:token-attributes`" - (t/is (true? (wtt/token-applied? {:id :a} {:applied-tokens {:x :a}} #{:x})))) + (t/is (true? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:x})))) (t/testing "doesn't match empty token" - (t/is (nil? (wtt/token-applied? {} {:applied-tokens {:x :a}} #{:x})))) + (t/is (nil? (wtt/token-applied? {} {:applied-tokens {:x "a"}} #{:x})))) (t/testing "does't match passed token `:id`" - (t/is (nil? (wtt/token-applied? {:id :b} {:applied-tokens {:x :a}} #{:x})))) + (t/is (nil? (wtt/token-applied? {:name "b"} {:applied-tokens {:x "a"}} #{:x})))) (t/testing "doesn't match passed `:token-attributes`" - (t/is (nil? (wtt/token-applied? {:id :a} {:applied-tokens {:x :a}} #{:y}))))) + (t/is (nil? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:y}))))) (t/deftest token-applied-attributes - (t/is (= #{:x} (wtt/token-applied-attributes {:id :a} - {:applied-tokens {:x :a :y :b}} + (t/is (= #{:x} (wtt/token-applied-attributes {:name "a"} + {:applied-tokens {:x "a" :y "b"}} #{:x :missing})))) (t/deftest shapes-ids-by-applied-attributes (t/testing "Returns set of matched attributes that fit the applied token" (let [attributes #{:x :y :z} - shape-applied-x {:id :shape-applied-x - :applied-tokens {:x 1}} - shape-applied-y {:id :shape-applied-y - :applied-tokens {:y 1}} - shape-applied-x-y {:id :shape-applied-x-y - :applied-tokens {:x 1 :y 1}} - shape-applied-none {:id :shape-applied-none + shape-applied-x {:id "shape-applied-x" + :applied-tokens {:x "1"}} + shape-applied-y {:id "shape-applied-y" + :applied-tokens {:y "1"}} + shape-applied-x-y {:id "shape-applied-x-y" + :applied-tokens {:x "1" :y "1"}} + shape-applied-none {:id "shape-applied-none" :applied-tokens {}} - shape-applied-all {:id :shape-applied-all - :applied-tokens {:x 1 :y 1 :z 1}} - ids-set (fn [& xs] (into #{} (map :id xs))) + shape-applied-all {:id "shape-applied-all" + :applied-tokens {:x "1" :y "1" :z "1"}} + shape-ids (fn [& xs] (into #{} (map :id xs))) shapes [shape-applied-x shape-applied-y shape-applied-x-y shape-applied-all shape-applied-none] - expected (wtt/shapes-ids-by-applied-attributes {:id 1} shapes attributes)] - (t/is (= (:x expected) (ids-set shape-applied-x - shape-applied-x-y - shape-applied-all))) - (t/is (= (:y expected) (ids-set shape-applied-y - shape-applied-x-y - shape-applied-all))) - (t/is (= (:z expected) (ids-set shape-applied-all))) - (t/is (true? (wtt/shapes-applied-all? expected (ids-set shape-applied-all) attributes))) - (t/is (false? (wtt/shapes-applied-all? expected (apply ids-set shapes) attributes)))))) + expected (wtt/shapes-ids-by-applied-attributes {:name "1"} shapes attributes)] + (t/is (= (:x expected) (shape-ids shape-applied-x + shape-applied-x-y + shape-applied-all))) + (t/is (= (:y expected) (shape-ids shape-applied-y + shape-applied-x-y + shape-applied-all))) + (t/is (= (:z expected) (shape-ids shape-applied-all))) + (t/is (true? (wtt/shapes-applied-all? expected (shape-ids shape-applied-all) attributes))) + (t/is (false? (wtt/shapes-applied-all? expected (apply shape-ids shapes) attributes))) + (shape-ids shape-applied-x + shape-applied-x-y + shape-applied-all)))) (t/deftest tokens-applied-test (t/testing "is true when single shape matches the token and attributes" - (t/is (true? (wtt/shapes-token-applied? {:id :a} [{:applied-tokens {:x :a}} - {:applied-tokens {:x :b}}] + (t/is (true? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}} + {:applied-tokens {:x "b"}}] #{:x})))) (t/testing "is false when no shape matches the token or attributes" - (t/is (nil? (wtt/shapes-token-applied? {:id :a} [{:applied-tokens {:x :b}} - {:applied-tokens {:x :b}}] + (t/is (nil? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "b"}} + {:applied-tokens {:x "b"}}] #{:x}))) - (t/is (nil? (wtt/shapes-token-applied? {:id :a} [{:applied-tokens {:x :a}} - {:applied-tokens {:x :a}}] + (t/is (nil? (wtt/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}} + {:applied-tokens {:x "a"}}] #{:y}))))) (t/deftest name->path-test