🐛 Validate the enclosing root when instantiating a nested copy

generate-instantiate-component validated the new copy on its own. When
the copy is created inside another component it is a nested copy, but
the validator starts with a :not-component context and reported
:nested-copy-not-allowed, a false error that broke 34 common tests on JS.

When the new copy is not an instance root, validate the instance roots
that hold its parent instead. The validator then walks the copy with the
right context, and also checks the parents that may change (e.g.
layouts).

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-10-01 12:12:21 +02:00 committed by Andrés Moya
parent 05ea874f79
commit b136097e18
2 changed files with 46 additions and 4 deletions

View File

@ -322,9 +322,15 @@
changes
(rest new-shapes))
ids-to-validate (cond-> [(:id first-shape)]
grid-parent?
(conj (:parent-id first-shape)))
;; A copy nested inside another component can't be validated on
;; its own, as the validator needs the context of its ancestors.
;; Validate the root of the enclosing instance instead, which also
;; checks the parents that may change (e.g. layouts)
ids-to-validate (if (ctk/instance-root? first-shape)
(cond-> [(:id first-shape)]
grid-parent?
(conj (:parent-id first-shape)))
(ctn/get-all-instance-roots objects [(:parent-id first-shape)]))
changes (if (seq ids-to-validate)
(pcb/validate-shapes changes

View File

@ -501,7 +501,43 @@
(t/is (ctk/instance-of? copy1-root' (:id file') (:id component')))
(t/is (ctk/is-main-of? main1-root' copy1-root'))
(t/is (ctk/is-main-of? main1-child' copy1-child'))
(t/is (ctst/parent-of? copy1-root' copy1-child'))))
(t/is (ctst/parent-of? copy1-root' copy1-child'))
(t/is (thf/validates-shapes-last? changes (:id page) [(:id new-shape)]))))
(t/deftest test-instantiate-component-inside-main-validates-root
;; A copy nested in a main can't be validated on its own: the root of the
;; main is validated instead
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-simple-component :component1 :main1-root :main1-child)
(tho/add-simple-component :component2 :main2-root :main2-child))
page (thf/current-page file)
component (thc/get-component file :component1)
main2-id (thi/id :main2-root)
;; ==== Action
[new-shape changes]
(cll/generate-instantiate-component (-> (pcb/empty-changes nil (:id page))
(pcb/with-objects (:objects page)))
(:objects page)
(:id file)
(:id component)
(gpt/point 1000 1000)
page
{(:id file) file}
nil
main2-id
main2-id
{})
file' (thf/apply-changes file changes)
copy1' (ths/get-shape-by-id file' (:id new-shape))]
;; ==== Check
(t/is (= main2-id (:parent-id copy1')))
(t/is (not (ctk/instance-root? copy1')))
(t/is (thf/validates-shapes-last? changes (:id page) [main2-id]))))
(t/deftest test-instantiate-component-from-lib
(let [;; ==== Setup