diff --git a/common/src/app/common/types/typography.cljc b/common/src/app/common/types/typography.cljc index 1c3f2fab93..444e513b6a 100644 --- a/common/src/app/common/types/typography.cljc +++ b/common/src/app/common/types/typography.cljc @@ -101,10 +101,11 @@ (dissoc node :typography-ref-file :typography-ref-id)) (defn remove-external-typographies - "Change the shape so that any use of an external typography now is removed" - [shape file-id] + "Change the shape so that any use of a typography that isn't available + in the given file-id or set of libraries now is removed" + [shape valid-file-ids] (update shape :content (fn [content] - (txt/transform-nodes #(not= (:typography-ref-file %) file-id) + (txt/transform-nodes #(not (contains? valid-file-ids (:typography-ref-file %))) remove-typography-from-node content)))) diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index d05c3466af..002a1596b5 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -853,7 +853,7 @@ (assoc change :index (get map-ids (:old-id change))) change))) - (process-shape [file-id frame-id parent-id shape] + (process-shape [valid-file-ids frame-id parent-id shape] (cond-> shape :always (assoc :frame-id frame-id :parent-id parent-id) @@ -864,7 +864,7 @@ (assoc :shapes []) (cfh/text-shape? shape) - (ctt/remove-external-typographies file-id)))] + (ctt/remove-external-typographies valid-file-ids)))] (ptk/reify ::paste-shapes ptk/WatchEvent @@ -915,7 +915,9 @@ (into (d/ordered-set) (reverse selected)) selected) - objects (update-vals objects (partial process-shape file-id frame-id parent-id)) + valid-file-ids (conj (set (keys libraries)) file-id) + + objects (update-vals objects (partial process-shape valid-file-ids frame-id parent-id)) all-objects (merge page-objects objects) diff --git a/frontend/test/frontend_tests/helpers/state.cljs b/frontend/test/frontend_tests/helpers/state.cljs index 8f891de222..894caa2783 100644 --- a/frontend/test/frontend_tests/helpers/state.cljs +++ b/frontend/test/frontend_tests/helpers/state.cljs @@ -34,12 +34,14 @@ (defn setup-store ([file] (setup-store file nil)) - ([file {:keys [renderer] :as _opts}] + ([file {:keys [renderer libraries] :as _opts}] (let [state (-> initial-state (assoc :current-file-id (:id file) :current-page-id (cthf/current-page-id file) :permissions {:can-edit true} - :files {(:id file) file}) + :files (into {(:id file) file} + (map (juxt :id identity)) + libraries)) (cond-> (some? renderer) (assoc-in [:profile :props :renderer] renderer))) store (ptk/store {:state state :on-error on-error})] diff --git a/frontend/test/frontend_tests/logic/copy_paste_typography_test.cljs b/frontend/test/frontend_tests/logic/copy_paste_typography_test.cljs new file mode 100644 index 0000000000..4f13893657 --- /dev/null +++ b/frontend/test/frontend_tests/logic/copy_paste_typography_test.cljs @@ -0,0 +1,119 @@ +;; 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.logic.copy-paste-typography-test + (:require + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.test-helpers.shapes :as cths] + [app.common.types.text :as txt] + [app.main.data.workspace :as dw] + [app.main.data.workspace.selection :as dws] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.pages :as thp] + [frontend-tests.helpers.state :as ths])) + +(t/use-fixtures :each + {:before thp/reset-idmap!}) + +(defn- setup-library-file [] + (-> (cthf/sample-file :library-file) + (cths/add-sample-typography :typography-1 :name "Heading"))) + +(defn- setup-main-file [library-file] + (let [content (txt/change-text nil "Hello world" + :typography-ref-id (cthi/id :typography-1) + :typography-ref-file (:id library-file))] + (-> (cthf/sample-file :main-file) + (ctho/add-frame :frame-1 :name "frame-1") + (cths/add-sample-shape :text-1 :type :text :parent-label :frame-1 :content content)))) + +(defn- get-typography-ref + [shape] + (first (txt/node-seq #(some? (:typography-ref-id %)) (:content shape)))) + +(t/deftest copy-paste-text-keeps-external-typography-ref + "Copying and pasting a text shape retains the association with a text + style defined in an external library" + (t/async + done + (let [;; ==== Setup + library-file (setup-library-file) + file (setup-main-file library-file) + store (ths/setup-store file {:libraries [library-file]}) + + ;; ==== Action + page (cthf/current-page file) + frame-1 (cths/get-shape file :frame-1) + text-1 (cths/get-shape file :text-1) + features #{} + version 67 + + pdata (thp/simulate-copy-shape #{(:id text-1)} (:objects page) {(:id file) file} page file features version) + + events + [(dws/select-shape (:id frame-1)) + (dw/paste-shapes pdata)]] + + (ths/run-store + store done events + (fn [new-state] + (let [;; ==== Get + file' (ths/get-file-from-state new-state) + frame-1' (cths/get-shape file' :frame-1) + pasted-id (->> (:shapes frame-1') + (remove #(= % (:id text-1))) + first) + pasted (cths/get-shape-by-id file' pasted-id) + ref-node (get-typography-ref pasted)] + + ;; ==== Check + (t/is (some? pasted)) + (t/is (some? ref-node)) + (t/is (= (:typography-ref-id ref-node) (cthi/id :typography-1))) + (t/is (= (:typography-ref-file ref-node) (:id library-file))))))))) + +(t/deftest copy-paste-text-drops-typography-ref-from-unlinked-file + "Copying and pasting a text shape removes typography references that + point to a file which isn't the current file nor a linked library" + (t/async + done + (let [;; ==== Setup + unrelated-file (setup-library-file) + library-file (-> (cthf/sample-file :library-file-2) + (cths/add-sample-typography :typography-2 :name "Body")) + file (setup-main-file unrelated-file) + store (ths/setup-store file {:libraries [library-file]}) + + ;; ==== Action + page (cthf/current-page file) + frame-1 (cths/get-shape file :frame-1) + text-1 (cths/get-shape file :text-1) + features #{} + version 67 + + pdata (thp/simulate-copy-shape #{(:id text-1)} (:objects page) {(:id file) file} page file features version) + + events + [(dws/select-shape (:id frame-1)) + (dw/paste-shapes pdata)]] + + (ths/run-store + store done events + (fn [new-state] + (let [;; ==== Get + file' (ths/get-file-from-state new-state) + frame-1' (cths/get-shape file' :frame-1) + pasted-id (->> (:shapes frame-1') + (remove #(= % (:id text-1))) + first) + pasted (cths/get-shape-by-id file' pasted-id) + ref-node (get-typography-ref pasted)] + + ;; ==== Check + (t/is (some? pasted)) + (t/is (nil? ref-node)))))))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 1dae772e0f..8cf3b8162b 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -35,6 +35,7 @@ [frontend-tests.helpers-shapes-test] [frontend-tests.logic.comp-remove-swap-slots-test] [frontend-tests.logic.components-and-tokens] + [frontend-tests.logic.copy-paste-typography-test] [frontend-tests.logic.copying-and-duplicating-test] [frontend-tests.logic.frame-guides-test] [frontend-tests.logic.groups-test] @@ -152,6 +153,7 @@ 'frontend-tests.helpers-shapes-test 'frontend-tests.logic.comp-remove-swap-slots-test 'frontend-tests.logic.components-and-tokens + 'frontend-tests.logic.copy-paste-typography-test 'frontend-tests.logic.copying-and-duplicating-test 'frontend-tests.logic.frame-guides-test 'frontend-tests.logic.groups-test