mirror of
https://github.com/penpot/penpot.git
synced 2026-09-18 18:06:14 +00:00
🐛 Fix nil dereference crash during flex layout drag operations (#10845)
Production crash where @(get bounds id) threw "No protocol method IDeref.-deref defined for type null" when a shape ID had no corresponding entry in the bounds map during layout calculations. Added defensive nil guards (when-let / when) to all unprotected bounds dereference sites: - flex_layout/bounds.cljc: layout-content-points (parent + child) and layout-content-bounds - grid_layout/bounds.cljc: layout-content-points and layout-content-bounds - min_size_layout.cljc: child-min-width grid branch (3 sites) and child-min-height grid branch Added 7 new tests in geom_bounds_layout_nil_test.cljc covering all nil-bounds edge cases for flex, grid, and min-size layout paths. Registered in runner.cljc. Closes #10843 AI-assisted-by: qwen3.7-plus
This commit is contained in:
parent
764b62906b
commit
d04cbf175e
@ -121,74 +121,79 @@
|
|||||||
(defn layout-content-points
|
(defn layout-content-points
|
||||||
[bounds parent children objects]
|
[bounds parent children objects]
|
||||||
|
|
||||||
(let [parent-id (dm/get-prop parent :id)
|
(let [parent-id (dm/get-prop parent :id)
|
||||||
parent-bounds @(get bounds parent-id)
|
parent-bounds (get bounds parent-id)]
|
||||||
reverse? (ctl/reverse? parent)
|
(when-let [parent-bounds (some-> parent-bounds deref)]
|
||||||
children (cond->> children (not reverse?) reverse)]
|
(let [reverse? (ctl/reverse? parent)
|
||||||
|
children (cond->> children (not reverse?) reverse)]
|
||||||
|
|
||||||
(loop [children (seq children)
|
(loop [children (seq children)
|
||||||
result (transient [])
|
result (transient [])
|
||||||
correct-v (gpt/point 0)]
|
correct-v (gpt/point 0)]
|
||||||
|
|
||||||
(if (not children)
|
(if (not children)
|
||||||
(persistent! result)
|
(persistent! result)
|
||||||
|
|
||||||
(let [child (first children)
|
(let [child (first children)
|
||||||
child-id (dm/get-prop child :id)
|
child-id (dm/get-prop child :id)
|
||||||
child-bounds @(get bounds child-id)
|
child-bounds-ref (get bounds child-id)
|
||||||
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
|
child-bounds (some-> child-bounds-ref deref)
|
||||||
|
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
|
||||||
|
|
||||||
[child-bounds correct-v]
|
[child-bounds correct-v]
|
||||||
(if (or (ctl/fill-width? child) (ctl/fill-height? child))
|
(if (and child-bounds
|
||||||
(child-layout-bound-points parent child parent-bounds child-bounds correct-v bounds objects)
|
(or (ctl/fill-width? child) (ctl/fill-height? child)))
|
||||||
[(->> child-bounds (map #(gpt/add % correct-v))) correct-v])
|
(child-layout-bound-points parent child parent-bounds child-bounds correct-v bounds objects)
|
||||||
|
[(when child-bounds
|
||||||
|
(->> child-bounds (map #(gpt/add % correct-v))))
|
||||||
|
correct-v])
|
||||||
|
|
||||||
child-bounds
|
child-bounds
|
||||||
(when (d/not-empty? child-bounds)
|
(when (d/not-empty? child-bounds)
|
||||||
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
|
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
|
||||||
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left))))]
|
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left))))]
|
||||||
|
|
||||||
(recur (next children)
|
(recur (next children)
|
||||||
(cond-> result (some? child-bounds) (conj! child-bounds))
|
(cond-> result (some? child-bounds) (conj! child-bounds))
|
||||||
correct-v))))))
|
correct-v))))))))
|
||||||
|
|
||||||
(defn layout-content-bounds
|
(defn layout-content-bounds
|
||||||
[bounds {:keys [layout-padding] :as parent} children objects]
|
[bounds {:keys [layout-padding] :as parent} children objects]
|
||||||
|
|
||||||
(let [parent-id (:id parent)
|
(let [parent-id (:id parent)
|
||||||
parent-bounds @(get bounds parent-id)
|
parent-bounds (get bounds parent-id)]
|
||||||
|
(when-let [parent-bounds (some-> parent-bounds deref)]
|
||||||
|
(let [row? (ctl/row? parent)
|
||||||
|
col? (ctl/col? parent)
|
||||||
|
space-around? (ctl/space-around? parent)
|
||||||
|
space-evenly? (ctl/space-evenly? parent)
|
||||||
|
content-evenly? (ctl/content-evenly? parent)
|
||||||
|
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
||||||
|
|
||||||
row? (ctl/row? parent)
|
row-pad (if (or (and col? space-evenly?)
|
||||||
col? (ctl/col? parent)
|
(and col? space-around?)
|
||||||
space-around? (ctl/space-around? parent)
|
(and row? content-evenly?))
|
||||||
space-evenly? (ctl/space-evenly? parent)
|
layout-gap-row
|
||||||
content-evenly? (ctl/content-evenly? parent)
|
0)
|
||||||
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
|
||||||
|
|
||||||
row-pad (if (or (and col? space-evenly?)
|
col-pad (if (or (and row? space-evenly?)
|
||||||
(and col? space-around?)
|
(and row? space-around?)
|
||||||
(and row? content-evenly?))
|
(and col? content-evenly?))
|
||||||
layout-gap-row
|
layout-gap-col
|
||||||
0)
|
0)
|
||||||
|
|
||||||
col-pad (if (or (and row? space-evenly?)
|
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||||
(and row? space-around?)
|
pad-top (+ (or pad-top 0) row-pad)
|
||||||
(and col? content-evenly?))
|
pad-right (+ (or pad-right 0) col-pad)
|
||||||
layout-gap-col
|
pad-bottom (+ (or pad-bottom 0) row-pad)
|
||||||
0)
|
pad-left (+ (or pad-left 0) col-pad)
|
||||||
|
|
||||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
layout-points
|
||||||
pad-top (+ (or pad-top 0) row-pad)
|
(layout-content-points bounds parent children objects)]
|
||||||
pad-right (+ (or pad-right 0) col-pad)
|
|
||||||
pad-bottom (+ (or pad-bottom 0) row-pad)
|
|
||||||
pad-left (+ (or pad-left 0) col-pad)
|
|
||||||
|
|
||||||
layout-points
|
(if (d/not-empty? layout-points)
|
||||||
(layout-content-points bounds parent children objects)]
|
(-> layout-points
|
||||||
|
(gpo/merge-parent-coords-bounds parent-bounds)
|
||||||
(if (d/not-empty? layout-points)
|
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
||||||
(-> layout-points
|
;; Cannot create some bounds from the children so we return the parent's
|
||||||
(gpo/merge-parent-coords-bounds parent-bounds)
|
parent-bounds)))))
|
||||||
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
|
||||||
;; Cannot create some bounds from the children so we return the parent's
|
|
||||||
parent-bounds)))
|
|
||||||
|
|||||||
@ -12,36 +12,36 @@
|
|||||||
|
|
||||||
(defn layout-content-points
|
(defn layout-content-points
|
||||||
[bounds parent {:keys [row-tracks column-tracks]}]
|
[bounds parent {:keys [row-tracks column-tracks]}]
|
||||||
(let [parent-id (:id parent)
|
(let [parent-id (:id parent)
|
||||||
parent-bounds @(get bounds parent-id)
|
parent-bounds (get bounds parent-id)]
|
||||||
|
(when-let [parent-bounds (some-> parent-bounds deref)]
|
||||||
hv #(gpo/start-hv parent-bounds %)
|
(let [hv #(gpo/start-hv parent-bounds %)
|
||||||
vv #(gpo/start-vv parent-bounds %)]
|
vv #(gpo/start-vv parent-bounds %)]
|
||||||
(d/concat-vec
|
(d/concat-vec
|
||||||
(->> row-tracks
|
(->> row-tracks
|
||||||
(mapcat #(vector (:start-p %)
|
(mapcat #(vector (:start-p %)
|
||||||
(gpt/add (:start-p %) (vv (:size %))))))
|
(gpt/add (:start-p %) (vv (:size %))))))
|
||||||
(->> column-tracks
|
(->> column-tracks
|
||||||
(mapcat #(vector (:start-p %)
|
(mapcat #(vector (:start-p %)
|
||||||
(gpt/add (:start-p %) (hv (:size %)))))))))
|
(gpt/add (:start-p %) (hv (:size %)))))))))))
|
||||||
|
|
||||||
(defn layout-content-bounds
|
(defn layout-content-bounds
|
||||||
[bounds {:keys [layout-padding] :as parent} layout-data]
|
[bounds {:keys [layout-padding] :as parent} layout-data]
|
||||||
|
|
||||||
(let [parent-id (:id parent)
|
(let [parent-id (:id parent)
|
||||||
parent-bounds @(get bounds parent-id)
|
parent-bounds (get bounds parent-id)]
|
||||||
|
(when-let [parent-bounds (some-> parent-bounds deref)]
|
||||||
|
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||||
|
pad-top (or pad-top 0)
|
||||||
|
pad-right (or pad-right 0)
|
||||||
|
pad-bottom (or pad-bottom 0)
|
||||||
|
pad-left (or pad-left 0)
|
||||||
|
|
||||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
layout-points (layout-content-points bounds parent layout-data)]
|
||||||
pad-top (or pad-top 0)
|
|
||||||
pad-right (or pad-right 0)
|
|
||||||
pad-bottom (or pad-bottom 0)
|
|
||||||
pad-left (or pad-left 0)
|
|
||||||
|
|
||||||
layout-points (layout-content-points bounds parent layout-data)]
|
(if (d/not-empty? layout-points)
|
||||||
|
(-> layout-points
|
||||||
(if (d/not-empty? layout-points)
|
(gpo/merge-parent-coords-bounds parent-bounds)
|
||||||
(-> layout-points
|
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
||||||
(gpo/merge-parent-coords-bounds parent-bounds)
|
;; Cannot create some bounds from the children so we return the parent's
|
||||||
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
parent-bounds)))))
|
||||||
;; Cannot create some bounds from the children so we return the parent's
|
|
||||||
parent-bounds)))
|
|
||||||
|
|||||||
@ -31,13 +31,17 @@
|
|||||||
|
|
||||||
(and (ctl/fill-width? child)
|
(and (ctl/fill-width? child)
|
||||||
(ctl/grid-layout? child))
|
(ctl/grid-layout? child))
|
||||||
(let [children
|
(let [child-bounds-ref (get bounds (:id child))]
|
||||||
(->> (cfh/get-immediate-children objects (:id child))
|
(if child-bounds-ref
|
||||||
(remove ctl/position-absolute?)
|
(let [children
|
||||||
(map #(vector @(get bounds (:id %)) %)))
|
(->> (cfh/get-immediate-children objects (:id child))
|
||||||
layout-data (gd/calc-layout-data child @(get bounds (:id child)) children bounds objects true)]
|
(remove ctl/position-absolute?)
|
||||||
(max (ctl/child-min-width child)
|
(keep #(when-let [b (get bounds (:id %))]
|
||||||
(gpo/width-points (gb/layout-content-bounds bounds child layout-data))))
|
[@b %])))
|
||||||
|
layout-data (gd/calc-layout-data child @child-bounds-ref children bounds objects true)]
|
||||||
|
(max (ctl/child-min-width child)
|
||||||
|
(gpo/width-points (gb/layout-content-bounds bounds child layout-data))))
|
||||||
|
(ctl/child-min-width child)))
|
||||||
|
|
||||||
(ctl/fill-width? child)
|
(ctl/fill-width? child)
|
||||||
(ctl/child-min-width child)
|
(ctl/child-min-width child)
|
||||||
@ -63,11 +67,15 @@
|
|||||||
(let [children
|
(let [children
|
||||||
(->> (cfh/get-immediate-children objects (dm/get-prop child :id))
|
(->> (cfh/get-immediate-children objects (dm/get-prop child :id))
|
||||||
(remove ctl/position-absolute?)
|
(remove ctl/position-absolute?)
|
||||||
(map (fn [child] [@(get bounds (:id child)) child])))
|
(keep (fn [c]
|
||||||
|
(when-let [b (get bounds (:id c))]
|
||||||
|
[@b c]))))
|
||||||
layout-data (gd/calc-layout-data child (:points child) children bounds objects true)
|
layout-data (gd/calc-layout-data child (:points child) children bounds objects true)
|
||||||
auto-bounds (gb/layout-content-bounds bounds child layout-data)]
|
auto-bounds (gb/layout-content-bounds bounds child layout-data)]
|
||||||
(max (ctl/child-min-height child)
|
(if auto-bounds
|
||||||
(gpo/height-points auto-bounds)))
|
(max (ctl/child-min-height child)
|
||||||
|
(gpo/height-points auto-bounds))
|
||||||
|
(ctl/child-min-height child)))
|
||||||
|
|
||||||
(ctl/fill-height? child)
|
(ctl/fill-height? child)
|
||||||
(ctl/child-min-height child)
|
(ctl/child-min-height child)
|
||||||
|
|||||||
213
common/test/common_tests/geom_bounds_layout_nil_test.cljc
Normal file
213
common/test/common_tests/geom_bounds_layout_nil_test.cljc
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
;; 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 Sucursal en España SL
|
||||||
|
|
||||||
|
(ns common-tests.geom-bounds-layout-nil-test
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.bounds-map :as gbm]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.rect :as grc]
|
||||||
|
[app.common.geom.shapes.flex-layout.bounds :as fb]
|
||||||
|
[app.common.geom.shapes.grid-layout.bounds :as gb]
|
||||||
|
[app.common.geom.shapes.min-size-layout :as msl]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[clojure.test :as t]))
|
||||||
|
|
||||||
|
;; ---- Helpers ----
|
||||||
|
|
||||||
|
(defn- make-rect
|
||||||
|
[id x y w h]
|
||||||
|
(-> (cts/setup-shape {:id id
|
||||||
|
:type :rect
|
||||||
|
:name (str "rect-" id)
|
||||||
|
:x x :y y :width w :height h})
|
||||||
|
(assoc :parent-id uuid/zero
|
||||||
|
:frame-id uuid/zero)))
|
||||||
|
|
||||||
|
(defn- make-flex-frame
|
||||||
|
[id child-ids & {:keys [x y w h dir]
|
||||||
|
:or {x 0 y 0 w 200 h 200 dir :row}}]
|
||||||
|
(-> (cts/setup-shape {:id id
|
||||||
|
:type :frame
|
||||||
|
:name (str "flex-" id)
|
||||||
|
:layout :flex
|
||||||
|
:layout-flex-dir dir
|
||||||
|
:x x :y y :width w :height h})
|
||||||
|
(assoc :parent-id uuid/zero
|
||||||
|
:frame-id uuid/zero
|
||||||
|
:shapes (vec child-ids))))
|
||||||
|
|
||||||
|
(defn- make-grid-frame
|
||||||
|
[id child-ids & {:keys [x y w h dir]
|
||||||
|
:or {x 0 y 0 w 200 h 200 dir :row}}]
|
||||||
|
(let [cell-id (uuid/next)]
|
||||||
|
(-> (cts/setup-shape {:id id
|
||||||
|
:type :frame
|
||||||
|
:name (str "grid-" id)
|
||||||
|
:layout :grid
|
||||||
|
:layout-grid-dir dir
|
||||||
|
:layout-grid-columns [{:type :flex :value 1}]
|
||||||
|
:layout-grid-rows [{:type :flex :value 1}]
|
||||||
|
:layout-grid-cells
|
||||||
|
{cell-id {:id cell-id
|
||||||
|
:row 1
|
||||||
|
:row-span 1
|
||||||
|
:column 1
|
||||||
|
:column-span 1
|
||||||
|
:shapes (vec child-ids)}}
|
||||||
|
:layout-padding-type :multiple
|
||||||
|
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
|
||||||
|
:layout-gap {:column-gap 0 :row-gap 0}
|
||||||
|
:x x :y y :width w :height h})
|
||||||
|
(assoc :parent-id uuid/zero
|
||||||
|
:frame-id uuid/zero
|
||||||
|
:shapes (vec child-ids)))))
|
||||||
|
|
||||||
|
(defn- make-objects
|
||||||
|
[shapes]
|
||||||
|
(let [shape-map (into {} (map (fn [s] [(:id s) s]) shapes))]
|
||||||
|
(reduce-kv (fn [m _id shape]
|
||||||
|
(if (contains? shape :shapes)
|
||||||
|
(reduce (fn [m' child-id]
|
||||||
|
(assoc-in m' [child-id :parent-id] (:id shape)))
|
||||||
|
m
|
||||||
|
(:shapes shape))
|
||||||
|
m))
|
||||||
|
shape-map
|
||||||
|
shape-map)))
|
||||||
|
|
||||||
|
(defn- bounds-map-from-objects
|
||||||
|
"Build a bounds map from objects, optionally excluding some IDs."
|
||||||
|
[objects & {:keys [exclude-ids]}]
|
||||||
|
(let [full (gbm/objects->bounds-map objects)]
|
||||||
|
(if (seq exclude-ids)
|
||||||
|
(apply dissoc full exclude-ids)
|
||||||
|
full)))
|
||||||
|
|
||||||
|
;; ---- Tests for flex layout bounds with nil bounds ----
|
||||||
|
|
||||||
|
(t/deftest layout-content-points-with-missing-parent-bounds
|
||||||
|
(t/testing "layout-content-points returns nil when parent is not in bounds map"
|
||||||
|
(let [child-id (uuid/next)
|
||||||
|
parent-id (uuid/next)
|
||||||
|
child (make-rect child-id 10 10 50 50)
|
||||||
|
parent (make-flex-frame parent-id [child-id])
|
||||||
|
objects (make-objects [parent child])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{parent-id})]
|
||||||
|
|
||||||
|
(t/is (nil? (fb/layout-content-points bounds parent [child] objects))))))
|
||||||
|
|
||||||
|
(t/deftest layout-content-points-with-missing-child-bounds
|
||||||
|
(t/testing "layout-content-points skips children with missing bounds"
|
||||||
|
(let [child1-id (uuid/next)
|
||||||
|
child2-id (uuid/next)
|
||||||
|
parent-id (uuid/next)
|
||||||
|
child1 (make-rect child1-id 10 10 50 50)
|
||||||
|
child2 (make-rect child2-id 70 10 50 50)
|
||||||
|
parent (make-flex-frame parent-id [child1-id child2-id])
|
||||||
|
objects (make-objects [parent child1 child2])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{child1-id})]
|
||||||
|
|
||||||
|
(let [result (fb/layout-content-points bounds parent [child1 child2] objects)]
|
||||||
|
(t/is (some? result))
|
||||||
|
;; Only child2's bounds should be in the result
|
||||||
|
(t/is (pos? (count result)))))))
|
||||||
|
|
||||||
|
(t/deftest layout-content-bounds-with-missing-parent-bounds
|
||||||
|
(t/testing "layout-content-bounds returns nil when parent is not in bounds map"
|
||||||
|
(let [child-id (uuid/next)
|
||||||
|
parent-id (uuid/next)
|
||||||
|
child (make-rect child-id 10 10 50 50)
|
||||||
|
parent (make-flex-frame parent-id [child-id])
|
||||||
|
objects (make-objects [parent child])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{parent-id})]
|
||||||
|
|
||||||
|
(t/is (nil? (fb/layout-content-bounds bounds parent [child] objects))))))
|
||||||
|
|
||||||
|
;; ---- Tests for grid layout bounds with nil bounds ----
|
||||||
|
|
||||||
|
(t/deftest grid-layout-content-points-with-missing-parent-bounds
|
||||||
|
(t/testing "grid layout-content-points returns nil when parent is not in bounds map"
|
||||||
|
(let [parent-id (uuid/next)
|
||||||
|
parent (make-grid-frame parent-id [])
|
||||||
|
objects (make-objects [parent])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{parent-id})
|
||||||
|
layout-data {:row-tracks [{:start-p (gpt/point 0 0) :size 100}]
|
||||||
|
:column-tracks [{:start-p (gpt/point 0 0) :size 100}]}]
|
||||||
|
|
||||||
|
(t/is (nil? (gb/layout-content-points bounds parent layout-data))))))
|
||||||
|
|
||||||
|
(t/deftest grid-layout-content-bounds-with-missing-parent-bounds
|
||||||
|
(t/testing "grid layout-content-bounds returns nil when parent is not in bounds map"
|
||||||
|
(let [parent-id (uuid/next)
|
||||||
|
parent (make-grid-frame parent-id [])
|
||||||
|
objects (make-objects [parent])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{parent-id})
|
||||||
|
layout-data {:row-tracks [{:start-p (gpt/point 0 0) :size 100}]
|
||||||
|
:column-tracks [{:start-p (gpt/point 0 0) :size 100}]}]
|
||||||
|
|
||||||
|
(t/is (nil? (gb/layout-content-bounds bounds parent layout-data))))))
|
||||||
|
|
||||||
|
;; ---- Tests for min-size-layout with nil bounds ----
|
||||||
|
|
||||||
|
(t/deftest child-min-width-grid-with-missing-child-bounds
|
||||||
|
(t/testing "child-min-width falls back when grid layout child bounds are missing"
|
||||||
|
(let [grandchild-id (uuid/next)
|
||||||
|
child-id (uuid/next)
|
||||||
|
grandchild (make-rect grandchild-id 0 0 30 30)
|
||||||
|
child (-> (make-grid-frame child-id [grandchild-id] :w 100 :h 100)
|
||||||
|
(assoc :layout-grid-dir :row
|
||||||
|
:layout-item-h-sizing :fill))
|
||||||
|
objects (make-objects [child grandchild])
|
||||||
|
;; Exclude grandchild from bounds to simulate missing entry
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{grandchild-id})
|
||||||
|
child-bounds (grc/rect->points (grc/make-rect 0 0 100 100))]
|
||||||
|
|
||||||
|
(let [result (msl/child-min-width child child-bounds bounds objects)]
|
||||||
|
(t/is (= (ctl/child-min-width child) result))))))
|
||||||
|
|
||||||
|
(t/deftest child-min-height-grid-with-missing-child-bounds
|
||||||
|
(t/testing "child-min-height falls back when grid layout child bounds are missing"
|
||||||
|
(let [grandchild-id (uuid/next)
|
||||||
|
child-id (uuid/next)
|
||||||
|
grandchild (make-rect grandchild-id 0 0 30 30)
|
||||||
|
child (-> (make-grid-frame child-id [grandchild-id] :w 100 :h 100)
|
||||||
|
(assoc :layout-grid-dir :column
|
||||||
|
:layout-item-v-sizing :fill))
|
||||||
|
objects (make-objects [child grandchild])
|
||||||
|
bounds (bounds-map-from-objects objects :exclude-ids #{grandchild-id})
|
||||||
|
child-bounds (grc/rect->points (grc/make-rect 0 0 100 100))]
|
||||||
|
|
||||||
|
(let [result (msl/child-min-height child child-bounds bounds objects)]
|
||||||
|
(t/is (= (ctl/child-min-height child) result))))))
|
||||||
|
|
||||||
|
(t/deftest child-min-width-grid-with-present-child-bounds
|
||||||
|
(t/testing "child-min-width handles bounded children in a fill-width grid"
|
||||||
|
(let [grandchild-id (uuid/next)
|
||||||
|
child-id (uuid/next)
|
||||||
|
grandchild (make-rect grandchild-id 0 0 30 30)
|
||||||
|
child (-> (make-grid-frame child-id [grandchild-id] :w 100 :h 100)
|
||||||
|
(assoc :layout-item-h-sizing :fill))
|
||||||
|
objects (make-objects [child grandchild])
|
||||||
|
bounds (bounds-map-from-objects objects)
|
||||||
|
child-bounds (grc/rect->points (grc/make-rect 0 0 100 100))]
|
||||||
|
|
||||||
|
(t/is (number? (msl/child-min-width child child-bounds bounds objects))))))
|
||||||
|
|
||||||
|
(t/deftest child-min-height-grid-with-present-child-bounds
|
||||||
|
(t/testing "child-min-height handles bounded children in a fill-height grid"
|
||||||
|
(let [grandchild-id (uuid/next)
|
||||||
|
child-id (uuid/next)
|
||||||
|
grandchild (make-rect grandchild-id 0 0 30 30)
|
||||||
|
child (-> (make-grid-frame child-id [grandchild-id] :w 100 :h 100)
|
||||||
|
(assoc :layout-item-v-sizing :fill))
|
||||||
|
objects (make-objects [child grandchild])
|
||||||
|
bounds (bounds-map-from-objects objects)
|
||||||
|
child-bounds (grc/rect->points (grc/make-rect 0 0 100 100))]
|
||||||
|
|
||||||
|
(t/is (number? (msl/child-min-height child child-bounds bounds objects))))))
|
||||||
@ -22,6 +22,7 @@
|
|||||||
[common-tests.files.shapes-builder-test]
|
[common-tests.files.shapes-builder-test]
|
||||||
[common-tests.files.validate-test]
|
[common-tests.files.validate-test]
|
||||||
[common-tests.geom-align-test]
|
[common-tests.geom-align-test]
|
||||||
|
[common-tests.geom-bounds-layout-nil-test]
|
||||||
[common-tests.geom-bounds-map-test]
|
[common-tests.geom-bounds-map-test]
|
||||||
[common-tests.geom-flex-layout-test]
|
[common-tests.geom-flex-layout-test]
|
||||||
[common-tests.geom-grid-layout-test]
|
[common-tests.geom-grid-layout-test]
|
||||||
@ -95,6 +96,7 @@
|
|||||||
'common-tests.files-migrations-test
|
'common-tests.files-migrations-test
|
||||||
'common-tests.files.validate-test
|
'common-tests.files.validate-test
|
||||||
'common-tests.geom-align-test
|
'common-tests.geom-align-test
|
||||||
|
'common-tests.geom-bounds-layout-nil-test
|
||||||
'common-tests.geom-bounds-map-test
|
'common-tests.geom-bounds-map-test
|
||||||
'common-tests.geom-flex-layout-test
|
'common-tests.geom-flex-layout-test
|
||||||
'common-tests.geom-grid-layout-test
|
'common-tests.geom-grid-layout-test
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user