mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
Merge remote-tracking branch 'penpot/develop' into token-studio-develop
This commit is contained in:
commit
0bd3b0598b
35 changed files with 4865 additions and 2407 deletions
|
@ -7,8 +7,15 @@
|
|||
(ns app.common.test-helpers.compositions
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.test-helpers.components :as thc]
|
||||
[app.common.test-helpers.shapes :as ths]))
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.types.container :as ctn]))
|
||||
|
||||
;; ----- File building
|
||||
|
||||
(defn add-rect
|
||||
[file rect-label & {:keys [] :as params}]
|
||||
|
@ -166,4 +173,164 @@
|
|||
:component2-params component2-params
|
||||
:main2-root-params main2-root-params
|
||||
:nested-head-params nested-head-params)
|
||||
(thc/instantiate-component component2-label copy2-root-label copy2-root-params)))
|
||||
(thc/instantiate-component component2-label copy2-root-label copy2-root-params)))
|
||||
|
||||
;; ----- Getters
|
||||
|
||||
(defn bottom-shape-by-id
|
||||
"Get the deepest descendant of a shape by id"
|
||||
[file id & {:keys [page-label]}]
|
||||
(let [shape (ths/get-shape-by-id file id :page-label page-label)]
|
||||
(if (some? (:shapes shape))
|
||||
(let [child-id (-> (:shapes shape)
|
||||
first)]
|
||||
(bottom-shape-by-id file child-id :page-label page-label))
|
||||
shape)))
|
||||
|
||||
(defn bottom-shape
|
||||
"Get the deepest descendant of a shape by tag"
|
||||
[file tag & {:keys [page-label]}]
|
||||
(let [shape (ths/get-shape file tag :page-label page-label)]
|
||||
(bottom-shape-by-id file (:id shape) :page-label page-label)))
|
||||
|
||||
(defn bottom-fill-color
|
||||
"Get the first fill color of the deepest descendant of a shape by tag"
|
||||
[file tag & {:keys [page-label]}]
|
||||
(-> (bottom-shape file tag :page-label page-label)
|
||||
:fills
|
||||
first
|
||||
:fill-color))
|
||||
|
||||
;; ----- File modifiers
|
||||
|
||||
(defn propagate-component-changes
|
||||
"Propagates the component changes for component specified by component-tag"
|
||||
[file component-tag]
|
||||
(let [file-id (:id file)
|
||||
|
||||
changes (-> (pcb/empty-changes)
|
||||
(cll/generate-sync-file-changes
|
||||
nil
|
||||
:components
|
||||
file-id
|
||||
(:id (thc/get-component file component-tag))
|
||||
file-id
|
||||
{file-id file}
|
||||
file-id))]
|
||||
(thf/apply-changes file changes)))
|
||||
|
||||
(defn swap-component
|
||||
"Swap the specified shape by the component specified by component-tag"
|
||||
[file shape component-tag & {:keys [page-label propagate-fn]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
|
||||
[_ _all-parents changes]
|
||||
(cll/generate-component-swap (pcb/empty-changes)
|
||||
(:objects page)
|
||||
shape
|
||||
(:data file)
|
||||
page
|
||||
{(:id file) file}
|
||||
(-> (thc/get-component file component-tag)
|
||||
:id)
|
||||
0
|
||||
nil
|
||||
{})
|
||||
|
||||
file' (thf/apply-changes file changes)]
|
||||
(if propagate-fn
|
||||
(propagate-fn file')
|
||||
file')))
|
||||
|
||||
(defn swap-component-in-shape [file shape-tag component-tag & {:keys [page-label propagate-fn]}]
|
||||
(swap-component file (ths/get-shape file shape-tag :page-label page-label) component-tag :page-label page-label :propagate-fn propagate-fn))
|
||||
|
||||
(defn swap-component-in-first-child [file shape-tag component-tag & {:keys [page-label propagate-fn]}]
|
||||
(let [first-child-id (->> (ths/get-shape file shape-tag :page-label page-label)
|
||||
:shapes
|
||||
first)]
|
||||
(swap-component file
|
||||
(ths/get-shape-by-id file first-child-id :page-label page-label)
|
||||
component-tag
|
||||
:page-label page-label
|
||||
:propagate-fn propagate-fn)))
|
||||
|
||||
(defn update-color
|
||||
"Update the first fill color for the shape identified by shape-tag"
|
||||
[file shape-tag color & {:keys [page-label propagate-fn]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
changes
|
||||
(cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
#{(:id (ths/get-shape file shape-tag :page-label page-label))}
|
||||
(fn [shape]
|
||||
(assoc shape :fills (ths/sample-fills-color :fill-color color)))
|
||||
(:objects page)
|
||||
{})
|
||||
file' (thf/apply-changes file changes)]
|
||||
(if propagate-fn
|
||||
(propagate-fn file')
|
||||
file')))
|
||||
|
||||
(defn update-bottom-color
|
||||
"Update the first fill color of the deepest descendant for the shape identified by shape-tag"
|
||||
[file shape-tag color & {:keys [page-label propagate-fn]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
changes
|
||||
(cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
#{(:id (bottom-shape file shape-tag :page-label page-label))}
|
||||
(fn [shape]
|
||||
(assoc shape :fills (ths/sample-fills-color :fill-color color)))
|
||||
(:objects page)
|
||||
{})
|
||||
file' (thf/apply-changes file changes)]
|
||||
(if propagate-fn
|
||||
(propagate-fn file')
|
||||
file')))
|
||||
|
||||
(defn reset-overrides [file shape & {:keys [page-label propagate-fn]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
container (ctn/make-container page :page)
|
||||
file-id (:id file)
|
||||
changes (-> (pcb/empty-changes)
|
||||
(cll/generate-reset-component
|
||||
file
|
||||
{file-id file}
|
||||
(ctn/make-container container :page)
|
||||
(:id shape)
|
||||
true))
|
||||
file' (thf/apply-changes file changes)]
|
||||
(if propagate-fn
|
||||
(propagate-fn file')
|
||||
file')))
|
||||
|
||||
(defn reset-overrides-in-first-child [file shape-tag & {:keys [page-label propagate-fn]}]
|
||||
(let [first-child-id (->>
|
||||
(ths/get-shape file shape-tag :page-label page-label)
|
||||
:shapes
|
||||
first)
|
||||
shape (ths/get-shape-by-id file first-child-id :page-label page-label)]
|
||||
(reset-overrides file shape :page-label page-label :propagate-fn propagate-fn)))
|
||||
|
||||
(defn delete-shape [file shape-tag & {:keys [page-label propagate-fn]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
[_ changes] (cls/generate-delete-shapes (pcb/empty-changes nil (:id page))
|
||||
file
|
||||
page
|
||||
(:objects page)
|
||||
#{(-> (ths/get-shape file shape-tag :page-label page-label)
|
||||
:id)}
|
||||
{:components-v2 true})
|
||||
file' (thf/apply-changes file changes)]
|
||||
(if propagate-fn
|
||||
(propagate-fn file')
|
||||
file')))
|
||||
|
|
|
@ -406,8 +406,12 @@
|
|||
[shape text]
|
||||
(let [content (:content shape)
|
||||
|
||||
paragraph-style (select-keys (->> content (node-seq is-paragraph-node?) first) text-all-attrs)
|
||||
text-style (select-keys (->> content (node-seq is-text-node?) first) text-all-attrs)
|
||||
paragraph-style (merge
|
||||
default-text-attrs
|
||||
(select-keys (->> content (node-seq is-paragraph-node?) first) text-all-attrs))
|
||||
text-style (merge
|
||||
default-text-attrs
|
||||
(select-keys (->> content (node-seq is-text-node?) first) text-all-attrs))
|
||||
|
||||
paragraph-texts (str/split text "\n")
|
||||
|
||||
|
|
BIN
common/test/cases/duplicated-pages.penpot
Normal file
BIN
common/test/cases/duplicated-pages.penpot
Normal file
Binary file not shown.
BIN
common/test/cases/multiple-testing-levels.penpot
Normal file
BIN
common/test/cases/multiple-testing-levels.penpot
Normal file
Binary file not shown.
BIN
common/test/cases/swas-as-override.penpot
Normal file
BIN
common/test/cases/swas-as-override.penpot
Normal file
Binary file not shown.
|
@ -37,7 +37,7 @@
|
|||
(letfn [(setup []
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-frame :frame-comp-1)
|
||||
(ths/add-sample-shape :rectangle :parent-label :frame-comp-1)
|
||||
(ths/add-sample-shape :rectangle :parent-label :frame-comp-1 :fills (ths/sample-fills-color :fill-color "#2653d8"))
|
||||
(thc/make-component :comp-1 :frame-comp-1)
|
||||
|
||||
(tho/add-frame :frame-comp-2)
|
||||
|
@ -48,90 +48,39 @@
|
|||
(thc/instantiate-component :comp-2 :copy-comp-2 :parent-label :frame-comp-3 :children-labels [:comp-1-comp-2])
|
||||
(thc/make-component :comp-3 :frame-comp-3)))
|
||||
|
||||
(step-update-color-comp-2 [file]
|
||||
(let [page (thf/current-page file)
|
||||
(propagate-all-component-changes [file]
|
||||
(-> file
|
||||
(tho/propagate-component-changes :comp-1)
|
||||
(tho/propagate-component-changes :comp-2)))
|
||||
|
||||
;; Changes to update the color of the contained rectangle in component comp-2
|
||||
changes-update-color-comp-1
|
||||
(cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
(:shapes (ths/get-shape file :copy-comp-1))
|
||||
(fn [shape]
|
||||
(assoc shape :fills (ths/sample-fills-color :fill-color "#FF0000")))
|
||||
(:objects page)
|
||||
{})
|
||||
(fill-colors [file]
|
||||
[(tho/bottom-fill-color file :frame-comp-1)
|
||||
(tho/bottom-fill-color file :frame-comp-2)
|
||||
(tho/bottom-fill-color file :frame-comp-3)])
|
||||
|
||||
file' (thf/apply-changes file changes-update-color-comp-1)]
|
||||
|
||||
(t/is (= (first-child-fill-color file' :comp-1-comp-2) "#B1B2B5"))
|
||||
file'))
|
||||
|
||||
(step-propagate-comp-2 [file]
|
||||
(let [page (thf/current-page file)
|
||||
file-id (:id file)
|
||||
|
||||
;; Changes to propagate the color changes of component comp-1
|
||||
changes-sync-comp-1 (-> (pcb/empty-changes)
|
||||
(cll/generate-sync-file-changes
|
||||
nil
|
||||
:components
|
||||
file-id
|
||||
(:id (thc/get-component file :comp-2))
|
||||
file-id
|
||||
{file-id file}
|
||||
file-id))
|
||||
|
||||
file' (thf/apply-changes file changes-sync-comp-1)]
|
||||
|
||||
(t/is (= (first-fill-color file' :rect-comp-2) "#FF0000"))
|
||||
(t/is (= (first-child-fill-color file' :comp-1-comp-2) "#FF0000"))
|
||||
file'))
|
||||
|
||||
(step-update-color-comp-3 [file]
|
||||
(let [page (thf/current-page file)
|
||||
page-id (:id page)
|
||||
comp-1-comp-2 (ths/get-shape file :comp-1-comp-2)
|
||||
rect-comp-3 (ths/get-shape-by-id file (first (:shapes comp-1-comp-2)))
|
||||
;; Changes to update the color of the contained rectangle in component comp-3
|
||||
changes-update-color-comp-3
|
||||
(cls/generate-update-shapes (pcb/empty-changes nil page-id)
|
||||
[(:id rect-comp-3)]
|
||||
(fn [shape]
|
||||
(assoc shape :fills (ths/sample-fills-color :fill-color "#00FF00")))
|
||||
(:objects page)
|
||||
{})
|
||||
|
||||
file' (thf/apply-changes file changes-update-color-comp-3)]
|
||||
|
||||
(t/is (= (first-child-fill-color file' :comp-1-comp-2) "#00FF00"))
|
||||
file'))
|
||||
|
||||
(step-reset [file]
|
||||
(let [page (thf/current-page file)
|
||||
file-id (:id file)
|
||||
comp-1-comp-2 (ths/get-shape file :comp-1-comp-2)
|
||||
;; Changes to reset the changes on comp-1 inside comp-3
|
||||
changes-reset (cll/generate-reset-component (pcb/empty-changes)
|
||||
file
|
||||
{file-id file}
|
||||
(ctn/make-container page :page)
|
||||
(:id comp-1-comp-2)
|
||||
true)
|
||||
file' (thf/apply-changes file changes-reset)]
|
||||
|
||||
(t/is (= (first-child-fill-color file' :comp-1-comp-2) "#FF0000"))
|
||||
file'))]
|
||||
(validate [file validator]
|
||||
(validator file)
|
||||
file)]
|
||||
|
||||
(-> (setup)
|
||||
step-update-color-comp-2
|
||||
step-propagate-comp-2
|
||||
step-update-color-comp-3
|
||||
step-reset)))
|
||||
;; Change the color of Comp1 inside Comp2 to red. It will propagate to Comp1 inside Comp3
|
||||
(tho/update-bottom-color :frame-comp-2 "#FF0000" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#2653d8" "#FF0000" "#FF0000"])))
|
||||
|
||||
;; Change the color of Comp1 inside Comp3 to green.
|
||||
(tho/update-bottom-color :frame-comp-3 "#00FF00" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#2653d8" "#FF0000" "#00FF00"])))
|
||||
|
||||
;; Select Comp1 inside Comp3, and do a ‘Reset override’
|
||||
;; Desired result: Comp1 inside Comp3 change its color to red, like Comp1 inside Comp2.
|
||||
(tho/reset-overrides-in-first-child :copy-comp-2)
|
||||
(validate #(t/is (= (fill-colors %) ["#2653d8" "#FF0000" "#FF0000"]))))))
|
||||
|
||||
(t/deftest test-propagation-with-deleted-component
|
||||
(letfn [(setup []
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-frame :frame-comp-4)
|
||||
(ths/add-sample-shape :rectangle :parent-label :frame-comp-4)
|
||||
(ths/add-sample-shape :rectangle :parent-label :frame-comp-4 :fills (ths/sample-fills-color :fill-color "#b1b2b5"))
|
||||
(thc/make-component :comp-4 :frame-comp-4)
|
||||
|
||||
(tho/add-frame :frame-comp-5)
|
||||
|
@ -142,71 +91,26 @@
|
|||
(thc/instantiate-component :comp-5 :copy-comp-5 :parent-label :frame-comp-6 :children-labels [:comp-4-comp-5])
|
||||
(thc/make-component :comp-6 :frame-comp-6)))
|
||||
|
||||
(step-delete-comp-5 [file]
|
||||
(let [page (thf/current-page file)
|
||||
;; Changes to delete comp-5
|
||||
[_ changes-delete] (cls/generate-delete-shapes (pcb/empty-changes nil (:id page))
|
||||
file
|
||||
page
|
||||
(:objects page)
|
||||
#{(-> (ths/get-shape file :frame-comp-5)
|
||||
:id)}
|
||||
{:components-v2 true})
|
||||
(propagate-all-component-changes [file]
|
||||
(-> file
|
||||
(tho/propagate-component-changes :comp-4)
|
||||
(tho/propagate-component-changes :comp-5)))
|
||||
|
||||
file' (thf/apply-changes file changes-delete)]
|
||||
(t/is (= (first-child-fill-color file' :comp-4-comp-5) "#B1B2B5"))
|
||||
file'))
|
||||
(fill-colors [file]
|
||||
[(tho/bottom-fill-color file :frame-comp-4)
|
||||
(tho/bottom-fill-color file :frame-comp-5)
|
||||
(tho/bottom-fill-color file :frame-comp-6)])
|
||||
|
||||
(step-update-color-comp-4 [file]
|
||||
(let [page (thf/current-page file)
|
||||
;; Changes to update the color of the contained rectangle in component comp-4
|
||||
changes-update-color-comp-4
|
||||
(cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||
[(-> (ths/get-shape file :rectangle)
|
||||
:id)]
|
||||
(fn [shape]
|
||||
(assoc shape :fills (ths/sample-fills-color :fill-color "#FF0000")))
|
||||
(:objects page)
|
||||
{})
|
||||
|
||||
file' (thf/apply-changes file changes-update-color-comp-4)]
|
||||
(t/is (= (first-fill-color file' :rectangle) "#FF0000"))
|
||||
file'))
|
||||
|
||||
(step-propagate-comp-4 [file]
|
||||
(let [file-id (:id file)
|
||||
;; Changes to propagate the color changes of component comp-4
|
||||
changes-sync-comp-4 (-> (pcb/empty-changes)
|
||||
(cll/generate-sync-file-changes
|
||||
nil
|
||||
:components
|
||||
file-id
|
||||
(:id (thc/get-component file :comp-4))
|
||||
file-id
|
||||
{file-id file}
|
||||
file-id))
|
||||
|
||||
file' (thf/apply-changes file changes-sync-comp-4)]
|
||||
file'))
|
||||
|
||||
(step-propagate-comp-5 [file]
|
||||
(let [file-id (:id file)
|
||||
;; Changes to propagate the color changes of component comp-5
|
||||
changes-sync-comp-5 (-> (pcb/empty-changes)
|
||||
(cll/generate-sync-file-changes
|
||||
nil
|
||||
:components
|
||||
file-id
|
||||
(:id (thc/get-component file :comp-5))
|
||||
file-id
|
||||
{file-id file}
|
||||
file-id))
|
||||
file' (thf/apply-changes file changes-sync-comp-5)]
|
||||
(t/is (= (first-child-fill-color file' :comp-4-comp-5) "#FF0000"))
|
||||
file'))]
|
||||
(validate [file validator]
|
||||
(validator file)
|
||||
file)]
|
||||
|
||||
(-> (setup)
|
||||
step-delete-comp-5
|
||||
step-update-color-comp-4
|
||||
step-propagate-comp-4
|
||||
step-propagate-comp-5)))
|
||||
;; Delete Comp5.
|
||||
(tho/delete-shape :frame-comp-5)
|
||||
(validate #(t/is (= (fill-colors %) ["#b1b2b5" nil "#b1b2b5"])))
|
||||
|
||||
;; Change the color of Comp4
|
||||
;; Desired result: Comp6 change color
|
||||
(tho/update-bottom-color :frame-comp-4 "#FF0000" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#FF0000" nil "#FF0000"]))))))
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
;; :blue1 [:name Frame1, :swap-slot-label :red-copy] @--> :frame-blue
|
||||
;; :frame-yellow [:name Frame1]
|
||||
;; :green-copy [:name Frame1] @--> :frame-green
|
||||
;; :blue-copy-in-green-copy [:name Frame1, :swap-slot-label :red-copy-green] @--> :frame-blue
|
||||
;; :blue2 [:name Frame1, :swap-slot-label :red-copy-green] @--> :frame-blue
|
||||
;; {:frame-b2} [:name Frame1] # [Component :b2]
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-frame :frame-red)
|
||||
|
@ -49,7 +49,7 @@
|
|||
(thc/instantiate-component :red :red-copy :parent-label :frame-b1)
|
||||
(thc/component-swap :red-copy :blue :blue1)
|
||||
(thc/instantiate-component :green :green-copy :parent-label :frame-b1 :children-labels [:red-copy-in-green-copy])
|
||||
(thc/component-swap :red-copy-in-green-copy :blue :blue-copy-in-green-copy)
|
||||
(thc/component-swap :red-copy-in-green-copy :blue :blue2)
|
||||
(tho/add-frame :frame-b2)
|
||||
(thc/make-component :b2 :frame-b2)))
|
||||
|
||||
|
@ -450,3 +450,314 @@
|
|||
;; duplicated-blue1'' has not swap-id
|
||||
(t/is (some? duplicated-blue1''))
|
||||
(t/is (nil? (ctk/get-swap-slot duplicated-blue1'')))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-relocating-blue1-before-yellow
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue1 (ths/get-shape file :blue1)
|
||||
|
||||
;; ==== Action
|
||||
changes (cls/generate-relocate-shapes (pcb/empty-changes nil)
|
||||
(:objects page)
|
||||
#{(:parent-id blue1)} ;; parents
|
||||
(:parent-id blue1) ;; parent-id
|
||||
(:id page) ;; page-id
|
||||
2 ;; to-index
|
||||
#{(:id blue1)}) ;; ids
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
blue1' (ths/get-shape file' :blue1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 had swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue1)))
|
||||
|
||||
;; blue1 still has swap-id after move
|
||||
(t/is (some? blue1'))
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-move-blue1-inside-and-outside-yellow
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
page (thf/current-page file)
|
||||
blue1 (ths/get-shape file :blue1)
|
||||
yellow (ths/get-shape file :frame-yellow)
|
||||
|
||||
;; ==== Action
|
||||
;; Move blue1 into yellow
|
||||
changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil)
|
||||
#{(:id blue1)} ;; ids
|
||||
(:id yellow) ;; frame-id
|
||||
(:id page) ;; page-id
|
||||
(:objects page) ;; objects
|
||||
0 ;; drop-index
|
||||
nil) ;; cell
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; Move blue1 outside yellow
|
||||
page' (thf/current-page file')
|
||||
blue1' (ths/get-shape file' :blue1)
|
||||
b1' (ths/get-shape file' :frame-b1)
|
||||
changes' (cls/generate-move-shapes-to-frame (pcb/empty-changes nil)
|
||||
#{(:id blue1')} ;; ids
|
||||
(:id b1') ;; frame-id
|
||||
(:id page') ;; page-id
|
||||
(:objects page') ;; objects
|
||||
0 ;; drop-index
|
||||
nil) ;; cell
|
||||
|
||||
file'' (thf/apply-changes file' changes')
|
||||
|
||||
;; ==== Get
|
||||
blue1'' (ths/get-shape file'' :blue1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue1)))
|
||||
|
||||
;;blue1 still has swap-id after move
|
||||
(t/is (some? blue1''))
|
||||
(t/is (some? (ctk/get-swap-slot blue1'')))))
|
||||
|
||||
|
||||
(t/deftest test-keep-swap-slot-relocate-blue1-inside-and-outside-yellow
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
page (thf/current-page file)
|
||||
blue1 (ths/get-shape file :blue1)
|
||||
yellow (ths/get-shape file :frame-yellow)
|
||||
|
||||
;; ==== Action
|
||||
;; Relocate blue1 into yellow
|
||||
changes (cls/generate-relocate-shapes (pcb/empty-changes nil)
|
||||
(:objects page)
|
||||
#{(:parent-id blue1)} ;; parents
|
||||
(:id yellow) ;; parent-id
|
||||
(:id page) ;; page-id
|
||||
0 ;; to-index
|
||||
#{(:id blue1)}) ;; ids
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; Relocate blue1 outside yellow
|
||||
page' (thf/current-page file')
|
||||
blue1' (ths/get-shape file' :blue1)
|
||||
b1' (ths/get-shape file' :frame-b1)
|
||||
changes' (cls/generate-relocate-shapes (pcb/empty-changes nil)
|
||||
(:objects page')
|
||||
#{(:parent-id blue1')} ;; parents
|
||||
(:id b1') ;; parent-id
|
||||
(:id page') ;; page-id
|
||||
0 ;; to-index
|
||||
#{(:id blue1')}) ;; ids
|
||||
|
||||
file'' (thf/apply-changes file' changes')
|
||||
|
||||
;; ==== Get
|
||||
blue1'' (ths/get-shape file'' :blue1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue1)))
|
||||
|
||||
;;blue1 still has swap-id after move
|
||||
(t/is (some? blue1''))
|
||||
(t/is (some? (ctk/get-swap-slot blue1'')))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-relocating-green-copy-to-root
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue2 (ths/get-shape file :blue2)
|
||||
green-copy (ths/get-shape file :green-copy)
|
||||
|
||||
;; ==== Action
|
||||
changes (cls/generate-relocate-shapes (pcb/empty-changes nil)
|
||||
(:objects page)
|
||||
#{(:parent-id green-copy)} ;; parents
|
||||
uuid/zero ;; parent-id
|
||||
(:id page) ;; page-id
|
||||
0 ;; to-index
|
||||
#{(:id green-copy)}) ;; ids
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
blue2' (ths/get-shape file' :blue2)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 had swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue2)))
|
||||
|
||||
;; blue1still has swap-id after move
|
||||
(t/is (some? blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-moving-green-copy-to-root
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue2 (ths/get-shape file :blue2)
|
||||
green-copy (ths/get-shape file :green-copy)
|
||||
|
||||
;; ==== Action
|
||||
changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil)
|
||||
#{(:id green-copy)} ;; ids
|
||||
uuid/zero ;; frame-id
|
||||
(:id page) ;; page-id
|
||||
(:objects page) ;; objects
|
||||
0 ;; drop-index
|
||||
nil) ;; cell
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
blue2' (ths/get-shape file' :blue2)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 had swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue2)))
|
||||
|
||||
;; blue1still has swap-id after move
|
||||
(t/is (some? blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-relocating-green-copy-to-b2
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue2 (ths/get-shape file :blue2)
|
||||
green-copy (ths/get-shape file :green-copy)
|
||||
b2 (ths/get-shape file :frame-b2)
|
||||
|
||||
;; ==== Action
|
||||
changes (cls/generate-relocate-shapes (pcb/empty-changes nil)
|
||||
(:objects page)
|
||||
#{(:parent-id green-copy)} ;; parents
|
||||
(:id b2) ;; parent-id
|
||||
(:id page) ;; page-id
|
||||
0 ;; to-index
|
||||
#{(:id green-copy)}) ;; ids
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
blue2' (ths/get-shape file' :blue2)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 had swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue2)))
|
||||
|
||||
;; blue1still has swap-id after move
|
||||
(t/is (some? blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-moving-green-copy-to-b2
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue2 (ths/get-shape file :blue2)
|
||||
green-copy (ths/get-shape file :green-copy)
|
||||
b2 (ths/get-shape file :frame-b2)
|
||||
|
||||
;; ==== Action
|
||||
changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil)
|
||||
#{(:id green-copy)} ;; ids
|
||||
(:id b2) ;; frame-id
|
||||
(:id page) ;; page-id
|
||||
(:objects page) ;; objects
|
||||
0 ;; drop-index
|
||||
nil) ;; cell
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
blue2' (ths/get-shape file' :blue2)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 had swap-id before move
|
||||
(t/is (some? (ctk/get-swap-slot blue2)))
|
||||
|
||||
;; blue1still has swap-id after move
|
||||
(t/is (some? blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-duplicating-green-copy
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
green-copy (ths/get-shape file :green-copy)
|
||||
|
||||
;; ==== Action
|
||||
changes (-> (pcb/empty-changes nil)
|
||||
(cll/generate-duplicate-changes (:objects page) ;; objects
|
||||
page ;; page
|
||||
#{(:id green-copy)} ;; ids
|
||||
(gpt/point 0 0) ;; delta
|
||||
{(:id file) file} ;; libraries
|
||||
(:data file) ;; library-data
|
||||
(:id file)) ;; file-id
|
||||
(cll/generate-duplicate-changes-update-indices (:objects page) ;; objects
|
||||
#{(:id green-copy)})) ;; ids
|
||||
|
||||
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
page' (thf/current-page file')
|
||||
blue1' (ths/get-shape file' :blue1)
|
||||
green-copy' (ths/get-shape file :green-copy)
|
||||
duplicated-green-copy' (find-duplicated-shape green-copy' page')
|
||||
duplicated-blue1-id' (-> duplicated-green-copy'
|
||||
:shapes
|
||||
first)
|
||||
duplicated-blue1' (get (:objects page') duplicated-blue1-id')]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; duplicated-blue1 also has swap-id
|
||||
(t/is (some? duplicated-blue1'))
|
||||
(t/is (some? (ctk/get-swap-slot duplicated-blue1')))))
|
||||
|
||||
(t/deftest test-swap-outside-component-doesnt-have-swap-slot
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
|
||||
page (thf/current-page file)
|
||||
blue1 (ths/get-shape file :blue1)
|
||||
|
||||
;; ==== Action
|
||||
|
||||
file' (-> file
|
||||
(thc/instantiate-component :red :red-copy1)
|
||||
(thc/component-swap :red-copy1 :blue :blue-copy1))
|
||||
|
||||
;; ==== Get
|
||||
blue-copy1' (ths/get-shape file' :blue-copy1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue-copy1 has not swap-id
|
||||
(t/is (some? blue-copy1'))
|
||||
(t/is (nil? (ctk/get-swap-slot blue-copy1')))))
|
||||
|
|
117
common/test/common_tests/logic/duplicated_pages_test.cljc
Normal file
117
common/test/common_tests/logic/duplicated_pages_test.cljc
Normal file
|
@ -0,0 +1,117 @@
|
|||
;; 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
|
||||
|
||||
(ns common-tests.logic.duplicated-pages-test
|
||||
(:require
|
||||
[app.common.files.changes :as ch]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.pprint :as pp]
|
||||
[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.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
;; Related .penpot file: common/test/cases/duplicated-pages.penpot
|
||||
(t/deftest test-propagation-with-anidated-components
|
||||
(letfn [(setup []
|
||||
(-> (thf/sample-file :file1 :page-label :page-1)
|
||||
(tho/add-frame :frame-ellipse-1 :fills [])
|
||||
(ths/add-sample-shape :ellipse-shape-1 :parent-label :frame-ellipse-1 :fills (ths/sample-fills-color :fill-color "#204fdc"))
|
||||
(thc/make-component :ellipse-1 :frame-ellipse-1)
|
||||
|
||||
(tho/add-frame :frame-ellipse-2 :fills [])
|
||||
(ths/add-sample-shape :ellipse-shape-2 :parent-label :frame-ellipse-2 :fills (ths/sample-fills-color :fill-color "#dc3020"))
|
||||
(thc/make-component :ellipse-2 :frame-ellipse-2)
|
||||
|
||||
(tho/add-frame :frame-ellipse-3 :fills [])
|
||||
(ths/add-sample-shape :ellipse-shape-3 :parent-label :frame-ellipse-3 :fills (ths/sample-fills-color :fill-color "#d8dc20"))
|
||||
(thc/make-component :ellipse-3 :frame-ellipse-3)
|
||||
|
||||
(tho/add-frame :frame-board-1 :fills (ths/sample-fills-color :fill-color "#FFFFFF"))
|
||||
(thc/instantiate-component :ellipse-1 :copy-ellipse-1 :parent-label :frame-board-1 :children-labels [:ellipse-shape-1-board-1])
|
||||
(thc/make-component :board-1 :frame-board-1)
|
||||
|
||||
(thf/add-sample-page :page-2)
|
||||
(tho/add-frame :frame-board-2 :fills (ths/sample-fills-color :fill-color "#FFFFFF"))
|
||||
(thc/instantiate-component :board-1 :copy-board-1 :parent-label :frame-board-2 :children-labels [:board-1-board-2])
|
||||
(thc/make-component :board-2 :frame-board-2)
|
||||
|
||||
(thf/add-sample-page :page-3)
|
||||
(tho/add-frame :frame-board-3 :fills (ths/sample-fills-color :fill-color "#FFFFFF"))
|
||||
(thc/instantiate-component :board-2 :copy-board-2 :parent-label :frame-board-3 :children-labels [:board-2-board-3])
|
||||
(thc/make-component :board-3 :frame-board-3)))
|
||||
|
||||
(propagate-all-component-changes [file]
|
||||
(-> file
|
||||
(tho/propagate-component-changes :ellipse-1)
|
||||
(tho/propagate-component-changes :ellipse-2)
|
||||
(tho/propagate-component-changes :ellipse-3)
|
||||
(tho/propagate-component-changes :board-1)
|
||||
(tho/propagate-component-changes :board-2)))
|
||||
|
||||
(reset-all-overrides [file]
|
||||
(-> file
|
||||
(tho/reset-overrides-in-first-child :frame-board-1 :page-label :page-1)
|
||||
(tho/reset-overrides-in-first-child :copy-board-1 :page-label :page-2)
|
||||
(propagate-all-component-changes)))
|
||||
|
||||
(fill-colors [file]
|
||||
[(tho/bottom-fill-color file :frame-ellipse-1 :page-label :page-1)
|
||||
(tho/bottom-fill-color file :frame-ellipse-2 :page-label :page-1)
|
||||
(tho/bottom-fill-color file :frame-ellipse-3 :page-label :page-1)
|
||||
(tho/bottom-fill-color file :frame-board-1 :page-label :page-1)
|
||||
(tho/bottom-fill-color file :frame-board-2 :page-label :page-2)
|
||||
(tho/bottom-fill-color file :frame-board-3 :page-label :page-3)
|
||||
(->
|
||||
(ths/get-shape file :frame-board-1 :page-label :page-1)
|
||||
:fills
|
||||
first
|
||||
:fill-color)
|
||||
(->
|
||||
(ths/get-shape file :copy-board-1 :page-label :page-2)
|
||||
:fills
|
||||
first
|
||||
:fill-color)])
|
||||
|
||||
(validate [file validator]
|
||||
(validator file)
|
||||
file)]
|
||||
|
||||
(-> (setup)
|
||||
;; Swap the copy inside main of Board1 to Ellipse2, and see that it propagates to copies in other pages.
|
||||
(tho/swap-component-in-shape :copy-ellipse-1 :ellipse-2 :page-label :page-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#dc3020" "#d8dc20" "#dc3020" "#dc3020" "#dc3020" "#FFFFFF" "#FFFFFF"])))
|
||||
|
||||
;; Change color of Ellipse2 main, and see that it propagates to all copies.
|
||||
(tho/update-bottom-color :frame-ellipse-2 "#abcdef" :page-label :page-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#abcdef" "#abcdef" "#abcdef" "#FFFFFF" "#FFFFFF"])))
|
||||
|
||||
;;Change color of copies of Ellipse2 and see that the override works.
|
||||
(tho/update-bottom-color :frame-board-1 "#efaade" :page-label :page-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#efaade" "#efaade" "#efaade" "#FFFFFF" "#FFFFFF"])))
|
||||
(tho/update-bottom-color :copy-board-1 "#aaefcb" :page-label :page-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#efaade" "#aaefcb" "#aaefcb" "#FFFFFF" "#FFFFFF"])))
|
||||
|
||||
;; Reset all overrides.
|
||||
(reset-all-overrides)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#abcdef" "#abcdef" "#abcdef" "#FFFFFF" "#FFFFFF"])))
|
||||
|
||||
;; Swap the copy of Ellipse2 inside copies of Board1 to Ellipse 3. Then make
|
||||
;; changes in Board1 main and see that they are not propagated.
|
||||
(tho/swap-component-in-first-child :copy-board-1 :ellipse-3 :page-label :page-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#abcdef" "#d8dc20" "#d8dc20" "#FFFFFF" "#FFFFFF"])))
|
||||
(tho/update-color :frame-board-1 "#fabada" :page-label :page-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#204fdc" "#abcdef" "#d8dc20" "#abcdef" "#d8dc20" "#d8dc20" "#fabada" "#fabada"]))))))
|
123
common/test/common_tests/logic/multiple_nesting_levels_test.cljc
Normal file
123
common/test/common_tests/logic/multiple_nesting_levels_test.cljc
Normal file
|
@ -0,0 +1,123 @@
|
|||
;; 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
|
||||
|
||||
(ns common-tests.logic.multiple-nesting-levels-test
|
||||
(:require
|
||||
[app.common.files.changes :as ch]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.pprint :as pp]
|
||||
[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.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
;; Related .penpot file: common/test/cases/multiple-testing-levels.penpot
|
||||
(t/deftest test-multiple-nesting-levels
|
||||
(letfn [(setup []
|
||||
(-> (thf/sample-file :file1)
|
||||
|
||||
(tho/add-frame :frame-simple-1)
|
||||
(ths/add-sample-shape :rectangle :parent-label :frame-simple-1 :fills (ths/sample-fills-color :fill-color "#2152e5"))
|
||||
(thc/make-component :simple-1 :frame-simple-1)
|
||||
|
||||
(tho/add-frame :frame-simple-2)
|
||||
(ths/add-sample-shape :circle :parent-label :frame-simple-2 :fills (ths/sample-fills-color :fill-color "#e56d21"))
|
||||
(thc/make-component :simple-2 :frame-simple-2)
|
||||
|
||||
(tho/add-frame :frame-composed-1)
|
||||
(thc/instantiate-component :simple-1 :copy-simple-1 :parent-label :frame-composed-1 :children-labels [:simple-1-composed-1])
|
||||
(thc/make-component :composed-1 :frame-composed-1)
|
||||
|
||||
(tho/add-frame :frame-composed-2)
|
||||
(thc/instantiate-component :composed-1 :copy-frame-composed-1 :parent-label :frame-composed-2 :children-labels [:composed-1-composed-2])
|
||||
(thc/make-component :composed-2 :frame-composed-2)
|
||||
|
||||
(thc/instantiate-component :composed-2 :copy-frame-composed-2 :children-labels [:composed-1-composed-2-copy])))
|
||||
|
||||
(propagate-all-component-changes [file]
|
||||
(-> file
|
||||
(tho/propagate-component-changes :simple-1)
|
||||
(tho/propagate-component-changes :simple-2)
|
||||
(tho/propagate-component-changes :composed-1)
|
||||
(tho/propagate-component-changes :composed-2)))
|
||||
|
||||
(reset-all-overrides [file]
|
||||
(-> file
|
||||
(tho/reset-overrides (ths/get-shape file :copy-simple-1))
|
||||
(tho/reset-overrides (ths/get-shape file :copy-frame-composed-1))
|
||||
(tho/reset-overrides (ths/get-shape file :composed-1-composed-2-copy))
|
||||
(propagate-all-component-changes)))
|
||||
|
||||
(fill-colors [file]
|
||||
[(tho/bottom-fill-color file :frame-simple-1)
|
||||
(tho/bottom-fill-color file :frame-simple-2)
|
||||
(tho/bottom-fill-color file :frame-composed-1)
|
||||
(tho/bottom-fill-color file :frame-composed-2)
|
||||
(tho/bottom-fill-color file :copy-frame-composed-2)])
|
||||
|
||||
(validate [file validator]
|
||||
(validator file)
|
||||
file)]
|
||||
(-> (setup)
|
||||
;; Change color of Simple1 and see that it's propagated to all copies.
|
||||
(tho/update-bottom-color :frame-simple-1 "#e521a8" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#e521a8" "#e56d21" "#e521a8" "#e521a8" "#e521a8"])))
|
||||
|
||||
;; Override color in copy inside Composed1, Composed2 and the copy
|
||||
;; of Composed2 and see in all cases that a change in the main is overriden.
|
||||
(tho/update-bottom-color :simple-1-composed-1 "#21e59e" :propagate-fn propagate-all-component-changes)
|
||||
(tho/update-bottom-color :composed-1-composed-2 "#2186e5" :propagate-fn propagate-all-component-changes)
|
||||
(tho/update-bottom-color :composed-1-composed-2-copy "#e5a221" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#e521a8" "#e56d21" "#21e59e" "#2186e5" "#e5a221"])))
|
||||
(tho/update-bottom-color :frame-simple-1 "#b2e521" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#b2e521" "#e56d21" "#21e59e" "#2186e5" "#e5a221"])))
|
||||
|
||||
;; Reset all overrides and check again the propagation from mains.
|
||||
(reset-all-overrides)
|
||||
(tho/update-bottom-color :frame-simple-1 "#21aae5" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#21aae5" "#e56d21" "#21aae5" "#21aae5" "#21aae5"])))
|
||||
|
||||
;; Swap in Composed1 to Simple2 and see that it propagates ok.
|
||||
(tho/swap-component-in-shape :copy-simple-1 :simple-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#21aae5" "#e56d21" "#e56d21" "#e56d21" "#e56d21"])))
|
||||
|
||||
;; Change color of Simple 2 and see that it propagates ok.
|
||||
(tho/update-bottom-color :frame-simple-2 "#c321e5" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#21aae5" "#c321e5" "#c321e5" "#c321e5" "#c321e5"])))
|
||||
|
||||
;; Swap Simple 2 copy in Composed2. Check propagations.
|
||||
(tho/swap-component-in-first-child :copy-frame-composed-1 :simple-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#21aae5" "#c321e5" "#c321e5" "#21aae5" "#21aae5"])))
|
||||
|
||||
;; Change color of Simple 1 and check propagation.
|
||||
(tho/update-bottom-color :frame-simple-1 "#e521a8" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#e521a8" "#c321e5" "#c321e5" "#e521a8" "#e521a8"])))
|
||||
|
||||
;; Reset overrides in Composed2 main, and swap Simple 2 copy in
|
||||
;; Composed2 copy. Change color of Simple 2 and check propatagion.
|
||||
(tho/reset-overrides-in-first-child :copy-frame-composed-1 :propagate-fn propagate-all-component-changes)
|
||||
(tho/swap-component-in-first-child :composed-1-composed-2-copy :simple-1 :propagate-fn propagate-all-component-changes)
|
||||
(tho/update-bottom-color :frame-simple-2 "#21e55d" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#e521a8" "#21e55d" "#21e55d" "#21e55d" "#e521a8"])))
|
||||
|
||||
;; Swap all of three (Composed 1, Composed2 and copy of Composed2)
|
||||
;; and check propagations from Simple mains.
|
||||
(tho/swap-component-in-first-child :frame-composed-1 :simple-1 :propagate-fn propagate-all-component-changes)
|
||||
(tho/swap-component-in-first-child :copy-frame-composed-1 :simple-2 :propagate-fn propagate-all-component-changes)
|
||||
(tho/swap-component-in-first-child :composed-1-composed-2-copy :simple-2 :propagate-fn propagate-all-component-changes)
|
||||
(tho/update-bottom-color :frame-simple-1 "#111111" :propagate-fn propagate-all-component-changes)
|
||||
(tho/update-bottom-color :frame-simple-2 "#222222" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#222222" "#111111" "#222222" "#222222"]))))))
|
|
@ -20,156 +20,65 @@
|
|||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
;; Related .penpot file: common/test/cases/swap-and-reset.penpot
|
||||
(t/deftest test-simple-swap
|
||||
(let [;; ==== Setup
|
||||
file (-> (thf/sample-file :file1)
|
||||
(tho/add-simple-component-with-copy :component-1
|
||||
:component-1-main-root
|
||||
:component-1-main-child
|
||||
:component-1-copy-root)
|
||||
(tho/add-simple-component :component-2
|
||||
:component-2-root
|
||||
:component-2-child))
|
||||
|
||||
component-1-copy-root (ths/get-shape file :component-1-copy-root)
|
||||
component-2 (thc/get-component file :component-2)
|
||||
page (thf/current-page file)
|
||||
|
||||
;; ==== Action
|
||||
[new-shape _all-parents changes]
|
||||
(cll/generate-component-swap (pcb/empty-changes)
|
||||
(:objects page)
|
||||
component-1-copy-root
|
||||
(:data file)
|
||||
page
|
||||
{(:id file) file}
|
||||
(:id component-2)
|
||||
0
|
||||
nil
|
||||
{})
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
swapped (ths/get-shape-by-id file' (:id new-shape))]
|
||||
|
||||
;; ==== Check
|
||||
(t/is (not= (:component-id component-1-copy-root) (:component-id swapped)))
|
||||
(t/is (= (:id component-2) (:component-id swapped)))
|
||||
(t/is (= (:id file) (:component-file swapped)))))
|
||||
|
||||
(t/deftest test-swap-nested
|
||||
(let [;; ==== Setup
|
||||
file
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-simple-component :component-1 :component-1-main-root :component-1-main-child)
|
||||
(tho/add-frame :component-container)
|
||||
(thc/instantiate-component :component-1 :component-1-copy-root :parent-label :component-container)
|
||||
(thc/make-component :component-container-main :component-container)
|
||||
(thc/instantiate-component :component-container-main :component-container-instance)
|
||||
(tho/add-simple-component :component-2 :component-2-main-root :component-2-main-child))
|
||||
|
||||
page (thf/current-page file)
|
||||
component-2 (thc/get-component file :component-2)
|
||||
|
||||
copy
|
||||
(->>
|
||||
(ths/get-shape file :component-container-instance)
|
||||
:shapes
|
||||
first
|
||||
(ths/get-shape-by-id file))
|
||||
|
||||
libraries {(:id file) file}
|
||||
|
||||
;; ==== Action
|
||||
[new-shape _all-parents changes]
|
||||
(cll/generate-component-swap (pcb/empty-changes)
|
||||
(:objects page)
|
||||
copy
|
||||
(:data file)
|
||||
page
|
||||
libraries
|
||||
(:id component-2)
|
||||
0
|
||||
nil
|
||||
{})
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
libraries' {(:id file') file'}
|
||||
page' (thf/current-page file')
|
||||
|
||||
;; ==== Get
|
||||
swapped (ths/get-shape-by-id file' (:id new-shape))
|
||||
component-1-copy-root (ths/get-shape file' :component-1-copy-root)
|
||||
slot (-> (ctf/find-swap-slot swapped
|
||||
page'
|
||||
file'
|
||||
libraries')
|
||||
(ctk/build-swap-slot-group))]
|
||||
|
||||
;; ==== Check
|
||||
(t/is (not= (:component-id copy) (:component-id swapped)))
|
||||
(t/is (= (:id component-2) (:component-id swapped)))
|
||||
(t/is (= (:id file) (:component-file swapped)))
|
||||
(t/is (contains? (:touched swapped) slot))
|
||||
(t/is (= (ctk/get-swap-slot swapped) (:id component-1-copy-root)))))
|
||||
|
||||
(t/deftest test-swap-and-reset-override
|
||||
(let [;; ==== Setup
|
||||
file
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-simple-component :component-1 :component-1-main-root :component-1-main-child)
|
||||
(tho/add-frame :component-container)
|
||||
(thc/instantiate-component :component-1 :component-1-copy-root :parent-label :component-container)
|
||||
(thc/make-component :component-container-main :component-container)
|
||||
(thc/instantiate-component :component-container-main :component-container-instance)
|
||||
(tho/add-simple-component :component-2 :component-2-main-root :component-2-main-child))
|
||||
(letfn [(setup []
|
||||
(-> (thf/sample-file :file1)
|
||||
|
||||
page (thf/current-page file)
|
||||
component-1 (thc/get-component file :component-1)
|
||||
component-2 (thc/get-component file :component-2)
|
||||
(tho/add-frame :frame-rectangle)
|
||||
(ths/add-sample-shape :rectangle-shape :parent-label :frame-rectangle :type :rect)
|
||||
(thc/make-component :rectangle :frame-rectangle)
|
||||
|
||||
copy
|
||||
(->>
|
||||
(ths/get-shape file :component-container-instance)
|
||||
:shapes
|
||||
first
|
||||
(ths/get-shape-by-id file))
|
||||
(tho/add-frame :frame-circle)
|
||||
(ths/add-sample-shape :circle :parent-label :frame-circle :type :circle)
|
||||
(thc/make-component :circle :frame-circle)
|
||||
|
||||
;; ==== Action
|
||||
[new-shape _all-parents changes-swap]
|
||||
(cll/generate-component-swap (pcb/empty-changes)
|
||||
(:objects page)
|
||||
copy
|
||||
(:data file)
|
||||
page
|
||||
{(:id file) file}
|
||||
(:id component-2)
|
||||
0
|
||||
nil
|
||||
{})
|
||||
(tho/add-frame :frame-main)
|
||||
(thc/instantiate-component :rectangle :copy-rectangle :parent-label :frame-main :children-labels [:copy-rectangle-shape])
|
||||
(thc/make-component :main :frame-main)
|
||||
|
||||
file-swap (thf/apply-changes file changes-swap)
|
||||
page-swap (thf/current-page file-swap)
|
||||
(thc/instantiate-component :main :copy :children-labels [:copy-copy-rectangle])))
|
||||
|
||||
changes
|
||||
(cll/generate-reset-component (pcb/empty-changes)
|
||||
file-swap
|
||||
{(:id file-swap) file-swap}
|
||||
page-swap
|
||||
(:id new-shape)
|
||||
true)
|
||||
(copy-type [file]
|
||||
(:type (tho/bottom-shape file :copy)))
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
(nested-component-id [file]
|
||||
(->>
|
||||
(ths/get-shape file :copy)
|
||||
:shapes
|
||||
first
|
||||
(ths/get-shape-by-id file)
|
||||
(:component-id)))
|
||||
|
||||
;; ==== Get
|
||||
reset
|
||||
(->>
|
||||
(ths/get-shape file' :component-container-instance)
|
||||
:shapes
|
||||
first
|
||||
(ths/get-shape-by-id file'))]
|
||||
(nested-swap-slot [file]
|
||||
(->>
|
||||
(ths/get-shape file :copy)
|
||||
:shapes
|
||||
first
|
||||
(ths/get-shape-by-id file)
|
||||
(ctk/get-swap-slot)))
|
||||
|
||||
;; ==== Check
|
||||
(t/is (= (:id component-1) (:component-id reset)))
|
||||
(t/is (nil? (ctk/get-swap-slot reset)))))
|
||||
(circle-component-id [file]
|
||||
(:id (thc/get-component file :circle)))
|
||||
|
||||
(rectangle-component-id [file]
|
||||
(:id (thc/get-component file :rectangle)))
|
||||
|
||||
(copy-rectangle-id [file]
|
||||
(:id (ths/get-shape file :copy-rectangle)))
|
||||
|
||||
(validate [file validator]
|
||||
(validator file)
|
||||
file)]
|
||||
|
||||
(-> (setup)
|
||||
;; Select the Rectangle inside Copy and swap it for an Ellipse
|
||||
(tho/swap-component-in-shape :copy-copy-rectangle :circle)
|
||||
(validate #(t/is (= (copy-type %) :circle)))
|
||||
(validate #(t/is (= (nested-component-id %) (circle-component-id %))))
|
||||
(validate #(t/is (= (copy-rectangle-id %) (nested-swap-slot %))))
|
||||
|
||||
;; Do a "Reset override" on the newly created Ellipse. It should swap for a Rectangle
|
||||
(tho/reset-overrides-in-first-child :copy)
|
||||
(validate #(t/is (= (copy-type %) :rect)))
|
||||
(validate #(t/is (= (nested-component-id %) (rectangle-component-id %))))
|
||||
(validate #(t/is (nil? (nested-swap-slot %)))))))
|
||||
|
|
141
common/test/common_tests/logic/swap_as_override_test.cljc
Normal file
141
common/test/common_tests/logic/swap_as_override_test.cljc
Normal file
|
@ -0,0 +1,141 @@
|
|||
;; 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
|
||||
|
||||
(ns common-tests.logic.swap-as-override-test
|
||||
(:require
|
||||
[app.common.files.changes :as ch]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.pprint :as pp]
|
||||
[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.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(defn- setup []
|
||||
(-> (thf/sample-file :file1)
|
||||
|
||||
(tho/add-simple-component :component-1 :frame-component-1 :child-component-1 :child-params {:name "child-component-1" :type :rect :fills (ths/sample-fills-color :fill-color "#111111")})
|
||||
(tho/add-simple-component :component-2 :frame-component-2 :child-component-2 :child-params {:name "child-component-2" :type :rect :fills (ths/sample-fills-color :fill-color "#222222")})
|
||||
(tho/add-simple-component :component-3 :frame-component-3 :child-component-3 :child-params {:name "child-component-3" :type :rect :fills (ths/sample-fills-color :fill-color "#333333")})
|
||||
|
||||
(tho/add-frame :frame-icon-and-text)
|
||||
(thc/instantiate-component :component-1 :copy-component-1 :parent-label :frame-icon-and-text :children-labels [:component-1-icon-and-text])
|
||||
(ths/add-sample-shape :text
|
||||
{:type :text
|
||||
:name "icon+text"
|
||||
:parent-label :frame-icon-and-text})
|
||||
(thc/make-component :icon-and-text :frame-icon-and-text)
|
||||
|
||||
(tho/add-frame :frame-panel)
|
||||
(thc/instantiate-component :icon-and-text :copy-icon-and-text :parent-label :frame-panel :children-labels [:icon-and-text-panel])
|
||||
(thc/make-component :panel :frame-panel)
|
||||
|
||||
(thc/instantiate-component :panel :copy-panel :children-labels [:copy-icon-and-text-panel])))
|
||||
|
||||
(defn- propagate-all-component-changes [file]
|
||||
(-> file
|
||||
(tho/propagate-component-changes :icon-and-text)
|
||||
(tho/propagate-component-changes :panel)))
|
||||
|
||||
(defn- fill-colors [file]
|
||||
[(tho/bottom-fill-color file :frame-icon-and-text)
|
||||
(tho/bottom-fill-color file :frame-panel)
|
||||
(tho/bottom-fill-color file :copy-panel)])
|
||||
|
||||
(defn- validate [file validator]
|
||||
(validator file)
|
||||
file)
|
||||
|
||||
;; Related .penpot file: common/test/cases/xxxxxx
|
||||
(t/deftest swap-main-then-copy
|
||||
(-> (setup)
|
||||
;; Swap icon in icon+text main. Check that it propagates to copies.
|
||||
(tho/swap-component-in-shape :copy-component-1 :component-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#222222" "#222222" "#222222"])))
|
||||
|
||||
;; Change color of icon in icon+text main. Check that it propagates to copies.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#333333" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#333333" "#333333"])))
|
||||
|
||||
;; Swap icon inside panel main. Check it propagates to panel copy.
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text :component-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#111111" "#111111"])))
|
||||
|
||||
;; Change color of icon in icon+text. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#444444" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#444444" "#111111" "#111111"])))
|
||||
|
||||
;; Change color of icon in panel main. Check that it propagates.
|
||||
(tho/update-bottom-color :frame-panel "#555555" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#444444" "#555555" "#555555"])))))
|
||||
|
||||
(t/deftest swap-copy-then-main
|
||||
(-> (setup)
|
||||
;; Swap icon inside panel main. Check that it propagates to panel copy.
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text :component-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#222222" "#222222"])))
|
||||
|
||||
;; Change color of icon in icon+text. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#333333" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#222222" "#222222"])))
|
||||
|
||||
;;Change color of icon in panel main. Check that it propagates
|
||||
(tho/update-bottom-color :frame-panel "#444444" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#444444" "#444444"])))
|
||||
|
||||
;; Swap icon in icon+text main. Check that it does not propagate.
|
||||
(tho/swap-component-in-shape :copy-component-1 :component-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#222222" "#444444" "#444444"])))
|
||||
|
||||
;; Change color of icon in icon+text. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#555555" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#555555" "#444444" "#444444"])))))
|
||||
|
||||
(t/deftest swap-copy-then-2nd-copy
|
||||
(-> (setup)
|
||||
;; Swap icon inside panel main. Check that it propagates to panel copy.
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text :component-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#222222" "#222222"])))
|
||||
|
||||
;; Swap icon inside panel copy.
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text-panel :component-1 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#222222" "#111111"])))
|
||||
|
||||
;; Change color of icon in icon+text. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#333333" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#222222" "#111111"])))
|
||||
|
||||
;; Change color of icon in panel main. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-panel "#444444" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#333333" "#444444" "#111111"])))))
|
||||
|
||||
(t/deftest swap-2nd-copy-then-copy
|
||||
(-> (setup)
|
||||
;; Swap icon inside panel copy
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text-panel :component-2 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#111111" "#222222"])))
|
||||
|
||||
;; Swap icon inside panel main. Check that it does not propagate.
|
||||
(tho/swap-component-in-first-child :copy-icon-and-text :component-3 :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#111111" "#333333" "#222222"])))
|
||||
|
||||
;; Change color of icon in icon+text. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-icon-and-text "#444444" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#444444" "#333333" "#222222"])))
|
||||
|
||||
;; Change color of icon in panel main. Check that it does not propagate.
|
||||
(tho/update-bottom-color :frame-panel "#555555" :propagate-fn propagate-all-component-changes)
|
||||
(validate #(t/is (= (fill-colors %) ["#444444" "#555555" "#222222"])))))
|
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 4 : undefined,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
|
|
|
@ -2,17 +2,19 @@ export class MockWebSocketHelper extends EventTarget {
|
|||
static #mocks = new Map();
|
||||
|
||||
static async init(page) {
|
||||
await page.exposeFunction("MockWebSocket$$constructor", (url, protocols) => {
|
||||
const webSocket = new MockWebSocketHelper(page, url, protocols);
|
||||
this.#mocks = new Map();
|
||||
|
||||
await page.exposeFunction("onMockWebSocketConstructor", (url) => {
|
||||
const webSocket = new MockWebSocketHelper(page, url);
|
||||
this.#mocks.set(url, webSocket);
|
||||
});
|
||||
await page.exposeFunction("MockWebSocket$$spyMessage", (url, data) => {
|
||||
await page.exposeFunction("onMockWebSocketSpyMessage", (url, data) => {
|
||||
if (!this.#mocks.has(url)) {
|
||||
throw new Error(`WebSocket with URL ${url} not found`);
|
||||
}
|
||||
this.#mocks.get(url).dispatchEvent(new MessageEvent("message", { data }));
|
||||
});
|
||||
await page.exposeFunction("MockWebSocket$$spyClose", (url, code, reason) => {
|
||||
await page.exposeFunction("onMockWebSocketSpyClose", (url, code, reason) => {
|
||||
if (!this.#mocks.has(url)) {
|
||||
throw new Error(`WebSocket with URL ${url} not found`);
|
||||
}
|
||||
|
@ -36,39 +38,52 @@ export class MockWebSocketHelper extends EventTarget {
|
|||
|
||||
#page = null;
|
||||
#url;
|
||||
#protocols;
|
||||
|
||||
constructor(page, url, protocols) {
|
||||
super();
|
||||
this.#page = page;
|
||||
this.#url = url;
|
||||
this.#protocols = protocols;
|
||||
}
|
||||
|
||||
mockOpen(options) {
|
||||
return this.#page.evaluate(({ url, options }) => {
|
||||
if (typeof WebSocket.getByURL !== 'function') {
|
||||
throw new Error('WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?')
|
||||
}
|
||||
WebSocket.getByURL(url).mockOpen(options);
|
||||
}, { url: this.#url, options });
|
||||
return this.#page.evaluate(
|
||||
({ url, options }) => {
|
||||
if (typeof WebSocket.getByURL !== "function") {
|
||||
throw new Error(
|
||||
"WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
|
||||
);
|
||||
}
|
||||
WebSocket.getByURL(url).mockOpen(options);
|
||||
},
|
||||
{ url: this.#url, options },
|
||||
);
|
||||
}
|
||||
|
||||
mockMessage(data) {
|
||||
return this.#page.evaluate(({ url, data }) => {
|
||||
if (typeof WebSocket.getByURL !== 'function') {
|
||||
throw new Error('WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?')
|
||||
}
|
||||
WebSocket.getByURL(url).mockMessage(data);
|
||||
}, { url: this.#url, data });
|
||||
return this.#page.evaluate(
|
||||
({ url, data }) => {
|
||||
if (typeof WebSocket.getByURL !== "function") {
|
||||
throw new Error(
|
||||
"WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
|
||||
);
|
||||
}
|
||||
WebSocket.getByURL(url).mockMessage(data);
|
||||
},
|
||||
{ url: this.#url, data },
|
||||
);
|
||||
}
|
||||
|
||||
mockClose() {
|
||||
return this.#page.evaluate(({ url }) => {
|
||||
if (typeof WebSocket.getByURL !== 'function') {
|
||||
throw new Error('WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?')
|
||||
}
|
||||
WebSocket.getByURL(url).mockClose();
|
||||
}, { url: this.#url });
|
||||
return this.#page.evaluate(
|
||||
({ url }) => {
|
||||
if (typeof WebSocket.getByURL !== "function") {
|
||||
throw new Error(
|
||||
"WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
|
||||
);
|
||||
}
|
||||
WebSocket.getByURL(url).mockClose();
|
||||
},
|
||||
{ url: this.#url },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,14 +46,14 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
|
||||
MockWebSocket.#mocks.set(this.#url, this);
|
||||
|
||||
if (typeof window["MockWebSocket$$constructor"] === "function") {
|
||||
MockWebSocket$$constructor(this.#url, this.#protocols);
|
||||
if (typeof window["onMockWebSocketConstructor"] === "function") {
|
||||
onMockWebSocketConstructor(this.#url, this.#protocols);
|
||||
}
|
||||
if (typeof window["MockWebSocket$$spyMessage"] === "function") {
|
||||
this.#spyMessage = MockWebSocket$$spyMessage;
|
||||
if (typeof window["onMockWebSocketSpyMessage"] === "function") {
|
||||
this.#spyMessage = onMockWebSocketSpyMessage;
|
||||
}
|
||||
if (typeof window["MockWebSocket$$spyClose"] === "function") {
|
||||
this.#spyClose = MockWebSocket$$spyClose;
|
||||
if (typeof window["onMockWebSocketSpyClose"] === "function") {
|
||||
this.#spyClose = onMockWebSocketSpyClose;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ export class BaseWebSocketPage extends BasePage {
|
|||
* @param {Page} page
|
||||
* @returns
|
||||
*/
|
||||
static initWebSockets(page) {
|
||||
return MockWebSocketHelper.init(page);
|
||||
static async initWebSockets(page) {
|
||||
await MockWebSocketHelper.init(page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@ test.beforeEach(async ({ page }) => {
|
|||
await WorkspacePage.init(page);
|
||||
});
|
||||
|
||||
test.skip("User loads worskpace with empty file", async ({ page }) => {
|
||||
test("User loads worskpace with empty file", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
|
@ -15,7 +15,7 @@ test.skip("User loads worskpace with empty file", async ({ page }) => {
|
|||
await expect(workspacePage.pageName).toHaveText("Page 1");
|
||||
});
|
||||
|
||||
test.skip("User receives presence notifications updates in the workspace", async ({ page }) => {
|
||||
test("User receives presence notifications updates in the workspace", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
|
@ -25,7 +25,7 @@ test.skip("User receives presence notifications updates in the workspace", async
|
|||
await expect(page.getByTestId("active-users-list").getByAltText("Princesa Leia")).toHaveCount(2);
|
||||
});
|
||||
|
||||
test.skip("User draws a rect", async ({ page }) => {
|
||||
test("User draws a rect", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-create-rect.json");
|
||||
|
@ -35,6 +35,6 @@ test.skip("User draws a rect", async ({ page }) => {
|
|||
await workspacePage.clickWithDragViewportAt(128, 128, 200, 100);
|
||||
|
||||
const shape = await workspacePage.rootShape.locator("rect");
|
||||
expect(shape).toHaveAttribute("width", "200");
|
||||
expect(shape).toHaveAttribute("height", "100");
|
||||
await expect(shape).toHaveAttribute("width", "200");
|
||||
await expect(shape).toHaveAttribute("height", "100");
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,7 @@
|
|||
<script defer src="{{& polyfills}}"></script>
|
||||
{{/manifest}}
|
||||
|
||||
<script type="module" src="{{pluginRuntimeUri}}/index.mjs"></script>
|
||||
<script type="module" src="{{pluginRuntimeUri}}/index.js"></script>
|
||||
|
||||
<script>
|
||||
window.penpotTranslations = JSON.parse({{& translations}});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.exports :as de]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.preview :as dp]
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.users :as du]
|
||||
|
@ -23,6 +24,7 @@
|
|||
[app.main.data.workspace.texts :as dwtxt]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.hooks.resize :as r]
|
||||
|
@ -552,7 +554,15 @@
|
|||
:command (ds/a-mod "m")
|
||||
:subsections [:basics]
|
||||
:fn #(st/emit! (with-meta (du/toggle-theme)
|
||||
{::ev/origin "workspace:shortcut"}))}})
|
||||
{::ev/origin "workspace:shortcut"}))}
|
||||
|
||||
|
||||
;; PLUGINS
|
||||
:plugins {:tooltip (ds/meta (ds/alt "P"))
|
||||
:command (ds/c-mod "alt+p")
|
||||
:subsections [:basics]
|
||||
:fn #(when (features/active-feature? @st/state "plugins/runtime")
|
||||
(st/emit! (modal/show :plugin-management {})))}})
|
||||
|
||||
(def debug-shortcuts
|
||||
;; PREVIEW
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.components.search-bar
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
|
@ -23,7 +24,7 @@
|
|||
icon (unchecked-get props "icon")
|
||||
autofocus (unchecked-get props "auto-focus")
|
||||
id (unchecked-get props "id")
|
||||
|
||||
input-class (unchecked-get props "class")
|
||||
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
|
@ -51,7 +52,7 @@
|
|||
[:span {:class (stl/css-case :search-box true
|
||||
:has-children (some? children))}
|
||||
children
|
||||
[:div {:class (stl/css :search-input-wrapper)}
|
||||
[:div {:class (dm/str input-class " " (stl/css :search-input-wrapper))}
|
||||
icon
|
||||
[:input {:id id
|
||||
:on-change handle-change
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
[app.main.ui.workspace.libraries]
|
||||
[app.main.ui.workspace.nudge]
|
||||
[app.main.ui.workspace.palette :refer [palette]]
|
||||
[app.main.ui.workspace.plugins]
|
||||
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
||||
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
(ns app.main.ui.workspace.main-menu
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
|
@ -20,12 +22,14 @@
|
|||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.hooks.resize :as r]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.plugins :as uwp]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
|
@ -60,6 +64,9 @@
|
|||
nav-to-feedback
|
||||
(mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback})))
|
||||
|
||||
plugins?
|
||||
(features/active-feature? @st/state "plugins/runtime")
|
||||
|
||||
show-shortcuts
|
||||
(mf/use-fn
|
||||
(mf/deps layout)
|
||||
|
@ -83,7 +90,8 @@
|
|||
[:& dropdown-menu {:show true
|
||||
:on-close on-close
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:help-info true)}
|
||||
:help-info plugins?
|
||||
:help-info-old (not plugins?))}
|
||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||
:on-click nav-to-helpc-center
|
||||
:on-key-down (fn [event]
|
||||
|
@ -148,7 +156,6 @@
|
|||
:id "file-menu-shortcuts"}
|
||||
[:span {:class (stl/css :item-name)} (tr "label.shortcuts")]
|
||||
[:span {:class (stl/css :shortcut)}
|
||||
|
||||
(for [sc (scd/split-sc (sc/get-tooltip :show-shortcuts))]
|
||||
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
|
||||
|
||||
|
@ -596,6 +603,41 @@
|
|||
[:span {:class (stl/css :item-name)}
|
||||
(tr "dashboard.export-frames")]])]))
|
||||
|
||||
(mf/defc plugins-menu
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[{:keys [open-plugins on-close]}]
|
||||
(when (features/active-feature? @st/state "plugins/runtime")
|
||||
(let [plugins (uwp/load-from-store)]
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true :plugins true)
|
||||
:on-close on-close}
|
||||
[:> dropdown-menu-item* {:on-click open-plugins
|
||||
:class (stl/css :submenu-item)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(open-plugins event)))
|
||||
:data-test "open-plugins"
|
||||
:id "file-menu-open-plugins"}
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "workspace.plugins.menu.plugins-manager")]
|
||||
[:span {:class (stl/css :shortcut)}
|
||||
(for [sc (scd/split-sc (sc/get-tooltip :plugins))]
|
||||
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
|
||||
|
||||
|
||||
(when (d/not-empty? plugins)
|
||||
[:div {:class (stl/css :separator)}])
|
||||
|
||||
(for [[idx {:keys [name url]}] (d/enumerate plugins)]
|
||||
[:> dropdown-menu-item* {:key (dm/str "plugins-menu-" idx)
|
||||
:on-click #(uwp/open-plugin! url)
|
||||
:class (stl/css :submenu-item)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
#(uwp/open-plugin! url)))}
|
||||
[:span {:class (stl/css :item-name)} name]])])))
|
||||
|
||||
(mf/defc menu
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [layout file profile]}]
|
||||
|
@ -644,12 +686,19 @@
|
|||
(reset! show-menu* false)
|
||||
(reset! sub-menu* nil))))
|
||||
|
||||
|
||||
toggle-theme
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (du/toggle-theme))))]
|
||||
(st/emit! (du/toggle-theme))))
|
||||
|
||||
open-plugins-manager
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! show-menu* false)
|
||||
(reset! sub-menu* nil)
|
||||
(st/emit! (modal/show :plugin-management {}))))]
|
||||
|
||||
|
||||
[:*
|
||||
|
@ -703,6 +752,19 @@
|
|||
:id "file-menu-preferences"}
|
||||
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.preferences")]
|
||||
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
||||
|
||||
(when (features/active-feature? @st/state "plugins/runtime")
|
||||
[:> dropdown-menu-item* {:class (stl/css :menu-item)
|
||||
:on-click on-menu-click
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(on-menu-click event)))
|
||||
:on-pointer-enter on-menu-click
|
||||
:data-test "plugins"
|
||||
:id "file-menu-plugins"}
|
||||
[:span {:class (stl/css :item-name)} (tr "workspace.plugins.menu.title")]
|
||||
[:span {:class (stl/css :open-arrow)} i/arrow]])
|
||||
|
||||
[:div {:class (stl/css :separator)}]
|
||||
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
|
||||
:on-click on-menu-click
|
||||
|
@ -739,6 +801,11 @@
|
|||
:toggle-theme toggle-theme
|
||||
:on-close close-sub-menu}]
|
||||
|
||||
:plugins
|
||||
[:& plugins-menu
|
||||
{:open-plugins open-plugins-manager
|
||||
:on-close close-sub-menu}]
|
||||
|
||||
:help-info
|
||||
[:& help-info-menu
|
||||
{:layout layout
|
||||
|
|
|
@ -95,7 +95,15 @@
|
|||
top: $s-148;
|
||||
}
|
||||
|
||||
&.plugins {
|
||||
top: $s-180;
|
||||
}
|
||||
|
||||
&.help-info {
|
||||
top: $s-196;
|
||||
top: $s-232;
|
||||
}
|
||||
|
||||
&.help-info-old {
|
||||
top: $s-192;
|
||||
}
|
||||
}
|
||||
|
|
180
frontend/src/app/main/ui/workspace/plugins.cljs
Normal file
180
frontend/src/app/main/ui/workspace/plugins.cljs
Normal file
|
@ -0,0 +1,180 @@
|
|||
;; 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
|
||||
|
||||
(ns app.main.ui.workspace.plugins
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.components.search-bar :refer [search-bar]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.http :as http]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.object :as obj]
|
||||
[beicon.v2.core :as rx]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private close-icon
|
||||
(i/icon-xref :close (stl/css :close-icon)))
|
||||
|
||||
|
||||
(mf/defc plugin-entry
|
||||
[{:keys [index _icon url name description on-open-plugin on-remove-plugin]}]
|
||||
|
||||
(let [handle-open-click
|
||||
(mf/use-callback
|
||||
(mf/deps index url on-open-plugin)
|
||||
(fn []
|
||||
(when on-open-plugin
|
||||
(on-open-plugin index url))))
|
||||
|
||||
handle-delete-click
|
||||
(mf/use-callback
|
||||
(mf/deps index url on-remove-plugin)
|
||||
(fn []
|
||||
(when on-remove-plugin
|
||||
(on-remove-plugin index url))))]
|
||||
[:div {:class (stl/css :plugins-list-element)}
|
||||
[:div {:class (stl/css :plugin-icon)} ""]
|
||||
[:div {:class (stl/css :plugin-description)}
|
||||
[:div {:class (stl/css :plugin-title)} name]
|
||||
[:div {:class (stl/css :plugin-summary)} description]]
|
||||
[:button {:class (stl/css :open-button)
|
||||
:on-click handle-open-click} (tr "workspace.plugins.button-open")]
|
||||
[:button {:class (stl/css :trash-button)
|
||||
:on-click handle-delete-click} i/delete]]))
|
||||
|
||||
(defn load-from-store
|
||||
[]
|
||||
(let [ls (.-localStorage js/window)
|
||||
plugins-val (.getItem ls "plugins")]
|
||||
(when plugins-val
|
||||
(let [plugins-js (.parse js/JSON plugins-val)]
|
||||
(js->clj plugins-js {:keywordize-keys true})))))
|
||||
|
||||
(defn save-to-store
|
||||
[plugins]
|
||||
(let [ls (.-localStorage js/window)
|
||||
plugins-js (clj->js plugins)
|
||||
plugins-val (.stringify js/JSON plugins-js)]
|
||||
(.setItem ls "plugins" plugins-val)))
|
||||
|
||||
(defn open-plugin!
|
||||
[url]
|
||||
(.ɵloadPlugin js/window #js {:manifest url}))
|
||||
|
||||
(mf/defc plugin-management-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :plugin-management}
|
||||
[]
|
||||
|
||||
(let [plugins-state* (mf/use-state [])
|
||||
plugins-state @plugins-state*
|
||||
|
||||
plugin-url* (mf/use-state "")
|
||||
plugin-url @plugin-url*
|
||||
|
||||
input-status* (mf/use-state nil) ;; :error-url :error-manifest :success
|
||||
input-status @input-status*
|
||||
|
||||
error? (contains? #{:error-url :error-manifest} input-status)
|
||||
|
||||
handle-close-dialog
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(modal/hide!)))
|
||||
|
||||
handle-url-input
|
||||
(mf/use-callback
|
||||
(fn [value]
|
||||
(reset! input-status* nil)
|
||||
(reset! plugin-url* value)))
|
||||
|
||||
handle-install-click
|
||||
(mf/use-callback
|
||||
(mf/deps plugins-state plugin-url)
|
||||
(fn []
|
||||
(->> (http/send! {:method :get
|
||||
:uri plugin-url
|
||||
:response-type :json})
|
||||
(rx/map :body)
|
||||
(rx/subs!
|
||||
(fn [body]
|
||||
(let [name (obj/get body "name")
|
||||
new-state (conj plugins-state {:name name :url plugin-url})]
|
||||
(reset! input-status* :success)
|
||||
(reset! plugin-url* "")
|
||||
(reset! plugins-state* new-state)
|
||||
(save-to-store new-state)))
|
||||
(fn [_]
|
||||
(reset! input-status* :error-url))))))
|
||||
|
||||
handle-open-plugin
|
||||
(mf/use-callback
|
||||
(fn [_ url]
|
||||
(open-plugin! url)
|
||||
(modal/hide!)))
|
||||
|
||||
handle-remove-plugin
|
||||
(mf/use-callback
|
||||
(mf/deps plugins-state)
|
||||
(fn [rm-idx _]
|
||||
(let [new-state
|
||||
(into []
|
||||
(keep-indexed (fn [idx item]
|
||||
(when (not= idx rm-idx) item)))
|
||||
plugins-state)]
|
||||
|
||||
(reset! plugins-state* new-state)
|
||||
(save-to-store new-state))))]
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(reset! plugins-state* (d/nilv (load-from-store) []))))
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
|
||||
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.title")]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div {:class (stl/css :top-bar)}
|
||||
[:& search-bar {:on-change handle-url-input
|
||||
:value plugin-url
|
||||
:placeholder (tr "workspace.plugins.search-placeholder")
|
||||
:class (stl/css-case :input-error error?)}]
|
||||
|
||||
[:button {:class (stl/css :primary-button)
|
||||
:disabled (empty? plugin-url)
|
||||
:on-click handle-install-click} (tr "workspace.plugins.install")]]
|
||||
|
||||
(when error?
|
||||
[:div {:class (stl/css-case :info true :error error?)}
|
||||
(tr "workspace.plugins.error.url")])
|
||||
|
||||
[:hr]
|
||||
|
||||
[:& title-bar {:collapsable false
|
||||
:title (tr "workspace.plugins.installed-plugins")}]
|
||||
|
||||
(if (empty? plugins-state)
|
||||
[:div {:class (stl/css :plugins-empty)}
|
||||
[:div {:class (stl/css :plugins-empty-logo)} i/logo-icon]
|
||||
[:div {:class (stl/css :plugins-empty-text)} (tr "workspace.plugins.empty-plugins")]]
|
||||
|
||||
[:div {:class (stl/css :plugins-list)}
|
||||
|
||||
(for [[idx {:keys [name url]}] (d/enumerate plugins-state)]
|
||||
[:& plugin-entry {:key (dm/str "plugin-" idx)
|
||||
:name name
|
||||
:url url
|
||||
:index idx
|
||||
:icon nil
|
||||
:description "Nullam ullamcorper ligula ac felis commodo pulvinar."
|
||||
:on-open-plugin handle-open-plugin
|
||||
:on-remove-plugin handle-remove-plugin}])])]]]))
|
167
frontend/src/app/main/ui/workspace/plugins.scss
Normal file
167
frontend/src/app/main/ui/workspace/plugins.scss
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.modal-overlay {
|
||||
@extend .modal-overlay-base;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
@extend .modal-container-base;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: $s-472;
|
||||
max-height: $s-472;
|
||||
width: $s-472;
|
||||
max-width: $s-472;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@extend .modal-close-btn-base;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include headlineMediumTypography;
|
||||
margin-block-end: $s-16;
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: $s-380;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
@extend .button-primary;
|
||||
@include headlineSmallTypography;
|
||||
padding: $s-0 $s-16;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
@include flexCenter;
|
||||
width: $s-20;
|
||||
padding: 0 0 0 $s-8;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
display: flex;
|
||||
gap: $s-8;
|
||||
}
|
||||
|
||||
.open-button {
|
||||
@extend .button-secondary;
|
||||
width: $s-68;
|
||||
min-width: $s-68;
|
||||
height: $s-32;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.trash-button {
|
||||
@extend .button-tertiary;
|
||||
width: $s-32;
|
||||
height: $s-32;
|
||||
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.plugins-list {
|
||||
padding-top: $s-20;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.plugins-list-element {
|
||||
display: flex;
|
||||
gap: $s-12;
|
||||
}
|
||||
|
||||
.plugin-icon {
|
||||
min-width: $s-32;
|
||||
min-height: $s-32;
|
||||
width: $s-32;
|
||||
height: $s-32;
|
||||
background: #b1b2b5;
|
||||
}
|
||||
|
||||
.plugin-description {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-8;
|
||||
}
|
||||
|
||||
.plugin-title {
|
||||
@include bodyMediumTypography;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.plugin-summary {
|
||||
@include bodySmallTypography;
|
||||
color: #8f9da3;
|
||||
}
|
||||
|
||||
.plugins-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.plugins-empty-logo {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #212426;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: #8f9da3;
|
||||
}
|
||||
}
|
||||
|
||||
.plugins-empty-text {
|
||||
@include bodySmallTypography;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.input-error {
|
||||
border: 1px solid var(--input-border-color-error);
|
||||
}
|
||||
|
||||
.info {
|
||||
@include bodySmallTypography;
|
||||
margin-top: $s-4;
|
||||
|
||||
&.error {
|
||||
color: var(--input-border-color-error);
|
||||
}
|
||||
|
||||
&.success {
|
||||
color: var(--input-border-color-success);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.files.changes-builder :as cb]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.record :as cr]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as ch]
|
||||
|
@ -19,6 +20,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.plugins.events :as events]
|
||||
[app.plugins.file :as file]
|
||||
[app.plugins.library :as library]
|
||||
[app.plugins.page :as page]
|
||||
[app.plugins.shape :as shape]
|
||||
[app.plugins.utils :as utils]
|
||||
|
@ -129,13 +131,29 @@
|
|||
[_]
|
||||
(create-shape :rect))
|
||||
|
||||
(createText
|
||||
[_ text]
|
||||
(let [page-id (:current-page-id @st/state)
|
||||
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
|
||||
shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
|
||||
(txt/change-text text)
|
||||
(assoc :position-data nil))
|
||||
changes
|
||||
(-> (cb/empty-changes)
|
||||
(cb/with-page page)
|
||||
(cb/with-objects (:objects page))
|
||||
(cb/add-object shape))]
|
||||
(st/emit! (ch/commit-changes changes))
|
||||
(shape/data->shape-proxy shape)))
|
||||
|
||||
(createShapeFromSvg
|
||||
[_ svg-string]
|
||||
(let [id (uuid/next)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
||||
(shape/data->shape-proxy
|
||||
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id])))))
|
||||
(when (some? svg-string)
|
||||
(let [id (uuid/next)
|
||||
page-id (:current-page-id @st/state)]
|
||||
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
|
||||
(shape/data->shape-proxy
|
||||
(dm/get-in @st/state [:workspace-data :pages-index page-id :objects id]))))))
|
||||
|
||||
(defn create-context
|
||||
[]
|
||||
|
@ -144,4 +162,5 @@
|
|||
{:name "root" :get #(.getRoot ^js %)}
|
||||
{:name "currentPage" :get #(.getPage ^js %)}
|
||||
{:name "selection" :get #(.getSelectedShapes ^js %)}
|
||||
{:name "viewport" :get #(.getViewport ^js %)}))
|
||||
{:name "viewport" :get #(.getViewport ^js %)}
|
||||
{:name "library" :get (fn [_] (library/create-library-subcontext))}))
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value}))))
|
||||
|
||||
(addRowAtIndex
|
||||
[self type value index]
|
||||
[self index type value]
|
||||
(let [id (get-data self :id)
|
||||
type (keyword type)]
|
||||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index))))
|
||||
|
@ -46,7 +46,7 @@
|
|||
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value}))))
|
||||
|
||||
(addColumnAtIndex
|
||||
[self type value index]
|
||||
[self index type value]
|
||||
(let [id (get-data self :id)
|
||||
type (keyword type)]
|
||||
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index))))
|
||||
|
@ -170,4 +170,37 @@
|
|||
(fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))})))
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
|
||||
|
||||
|
||||
{:name "topPadding"
|
||||
:get #(:p1 (get-state % :layout-padding))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
|
||||
|
||||
{:name "rightPadding"
|
||||
:get #(:p2 (get-state % :layout-padding))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
|
||||
|
||||
{:name "bottomPadding"
|
||||
:get #(:p3 (get-state % :layout-padding))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
|
||||
|
||||
{:name "leftPadding"
|
||||
:get #(:p4 (get-state % :layout-padding))
|
||||
:set
|
||||
(fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
|
||||
|
|
96
frontend/src/app/plugins/library.cljs
Normal file
96
frontend/src/app/plugins/library.cljs
Normal file
|
@ -0,0 +1,96 @@
|
|||
;; 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
|
||||
|
||||
(ns app.plugins.library
|
||||
"RPC for plugins runtime."
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.record :as cr]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.utils :as utils :refer [get-data]]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
(defn get-library-info
|
||||
([self attr]
|
||||
(let [lib-id (get-data self :id)
|
||||
current-file-id (:current-file-id @st/state)]
|
||||
(if (= lib-id current-file-id)
|
||||
(dm/get-in @st/state [:workspace-file attr])
|
||||
(dm/get-in @st/state [:workspace-libraries lib-id attr]))))
|
||||
|
||||
([self attr mapfn]
|
||||
(-> (get-library-info self attr)
|
||||
(mapfn))))
|
||||
|
||||
(defn get-library-data
|
||||
([self attr]
|
||||
(let [lib-id (get-data self :id)
|
||||
current-file-id (:current-file-id @st/state)]
|
||||
(if (= lib-id current-file-id)
|
||||
(dm/get-in @st/state [:workspace-data attr])
|
||||
(dm/get-in @st/state [:workspace-libraries lib-id :data attr]))))
|
||||
|
||||
([self attr mapfn]
|
||||
(-> (get-library-data self attr)
|
||||
(mapfn))))
|
||||
|
||||
(defn- array-to-js
|
||||
[value]
|
||||
(.freeze
|
||||
js/Object
|
||||
(apply array (->> value (map utils/to-js)))))
|
||||
|
||||
(deftype Library [_data]
|
||||
Object)
|
||||
|
||||
(defn create-library
|
||||
[data]
|
||||
(cr/add-properties!
|
||||
(Library. data)
|
||||
{:name "_data"
|
||||
:enumerable false}
|
||||
|
||||
{:name "id"
|
||||
:get (fn [self]
|
||||
(str (:id (obj/get self "_data"))))}
|
||||
|
||||
{:name "name"
|
||||
:get (fn [self]
|
||||
(get-library-info self :name))}
|
||||
|
||||
{:name "colors"
|
||||
:get (fn [self]
|
||||
(array-to-js (get-library-data self :colors vals)))}
|
||||
|
||||
{:name "typographies"
|
||||
:get (fn [self]
|
||||
(array-to-js (get-library-data self :typographies vals)))}
|
||||
|
||||
{:name "components"
|
||||
:get (fn [self]
|
||||
(array-to-js (get-library-data self :components vals)))}))
|
||||
|
||||
(deftype PenpotLibrarySubcontext []
|
||||
Object
|
||||
(find
|
||||
[_ _name])
|
||||
|
||||
(find [_]))
|
||||
|
||||
(defn create-library-subcontext
|
||||
[]
|
||||
(cr/add-properties!
|
||||
(PenpotLibrarySubcontext.)
|
||||
{:name "local" :get
|
||||
(fn [_]
|
||||
(let [file (get @st/state :workspace-file)
|
||||
data (get @st/state :workspace-data)]
|
||||
(create-library (assoc file :data data))))}
|
||||
|
||||
{:name "connected" :get
|
||||
(fn [_]
|
||||
(let [libraries (get @st/state :workspace-libraries)]
|
||||
(apply array (->> libraries vals (map create-library)))))}))
|
|
@ -7,10 +7,13 @@
|
|||
(ns app.plugins.shape
|
||||
"RPC for plugins runtime."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.record :as crc]
|
||||
[app.common.spec :as us]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.changes :as dwc]
|
||||
|
@ -24,17 +27,11 @@
|
|||
|
||||
(declare data->shape-proxy)
|
||||
|
||||
(defn- make-fills
|
||||
[fills]
|
||||
(defn- array-to-js
|
||||
[value]
|
||||
(.freeze
|
||||
js/Object
|
||||
(apply array (->> fills (map utils/to-js)))))
|
||||
|
||||
(defn- make-strokes
|
||||
[strokes]
|
||||
(.freeze
|
||||
js/Object
|
||||
(apply array (->> strokes (map utils/to-js)))))
|
||||
(apply array (->> value (map utils/to-js)))))
|
||||
|
||||
(defn- locate-shape
|
||||
[shape-id]
|
||||
|
@ -86,7 +83,8 @@
|
|||
|
||||
(addGridLayout [self]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false)))))
|
||||
(st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false))
|
||||
(grid/grid-layout-proxy (obj/get self "_data")))))
|
||||
|
||||
(crc/define-properties!
|
||||
ShapeProxy
|
||||
|
@ -107,6 +105,118 @@
|
|||
{:name "type"
|
||||
:get (get-data-fn :type name)}
|
||||
|
||||
{:name "name"
|
||||
:get #(get-state % :name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))}
|
||||
|
||||
{:name "blocked"
|
||||
:get #(get-state % :blocked boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blocked value)))))}
|
||||
|
||||
{:name "hidden"
|
||||
:get #(get-state % :hidden boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :hidden value)))))}
|
||||
|
||||
{:name "proportionLock"
|
||||
:get #(get-state % :proportion-lock boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :proportion-lock value)))))}
|
||||
|
||||
{:name "constraintsHorizontal"
|
||||
:get #(get-state % :constraints-h d/name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (keyword value)]
|
||||
(when (contains? cts/horizontal-constraint-types value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-h value))))))}
|
||||
|
||||
{:name "constraintsVertical"
|
||||
:get #(get-state % :constraints-v d/name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (keyword value)]
|
||||
(when (contains? cts/vertical-constraint-types value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-v value))))))}
|
||||
|
||||
{:name "borderRadius"
|
||||
:get #(get-state % :rx)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :rx value :ry value))))))}
|
||||
|
||||
{:name "borderRadiusTopLeft"
|
||||
:get #(get-state % :r1)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :r1 value))))))}
|
||||
|
||||
{:name "borderRadiusTopRight"
|
||||
:get #(get-state % :r2)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :r2 value))))))}
|
||||
|
||||
{:name "borderRadiusBottomRight"
|
||||
:get #(get-state % :r3)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :r3 value))))))}
|
||||
|
||||
{:name "borderRadiusBottomLeft"
|
||||
:get #(get-state % :r4)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (us/safe-int? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :r4 value))))))}
|
||||
|
||||
{:name "opacity"
|
||||
:get #(get-state % :opacity)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(when (and (us/safe-number? value) (>= value 0) (<= value 1))
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :opacity value))))))}
|
||||
|
||||
{:name "blendMode"
|
||||
:get #(get-state % :blend-mode d/name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (keyword value)]
|
||||
(when (contains? cts/blend-modes value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||
|
||||
{:name "shadows"
|
||||
:get #(get-state % :shadow array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :shadows value)))))}
|
||||
|
||||
{:name "blur"
|
||||
:get #(get-state % :blur utils/to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (utils/from-js value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blur value)))))}
|
||||
|
||||
{:name "exports"
|
||||
:get #(get-state % :exports array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :exports value)))))}
|
||||
|
||||
;; Geometry properties
|
||||
{:name "x"
|
||||
:get #(get-state % :x)
|
||||
:set
|
||||
|
@ -183,21 +293,22 @@
|
|||
{:name "height"
|
||||
:get #(get-state % :height)}
|
||||
|
||||
{:name "name"
|
||||
:get #(get-state % :name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))}
|
||||
{:name "flipX"
|
||||
:get #(get-state % :flip-x)}
|
||||
|
||||
{:name "flipY"
|
||||
:get #(get-state % :flip-y)}
|
||||
|
||||
;; Strokes and fills
|
||||
{:name "fills"
|
||||
:get #(get-state % :fills make-fills)
|
||||
:get #(get-state % :fills array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
|
||||
|
||||
{:name "strokes"
|
||||
:get #(get-state % :strokes make-strokes)
|
||||
:get #(get-state % :strokes array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
|
@ -220,8 +331,16 @@
|
|||
(fn [self]
|
||||
(let [layout (get-state self :layout)]
|
||||
(when (= :grid layout)
|
||||
(grid/grid-layout-proxy data))))})
|
||||
(grid/grid-layout-proxy data))))}
|
||||
|
||||
{:name "guides"
|
||||
:get #(get-state % :grids array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :grids value)))))})
|
||||
|
||||
;; TODO: Flex properties
|
||||
#_(crc/add-properties!
|
||||
{:name "flex"
|
||||
:get
|
||||
|
@ -235,9 +354,18 @@
|
|||
(obj/unset! "addFlexLayout")))
|
||||
|
||||
(cond-> (cfh/text-shape? data)
|
||||
(crc/add-properties!
|
||||
{:name "characters"
|
||||
:get #(get-state % :content txt/content->text)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))}))))
|
||||
(-> (crc/add-properties!
|
||||
{:name "characters"
|
||||
:get #(get-state % :content txt/content->text)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)]
|
||||
(st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))})
|
||||
|
||||
(crc/add-properties!
|
||||
{:name "growType"
|
||||
:get #(get-state % :grow-type d/name)
|
||||
:set (fn [self value]
|
||||
(let [id (get-data self :id)
|
||||
value (keyword value)]
|
||||
(when (contains? #{:auto-width :auto-height :fixed} value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :grow-type value))))))})))))
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
fills' (:fills shape1')
|
||||
fill' (first fills')]
|
||||
|
||||
(cthf/dump-shape shape1')
|
||||
|
||||
;; ==== Check
|
||||
(t/is (some? shape1'))
|
||||
(t/is (= (count fills') 1))
|
||||
|
|
|
@ -6,13 +6,19 @@
|
|||
|
||||
(ns frontend-tests.helpers.pages
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.changes :as cp]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.files.shapes-helpers :as cfsh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.layout :as layout]
|
||||
|
@ -186,3 +192,82 @@
|
|||
:components (:components data)}})
|
||||
(update :workspace-data
|
||||
assoc :components {} :pages [] :pages-index {}))))
|
||||
|
||||
|
||||
(defn simulate-copy-shape
|
||||
[selected objects libraries page file features version]
|
||||
(letfn [(sort-selected [data]
|
||||
(let [;; Narrow the objects map so it contains only relevant data for
|
||||
;; selected and its parents
|
||||
objects (cfh/selected-subtree objects selected)
|
||||
selected (->> (ctst/sort-z-index objects selected)
|
||||
(reverse)
|
||||
(into (d/ordered-set)))]
|
||||
|
||||
(assoc data :selected selected)))
|
||||
|
||||
;; Prepare the shape object.
|
||||
(prepare-object [objects parent-frame-id obj]
|
||||
(maybe-translate obj objects parent-frame-id))
|
||||
|
||||
;; Collects all the items together and split images into a
|
||||
;; separated data structure for a more easy paste process.
|
||||
(collect-data [result {:keys [id ::images] :as item}]
|
||||
(cond-> result
|
||||
:always
|
||||
(update :objects assoc id (dissoc item ::images))
|
||||
|
||||
(some? images)
|
||||
(update :images into images)))
|
||||
|
||||
(maybe-translate [shape objects parent-frame-id]
|
||||
(if (= parent-frame-id uuid/zero)
|
||||
shape
|
||||
(let [frame (get objects parent-frame-id)]
|
||||
(gsh/translate-to-frame shape frame))))
|
||||
|
||||
;; When copying an instance that is nested inside another one, we need to
|
||||
;; advance the shape refs to one or more levels of remote mains.
|
||||
(advance-copies [data]
|
||||
(let [heads (mapcat #(ctn/get-child-heads (:objects data) %) selected)]
|
||||
(update data :objects
|
||||
#(reduce (partial advance-copy file libraries page)
|
||||
%
|
||||
heads))))
|
||||
|
||||
(advance-copy [file libraries page objects shape]
|
||||
(if (and (ctk/instance-head? shape) (not (ctk/main-instance? shape)))
|
||||
(let [level-delta (ctn/get-nesting-level-delta (:objects page) shape uuid/zero)]
|
||||
(if (pos? level-delta)
|
||||
(reduce (partial advance-shape file libraries page level-delta)
|
||||
objects
|
||||
(cfh/get-children-with-self objects (:id shape)))
|
||||
objects))
|
||||
objects))
|
||||
|
||||
(advance-shape [file libraries page level-delta objects shape]
|
||||
(let [new-shape-ref (ctf/advance-shape-ref file page libraries shape level-delta {:include-deleted? true})]
|
||||
(cond-> objects
|
||||
(and (some? new-shape-ref) (not= new-shape-ref (:shape-ref shape)))
|
||||
(assoc-in [(:id shape) :shape-ref] new-shape-ref))))]
|
||||
|
||||
(let [file-id (:id file)
|
||||
frame-id (cfh/common-parent-frame objects selected)
|
||||
|
||||
initial {:type :copied-shapes
|
||||
:features features
|
||||
:version version
|
||||
:file-id file-id
|
||||
:selected selected
|
||||
:objects {}
|
||||
:images #{}
|
||||
:in-viewport false}
|
||||
|
||||
shapes (->> (cfh/selected-with-children objects selected)
|
||||
(keep (d/getf objects)))]
|
||||
|
||||
(->> shapes
|
||||
(map (partial prepare-object objects frame-id))
|
||||
(reduce collect-data initial)
|
||||
sort-selected
|
||||
advance-copies))))
|
||||
|
|
|
@ -54,3 +54,8 @@
|
|||
(doall (for [event events]
|
||||
(ptk/emit! store event)))
|
||||
(ptk/emit! store :the/end)))
|
||||
|
||||
(defn get-file-from-store
|
||||
[store]
|
||||
(-> (:workspace-file store)
|
||||
(assoc :data (:workspace-data store))))
|
||||
|
|
|
@ -0,0 +1,815 @@
|
|||
;; 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
|
||||
(ns frontend-tests.logic.comp-remove-swap-slots-test
|
||||
(:require
|
||||
[app.common.test-helpers.components :as cthc]
|
||||
[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.component :as ctk]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[cuerdas.core :as str]
|
||||
[frontend-tests.helpers.pages :as thp]
|
||||
[frontend-tests.helpers.state :as ths]))
|
||||
|
||||
;; Related .penpot file: common/test/cases/remove-swap-slots.penpot
|
||||
(defn- setup-file
|
||||
[]
|
||||
;; {:frame-red} [:name Frame1] # [Component :red]
|
||||
;; {:frame-blue} [:name Frame1] # [Component :blue]
|
||||
;; {:frame-green} [:name Frame1] # [Component :green]
|
||||
;; :red-copy-green [:name Frame1] @--> :frame-red
|
||||
;; {:frame-b1} [:name Frame1] # [Component :b1]
|
||||
;; :blue1 [:name Frame1, :swap-slot-label :red-copy] @--> :frame-blue
|
||||
;; :frame-yellow [:name Frame1]
|
||||
;; :green-copy [:name Frame1] @--> :frame-green
|
||||
;; :blue-copy-in-green-copy [:name Frame1, :swap-slot-label :red-copy-green] @--> :frame-blue
|
||||
;; {:frame-b2} [:name Frame1] # [Component :b2]
|
||||
(-> (cthf/sample-file :file1)
|
||||
(ctho/add-frame :frame-red)
|
||||
(cthc/make-component :red :frame-red)
|
||||
(ctho/add-frame :frame-blue :name "frame-blue")
|
||||
(cthc/make-component :blue :frame-blue)
|
||||
(ctho/add-frame :frame-green :name "frame-green")
|
||||
(cthc/make-component :green :frame-green)
|
||||
(cthc/instantiate-component :red :red-copy-green :parent-label :frame-green)
|
||||
(ctho/add-frame :frame-b1)
|
||||
(cthc/make-component :b1 :frame-b1)
|
||||
(ctho/add-frame :frame-yellow :parent-label :frame-b1 :name "frame-yellow")
|
||||
(cthc/instantiate-component :red :red-copy :parent-label :frame-b1)
|
||||
(cthc/component-swap :red-copy :blue :blue1)
|
||||
(cthc/instantiate-component :green :green-copy :parent-label :frame-b1 :children-labels [:red-copy-in-green-copy])
|
||||
(cthc/component-swap :red-copy-in-green-copy :blue :blue-copy-in-green-copy)
|
||||
(ctho/add-frame :frame-b2)
|
||||
(cthc/make-component :b2 :frame-b2)))
|
||||
|
||||
|
||||
(defn- setup-file-blue1-in-yellow
|
||||
[]
|
||||
;; {:frame-red} [:name Frame1] # [Component :red]
|
||||
;; {:frame-blue} [:name Frame1] # [Component :blue]
|
||||
;; {:frame-green} [:name Frame1] # [Component :green]
|
||||
;; :red-copy-green [:name Frame1] @--> :frame-red
|
||||
;; {:frame-b1} [:name Frame1] # [Component :b1]
|
||||
;; :frame-yellow [:name Frame1]
|
||||
;; :blue1 [:name Frame1, :swap-slot-label :red-copy] @--> :frame-blue
|
||||
;; :green-copy [:name Frame1] @--> :frame-green
|
||||
;; :blue-copy-in-green-copy [:name Frame1, :swap-slot-label :red-copy-green] @--> :frame-blue
|
||||
;; {:frame-b2} [:name Frame1] # [Component :b2]
|
||||
(-> (cthf/sample-file :file1)
|
||||
(ctho/add-frame :frame-red)
|
||||
(cthc/make-component :red :frame-red)
|
||||
(ctho/add-frame :frame-blue :name "frame-blue")
|
||||
(cthc/make-component :blue :frame-blue)
|
||||
(ctho/add-frame :frame-green)
|
||||
(cthc/make-component :green :frame-green)
|
||||
(cthc/instantiate-component :red :red-copy-green :parent-label :frame-green)
|
||||
(ctho/add-frame :frame-b1)
|
||||
(cthc/make-component :b1 :frame-b1)
|
||||
(ctho/add-frame :frame-yellow :parent-label :frame-b1 :name "frame-yellow")
|
||||
(cthc/instantiate-component :red :red-copy :parent-label :frame-yellow)
|
||||
(cthc/component-swap :red-copy :blue :blue1)
|
||||
(cthc/instantiate-component :green :green-copy :parent-label :frame-b1 :children-labels [:red-copy-in-green-copy])
|
||||
(cthc/component-swap :red-copy-in-green-copy :blue :blue-copy-in-green-copy)
|
||||
(ctho/add-frame :frame-b2)
|
||||
(cthc/make-component :b2 :frame-b2)))
|
||||
|
||||
(defn- find-copied-shape
|
||||
[original-shape page parent-id]
|
||||
;; copied shape has the same name, is in the specified parent, and doesn't have a label
|
||||
(->> (vals (:objects page))
|
||||
(filter #(and (= (:name %) (:name original-shape))
|
||||
(= (:parent-id %) parent-id)
|
||||
(str/starts-with? (cthi/label (:id %)) "<no-label")))
|
||||
first))
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-blue1-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' uuid/zero)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-blue1-to-b1
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b1 (cths/get-shape file :frame-b1)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b1))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b1' (cths/get-shape file' :frame-b1)
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id b1'))]
|
||||
|
||||
;; ==== Check
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-blue1-to-yellow
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id yellow))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
yellow' (cths/get-shape file' :frame-yellow)
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-blue1-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id b2'))]
|
||||
|
||||
;; ==== Check
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-blue1-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id blue1))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
copied-blue1' (find-copied-shape blue1 page' uuid/zero)]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-blue1-to-yellow
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id blue1))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id yellow))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
yellow' (cths/get-shape file' :frame-yellow)
|
||||
copied-blue1' (find-copied-shape blue1 page' (:id yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-blue1-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id blue1)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id blue1))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
copied-blue1' (find-copied-shape blue1 page' (:id b2'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-yellow-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
copied-yellow' (find-copied-shape yellow page' uuid/zero)
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-yellow-to-b1
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
b1 (cths/get-shape file :frame-b1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b1))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b1' (cths/get-shape file' :frame-b1)
|
||||
copied-yellow' (find-copied-shape yellow page' (:id b1'))
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-copy-paste-yellow-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
copied-yellow' (find-copied-shape yellow page' (:id b2'))
|
||||
blue1' (cths/get-shape file' :blue1)
|
||||
copied-blue1' (find-copied-shape blue1' page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue1')))
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-yellow-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id yellow))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
copied-yellow' (find-copied-shape yellow page' uuid/zero)
|
||||
copied-blue1' (find-copied-shape blue1 page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-yellow-to-b1
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
b1 (cths/get-shape file :frame-b1)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id yellow))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id b1))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b1' (cths/get-shape file' :frame-b1)
|
||||
copied-yellow' (find-copied-shape yellow page' (:id b1'))
|
||||
copied-blue1' (find-copied-shape blue1 page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-remove-swap-slot-cut-paste-yellow-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file-blue1-in-yellow)
|
||||
store (ths/setup-store file)
|
||||
blue1 (cths/get-shape file :blue1)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
yellow (cths/get-shape file :frame-yellow)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id yellow)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id yellow))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
copied-yellow' (find-copied-shape yellow page' (:id b2'))
|
||||
copied-blue1' (find-copied-shape blue1 page' (:id copied-yellow'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has not swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (nil? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-copy-paste-green-copy-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
green (cths/get-shape file :green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
blue2' (cths/get-shape file' :blue-copy-in-green-copy)
|
||||
copied-green' (find-copied-shape green page' uuid/zero)
|
||||
copied-blue2' (find-copied-shape blue2' page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))
|
||||
|
||||
;; copied-blue2 also has swap-id
|
||||
(t/is (some? copied-blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue2')))))))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-copy-paste-green-copy-to-b1
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b1 (cths/get-shape file :frame-b1)
|
||||
green (cths/get-shape file :green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b1))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b1' (cths/get-shape file' :frame-b1)
|
||||
blue2' (cths/get-shape file' :blue-copy-in-green-copy)
|
||||
copied-green' (find-copied-shape green page' (:id b1'))
|
||||
copied-blue2' (find-copied-shape blue2' page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue1 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))
|
||||
|
||||
;; copied-blue1 also has swap-id
|
||||
(t/is (some? copied-blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue2')))))))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-copy-paste-green-copy-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
green (cths/get-shape file :green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
blue2' (cths/get-shape file' :blue-copy-in-green-copy)
|
||||
copied-green' (find-copied-shape green page' (:id b2'))
|
||||
copied-blue2' (find-copied-shape blue2' page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; blue2 has swap-id
|
||||
(t/is (some? (ctk/get-swap-slot blue2')))
|
||||
|
||||
;; copied-blue1 also has swap-id
|
||||
(t/is (some? copied-blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue2')))))))))
|
||||
|
||||
|
||||
(t/deftest test-keep-swap-slot-cut-paste-green-copy-to-root
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
green (cths/get-shape file :green-copy)
|
||||
blue2 (cths/get-shape file :blue-copy-in-green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id green))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape uuid/zero)
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
copied-green' (find-copied-shape green page' uuid/zero)
|
||||
copied-blue1' (find-copied-shape blue2 page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has swap-id
|
||||
(t/is (some? copied-blue1'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue1')))))))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-cut-paste-green-copy-to-b1
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b1 (cths/get-shape file :frame-b1)
|
||||
green (cths/get-shape file :green-copy)
|
||||
blue2 (cths/get-shape file :blue-copy-in-green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id green))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id b1))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b1' (cths/get-shape file' :frame-b1)
|
||||
copied-green' (find-copied-shape green page' (:id b1'))
|
||||
copied-blue2' (find-copied-shape blue2 page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has swap-id
|
||||
(t/is (some? copied-blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue2')))))))))
|
||||
|
||||
(t/deftest test-keep-swap-slot-cut-paste-green-copy-to-b2
|
||||
(t/async
|
||||
done
|
||||
(let [;; ==== Setup
|
||||
file (setup-file)
|
||||
store (ths/setup-store file)
|
||||
|
||||
;; ==== Action
|
||||
page (cthf/current-page file)
|
||||
b2 (cths/get-shape file :frame-b2)
|
||||
blue2 (cths/get-shape file :blue-copy-in-green-copy)
|
||||
green (cths/get-shape file :green-copy)
|
||||
features #{"components/v2"}
|
||||
version 46
|
||||
|
||||
pdata (thp/simulate-copy-shape #{(:id green)} (:objects page) {(:id file) file} page file features version)
|
||||
|
||||
events
|
||||
[(dws/select-shape (:id green))
|
||||
(dw/delete-selected)
|
||||
(dws/select-shape (:id b2))
|
||||
(dw/paste-shapes pdata)]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
(fn [new-state]
|
||||
(let [;; ==== Get
|
||||
file' (ths/get-file-from-store new-state)
|
||||
page' (cthf/current-page file')
|
||||
b2' (cths/get-shape file' :frame-b2)
|
||||
copied-green' (find-copied-shape green page' (:id b2'))
|
||||
copied-blue2' (find-copied-shape blue2 page' (:id copied-green'))]
|
||||
|
||||
;; ==== Check
|
||||
;; copied-blue1 has swap-id
|
||||
(t/is (some? copied-blue2'))
|
||||
(t/is (some? (ctk/get-swap-slot copied-blue2')))))))))
|
|
@ -5175,3 +5175,33 @@ msgstr "Update"
|
|||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Click to close the path"
|
||||
|
||||
msgid "workspace.plugins.title"
|
||||
msgstr "Plugins"
|
||||
|
||||
msgid "workspace.plugins.search-placeholder"
|
||||
msgstr "Write a plugin URL"
|
||||
|
||||
msgid "workspace.plugins.install"
|
||||
msgstr "Install"
|
||||
|
||||
msgid "workspace.plugins.installed-plugins"
|
||||
msgstr "Installed plugins"
|
||||
|
||||
msgid "workspace.plugins.empty-plugins"
|
||||
msgstr "No plugins installed yet"
|
||||
|
||||
msgid "workspace.plugins.button-open"
|
||||
msgstr "Open"
|
||||
|
||||
msgid "workspace.plugins.error.url"
|
||||
msgstr "The plugin doesn't exist or the URL is not correct."
|
||||
|
||||
msgid "workspace.plugins.success"
|
||||
msgstr "Plugin correctly loaded."
|
||||
|
||||
msgid "workspace.plugins.menu.title"
|
||||
msgstr "Plugins"
|
||||
|
||||
msgid "workspace.plugins.menu.plugins-manager"
|
||||
msgstr "Plugins manager"
|
||||
|
|
|
@ -5301,3 +5301,34 @@ msgstr "Actualizar"
|
|||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Pulsar para cerrar la ruta"
|
||||
|
||||
msgid "workspace.plugins.title"
|
||||
msgstr "Extensiones"
|
||||
|
||||
msgid "workspace.plugins.search-placeholder"
|
||||
msgstr "Intruduzca URL de la extensión"
|
||||
|
||||
msgid "workspace.plugins.install"
|
||||
msgstr "Instalar"
|
||||
|
||||
msgid "workspace.plugins.installed-plugins"
|
||||
msgstr "Extensiones instaladas"
|
||||
|
||||
msgid "workspace.plugins.empty-plugins"
|
||||
msgstr "No se encuentran extensiones"
|
||||
|
||||
msgid "workspace.plugins.button-open"
|
||||
msgstr "Abrir"
|
||||
|
||||
msgid "workspace.plugins.error.url"
|
||||
msgstr "La extensión no existe o la url no es correcta."
|
||||
|
||||
msgid "workspace.plugins.success"
|
||||
msgstr "Extensión cargada correctamente."
|
||||
|
||||
msgid "workspace.plugins.menu.title"
|
||||
msgstr "Extensiones"
|
||||
|
||||
msgid "workspace.plugins.menu.plugins-manager"
|
||||
msgstr "Gestor de extensiones"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue