mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 14:30:37 +00:00
🐛 Keep grid cell overrides within newly inherited tracks
Create default cells in newly inherited grid tracks before assigning added copy children. A cell override previously made sync append an extra track; a later full sync shrank the tracks beneath the retained cells. Keep existing authored cells and skip positions covered by existing spans. Add common and real frontend row/column growth histories, including an opposite copy direction, retained overrides, exact undo/redo and stable full sync. The original common test failed 12 of 100 assertions and the frontend test failed 4 of 100. Keep those assertions and add checks that each child has exactly one cell. Sync benchmark lower quantiles (ms), baseline -> now (ratio): deepest three levels: 35.215 -> 18.770 (0.53) chain of dependents: 68.931 -> 37.764 (0.55) library update: 83.083 -> 49.418 (0.59) main among many shapes: 4.837 -> 3.008 (0.62) main into 10 copies: 1.790 -> 0.968 (0.54) main into 100 copies: 15.415 -> 9.474 (0.61) main into 1000 copies: 153.086 -> 97.063 (0.63) variant switch: 32.052 -> 22.157 (0.69) All worlds pass the recorded 10% regression limit. The baseline used another machine/JVM, so ratios are rough comparisons. Validation: common JVM 2,016 tests / 33,356 assertions; common JS 1,952 / 32,015; frontend 1,752 / 14,088; backend 1,793 / 9,007; exporter 16 / 40. Six properties pass 40 programs at seed 712 in both common runtimes. Lint and format checks pass for all four modules. Golden-master data is unchanged. AI-assisted-by: gpt-6
This commit is contained in:
parent
fcac23c52f
commit
40fc00bcf7
@ -490,7 +490,7 @@
|
||||
`:root-main`, and `:reset?`. The main shapes of the children of a copy
|
||||
shape that keeps its own geometry are placed like it first, so they get
|
||||
their expected geometry."
|
||||
[changes {:keys [container main-container library component reset?] :as ctx} shape-inst shape-main]
|
||||
[changes {:keys [container main-container component reset?] :as ctx} shape-inst shape-main]
|
||||
(shape-log :debug (:id shape-inst) container
|
||||
:msg "Sync shape direct recursive"
|
||||
:shape-inst (str (:name shape-inst) " " (pretty-uuid (:id shape-inst)))
|
||||
@ -500,6 +500,7 @@
|
||||
changes
|
||||
(let [;; a copy shape that keeps its own geometry passes it on to its
|
||||
;; children, which follow it as they follow their main's
|
||||
shape-before shape-inst
|
||||
placed (when (and (not reset?) (ctk/touched-group? shape-inst :geometry-group))
|
||||
(in-main-frame shape-inst (:root-inst ctx) (:root-main ctx)))
|
||||
main-container (cond-> main-container
|
||||
@ -533,7 +534,7 @@
|
||||
(reorder-copy-children shape-inst children-main container))
|
||||
|
||||
(ctsl/grid-layout? shape-inst)
|
||||
(update-grid-copy-attrs (:id shape-inst) shape-main library component (not reset?))))))
|
||||
(update-grid-copy-attrs shape-before shape-main (not reset?))))))
|
||||
|
||||
(defn generate-rename-component
|
||||
"Generate the changes for rename the component with the given id, in the current file library."
|
||||
@ -1542,13 +1543,30 @@
|
||||
%)
|
||||
{:ignore-touched true}))))
|
||||
|
||||
(defn- add-inherited-grid-cells
|
||||
"Add default cells in newly inherited tracks without replacing authored cells."
|
||||
[shape before]
|
||||
(let [old-rows (count (:layout-grid-rows before))
|
||||
old-cols (count (:layout-grid-columns before))
|
||||
rows (count (:layout-grid-rows shape))
|
||||
cols (count (:layout-grid-columns shape))
|
||||
added (concat (for [row (range (inc old-rows) (inc rows))
|
||||
column (range 1 (inc cols))] [row column])
|
||||
(for [row (range 1 (inc (min old-rows rows)))
|
||||
column (range (inc old-cols) (inc cols))] [row column]))]
|
||||
(reduce (fn [shape [row column]]
|
||||
(if (ctsl/cell-by-row-column shape row column)
|
||||
shape
|
||||
(ctsl/create-cells shape [column row 1 1])))
|
||||
shape added)))
|
||||
|
||||
(defn- update-grid-copy-attrs
|
||||
"Synchronizes the `layout-grid-cells` property from the main shape to the copies"
|
||||
[changes shape-copy-id shape-main _library _component omit-touched?]
|
||||
[changes shape-before shape-main omit-touched?]
|
||||
(-> changes
|
||||
(pcb/apply-changes-local)
|
||||
(pcb/update-shapes
|
||||
[shape-copy-id]
|
||||
[(:id shape-before)]
|
||||
(fn [shape-copy objects]
|
||||
(let [ids-map
|
||||
(into {}
|
||||
@ -1570,14 +1588,15 @@
|
||||
(fn [cell]
|
||||
(update cell :shapes #(filterv child? %)))))))
|
||||
;; Take cells from main and remap the shapes to assign it to the copy
|
||||
copy-cells (-> shape-copy :layout-grid-cells (remove-orphan-cells shape-copy))
|
||||
main-cells (-> shape-main (ctsl/remap-grid-cells ids-map) :layout-grid-cells)]
|
||||
copy-cells (-> shape-copy :layout-grid-cells (remove-orphan-cells shape-copy))
|
||||
main-cells (-> shape-main (ctsl/remap-grid-cells ids-map) :layout-grid-cells)
|
||||
keep-cells? (and omit-touched? (contains? (:touched shape-copy) :layout-grid-cells))]
|
||||
(-> shape-copy
|
||||
(assoc :layout-grid-cells
|
||||
(if (and omit-touched?
|
||||
(contains? (:touched shape-copy) :layout-grid-cells))
|
||||
(if keep-cells?
|
||||
copy-cells
|
||||
main-cells))
|
||||
(cond-> keep-cells? (add-inherited-grid-cells shape-before))
|
||||
(ctsl/assign-cells objects))))
|
||||
{:ignore-touched true :with-objects? true})))
|
||||
|
||||
|
||||
@ -18,7 +18,9 @@
|
||||
[app.common.test-helpers.composable.comp.nodes :as n]
|
||||
[app.common.test-helpers.composable.comp.runner :as r]
|
||||
[app.common.test-helpers.composable.comp.setups :as setup]
|
||||
[app.common.test-helpers.composable.comp.undo-check :as uc]
|
||||
[app.common.test-helpers.composable.core :as tm]
|
||||
[app.common.test-helpers.composable.gen :as cg]
|
||||
[app.common.test-helpers.compositions :as tho]
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
@ -33,6 +35,50 @@
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(t/deftest grid-growth-fills-inherited-tracks-before-assigning-copy-children
|
||||
(doseq [direction [:row :column]
|
||||
turn-copy? [false true]]
|
||||
(let [initial (cg/world [1 0 {:layout :grid :layout-children true :grid-square true}])
|
||||
main (n/main-head-of "random-0")
|
||||
copy (n/nth-copy-head-of "random-0" 0)
|
||||
other (n/nth-copy-head-of "random-0" 1)
|
||||
snapshot #(uc/snapshot (tm/file %))
|
||||
ops (concat (when (= direction :column) [(n/edit-grid main :direction :column)])
|
||||
[(n/edit-grid copy :cell {:row 1 :column 1 :props {:align-self :end}})]
|
||||
(when turn-copy? [(n/edit-grid copy :direction (if (= direction :row) :column :row))])
|
||||
[(n/add-child main :extra-a :width 20 :height 20)
|
||||
(n/add-child main :extra-b :width 20 :height 20)])
|
||||
grown (reduce (fn [before op]
|
||||
(let [after (-> before (r/step op) r/settle)
|
||||
undone (r/step after (n/undo))
|
||||
redone (r/step undone (n/redo))]
|
||||
(t/is (< (r/undo-depth before) (r/undo-depth after)))
|
||||
(t/is (uc/same-state? (snapshot before) (snapshot undone)))
|
||||
(t/is (uc/same-state? (snapshot after) (snapshot redone)))
|
||||
after)) initial ops)
|
||||
shapes (mapv #(tm/shape-by-id grown (% grown)) [main copy other])
|
||||
dims (if (= direction :row) [2 3] [3 2])
|
||||
coords (set (for [row (range 1 (inc (first dims)))
|
||||
column (range 1 (inc (second dims)))] [row column]))
|
||||
objects (:objects (thf/current-page (tm/file grown)))
|
||||
main-ids (set (:shapes (first shapes)))
|
||||
component (:component-id (first shapes))
|
||||
synced (r/settle grown #{component})]
|
||||
(doseq [shape shapes]
|
||||
(t/is (= dims (mapv #(count (get shape %)) [:layout-grid-rows :layout-grid-columns])))
|
||||
(t/is (= coords (set (map (juxt :row :column) (vals (:layout-grid-cells shape))))))
|
||||
(t/is (= main-ids (set (map #(or (:shape-ref (get objects %)) %)
|
||||
(mapcat :shapes (vals (:layout-grid-cells shape)))))))
|
||||
(t/is (= (frequencies main-ids)
|
||||
(frequencies (map #(or (:shape-ref (get objects %)) %)
|
||||
(mapcat :shapes (vals (:layout-grid-cells shape))))))))
|
||||
(t/is (= :end (:align-self (first (filter #(= [1 1] [(:row %) (:column %)])
|
||||
(vals (:layout-grid-cells (second shapes))))))))
|
||||
(t/is (= (cond-> #{:layout-grid-cells} turn-copy? (conj :layout-grid-dir))
|
||||
(:touched (second shapes))))
|
||||
(t/is (empty? (:touched (nth shapes 2))))
|
||||
(t/is (uc/same-state? (snapshot grown) (snapshot synced))))))
|
||||
|
||||
(t/deftest sync-keeps-a-real-subpixel-position-change
|
||||
(let [initial (setup/simple-component-with-labeled-copy)
|
||||
original (tm/resolve-shape initial :copy-child)
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.test-helpers.components :as thc]
|
||||
[app.common.test-helpers.composable.comp.nodes :as n]
|
||||
[app.common.test-helpers.composable.comp.undo-check :as uc]
|
||||
[app.common.test-helpers.composable.core :as tm]
|
||||
[app.common.test-helpers.compositions :as tho]
|
||||
@ -95,6 +96,46 @@
|
||||
[(ptk/data-event :layout/update {:ids [(thi/id :main) (thi/id :copy) (thi/id :other)]})]))))
|
||||
(t/is (= (get-cells :main) (get-cells :copy) (get-cells :other))))
|
||||
|
||||
(t/deftest ^:async grid-growth-keeps-overrides-within-inherited-tracks
|
||||
(await
|
||||
(mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock}
|
||||
(doseq [direction [:row :column]
|
||||
turn-copy? [false true]]
|
||||
(await (install-grid))
|
||||
(let [initial (tm/make-situation (ftm/current-file))
|
||||
_ (ftm/uninstall!)
|
||||
ops (concat (when (= direction :column) [(n/edit-grid :main :direction :column)])
|
||||
[(n/edit-grid :copy :cell {:row 1 :column 1 :props {:align-self :end}})]
|
||||
(when turn-copy? [(n/edit-grid :copy :direction (if (= direction :row) :column :row))])
|
||||
[(n/add-child :main :extra-a :width 20 :height 20)
|
||||
(n/add-child :main :extra-b :width 20 :height 20)])
|
||||
grown (loop [live (ftm/start-generated initial) ops ops]
|
||||
(if-let [op (first ops)]
|
||||
(let [next (await (ftm/run-generated-step live op))]
|
||||
(t/is (some? next))
|
||||
(t/is (empty? (uc/failures next)))
|
||||
(recur next (rest ops)))
|
||||
live))
|
||||
dims (if (= direction :row) [2 3] [3 2])
|
||||
coords (set (for [row (range 1 (inc (first dims)))
|
||||
column (range 1 (inc (second dims)))] [row column]))
|
||||
members (set (:shapes (get-shape :main)))]
|
||||
(doseq [label [:main :copy :other]]
|
||||
(t/is (= dims (mapv #(count (get (get-shape label) %)) [:layout-grid-rows :layout-grid-columns])))
|
||||
(t/is (= coords (set (map (juxt :row :column) (get-cells label)))))
|
||||
(t/is (= members (set (mapcat :shapes (get-cells label)))))
|
||||
(t/is (= (frequencies members) (frequencies (mapcat :shapes (get-cells label))))))
|
||||
(t/is (= :end (:align-self (first (get-cells :copy)))))
|
||||
(t/is (= (cond-> #{:layout-grid-cells} turn-copy? (conj :layout-grid-dir))
|
||||
(:touched (get-shape :copy))))
|
||||
(t/is (empty? (:touched (get-shape :other))))
|
||||
(let [before (ftm/snapshot)
|
||||
file-id (:id (ftm/current-file))]
|
||||
(t/is (= :settled (await (ftm/await-step [(dwl/sync-file file-id file-id :components (thi/id :component))]))))
|
||||
(t/is (uc/same-state? before (ftm/snapshot))))
|
||||
(t/is (empty? (uc/failures (await (ftm/finish-generated (ftm/refresh-generated grown))))))
|
||||
(ftm/uninstall!))))))
|
||||
|
||||
(t/deftest ^:async grid-direction-overrides-survive-main-edits-update-main-and-reset
|
||||
(await
|
||||
(mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user