From 3b36dadfdbcd20d0aa130b2ff4442bbced19d2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=98=81=ED=98=B8?= Date: Wed, 3 Jun 2026 21:47:17 +0900 Subject: [PATCH] fix(layout): preserve imported instance child bounds --- packages/core/src/layout/apply.ts | 6 ++ .../io/fig/roundtrip/exhaustive.test.ts | 3 +- .../layout/auto-layout/nested/basic.test.ts | 59 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/packages/core/src/layout/apply.ts b/packages/core/src/layout/apply.ts index 42c8e3c4e..99a6965a6 100644 --- a/packages/core/src/layout/apply.ts +++ b/packages/core/src/layout/apply.ts @@ -49,6 +49,10 @@ function updateChildFromYoga(graph: SceneGraph, child: SceneNode, yogaChild: Yog }) } +function preservesImportedInstanceInternals(child: SceneNode): boolean { + return child.type === 'INSTANCE' && child.source.format === 'fig' +} + function recomputeGridChild( graph: SceneGraph, child: SceneNode, @@ -90,6 +94,8 @@ export function applyYogaLayout( updateChildFromYoga(graph, child, yogaChild) + if (preservesImportedInstanceInternals(child)) continue + if (child.layoutMode !== 'NONE') { if (child.layoutMode === 'GRID' && child.visible && child.layoutPositioning !== 'ABSOLUTE') { computeLayout(graph, child.id) diff --git a/tests/engine/io/fig/roundtrip/exhaustive.test.ts b/tests/engine/io/fig/roundtrip/exhaustive.test.ts index 36939c384..45ce4535b 100644 --- a/tests/engine/io/fig/roundtrip/exhaustive.test.ts +++ b/tests/engine/io/fig/roundtrip/exhaustive.test.ts @@ -1,4 +1,4 @@ -import { beforeAll, afterAll, describe, expect, test, spyOn } from 'bun:test' +import { beforeAll, afterAll, describe, expect, test, spyOn, setDefaultTimeout } from 'bun:test' import { unzipSync } from 'fflate' @@ -24,6 +24,7 @@ import { } from './helpers' let dateSpy: { mockRestore(): void } | undefined +setDefaultTimeout(180_000) beforeAll(() => { dateSpy = spyOn(Date.prototype, 'toISOString').mockReturnValue('2026-05-24T12:00:00.000Z') }) diff --git a/tests/engine/layout/auto-layout/nested/basic.test.ts b/tests/engine/layout/auto-layout/nested/basic.test.ts index 702f8004b..0d2dc0cf3 100644 --- a/tests/engine/layout/auto-layout/nested/basic.test.ts +++ b/tests/engine/layout/auto-layout/nested/basic.test.ts @@ -94,4 +94,63 @@ describe('nested auto layout', () => { expect(outerChildren[0].y).toBe(0) expect(outerChildren[1].y).toBe(40) }) + + test('computeAllLayouts preserves fixed children inside instances', () => { + const graph = new SceneGraph() + const page = pageId(graph) + const outer = autoFrame(graph, page, { + width: 500, + height: 300, + paddingTop: 20, + paddingBottom: 20 + }) + const instance = graph.createNode('INSTANCE', outer.id, { + width: 120, + height: 120, + layoutMode: 'HORIZONTAL', + primaryAxisSizing: 'FIXED', + counterAxisSizing: 'FIXED', + paddingTop: 20, + paddingBottom: 20 + }) + graph.updateNode(instance.id, { source: { ...instance.source, format: 'fig' } }) + const fixedChild = autoFrame(graph, instance.id, { + width: 80, + height: 80, + paddingTop: 1665, + paddingBottom: 1665 + }) + + computeAllLayouts(graph, page) + + const childNode = getNodeOrThrow(graph, fixedChild.id) + expect(childNode.height).toBe(80) + expect(childNode.y).toBe(0) + }) + + test('computeAllLayouts relayouts user-created instance children', () => { + const graph = new SceneGraph() + const page = pageId(graph) + const component = graph.createNode('COMPONENT', page, { + layoutMode: 'VERTICAL', + primaryAxisSizing: 'FIXED', + counterAxisSizing: 'FIXED', + width: 100, + height: 100, + counterAxisAlign: 'STRETCH', + paddingLeft: 10, + paddingRight: 10 + }) + rect(graph, component.id, 80, 20, { layoutAlignSelf: 'STRETCH' }) + const outer = autoFrame(graph, page, { width: 300, height: 150 }) + const instance = graph.createInstance(component.id, outer.id, { width: 200, height: 100 }) + if (!instance) throw new Error('Expected instance creation to succeed') + const instanceChild = graph.getChildren(instance.id)[0] + + computeAllLayouts(graph, page) + + const childNode = getNodeOrThrow(graph, instanceChild.id) + expect(childNode.x).toBe(10) + expect(childNode.width).toBe(180) + }) })