149 lines
4.6 KiB
TypeScript
149 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'bun:test'
|
|
|
|
import {
|
|
Frame,
|
|
Text,
|
|
Rectangle,
|
|
defineVars,
|
|
designVar,
|
|
renderTree,
|
|
renderJSX
|
|
} from '@open-pencil/core/design-jsx'
|
|
|
|
import { getNodeOrThrow } from '#tests/helpers/assert'
|
|
import { addTestColorVariable, makeSceneGraph } from '#tests/helpers/scene'
|
|
|
|
function setup() {
|
|
const graph = makeSceneGraph()
|
|
graph.addCollection({
|
|
id: 'spacing',
|
|
name: 'Spacing',
|
|
modes: [
|
|
{ modeId: 'comfortable', name: 'Comfortable' },
|
|
{ modeId: 'compact', name: 'Compact' }
|
|
],
|
|
defaultModeId: 'comfortable',
|
|
variableIds: []
|
|
})
|
|
graph.addVariable({
|
|
id: 'space',
|
|
name: 'Space/medium',
|
|
type: 'FLOAT',
|
|
collectionId: 'spacing',
|
|
valuesByMode: { comfortable: 16, compact: 8 },
|
|
description: '',
|
|
hiddenFromPublishing: false
|
|
})
|
|
return { graph, token: designVar('space') }
|
|
}
|
|
|
|
describe('Design JSX scalar variables', () => {
|
|
it('binds layout shorthands and computes Hug sizing from their values', async () => {
|
|
const { graph, token } = setup()
|
|
const result = await renderTree(
|
|
graph,
|
|
Frame({
|
|
flex: 'col',
|
|
w: 100,
|
|
h: 'hug',
|
|
gap: token,
|
|
p: token,
|
|
rounded: token,
|
|
children: [Rectangle({ w: 20, h: 20 }), Rectangle({ w: 20, h: 20 })]
|
|
})
|
|
)
|
|
const node = getNodeOrThrow(graph, result.id)
|
|
expect(node.height).toBe(88)
|
|
expect(node.itemSpacing).toBe(16)
|
|
expect(node.boundVariables).toMatchObject({
|
|
itemSpacing: 'space',
|
|
paddingTop: 'space',
|
|
paddingRight: 'space',
|
|
paddingBottom: 'space',
|
|
paddingLeft: 'space',
|
|
cornerRadius: 'space'
|
|
})
|
|
})
|
|
|
|
it('preserves literal longhand precedence over a bound shorthand', async () => {
|
|
const { graph, token } = setup()
|
|
const result = await renderTree(graph, Frame({ w: 100, h: 100, p: token, px: 4, pt: 2 }))
|
|
const node = getNodeOrThrow(graph, result.id)
|
|
expect([node.paddingTop, node.paddingRight, node.paddingBottom, node.paddingLeft]).toEqual([
|
|
2, 4, 16, 4
|
|
])
|
|
expect(node.boundVariables).toEqual({ paddingBottom: 'space' })
|
|
})
|
|
|
|
it('preserves existing grid gap precedence with scalar bindings', async () => {
|
|
const { graph, token } = setup()
|
|
const result = await renderTree(
|
|
graph,
|
|
Frame({ grid: true, columns: 2, w: 100, h: 100, gap: token, columnGap: 3 })
|
|
)
|
|
const node = getNodeOrThrow(graph, result.id)
|
|
expect(node.gridColumnGap).toBe(16)
|
|
expect(node.gridRowGap).toBe(16)
|
|
expect(node.boundVariables).toEqual({ gridRowGap: 'space', gridColumnGap: 'space' })
|
|
})
|
|
|
|
it('supports numeric fallback types, name lookup, typography, and fixed sizes', async () => {
|
|
const { graph } = setup()
|
|
const vars = defineVars({ space: { name: 'Space/medium', value: 16 } })
|
|
const result = await renderTree(
|
|
graph,
|
|
Text({
|
|
w: vars.space,
|
|
size: vars.space,
|
|
lineHeight: vars.space,
|
|
letterSpacing: vars.space,
|
|
children: 'A'
|
|
})
|
|
)
|
|
const node = getNodeOrThrow(graph, result.id)
|
|
expect(node.boundVariables).toMatchObject({
|
|
width: 'space',
|
|
fontSize: 'space',
|
|
lineHeight: 'space',
|
|
letterSpacing: 'space'
|
|
})
|
|
expect(node.fontSize).toBe(16)
|
|
})
|
|
|
|
it('binds through JSX strings in the agent authoring path', async () => {
|
|
const { graph } = setup()
|
|
const [result] = await renderJSX(
|
|
graph,
|
|
'<Frame w={100} h="hug" flex="col" p={designVar("space", 16)} />'
|
|
)
|
|
expect(result).toBeDefined()
|
|
expect(getNodeOrThrow(graph, result.id).boundVariables.paddingTop).toBe('space')
|
|
})
|
|
|
|
it('inherits the parent collection mode', async () => {
|
|
const { graph, token } = setup()
|
|
const parent = graph.createNode('FRAME', graph.getPages()[0]?.id ?? '', {
|
|
variableModes: { spacing: 'compact' }
|
|
})
|
|
const result = await renderTree(
|
|
graph,
|
|
Frame({ w: 100, h: 'hug', flex: 'col', p: token, children: Rectangle({ w: 20, h: 20 }) }),
|
|
{ parentId: parent.id }
|
|
)
|
|
const node = getNodeOrThrow(graph, result.id)
|
|
expect(graph.resolveNumberVariableForNode(node.id, 'space')).toBe(8)
|
|
expect(node.height).toBe(36)
|
|
})
|
|
|
|
it('rejects missing and nonnumeric variables instead of storing objects in numeric fields', async () => {
|
|
const { graph } = setup()
|
|
addTestColorVariable(graph, 'color', 'Color')
|
|
await expect(renderTree(graph, Frame({ gap: designVar('color') }))).rejects.toThrow(
|
|
'Expected a FLOAT variable'
|
|
)
|
|
await expect(renderTree(graph, Frame({ gap: designVar('missing', 16) }))).rejects.toThrow(
|
|
'Expected a FLOAT variable'
|
|
)
|
|
})
|
|
})
|