From 331e9168b58e322ceea821d4ae5a1b803fbbfe4e Mon Sep 17 00:00:00 2001 From: Shreyash Agare Date: Mon, 5 Oct 2026 15:34:11 +0530 Subject: [PATCH] :bug: Keep interactions of flex children when syncing components (#11788) * :bug: Keep interactions of flex children when syncing components The flex-child sync between main and copy children propagated every attribute in swap-keep-attrs, which includes :interactions. Since :interactions has no sync group, the touched check never protected it, so any change that reflowed a flex component (like toggling a child's visibility) overwrote the copy children's interactions with the main's. Closes #11746 AI-assisted-by: claude-opus-5 Signed-off-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com> * :bug: Add another test and simplify comments --------- Signed-off-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com> Co-authored-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com> Co-authored-by: alonso.torres --- common/src/app/common/logic/libraries.cljc | 8 +- .../logic/comp_flex_interactions_test.cljc | 155 ++++++++++++++++++ common/test/common_tests/runner.cljc | 2 + 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 common/test/common_tests/logic/comp_flex_interactions_test.cljc diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index fc3c4d00de..5d003a64b2 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -56,6 +56,10 @@ ;; We don't do automatic update of the `layout-grid-cells` property. (remove #(= :layout-grid-cells %)))) +;; Flex-child layout attrs synced between main and copy (interactions are only kept on swap) +(def ^:private flex-child-attrs + (disj ctk/swap-keep-attrs :interactions)) + (defn enabled-shape? [id container] (or (empty? log-shape-ids) @@ -2402,7 +2406,7 @@ (fn [child-copy] (let [child-main (ctf/get-ref-shape main-container main-component child-copy)] (-> child-copy - (propagate-attrs child-main ctk/swap-keep-attrs omit-touched?)))) + (propagate-attrs child-main flex-child-attrs omit-touched?)))) {:ignore-touched true}))] (pcb/concat-changes changes new-changes))) @@ -2432,7 +2436,7 @@ (fn [child-main] (let [child-copy (ctf/get-shape-in-copy copy-container child-main shape-copy)] (-> child-main - (propagate-attrs child-copy ctk/swap-keep-attrs omit-touched?)))) + (propagate-attrs child-copy flex-child-attrs omit-touched?)))) {:ignore-touched true}))] (pcb/concat-changes changes new-changes))) diff --git a/common/test/common_tests/logic/comp_flex_interactions_test.cljc b/common/test/common_tests/logic/comp_flex_interactions_test.cljc new file mode 100644 index 0000000000..5ded09e9a3 --- /dev/null +++ b/common/test/common_tests/logic/comp_flex_interactions_test.cljc @@ -0,0 +1,155 @@ +;; 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 common-tests.logic.comp-flex-interactions-test + (:require + [app.common.files.changes-builder :as pcb] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] + [app.common.logic.libraries :as cll] + [app.common.logic.shapes :as cls] + [app.common.math :as mth] + [app.common.test-helpers.components :as thc] + [app.common.test-helpers.compositions :as tho] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [app.common.types.container :as ctn] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +(defn- setup-file + "Flex component and copy whose children navigate to different boards." + [] + (-> (thf/sample-file :file1) + (tho/add-simple-component-with-copy :component1 + :main-root + :main-child + :copy-root + :main-root-params {:layout :flex + :layout-flex-dir :row} + :copy-root-params {:children-labels [:copy-child]}) + (ths/add-sample-shape :main-popup :type :frame) + (ths/add-sample-shape :copy-popup :type :frame) + (ths/add-interaction :main-child :main-popup) + (ths/add-interaction :copy-child :copy-popup))) + +(defn- update-shape + [file label update-fn] + (let [page (thf/current-page file)] + (thf/apply-changes + file + (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) + #{(thi/id label)} + update-fn + (:objects page) + {})))) + +(defn- destinations + [shape] + (mapv :destination (:interactions shape))) + +(t/deftest test-sync-flex-main-keeps-copy-interactions + (let [;; ==== Setup + file (setup-file) + margin {:m1 5 :m2 5 :m3 5 :m4 5} + + ;; ==== Action + updated-file (update-shape file :main-child + #(assoc % :hidden true :layout-item-margin margin)) + changes (cll/generate-sync-file-changes (pcb/empty-changes) + nil + :components + (:id updated-file) + (thi/id :component1) + (:id updated-file) + {(:id updated-file) updated-file} + (:id updated-file)) + file' (thf/apply-changes updated-file changes) + + ;; ==== Get + copy-child' (ths/get-shape file' :copy-child)] + + ;; ==== Check + (t/is (true? (:hidden copy-child'))) + (t/is (= margin (:layout-item-margin copy-child'))) + (t/is (= [(thi/id :copy-popup)] (destinations copy-child'))))) + +(t/deftest test-sync-flex-copy-to-main-keeps-main-interactions + (let [;; ==== Setup + file (setup-file) + + ;; ==== Action + updated-file (update-shape file :copy-child #(assoc % :hidden true)) + page (thf/current-page updated-file) + container (ctn/make-container page :page) + changes (-> (pcb/empty-changes) + (pcb/with-container container) + (cll/generate-sync-shape-inverse (:data updated-file) + {(:id updated-file) updated-file} + container + (thi/id :copy-root))) + file' (thf/apply-changes updated-file changes) + + ;; ==== Get + main-child' (ths/get-shape file' :main-child)] + + ;; ==== Check + (t/is (true? (:hidden main-child'))) + (t/is (= [(thi/id :main-popup)] (destinations main-child'))))) + +(defn- setup-library-and-file + "Library flex component with a nested copy, and a file instance whose + nested copy has an interaction." + [] + (let [library (-> (thf/sample-file :library) + (tho/add-nested-component :component1 + :main1-root + :main1-child + :component2 + :main2-root + :nested-head + :main2-root-params {:layout :flex + :layout-flex-dir :row})) + file (-> (thf/sample-file :file) + (ths/add-sample-shape :copy-popup :type :frame) + (thc/instantiate-component :component2 + :copy2-root + :library library + :children-labels [:copy-nested-head]) + (ths/add-interaction :copy-nested-head :copy-popup))] + [library file])) + +(defn- sync-file-from-library + [file library] + (cll/generate-sync-file-changes (pcb/empty-changes) + nil + nil + (:id file) + nil + (:id library) + {(:id library) library + (:id file) file} + (:id file))) + +(t/deftest test-sync-library-flex-keeps-nested-copy-interactions + (let [;; ==== Setup + [library file] (setup-library-and-file) + copy-x (:x (tho/bottom-shape file :copy2-root)) + + ;; ==== Action + library' (-> library + (update-shape :main1-child #(gsh/move % (gpt/point 1 0))) + (tho/propagate-component-changes :component1)) + file' (thf/apply-changes file (sync-file-from-library file library')) + + ;; ==== Get + copy-nested-head' (ths/get-shape file' :copy-nested-head)] + + ;; ==== Check + (t/is (mth/close? (inc copy-x) (:x (tho/bottom-shape file' :copy2-root)))) + (t/is (= [(thi/id :copy-popup)] (destinations copy-nested-head'))))) diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc index 081516c73c..eb2e3bb1c7 100644 --- a/common/test/common_tests/runner.cljc +++ b/common/test/common_tests/runner.cljc @@ -56,6 +56,7 @@ [common-tests.logic.chained-propagation-test] [common-tests.logic.comp-creation-test] [common-tests.logic.comp-detach-with-nested-test] + [common-tests.logic.comp-flex-interactions-test] [common-tests.logic.comp-main-edit-breaks-copy-slots-test] [common-tests.logic.comp-remove-swap-slots-test] [common-tests.logic.comp-reset-test] @@ -157,6 +158,7 @@ 'common-tests.logic.chained-propagation-test 'common-tests.logic.comp-creation-test 'common-tests.logic.comp-detach-with-nested-test + 'common-tests.logic.comp-flex-interactions-test 'common-tests.logic.comp-main-edit-breaks-copy-slots-test 'common-tests.logic.comp-remove-swap-slots-test 'common-tests.logic.comp-reset-test