Expand the token tree after importing it (#10664)

Imported token sets were collapsed, hiding what had just been imported.

Fixes #9819

Signed-off-by: Akshit Nassa <akshitnassa412@gmail.com>
Co-authored-by: Akshit Nassa <akshitnassa412@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
AK 2026-09-23 06:09:22 -04:00 committed by GitHub
parent e62546a03a
commit 4968dc2f1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 164 additions and 0 deletions

View File

@ -165,6 +165,49 @@
[:workspace-tokens :unfolded-token-types] [:workspace-tokens :unfolded-token-types]
(make-unfolded-token-types-state file-id set-id #{})))))) (make-unfolded-token-types-state file-id set-id #{}))))))
(defn- get-token-types-by-set
"Returns a map of set id to the token types present on that set of `lib`."
[lib]
(reduce (fn [result token-set]
(let [set-id (ctob/get-id token-set)
types (into #{} (map :type) (vals (ctob/get-tokens lib set-id)))]
(cond-> result
(seq types) (assoc set-id types))))
{}
(ctob/get-sets lib)))
(defn- unfold-token-types-in-storage
[file-id types-by-set]
(swap! storage/user update :app.main.ui.workspace.tokens/unfolded-token-types
(fn [stored]
(reduce-kv (fn [stored set-id types]
(update-in stored [file-id set-id]
#(-> (set %) (into types) (vec))))
stored
types-by-set))))
(defn unfold-token-types
"Unfolds all the token types present on every set of `lib`, keeping the ones
already unfolded. The token sections restore their fold state from the storage
on mount and on set selection, so it also needs to be persisted there."
[lib]
(ptk/reify ::unfold-token-types
ptk/UpdateEvent
(update [_ state]
(let [file-id (:current-file-id state)
selected (get-in state [:workspace-tokens :selected-token-set-id])
;; The panel keeps the selected set if it is part of the imported
;; library, and falls back to its first set otherwise.
set-id (or (some->> selected (ctob/get-set lib) (ctob/get-id))
(some->> (ctob/get-sets lib) (first) (ctob/get-id)))]
(unfold-token-types-in-storage file-id (get-token-types-by-set lib))
(cond-> state
(some? set-id)
(assoc-in [:workspace-tokens :unfolded-token-types]
(make-unfolded-token-types-state
file-id set-id
(get-unfolded-token-types-from-storage file-id set-id))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS TREE - Toggle tree nodes ;; TOKENS TREE - Toggle tree nodes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -422,6 +465,7 @@
(pcb/set-tokens-lib lib) (pcb/set-tokens-lib lib)
(pcb/set-tokens-status status))] (pcb/set-tokens-status status))]
(rx/of (dch/commit-changes changes) (rx/of (dch/commit-changes changes)
(unfold-token-types lib)
(dwtp/propagate-workspace-tokens)))))) (dwtp/propagate-workspace-tokens))))))
(defn set-tokens-source (defn set-tokens-source

View File

@ -17,6 +17,7 @@
[app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.data.workspace.wasm-text :as dwwt] [app.main.data.workspace.wasm-text :as dwwt]
[app.util.storage :as storage]
[cljs.test :as t :include-macros true] [cljs.test :as t :include-macros true]
[cuerdas.core :as str] [cuerdas.core :as str]
[frontend-tests.helpers.pages :as thp] [frontend-tests.helpers.pages :as thp]
@ -1266,3 +1267,122 @@
(t/is (pos? (thw/call-count :set-shape-text-content))) (t/is (pos? (thw/call-count :set-shape-text-content)))
(t/is (pos? (thw/call-count :get-text-dimensions)))))) (t/is (pos? (thw/call-count :get-text-dimensions))))))
debounce-text-stop))))) debounce-text-stop)))))
;; Coverage for issue #9819 (token tree collapsed after import).
;;
;; The fold state of the token type sections lives in
;; `[:workspace-tokens :unfolded-token-types]` and is persisted in the user
;; storage. The sections restore that state from storage on mount and when the
;; selected set changes, so importing a library has to unfold the imported
;; token types on both places, otherwise the tree shows up collapsed.
(defn- unfolded-token-types
[state]
(get-in state [:workspace-tokens :unfolded-token-types :types]))
(defn- reset-unfolded-token-types-storage! []
(swap! storage/user dissoc :app.main.ui.workspace.tokens/unfolded-token-types))
(defn- setup-imported-lib []
(-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :imported-set-a)
:name "Imported A"))
(ctob/add-token (cthi/id :imported-set-a)
(ctob/make-token {:name "borderRadius.sm"
:value "12"
:type :border-radius}))
(ctob/add-token (cthi/id :imported-set-a)
(ctob/make-token {:name "color.primary"
:value "#ff0000"
:type :color}))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :imported-set-b)
:name "Imported B"))
(ctob/add-token (cthi/id :imported-set-b)
(ctob/make-token {:name "spacing.md"
:value "16"
:type :spacing}))))
(t/deftest test-import-tokens-lib-unfolds-token-types
(t/testing "importing a tokens library unfolds the token types of the shown set"
(t/async
done
(reset-unfolded-token-types-storage!)
(let [file (setup-file-with-empty-lib)
store (ths/setup-store file)
lib (setup-imported-lib)
events [(dwtl/import-tokens-lib lib)]]
(tohs/run-store-async
store done events
(fn [new-state]
(t/is (= #{:border-radius :color} (unfolded-token-types new-state)))
(t/is (= (cthi/id :imported-set-a)
(get-in new-state [:workspace-tokens :unfolded-token-types :set-id])))
(t/is (= (:id file)
(get-in new-state [:workspace-tokens :unfolded-token-types :file-id])))))))))
(t/deftest test-import-tokens-lib-persists-unfolded-token-types
(t/testing "the unfolded token types survive the storage restore done on section mount"
(t/async
done
(reset-unfolded-token-types-storage!)
(let [file (setup-file-with-empty-lib)
store (ths/setup-store file)
lib (setup-imported-lib)
;; Mirrors what the UI does after an import: the tokens panel selects
;; the first set and every token section restores its fold state from
;; storage on mount.
events [(dwtl/import-tokens-lib lib)
(dwtl/set-selected-token-set-id (cthi/id :imported-set-a))
(dwtl/restore-unfolded-token-types)]]
(tohs/run-store-async
store done events
(fn [new-state]
(t/is (= #{:border-radius :color} (unfolded-token-types new-state)))))))))
(t/deftest test-import-tokens-lib-unfolds-every-imported-set
(t/testing "switching to another imported set also shows it unfolded"
(t/async
done
(reset-unfolded-token-types-storage!)
(let [file (setup-file-with-empty-lib)
store (ths/setup-store file)
lib (setup-imported-lib)
events [(dwtl/import-tokens-lib lib)
(dwtl/set-selected-token-set-id (cthi/id :imported-set-b))]]
(tohs/run-store-async
store done events
(fn [new-state]
(t/is (= #{:spacing} (unfolded-token-types new-state)))))))))
(t/deftest test-import-tokens-lib-keeps-previously-unfolded-types
(t/testing "importing does not fold token types the user had already unfolded"
(t/async
done
(reset-unfolded-token-types-storage!)
(let [file (setup-file-with-tokens)
store (ths/setup-store file)
lib (-> (get-in file [:data :tokens-lib])
(ctob/add-token (cthi/id :set-a)
(ctob/make-token {:name "color.primary"
:value "#ff0000"
:type :color})))
events [(dwtl/set-selected-token-set-id (cthi/id :set-a))
(dwtl/open-token-type :opacity)
(dwtl/import-tokens-lib lib)]]
(tohs/run-store-async
store done events
(fn [new-state]
(t/is (= #{:border-radius :color :opacity} (unfolded-token-types new-state)))))))))
(t/deftest test-import-empty-tokens-lib
(t/testing "importing a library without sets does not fail nor unfold anything"
(t/async
done
(reset-unfolded-token-types-storage!)
(let [file (setup-file-with-empty-lib)
store (ths/setup-store file)
events [(dwtl/import-tokens-lib (ctob/make-tokens-lib))]]
(tohs/run-store-async
store done events
(fn [new-state]
(t/is (empty? (unfolded-token-types new-state)))))))))