From adbddca19a97e811bf06924b6241c3eb185a4941 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2026 15:11:30 +0200 Subject: [PATCH] :bug: Fix the variant events and the plugin component calls Fixes in the variant events of the workspace and in the component calls of the plugin API (Phase 3c). - Rename a variant in one commit. Renaming a variant renamed its board, then each of its components and mains, as separate commits inside an undo transaction. The transaction kept the undo group of its first commit, the board's, while the sync that the renamed mains started took the group of their commits and made an entry of its own: one undo reverted only the copies' names (F6, case AV). clv/generate-rename-variant builds the board, component and main renames in common, and the event commits them once, so the rename and its sync are one undo entry, like transform into a variant. Case AV no longer marks its round trip as a known failure; the frontend golden master records it without failures. - Keep overrides in a plugin swap only within a variant set. The plugin's swapComponent swapped with keep-touched for any component, while the UI keeps a copy's overrides only on a variant switch: the same swap kept different overrides by route (F33). It now keeps them only when the copy's component and the target are variants of one set (decision (p)), as swap-keeps-touched? says. shape-bugfixes-test: two variants of one set keep the overrides; of different sets, or plain components, do not. - Look up a plugin variant head's component in its own file. The plugin's isVariantHead looked the shape's component up in the file of the shape, so it was false for copies of a library's variants; and it looked it up before checking the shape is a head, so on a shape with no component it failed the lookup's assert (F91). It now checks the head first and looks in the shape's :component-file. shape-bugfixes-test: a copy of a library variant is a variant head, and a plain rect is not. - Add a variant with the shortcut when a variant set is selected. The context menu of a variant container offers "Add variant" and shows the create-component shortcut for it, but the shortcut's event did not know containers: with one selected it fell through to creating a component, which the container's own rules refuse (F95). With a single variant container selected, the shortcut now adds a variant to it, as the menu entry does. workspace-variants-test: the shortcut on a variant container gives the set a third member. - Refuse plugin variant edits whose variant is on another page. The variant events the plugin API calls (add a variant, add, remove or rename a property, set a property value) build their changes on the objects of the page the workspace shows. For a variant whose mains sit on another page, they updated the components but not their mains, so the mains kept their old :variant-name and the file failed validation (F94). The plugin now refuses these edits, as it refuses edits of shapes on another page, unless the variant is in the current file and on the current page. library-test: each edit goes through with the variant on the current page and is refused, with nothing emitted, with it elsewhere. - Check every component of an assets group before combining it. The assets panel offers to combine a group of components as variants when they can be: more than one, none a variant, all with their main on one page. It counted every component of the group, subgroups included, but checked the other two rules only on the components directly in it, while the combine takes them all; with none directly in it, those rules always held (F98). can-combine-group? now checks every component of the group. assets-components-test: a group whose only components are in a subgroup can be combined; a variant or a main on another page in a subgroup blocks it; one component cannot. AI-assisted-by: claude-opus-5-5 --- common/src/app/common/logic/variants.cljc | 18 ++++++++ .../test_helpers/composable/comp/cases.cljc | 12 ------ .../src/app/main/data/workspace/variants.cljs | 31 +++++--------- .../workspace/sidebar/assets/components.cljs | 32 +++++++++------ frontend/src/app/plugins/library.cljs | 25 +++++++++++ frontend/src/app/plugins/shape.cljs | 22 +++++++--- .../composable_tests/golden_master.edn | 4 +- .../data/workspace_variants_test.cljs | 17 ++++++++ .../frontend_tests/plugins/library_test.cljs | 41 +++++++++++++++++++ .../plugins/shape_bugfixes_test.cljs | 25 +++++++++++ frontend/test/frontend_tests/runner.cljs | 2 + .../ui/assets_components_test.cljs | 31 ++++++++++++++ 12 files changed, 209 insertions(+), 51 deletions(-) create mode 100644 frontend/test/frontend_tests/ui/assets_components_test.cljs diff --git a/common/src/app/common/logic/variants.cljc b/common/src/app/common/logic/variants.cljc index 5e13da1241..5947d4dc95 100644 --- a/common/src/app/common/logic/variants.cljc +++ b/common/src/app/common/logic/variants.cljc @@ -482,3 +482,21 @@ (cfh/components-nesting-loop? (cfh/get-children-with-self (:objects page) (:id root)) (cfh/get-parents-with-self objects (:parent-id shape))))))) + +(defn generate-rename-variant + "Rename the variant `variant-id` to `name`: its board, and each of its + components with its main. The changes must hold the objects of the page + of the variant and the data of its file." + [changes variant-id name] + (let [data (pcb/get-library-data changes) + objects (pcb/get-objects changes) + clean-name (cpn/clean-path name) + valid-name (cpn/clean-valid-name clean-name) + rename (fn [changes component] + (-> changes + (cll/generate-rename-component (:id component) valid-name data) + (pcb/update-shapes [(:main-instance-id component)] #(assoc % :name valid-name))))] + (cond-> (pcb/update-shapes changes [variant-id] #(assoc % :name clean-name)) + (some? valid-name) + (as-> changes (reduce rename changes (cfv/find-variant-components data objects variant-id)))))) + diff --git a/common/src/app/common/test_helpers/composable/comp/cases.cljc b/common/src/app/common/test_helpers/composable/comp/cases.cljc index c75c8c5f8b..9001f4d6b5 100644 --- a/common/src/app/common/test_helpers/composable/comp/cases.cljc +++ b/common/src/app/common/test_helpers/composable/comp/cases.cljc @@ -1421,18 +1421,6 @@ rename-copy (n/change-property nested :name "Mine") rename (n/rename-variant #(:id (n/member-root % vset "a")) "Renamed")] {:setup setup/empty-situation - ;; the rename's transaction keeps the undo group of its first commit, the - ;; container's, while the copies' sync takes the group of the main - ;; renames: one undo reverts only the sync - :undo-check {:known-failures [{:bug "F6" - :fails {:undo #{[:object :sync-vset-vcontainer :name] - [:object :sync-vset-v0-root :name] - [:object :sync-vset-v1-root :name] - [:component :sync-vset-v0-component :name] - [:component :sync-vset-v1-component :name]} - :undo-index #{[:undo-index 1]}} - :op rename - :runners #{:frontend}}]} :operation (tm/in-sequence [(n/create-component m base-color :size 100) (n/make-variant-container vset [["a" base-color] ["b" red]] :size 100) diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index 3908a2dafc..1e1ed2d76a 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -14,7 +14,6 @@ [app.common.geom.rect :as grc] [app.common.logic.variant-properties :as clvp] [app.common.logic.variants :as clv] - [app.common.path-names :as cpn] [app.common.types.component :as ctc] [app.common.types.components-list :as ctkl] [app.common.types.shape.layout :as ctsl] @@ -25,7 +24,6 @@ [app.main.data.helpers :as dsh] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.selection :as dws] - [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] [app.util.dom :as dom] @@ -290,8 +288,14 @@ (not (ctc/is-variant? first-shape)) (ctc/main-instance? first-shape)) add-new-variant? (every? ctc/is-variant? selected-shapes) + variant-container? (and single? (ctc/is-variant-container? first-shape)) undo-id (js/Symbol)] (cond + variant-container? + (rx/of + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-create-component"}) + (add-new-variant (:id first-shape))) + transform-in-variant? (rx/of (ev/event {::ev/name "transform-in-variant" ::ev/origin "workspace:shortcut"}) @@ -336,27 +340,14 @@ (rx/empty)))))) (defn rename-variant - "Rename the variant container and all components belonging to this variant" + "Rename the variant container and all components belonging to this variant + (`clv/generate-rename-variant`), in one commit." [variant-id name] (ptk/reify ::rename-variant - ptk/WatchEvent - (watch [_ state _] - (let [page-id (:current-page-id state) - data (dsh/lookup-file-data state) - objects (-> (dsh/get-page data page-id) - (get :objects)) - variant-components (cfv/find-variant-components data objects variant-id) - clean-name (cpn/clean-path name) - undo-id (js/Symbol)] - - (rx/concat - (rx/of (dwu/start-undo-transaction undo-id) - (dwsh/update-shapes [variant-id] #(assoc % :name clean-name))) - (rx/from (map - #(dwl/rename-component-and-main-instance (:id %) clean-name) - variant-components)) - (rx/of (dwu/commit-undo-transaction undo-id))))))) + (watch [it state _] + (rx/of (dch/commit-changes (-> (variant-changes it state) + (clv/generate-rename-variant variant-id name))))))) (defn rename-comp-or-variant-and-main "If the component is in a variant, rename the variant. diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index dc1b566385..ec80e1df06 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -177,18 +177,29 @@ :title (tr "workspace.assets.components.num-variants" num-variants)} [:> icon* {:icon-id i/variant :size "s"}]])])])) -(defn- count-leaves - "Counts the total number of leaf elements in a nested map structure. - A leaf element is considered any element inside a vector." +(defn- leaves + "The leaf elements of a nested map structure: the elements of each vector + in it." [m] (reduce-kv (fn [acc _ v] (cond - (map? v) (+ acc (count-leaves v)) - (vector? v) (+ acc (count v)) - :else acc)) - 0 + (map? v) (into acc (leaves v)) + (vector? v) (into acc v) + :else acc)) + [] m)) +(defn can-combine-group? + "Whether the components of a group of the assets panel, `groups` (its + subgroups by name, its own components under \"\"), nested ones included, + can be combined as variants: two or more, none a variant, all with their + main on one page." + [groups] + (let [components (leaves groups)] + (and (> (count components) 1) + (not-any? ctc/is-variant? components) + (apply = (map :main-instance-page components))))) + (mf/defc components-group* [{:keys [file-id prefix groups open-groups is-force-open renaming is-listing-thumbs selected on-asset-click on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-delete-group on-context-menu @@ -207,11 +218,8 @@ selected-full)) components (not-empty (get groups "" [])) - can-combine? (mf/with-memo [is-local groups components] - (and is-local - (> (count-leaves groups) 1) - (not-any? ctc/is-variant? components) - (apply = (map :main-instance-page components)))) + can-combine? (mf/with-memo [is-local groups] + (and is-local (can-combine-group? groups))) on-drag-enter (mf/use-fn (mf/deps dragging* prefix selected-paths is-local drag-data*) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index e8362106ee..871c48e15e 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -696,6 +696,16 @@ [file-id variant-id] (cfv/find-variant-components (-> file-id u/locate-file :data) variant-id)) +(defn- on-current-page? + "Whether the main of `component`, of the file `file-id`, is on the page the + workspace shows: the variant events build their changes there." + [file-id component] + (and (= file-id (:current-file-id @st/state)) + (u/page-active? (:main-instance-page component)))) + +(def ^:private not-on-current-page + "The variant is not on the current page") + (declare lib-component-proxy) (defn variant-proxy @@ -742,6 +752,9 @@ (not (r/check-permission plugin-id "library:write")) (u/not-valid plugin-id :addVariant "Plugin doesn't have 'library:write' permission") + (not (on-current-page? file-id (first (get-variant-components file-id id)))) + (u/not-valid plugin-id :addVariant not-on-current-page) + :else (st/emit! (se/event plugin-id "add-new-variant") @@ -753,6 +766,9 @@ (not (r/check-permission plugin-id "library:write")) (u/not-valid plugin-id :addProperty "Plugin doesn't have 'library:write' permission") + (not (on-current-page? file-id (first (get-variant-components file-id id)))) + (u/not-valid plugin-id :addProperty not-on-current-page) + :else (st/emit! (se/event plugin-id "add-new-property") @@ -772,6 +788,9 @@ (not (ctv/can-remove-property? properties)) (u/not-valid plugin-id :removeProperty "A variant must keep at least one property") + (not (on-current-page? file-id (first (get-variant-components file-id id)))) + (u/not-valid plugin-id :removeProperty not-on-current-page) + :else (st/emit! (se/event plugin-id "remove-property") @@ -790,6 +809,9 @@ (not (r/check-permission plugin-id "library:write")) (u/not-valid plugin-id :renameProperty "Plugin doesn't have 'library:write' permission") + (not (on-current-page? file-id (first (get-variant-components file-id id)))) + (u/not-valid plugin-id :renameProperty not-on-current-page) + :else (st/emit! (dwv/update-property-name id pos (ctv/normalize-property-text name) @@ -1101,6 +1123,9 @@ (not (r/check-permission plugin-id "library:write")) (u/not-valid plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission") + (not (on-current-page? file-id (u/locate-library-component file-id id))) + (u/not-valid plugin-id :setVariantProperty not-on-current-page) + :else (st/emit! (se/event plugin-id "variant-edit-property-value") diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 705ea9ad77..2610a61878 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -336,6 +336,13 @@ [shape] (and (ctk/instance-head? shape) (ctk/in-component-copy? shape))) +(defn swap-keeps-touched? + "Whether a swap of a copy of `component` for `target` keeps the copy's + overrides: only a switch between variants of one set does, as in the UI." + [component target] + (and (ctk/is-variant? component) + (= (:variant-id component) (:variant-id target)))) + (defn shape-proxy? [p] (obj/type-of? p "ShapeProxy")) @@ -1569,7 +1576,11 @@ valid-component? (obj/type-of? component "LibraryComponentProxy") target-file (when valid-component? (obj/get component "$file")) target-id (when valid-component? (obj/get component "$id")) - target-data (some-> target-file u/locate-file :data)] + target-data (some-> target-file u/locate-file :data) + component (when (copy-head? shape) + (u/locate-library-component (:component-file shape) (:component-id shape))) + target (when target-data + (dm/get-in target-data [:components target-id]))] (cond (not (r/check-permission plugin-id "content:write")) (u/not-valid plugin-id :swapComponent "Plugin doesn't have 'content:write' permission") @@ -1590,7 +1601,7 @@ (st/emit! (dwl/component-swap shape target-file target-id - true))))) + (swap-keeps-touched? component target)))))) :updateMain (fn [] @@ -1860,9 +1871,10 @@ :isVariantHead (fn [] - (let [shape (u/locate-shape file-id page-id id) - component (u/locate-library-component file-id (:component-id shape))] - (and (ctk/instance-head? shape) (ctk/is-variant? component)))) + (let [shape (u/locate-shape file-id page-id id)] + (and (ctk/instance-head? shape) + (ctk/is-variant? (u/locate-library-component (:component-file shape) + (:component-id shape)))))) :isVariantContainer (fn [] diff --git a/frontend/test/frontend_tests/composable_tests/golden_master.edn b/frontend/test/frontend_tests/composable_tests/golden_master.edn index 1f771c117d..7b4feb9103 100644 --- a/frontend/test/frontend_tests/composable_tests/golden_master.edn +++ b/frontend/test/frontend_tests/composable_tests/golden_master.edn @@ -67,8 +67,8 @@ [{:transcript " {:component \"first\", :id sync-first-component}\n {:component \"second\", :id sync-second-component}\n {:component \"first\", :copy sync-first-copyhead-0}\n {:component \"second\", :copy sync-second-copyhead-0}\n {:target #fn, :property :fills, :value \"#00ff00\"}\n {:action :combine, :params {:targets [#fn #fn], :selected? nil}}\n \n {:target #fn, :value #fn, :pos 0}", :states ["61c776d9e973c9dd" "0f68af4cace0f325" "97651ebcd2334fe5" "47fc4c7f440fdc13"]} {:transcript " {:component \"first\", :id sync-first-component}\n {:component \"second\", :id sync-second-component}\n {:component \"first\", :copy sync-first-copyhead-0}\n {:component \"second\", :copy sync-second-copyhead-0}\n {:target #fn, :property :fills, :value \"#00ff00\"}\n {:action :combine, :params {:targets [#fn #fn], :selected? true}}\n \n {:target #fn, :value #fn, :pos 0}", :states ["61c776d9e973c9dd" "0f68af4cace0f325" "97651ebcd2334fe5" "47fc4c7f440fdc13"]}] :case-av-variant-rename-reaches-copy-names - [{:transcript " {:component \"main\", :id sync-main-component}\n {:variant-set \"vset\", :variant-id sync-vset-vcontainer, :count 2}\n {:component \"main\", :level 0, :outer sync-main-outercomp-0}\n {:component \"main\", :copy sync-main-copyhead-0}\n {:target #fn, :property :name, :value \"Mine\"}\n {:action :rename, :params {:target #fn, :name \"Renamed\"}}", :states ["c6e889ca261596ce" "940f36855f4f991d" "d7a54338ab53a791"]} - {:transcript " {:component \"main\", :id sync-main-component}\n {:variant-set \"vset\", :variant-id sync-vset-vcontainer, :count 2}\n {:component \"main\", :level 0, :outer sync-main-outercomp-0}\n {:component \"main\", :copy sync-main-copyhead-0}\n one-of -> \n {:action :rename, :params {:target #fn, :name \"Renamed\"}}", :states ["5eb544d7e28709d7" "5e6794a9b313112e"]}] + [{:transcript " {:component \"main\", :id sync-main-component}\n {:variant-set \"vset\", :variant-id sync-vset-vcontainer, :count 2}\n {:component \"main\", :level 0, :outer sync-main-outercomp-0}\n {:component \"main\", :copy sync-main-copyhead-0}\n {:target #fn, :property :name, :value \"Mine\"}\n {:action :rename, :params {:target #fn, :name \"Renamed\"}}", :states ["c6e889ca261596ce" "940f36855f4f991d" "92ebbb582ece2af2"]} + {:transcript " {:component \"main\", :id sync-main-component}\n {:variant-set \"vset\", :variant-id sync-vset-vcontainer, :count 2}\n {:component \"main\", :level 0, :outer sync-main-outercomp-0}\n {:component \"main\", :copy sync-main-copyhead-0}\n one-of -> \n {:action :rename, :params {:target #fn, :name \"Renamed\"}}", :states ["5eb544d7e28709d7" "e76ffcfb4eccd16d"]}] :case-aw-variant-layer-rename-and-cleanup [{:transcript " {:component \"main\", :id sync-main-component}\n {:variant-set \"vset\", :variant-id sync-vset-vcontainer, :count 2}\n {:component \"main\", :level 0, :outer sync-main-outercomp-0}\n {:component \"main\", :copy sync-main-copyhead-0}\n {:target #fn, :property :fills, :value \"#00ff00\"}\n {:set-name \"vset\", :edit :rename-in-layers, :params {:member \"a\", :name \"Property 1=a2, Size=S\"}}\n \n {:set-name \"vset\", :edit :add, :params {:property-name \"Empty\"}}\n {:set-name \"vset\", :edit :remove-empty, :params {}}\n \n {:set-name \"vset\", :edit :rename-in-layers, :params {:member \"a\", :name \"Property 1=a2\"}}\n \n {:set-name \"vset\", :edit :error, :params {:member \"b\", :value \"Duplicated\"}}\n \n {:set-name \"vset\", :edit :error, :params {:member \"b\", :value nil}}\n \n {:target #fn, :value \"b\", :pos 0}", :states ["df9a042b9d59d699" "4b53486972986d97" "8553f89a9ebbcf54" "4b53486972986d97" "d65ad905aa97d661" "6daf5f692c6595f1" "d65ad905aa97d661" "cc7d2dfd618fc8cb" "6390b4d83995b629"]}] :case-ax-variant-shortcuts-add-members diff --git a/frontend/test/frontend_tests/data/workspace_variants_test.cljs b/frontend/test/frontend_tests/data/workspace_variants_test.cljs index c8c92054ce..3a9cb0f278 100644 --- a/frontend/test/frontend_tests/data/workspace_variants_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_variants_test.cljs @@ -27,6 +27,7 @@ [app.main.data.workspace.selection :as dws] [app.main.data.workspace.undo :as dwu] [app.main.data.workspace.variants :as dwv] + [app.util.dom :as dom] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] [frontend-tests.helpers.mock :as mock] @@ -195,6 +196,22 @@ (run-selected done (file-with-a-rect) [:rect] (dwv/add-component-or-variant) #(t/is (= 1 (count (ctkl/components-seq (:data %)))))))) +(t/deftest create-component-shortcut-on-a-variant-container + ;; the shortcut the container's "Add variant" menu entry shows; the new + ;; variant focuses its first property 250 ms later, which finds no DOM here + (t/async + done + (mock/with-mocks + {dom/get-element (constantly nil)} + (fn [inner-done] + (let [file (-> (cthf/sample-file :file1) + (cthv/add-variant :v01 :c01 :m01 :c02 :m02))] + (run-selected #(js/setTimeout inner-done 300) file [:v01] (dwv/add-component-or-variant) + (fn [file'] + (t/is (= 3 (count (filter #(= (cthi/id :v01) (:variant-id %)) + (ctkl/components-seq (:data file')))))))))) + done))) + (t/deftest rename-of-a-plain-component (t/async done diff --git a/frontend/test/frontend_tests/plugins/library_test.cljs b/frontend/test/frontend_tests/plugins/library_test.cljs index 9180376715..3a3d004401 100644 --- a/frontend/test/frontend_tests/plugins/library_test.cljs +++ b/frontend/test/frontend_tests/plugins/library_test.cljs @@ -388,6 +388,47 @@ (t/is (= [plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission"] (first @errors))))))) +(defn- variant-edit-errors + "The errors and emitted events of `call` on a variant edit of the file + `file-id`, with its main on the page `page-id`, while the workspace shows + the current file `current-file` and page `current-page`." + [{:keys [file-id page-id current-file current-page]} call] + (let [errors (atom []) + emitted (atom [])] + (with-redefs [r/check-permission (constantly true) + u/not-valid (mock/stub (fn [_ prop msg] (swap! errors conj [prop msg]))) + u/locate-library-component (constantly {:main-instance-page page-id + :variant-properties [{:name "color" :value "red"} + {:name "size" :value "s"}]}) + library/get-variant-components (constantly [{:main-instance-page page-id + :variant-properties [{:name "color" :value "red"} + {:name "size" :value "s"}]}]) + st/state (atom {:current-file-id current-file :current-page-id current-page}) + st/emit! (fn ([_] (swap! emitted conj :emitted)) + ([_ & _] (swap! emitted conj :emitted)))] + (call (library/variant-proxy "test-plugin" file-id (uuid/next)) + (library/lib-component-proxy "test-plugin" file-id (uuid/next)))) + {:errors @errors :emitted @emitted})) + +(t/deftest variant-edits-need-the-variant-on-the-current-page + (let [file-id (uuid/next) + page-id (uuid/next) + edits {:addVariant #(.addVariant ^js %1) + :addProperty #(.addProperty ^js %1) + :removeProperty #(.removeProperty ^js %1 0) + :renameProperty #(.renameProperty ^js %1 0 "shade") + :setVariantProperty #(.setVariantProperty ^js %2 0 "blue")}] + (doseq [[prop call] edits] + (t/testing (name prop) + (let [here (variant-edit-errors {:file-id file-id :page-id page-id + :current-file file-id :current-page page-id} call) + there (variant-edit-errors {:file-id file-id :page-id page-id + :current-file file-id :current-page (uuid/next)} call)] + (t/is (empty? (:errors here))) + (t/is (seq (:emitted here))) + (t/is (= [[prop "The variant is not on the current page"]] (:errors there))) + (t/is (empty? (:emitted there)))))))) + ;; --------------------------------------------------------------------------- ;; connectLibrary ;; --------------------------------------------------------------------------- diff --git a/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs b/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs index 59c688a3b4..6fb988e250 100644 --- a/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs +++ b/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs @@ -466,6 +466,31 @@ (t/is (= [[:detach "Only a copy that is not inside another copy can be detached"]] errors)) (t/is (empty? emitted)))) +(t/deftest swap-component-keeps-overrides-only-within-a-variant-set + (let [set-a (uuid/next) + set-b (uuid/next) + variant #(hash-map :id (uuid/next) :variant-id %)] + (t/is (true? (shape/swap-keeps-touched? (variant set-a) (variant set-a)))) + (t/is (false? (shape/swap-keeps-touched? (variant set-a) (variant set-b)))) + (t/is (not (shape/swap-keeps-touched? {:id (uuid/next)} {:id (uuid/next)}))) + (t/is (not (shape/swap-keeps-touched? (variant set-a) {:id (uuid/next)}))))) + +(t/deftest is-variant-head-reads-the-component-file-of-the-shape + ;; a copy, in this file, of a variant of a library + (let [library-id (uuid/next) + copy-id (uuid/next) + rect-id (uuid/next) + objects {copy-id {:id copy-id :type :frame :component-id (uuid/next) + :component-file library-id :component-root true :shape-ref (uuid/next)} + rect-id {:id rect-id :type :rect}} + head? (fn [id] + (with-redefs [u/locate-shape (fn [_ _ id] (get objects id)) + u/locate-library-component + (fn [file-id _] (when (= file-id library-id) {:variant-id (uuid/next)}))] + (.isVariantHead ^js (shape/shape-proxy "test-plugin" (uuid/next) (uuid/next) id))))] + (t/is (true? (head? copy-id))) + (t/is (false? (head? rect-id))))) + (t/deftest swap-component-rejects-a-value-that-is-no-component (let [{:keys [objects head]} (nested-copy-objects) {:keys [errors emitted]} (plugin-errors-of objects head #(.swapComponent ^js % #js {}))] diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 8c87826de0..3c358fce29 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -125,6 +125,7 @@ [frontend-tests.tokens.token-path-test] [frontend-tests.tokens.tokenscript-test] [frontend-tests.tokens.workspace-tokens-remap-test] + [frontend-tests.ui.assets-components-test] [frontend-tests.ui.check-updates-test] [frontend-tests.ui.color-token-position-test] [frontend-tests.ui.colorpicker-token-set-order-test] @@ -302,6 +303,7 @@ 'frontend-tests.tokens.logic.tokens-source-test 'frontend-tests.tokens.logic.tokens-status-test 'frontend-tests.tokens.workspace-tokens-remap-test + 'frontend-tests.ui.assets-components-test 'frontend-tests.ui.check-updates-test 'frontend-tests.ui.color-token-position-test 'frontend-tests.ui.colorpicker-token-set-order-test diff --git a/frontend/test/frontend_tests/ui/assets_components_test.cljs b/frontend/test/frontend_tests/ui/assets_components_test.cljs new file mode 100644 index 0000000000..50e8abf368 --- /dev/null +++ b/frontend/test/frontend_tests/ui/assets_components_test.cljs @@ -0,0 +1,31 @@ +;; 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 SUBSIDIARY SL + +(ns frontend-tests.ui.assets-components-test + "The check that offers to combine a group of the assets panel as variants." + (:require + [app.common.uuid :as uuid] + [app.main.ui.workspace.sidebar.assets.components :as assets] + [cljs.test :as t :include-macros true])) + +(def ^:private page-1 (uuid/next)) +(def ^:private page-2 (uuid/next)) + +(defn- component + [& {:as attrs}] + (merge {:id (uuid/next) :main-instance-page page-1} attrs)) + +(t/deftest combine-a-group-reads-its-nested-components + (t/testing "components only in subgroups" + (t/is (assets/can-combine-group? {"" [] "sub" {"" [(component) (component)]}}))) + (t/testing "a variant in a subgroup" + (t/is (not (assets/can-combine-group? {"" [(component)] + "sub" {"" [(component :variant-id (uuid/next))]}})))) + (t/testing "a subgroup with its main on another page" + (t/is (not (assets/can-combine-group? {"" [(component)] + "sub" {"" [(component :main-instance-page page-2)]}})))) + (t/testing "a single component" + (t/is (not (assets/can-combine-group? {"" [(component)]})))))