150 lines
5.5 KiB
TypeScript
150 lines
5.5 KiB
TypeScript
import { describe, expect, it } from 'bun:test'
|
|
|
|
import { renderJSX, sceneNodeToJSX } from '@open-pencil/core'
|
|
|
|
import { getNodeOrThrow, childIdAt } from '#tests/helpers/assert'
|
|
import { makeSceneGraph } from '#tests/helpers/scene'
|
|
|
|
describe('text props round-trip', () => {
|
|
it('lineHeight renders and exports', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" lineHeight={24}>Hello</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.lineHeight).toBe(24)
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('lineHeight={24}')
|
|
})
|
|
|
|
it('letterSpacing renders and exports', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" letterSpacing={2}>Spaced</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.letterSpacing).toBe(2)
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('letterSpacing={2}')
|
|
})
|
|
|
|
it('textDecoration renders and exports', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" textDecoration="underline">Link</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.textDecoration).toBe('UNDERLINE')
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('textDecoration="underline"')
|
|
})
|
|
|
|
it('textCase renders and exports', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" textCase="upper">label</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.textCase).toBe('UPPER')
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('textCase="upper"')
|
|
})
|
|
|
|
it('maxLines renders with truncation', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" maxLines={2}>Long text here</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.maxLines).toBe(2)
|
|
expect(n.textTruncation).toBe('ENDING')
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('maxLines={2}')
|
|
})
|
|
|
|
it('truncate without maxLines', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000" truncate>Overflow</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.textTruncation).toBe('ENDING')
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).toContain('truncate')
|
|
})
|
|
|
|
it('accepts Figma-style text aliases', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(
|
|
g,
|
|
'<Text text="Alias text" fontSize={24} fontFamily="Inter" fontWeight={700} fill="#123456" textHorizontalAlignment="CENTER" textVerticalAlignment="CENTER" />'
|
|
)
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.text).toBe('Alias text')
|
|
expect(n.fontSize).toBe(24)
|
|
expect(n.fontFamily).toBe('Inter')
|
|
expect(n.fontWeight).toBe(700)
|
|
expect(n.textAlignHorizontal).toBe('CENTER')
|
|
expect(n.textAlignVertical).toBe('CENTER')
|
|
expect(n.fills[0]?.type).toBe('SOLID')
|
|
})
|
|
|
|
it('accepts characters as a text content alias', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text characters="Characters text" color="#000" />')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.text).toBe('Characters text')
|
|
})
|
|
|
|
it('prefers text children over text prop alias', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text text="Prop text" color="#000">Child text</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.text).toBe('Child text')
|
|
})
|
|
|
|
it('defaults omit text props', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(g, '<Text color="#000">Plain</Text>')
|
|
const n = getNodeOrThrow(g, result.id)
|
|
expect(n.lineHeight).toBeNull()
|
|
expect(n.letterSpacing).toBe(0)
|
|
expect(n.textDecoration).toBe('NONE')
|
|
expect(n.textCase).toBe('ORIGINAL')
|
|
expect(n.maxLines).toBeNull()
|
|
const jsx = sceneNodeToJSX(n.id, g)
|
|
expect(jsx).not.toContain('lineHeight')
|
|
expect(jsx).not.toContain('letterSpacing')
|
|
expect(jsx).not.toContain('textDecoration')
|
|
expect(jsx).not.toContain('textCase')
|
|
expect(jsx).not.toContain('maxLines')
|
|
expect(jsx).not.toContain('truncate')
|
|
})
|
|
|
|
it('text w="fill" in flex="col" exports as w="fill" not w={computed}', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(
|
|
g,
|
|
`
|
|
<Frame name="Card" flex="col" w={300} p={20}>
|
|
<Text name="Title" size={22} weight="bold" color="#111" w="fill">Hello World</Text>
|
|
</Frame>
|
|
`
|
|
)
|
|
const card = getNodeOrThrow(g, result.id)
|
|
const title = getNodeOrThrow(g, childIdAt(card, 0))
|
|
expect(title.layoutAlignSelf).toBe('STRETCH')
|
|
expect(title.textAutoResize).toBe('HEIGHT')
|
|
const jsx = sceneNodeToJSX(title.id, g)
|
|
expect(jsx).toContain('w="fill"')
|
|
expect(jsx).not.toMatch(/w=\{/)
|
|
})
|
|
|
|
it('text grow={1} in flex="row" exports as grow not w={computed}', async () => {
|
|
const g = makeSceneGraph()
|
|
const [result] = await renderJSX(
|
|
g,
|
|
`
|
|
<Frame name="Row" flex="row" w={300}>
|
|
<Text name="Label" color="#999" w={60}>Label</Text>
|
|
<Text name="Value" color="#111" w="fill">Some value text</Text>
|
|
</Frame>
|
|
`
|
|
)
|
|
const row = getNodeOrThrow(g, result.id)
|
|
const value = getNodeOrThrow(g, childIdAt(row, 1))
|
|
expect(value.layoutGrow).toBe(1)
|
|
const jsx = sceneNodeToJSX(value.id, g)
|
|
expect(jsx).toContain('grow={1}')
|
|
expect(jsx).not.toMatch(/\bw=\{\d+\}/)
|
|
})
|
|
})
|