instance.detachInstance() turns an instance into a frame that keeps its content, as in Figma, from scripts run through eval. It reuses the graph's shared detach implementation, asserts editability like the proxy's other mutations, and joins the instance surface check against @figma/plugin-typings.
79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
|
|
import {
|
|
assertNodeEditable,
|
|
getNodeEditCapability,
|
|
ReadOnlyLibraryDefinitionError
|
|
} from '@open-pencil/core/editor'
|
|
import { FigmaAPI } from '@open-pencil/core/figma-api'
|
|
import { updateNode } from '@open-pencil/core/tools/modify'
|
|
import { SceneGraph } from '@open-pencil/scene-graph'
|
|
|
|
describe('library definition capabilities', () => {
|
|
test('protects materialized definitions and descendants but not their instances', async () => {
|
|
const graph = new SceneGraph()
|
|
const page = graph.getPages()[0]
|
|
const component = graph.createNode('COMPONENT', page.id, {
|
|
name: 'Remote button',
|
|
librarySource: {
|
|
identity: { libraryId: 'design-system', assetKey: 'button', revisionId: 'r1' },
|
|
sourceNodeId: 'source',
|
|
readOnly: true
|
|
}
|
|
})
|
|
const child = graph.createNode('TEXT', component.id, { name: 'Label', text: 'Button' })
|
|
const instance = graph.createInstance(component.id, page.id)
|
|
if (!instance) throw new Error('Expected instance')
|
|
|
|
expect(getNodeEditCapability(graph, child.id)).toEqual({
|
|
editable: false,
|
|
reason: 'library-definition',
|
|
libraryId: 'design-system',
|
|
assetKey: 'button'
|
|
})
|
|
expect(getNodeEditCapability(graph, instance.id)).toEqual({ editable: true })
|
|
expect(() => assertNodeEditable(graph, child.id)).toThrow(ReadOnlyLibraryDefinitionError)
|
|
|
|
const figma = new FigmaAPI(graph)
|
|
const proxy = figma.getNodeById(component.id)
|
|
if (!proxy) throw new Error('Expected proxy')
|
|
expect(() => {
|
|
proxy.name = 'Changed'
|
|
}).toThrow(ReadOnlyLibraryDefinitionError)
|
|
expect(() => {
|
|
proxy.opacity = 0.5
|
|
}).toThrow(ReadOnlyLibraryDefinitionError)
|
|
expect(() => proxy.remove()).toThrow(ReadOnlyLibraryDefinitionError)
|
|
expect(graph.getNode(component.id)?.name).toBe('Remote button')
|
|
const instanceProxy = figma.getNodeById(instance.id)
|
|
if (!instanceProxy) throw new Error('Expected instance proxy')
|
|
instanceProxy.opacity = 0.5
|
|
expect(graph.getNode(instance.id)?.opacity).toBe(0.5)
|
|
expect(() => updateNode.execute(figma, { id: component.id, opacity: 0.5 })).toThrow(
|
|
ReadOnlyLibraryDefinitionError
|
|
)
|
|
})
|
|
|
|
test('does not detach an instance inside a read-only definition', () => {
|
|
const graph = new SceneGraph()
|
|
const page = graph.getPages()[0]
|
|
const icon = graph.createNode('COMPONENT', page.id, { name: 'Icon' })
|
|
const component = graph.createNode('COMPONENT', page.id, {
|
|
name: 'Remote button',
|
|
librarySource: {
|
|
identity: { libraryId: 'design-system', assetKey: 'button', revisionId: 'r1' },
|
|
sourceNodeId: 'source',
|
|
readOnly: true
|
|
}
|
|
})
|
|
const nested = graph.createInstance(icon.id, component.id)
|
|
if (!nested) throw new Error('Expected instance')
|
|
|
|
const figma = new FigmaAPI(graph)
|
|
const nestedProxy = figma.getNodeById(nested.id)
|
|
if (!nestedProxy) throw new Error('Expected proxy')
|
|
expect(() => nestedProxy.detachInstance()).toThrow(ReadOnlyLibraryDefinitionError)
|
|
expect(graph.getNode(nested.id)?.type).toBe('INSTANCE')
|
|
})
|
|
})
|