mirror of
https://github.com/penpot/penpot.git
synced 2026-07-31 10:26:18 +00:00
🔧 Add TokensStatus data type in files
This commit is contained in:
parent
000401ecc4
commit
8a6e114c6a
@ -29,6 +29,7 @@
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.token-status :as ctos]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.uuid :as uuid]
|
||||
@ -86,7 +87,8 @@
|
||||
[:components {:optional true} schema:components]
|
||||
[:typographies {:optional true} schema:typographies]
|
||||
[:plugin-data {:optional true} schema:plugin-data]
|
||||
[:tokens-lib {:optional true} ctob/schema:tokens-lib]])
|
||||
[:tokens-lib {:optional true} ctob/schema:tokens-lib]
|
||||
[:token-status {:optional true} ctos/schema:token-status]])
|
||||
|
||||
(def schema:file
|
||||
"A schema for validate a file data structure; data is optional
|
||||
@ -199,7 +201,12 @@
|
||||
(defn ensure-tokens-lib
|
||||
"Ensure file-data has a :tokens-lib key, creating one if necessary."
|
||||
[file-data]
|
||||
(update file-data :tokens-lib #(or % (ctob/make-tokens-lib))))
|
||||
(if (and (some? (:tokens-lib file-data)) (nil? (:token-status file-data)))
|
||||
;; TODO: remove this when we deprecate old-style files without token-status
|
||||
(assoc file-data :token-status (ctos/make-token-status-from-lib (:tokens-lib file-data)))
|
||||
(-> file-data
|
||||
(update :tokens-lib #(or % (ctob/make-tokens-lib)))
|
||||
(update :token-status #(or % (ctos/make-token-status))))))
|
||||
|
||||
;; Helpers
|
||||
|
||||
|
||||
184
common/src/app/common/types/token_status.cljc
Normal file
184
common/src/app/common/types/token_status.cljc
Normal file
@ -0,0 +1,184 @@
|
||||
;; 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 Sucursal en España SL
|
||||
|
||||
(ns app.common.types.token-status
|
||||
(:require
|
||||
#?(:clj [app.common.fressian :as fres])
|
||||
#?(:clj [clojure.data.json :as c.json])
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[clojure.core.protocols :as cp]
|
||||
[clojure.datafy :refer [datafy]]
|
||||
[clojure.pprint :as pp]))
|
||||
|
||||
;; TokenStatus datatype contains the status of the active themes and sets
|
||||
;; in a tokens library.
|
||||
|
||||
(defprotocol ITokenStatus
|
||||
(activate-theme [_ theme-id] "Add a theme uuid to active themes")
|
||||
(deactivate-theme [_ theme-id] "Remove a theme uuid from active themes")
|
||||
(toggle-theme-active [_ theme-id] "Toggle a theme uuid in active themes")
|
||||
(theme-active? [_ theme-id] "Check if a theme uuid is active")
|
||||
(active-theme-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")
|
||||
(set-active? [_ set-id] "Check if a set uuid is active")
|
||||
(active-set-count [_] "Return the number of active sets"))
|
||||
|
||||
(deftype TokenStatus [active-themes active-sets]
|
||||
cp/Datafiable
|
||||
(datafy [_]
|
||||
{:active-themes active-themes
|
||||
:active-sets active-sets})
|
||||
|
||||
#?@(:clj
|
||||
[c.json/JSONWriter
|
||||
(-write [this writter options]
|
||||
(c.json/-write (datafy this) writter options))])
|
||||
|
||||
ITokenStatus
|
||||
(activate-theme [_ theme-id]
|
||||
(TokenStatus. (conj active-themes theme-id) active-sets))
|
||||
|
||||
(deactivate-theme [_ theme-id]
|
||||
(TokenStatus. (disj active-themes theme-id) active-sets))
|
||||
|
||||
(toggle-theme-active [this theme-id]
|
||||
(if (contains? active-themes theme-id)
|
||||
(deactivate-theme this theme-id)
|
||||
(activate-theme this theme-id)))
|
||||
|
||||
(theme-active? [_ theme-id]
|
||||
(contains? active-themes theme-id))
|
||||
|
||||
(active-theme-count [_]
|
||||
(prn active-themes)
|
||||
(count active-themes))
|
||||
|
||||
(activate-set [_ set-id]
|
||||
(TokenStatus. active-themes (conj active-sets set-id)))
|
||||
|
||||
(deactivate-set [_ set-id]
|
||||
(TokenStatus. active-themes (disj active-sets set-id)))
|
||||
|
||||
(toggle-set-active [this set-id]
|
||||
(if (contains? active-sets set-id)
|
||||
(deactivate-set this set-id)
|
||||
(activate-set this set-id)))
|
||||
|
||||
(set-active? [_ set-id]
|
||||
(contains? active-sets set-id))
|
||||
|
||||
(active-set-count [_]
|
||||
(count active-sets)))
|
||||
|
||||
;; === Helper & Predicate ===
|
||||
|
||||
(defn map->TokenStatus
|
||||
[{:keys [active-themes active-sets]}]
|
||||
(TokenStatus. active-themes active-sets))
|
||||
|
||||
(defn token-status?
|
||||
[o]
|
||||
(instance? TokenStatus o))
|
||||
|
||||
;; === Schemas, Check functions & Constructor ===
|
||||
|
||||
(declare make-token-status)
|
||||
|
||||
(def schema:token-status-attrs
|
||||
[:map {:title "TokenStatus"}
|
||||
[:active-themes [:set {:gen/max 5} ::sm/uuid]]
|
||||
[:active-sets [:set {:gen/max 5} ::sm/uuid]]])
|
||||
|
||||
(def schema:token-status
|
||||
[:and {:gen/gen (->> (sg/generator schema:token-status-attrs)
|
||||
(sg/fmap #(make-token-status %)))}
|
||||
[:fn token-status?]])
|
||||
|
||||
(def ^:private check-token-status-attrs
|
||||
(sm/check-fn schema:token-status-attrs
|
||||
:hint "expected valid params for token-status"))
|
||||
|
||||
(def check-token-status
|
||||
(sm/check-fn schema:token-status
|
||||
:hint "expected valid token-status"))
|
||||
|
||||
(defn make-token-status
|
||||
[& {:as attrs}]
|
||||
(-> attrs
|
||||
(update :active-themes #(or % #{}))
|
||||
(update :active-sets #(or % #{}))
|
||||
(check-token-status-attrs)
|
||||
(map->TokenStatus)))
|
||||
|
||||
(defn make-token-status-from-lib
|
||||
"Make a TokenStatus from a TokensLib, activating the themes and sets
|
||||
marked as active in the library (to migrate from legacy files)."
|
||||
[tokens-lib]
|
||||
(let [active-themes (into #{}
|
||||
(comp (map :id)
|
||||
(filter #(not= % ctob/hidden-theme-id)))
|
||||
(ctob/get-active-themes tokens-lib))
|
||||
active-sets (into #{}
|
||||
(comp (map #(ctob/get-set-by-name tokens-lib %))
|
||||
(map ctob/get-id))
|
||||
(ctob/get-active-themes-set-names tokens-lib))]
|
||||
(make-token-status :active-themes active-themes
|
||||
:active-sets active-sets)))
|
||||
|
||||
;; === Pretty-print for debugging ===
|
||||
|
||||
(defmethod pp/simple-dispatch TokenStatus [^TokenStatus obj]
|
||||
(.write *out* "#penpot/token-status ")
|
||||
(pp/pprint-newline :miser)
|
||||
(pp/pprint (datafy obj)))
|
||||
|
||||
#?(:clj
|
||||
(do
|
||||
(defmethod print-method TokenStatus
|
||||
[^TokenStatus this ^java.io.Writer w]
|
||||
(.write w "#penpot/token-status ")
|
||||
(print-method (datafy this) w))
|
||||
|
||||
(defmethod print-dup TokenStatus
|
||||
[^TokenStatus this ^java.io.Writer w]
|
||||
(print-method this w)))
|
||||
|
||||
:cljs
|
||||
(extend-type TokenStatus
|
||||
cljs.core/IPrintWithWriter
|
||||
(-pr-writer [this writer opts]
|
||||
(-write writer "#penpot/token-status ")
|
||||
(-pr-writer (datafy this) writer opts))
|
||||
|
||||
cljs.core/IEncodeJS
|
||||
(-clj->js [this]
|
||||
(clj->js (datafy this)))))
|
||||
|
||||
;; === Transit serialization ===
|
||||
|
||||
(t/add-handlers!
|
||||
{:id "penpot/token-status"
|
||||
:class TokenStatus
|
||||
:wfn datafy
|
||||
:rfn #(make-token-status %)})
|
||||
|
||||
;; === Fressian serialization ===
|
||||
|
||||
#?(:clj
|
||||
(fres/add-handlers!
|
||||
{:name "penpot/token-status/v1"
|
||||
:class TokenStatus
|
||||
:wfn (fn [n w o]
|
||||
(fres/write-tag! w n 1)
|
||||
(fres/write-object! w (datafy o)))
|
||||
:rfn (fn [r]
|
||||
(let [obj (fres/read-object! r)]
|
||||
(make-token-status obj)))}))
|
||||
58
common/test/common_tests/types/file_test.cljc
Normal file
58
common/test/common_tests/types/file_test.cljc
Normal file
@ -0,0 +1,58 @@
|
||||
;; 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 Sucursal en España SL
|
||||
|
||||
(ns common-tests.types.file-test
|
||||
(:require
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.token-status :as ctos]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/deftest test-ensure-tokens-lib
|
||||
(t/testing "ensure-tokens-lib should add a tokens-lib and token-status to the file data if they are missing, and should not modify them if they already exist"
|
||||
(let [file (thf/sample-file :file1)
|
||||
file-data (ctf/file-data file)
|
||||
file-data' (ctf/ensure-tokens-lib 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 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)))))
|
||||
|
||||
173
common/test/common_tests/types/token_status_test.cljc
Normal file
173
common/test/common_tests/types/token_status_test.cljc
Normal file
@ -0,0 +1,173 @@
|
||||
;; 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 Sucursal en España SL
|
||||
|
||||
(ns common-tests.types.token-status-test
|
||||
(:require
|
||||
#?(:clj [app.common.fressian :as fres])
|
||||
#?(: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.uuid :as uuid]
|
||||
[clojure.datafy :refer [datafy]]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/deftest make-token-status
|
||||
(let [theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-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/theme-active? status theme-id))
|
||||
(t/is (= (ctos/active-set-count status) 1))
|
||||
(t/is (ctos/set-active? status set-id))))
|
||||
|
||||
(t/deftest make-token-status-defaults
|
||||
(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-set-count status) 0))))
|
||||
|
||||
(t/deftest make-invalid-token-status
|
||||
(t/testing "non-set for active-themes"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception)
|
||||
#"expected valid params for token-status"
|
||||
(ctos/make-token-status :active-themes []))))
|
||||
(t/testing "non-uuid in active-sets"
|
||||
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception)
|
||||
#"expected valid params for token-status"
|
||||
(ctos/make-token-status :active-sets #{"not-a-uuid"})))))
|
||||
|
||||
(t/deftest activate-theme
|
||||
(let [theme-id (uuid/next)
|
||||
status (ctos/make-token-status)
|
||||
status' (ctos/activate-theme status 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/deftest deactivate-theme
|
||||
(let [theme-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-id})
|
||||
status' (ctos/deactivate-theme status 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/deftest toggle-theme-active
|
||||
(let [theme-id (uuid/next)
|
||||
status (ctos/make-token-status)
|
||||
status' (ctos/toggle-theme-active status theme-id)
|
||||
status'' (ctos/toggle-theme-active status' 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') 1))
|
||||
(t/is (= (ctos/active-theme-count status'') 0))))
|
||||
|
||||
(t/deftest activate-set
|
||||
(let [set-id (uuid/next)
|
||||
status (ctos/make-token-status)
|
||||
status' (ctos/activate-set status set-id)]
|
||||
(t/is (not (ctos/set-active? status set-id)))
|
||||
(t/is (ctos/set-active? status' set-id))
|
||||
(t/is (= (ctos/active-set-count status') 1))))
|
||||
|
||||
(t/deftest deactivate-set
|
||||
(let [set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-sets #{set-id})
|
||||
status' (ctos/deactivate-set status set-id)]
|
||||
(t/is (ctos/set-active? status set-id))
|
||||
(t/is (not (ctos/set-active? status' set-id)))
|
||||
(t/is (= (ctos/active-set-count status') 0))))
|
||||
|
||||
(t/deftest toggle-set-active
|
||||
(let [set-id (uuid/next)
|
||||
status (ctos/make-token-status)
|
||||
status' (ctos/toggle-set-active status set-id)
|
||||
status'' (ctos/toggle-set-active status' set-id)]
|
||||
(t/is (ctos/set-active? status' set-id))
|
||||
(t/is (not (ctos/set-active? status'' set-id)))
|
||||
(t/is (= (ctos/active-set-count status') 1))
|
||||
(t/is (= (ctos/active-set-count status'') 0))))
|
||||
|
||||
(t/deftest datafy-token-status
|
||||
(let [theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-id}
|
||||
:active-sets #{set-id})
|
||||
result (datafy status)]
|
||||
(t/is (map? result))
|
||||
(t/is (not (ctos/token-status? result)))
|
||||
(t/is (= (:active-themes result) #{theme-id}))
|
||||
(t/is (= (:active-sets result) #{set-id}))))
|
||||
|
||||
(t/deftest transit-serialization
|
||||
(let [theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-id}
|
||||
:active-sets #{set-id})
|
||||
encoded (tr/encode-str status)
|
||||
status' (tr/decode-str encoded)]
|
||||
(t/is (ctos/token-status? status'))
|
||||
(t/is (= (datafy status') (datafy status)))))
|
||||
|
||||
#?(:clj
|
||||
(t/deftest fressian-serialization
|
||||
(let [theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-id}
|
||||
:active-sets #{set-id})
|
||||
encoded (fres/encode status)
|
||||
status' (fres/decode encoded)]
|
||||
(t/is (ctos/token-status? status'))
|
||||
(t/is (= (datafy status') (datafy status))))))
|
||||
|
||||
#?(:clj
|
||||
(t/deftest json-serialization
|
||||
(let [theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
status (ctos/make-token-status :active-themes #{theme-id}
|
||||
:active-sets #{set-id})
|
||||
json-str (json/write-str status)
|
||||
parsed (json/read-str json-str :key-fn keyword)]
|
||||
(t/is (map? parsed))
|
||||
(t/is (= [(str theme-id)] (:active-themes parsed)))
|
||||
(t/is (= [(str set-id)] (:active-sets parsed))))))
|
||||
|
||||
;; Make TokenStatus from a TokensLib (to migrate from legacy files)
|
||||
(t/deftest make-token-status-from-tokens-lib
|
||||
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a)
|
||||
:name "set-a"))
|
||||
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-b)
|
||||
:name "set-b"))
|
||||
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-c)
|
||||
:name "set-c"))
|
||||
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-d)
|
||||
:name "set-d"))
|
||||
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-1)
|
||||
:name "theme-1"
|
||||
:sets #{"set-a" "set-b"}))
|
||||
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-2)
|
||||
:name "theme-2"
|
||||
:sets #{"set-b"}))
|
||||
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-3)
|
||||
:name "theme-3"
|
||||
:sets #{"set-c" "set-d"}))
|
||||
(ctob/set-active-themes #{"/theme-1" "/theme-2"}))
|
||||
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/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))
|
||||
(t/is (ctos/set-active? token-status (thi/id :set-a)))
|
||||
(t/is (ctos/set-active? token-status (thi/id :set-b)))))
|
||||
@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.time :as ct]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[clojure.datafy :refer [datafy]]
|
||||
[clojure.test :as t]))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user