mirror of
https://github.com/penpot/penpot.git
synced 2025-04-09 21:41:23 -05:00
Merge pull request #245 from tokens-studio/use-token-name-ref
Use token name ref
This commit is contained in:
commit
c7d4db900e
11 changed files with 264 additions and 228 deletions
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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`."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))))))))))
|
||||
|
|
|
@ -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}"))))
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue