Skip to content

Commit 92eb79b

Browse files
committed
Update export devup.json
1 parent a1a51de commit 92eb79b

File tree

9 files changed

+857
-7
lines changed

9 files changed

+857
-7
lines changed

bunfig.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
[test]
22
coverage = true
33
coverageSkipTestFiles = true
4-
coveragePathIgnorePatterns = ["dist/**", "src/commands/exportPagesAndComponents.ts"]
5-
# coverageThreshold = 0.9999
4+
coveragePathIgnorePatterns = [
5+
"dist/**",
6+
"src/commands/exportPagesAndComponents.ts",
7+
"src/commands/devup/import-devup.ts",
8+
"src/commands/devup/export-devup.ts",
9+
"src/codegen/responsive/index.ts",
10+
]
11+
coverageThreshold = 1

src/codegen/props/__tests__/bound-variables.test.ts

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,77 @@ describe('length bound variables (padding / gap / size / radius)', () => {
231231
borderRadius: '$radiusTl $radiusRl 12px',
232232
})
233233
})
234+
235+
test('getBorderRadiusProps uses two-value shorthand when tl===br and tr===bl', async () => {
236+
setupFigmaMocks({
237+
variableNamesById: {
238+
'var-a': 'radiusA',
239+
'var-b': 'radiusB',
240+
},
241+
})
242+
243+
const node = {
244+
type: 'RECTANGLE',
245+
cornerRadius: 8,
246+
topLeftRadius: 8,
247+
topRightRadius: 4,
248+
bottomRightRadius: 8,
249+
bottomLeftRadius: 4,
250+
boundVariables: {
251+
topLeftRadius: { id: 'var-a' },
252+
topRightRadius: { id: 'var-b' },
253+
bottomRightRadius: { id: 'var-a' },
254+
bottomLeftRadius: { id: 'var-b' },
255+
},
256+
} as unknown as SceneNode
257+
258+
expect(await getBorderRadiusProps(node)).toEqual({
259+
borderRadius: '$radiusA $radiusB',
260+
})
261+
})
262+
263+
test('getBorderRadiusProps uses four-value when all corners differ', async () => {
264+
setupFigmaMocks({
265+
variableNamesById: {
266+
'var-a': 'a',
267+
'var-b': 'b',
268+
'var-c': 'c',
269+
'var-d': 'd',
270+
},
271+
})
272+
273+
const node = {
274+
type: 'RECTANGLE',
275+
cornerRadius: 8,
276+
topLeftRadius: 1,
277+
topRightRadius: 2,
278+
bottomRightRadius: 3,
279+
bottomLeftRadius: 4,
280+
boundVariables: {
281+
topLeftRadius: { id: 'var-a' },
282+
topRightRadius: { id: 'var-b' },
283+
bottomRightRadius: { id: 'var-c' },
284+
bottomLeftRadius: { id: 'var-d' },
285+
},
286+
} as unknown as SceneNode
287+
288+
expect(await getBorderRadiusProps(node)).toEqual({
289+
borderRadius: '$a $b $c $d',
290+
})
291+
})
292+
293+
test('getBorderRadiusProps falls back to cornerRadius when corner fields are unavailable', async () => {
294+
setupFigmaMocks()
295+
296+
const node = {
297+
type: 'VECTOR',
298+
cornerRadius: 10,
299+
} as unknown as SceneNode
300+
301+
expect(await getBorderRadiusProps(node)).toEqual({
302+
borderRadius: '10px',
303+
})
304+
})
234305
})
235306

236307
describe('effect/text-shadow bound variables and style tokens', () => {
@@ -281,6 +352,31 @@ describe('effect/text-shadow bound variables and style tokens', () => {
281352
})
282353
})
283354

355+
test('getEffectProps does not set __boxShadowToken when style has no name', async () => {
356+
setupFigmaMocks({
357+
styleNamesById: {}, // style lookup returns null
358+
})
359+
360+
const node = {
361+
type: 'FRAME',
362+
effectStyleId: 'style-no-name',
363+
effects: [
364+
{
365+
type: 'DROP_SHADOW',
366+
visible: true,
367+
offset: { x: 0, y: 4 },
368+
radius: 8,
369+
spread: 0,
370+
color: { r: 0, g: 0, b: 0, a: 1 },
371+
},
372+
],
373+
} as unknown as SceneNode
374+
375+
const result = await getEffectProps(node)
376+
expect(result?.boxShadow).toBe('0 4px 8px 0 #000')
377+
expect(result?.__boxShadowToken).toBeUndefined()
378+
})
379+
284380
test('getEffectProps does not set __boxShadowToken when effectStyleId is empty', async () => {
285381
setupFigmaMocks({
286382
styleNamesById: {
@@ -308,6 +404,35 @@ describe('effect/text-shadow bound variables and style tokens', () => {
308404
expect(result?.__boxShadowToken).toBeUndefined()
309405
})
310406

407+
test('getEffectProps falls back to raw values when bound variable ids are unresolved', async () => {
408+
setupFigmaMocks()
409+
410+
const node = {
411+
type: 'FRAME',
412+
effects: [
413+
{
414+
type: 'DROP_SHADOW',
415+
visible: true,
416+
offset: { x: 5, y: 7 },
417+
radius: 9,
418+
spread: 11,
419+
color: { r: 0.1, g: 0.2, b: 0.3, a: 1 },
420+
boundVariables: {
421+
offsetX: { id: 'unknown-x' },
422+
offsetY: { id: 'unknown-y' },
423+
radius: { id: 'unknown-r' },
424+
spread: { id: 'unknown-s' },
425+
color: { id: 'unknown-c' },
426+
},
427+
},
428+
],
429+
} as unknown as SceneNode
430+
431+
expect(await getEffectProps(node)).toEqual({
432+
boxShadow: '5px 7px 9px 11px #1A334D',
433+
})
434+
})
435+
311436
test('getTextShadowProps resolves effect style token and bound variables', async () => {
312437
setupFigmaMocks({
313438
variableNamesById: {
@@ -348,6 +473,30 @@ describe('effect/text-shadow bound variables and style tokens', () => {
348473
})
349474
})
350475

476+
test('getTextShadowProps does not set __textShadowToken when style has no name', async () => {
477+
setupFigmaMocks({
478+
styleNamesById: {},
479+
})
480+
481+
const node = {
482+
type: 'TEXT',
483+
effectStyleId: 'style-no-name',
484+
effects: [
485+
{
486+
type: 'DROP_SHADOW',
487+
visible: true,
488+
offset: { x: 1, y: 2 },
489+
radius: 3,
490+
color: { r: 0, g: 0, b: 0, a: 1 },
491+
},
492+
],
493+
} as unknown as TextNode
494+
495+
const result = await getTextShadowProps(node)
496+
expect(result?.textShadow).toBe('1px 2px 3px #000')
497+
expect(result?.__textShadowToken).toBeUndefined()
498+
})
499+
351500
test('getTextShadowProps does not set __textShadowToken when effectStyleId is empty', async () => {
352501
setupFigmaMocks()
353502

@@ -369,4 +518,31 @@ describe('effect/text-shadow bound variables and style tokens', () => {
369518
expect(result?.textShadow).toBe('2px 4px 6px #000')
370519
expect(result?.__textShadowToken).toBeUndefined()
371520
})
521+
522+
test('getTextShadowProps falls back to raw values when bound variable ids are unresolved', async () => {
523+
setupFigmaMocks()
524+
525+
const node = {
526+
type: 'TEXT',
527+
effects: [
528+
{
529+
type: 'DROP_SHADOW',
530+
visible: true,
531+
offset: { x: 3, y: 6 },
532+
radius: 9,
533+
color: { r: 0.5, g: 0.25, b: 0.75, a: 1 },
534+
boundVariables: {
535+
offsetX: { id: 'unknown-x' },
536+
offsetY: { id: 'unknown-y' },
537+
radius: { id: 'unknown-r' },
538+
color: { id: 'unknown-c' },
539+
},
540+
},
541+
],
542+
} as unknown as TextNode
543+
544+
expect(await getTextShadowProps(node)).toEqual({
545+
textShadow: '3px 6px 9px #8040BF',
546+
})
547+
})
372548
})

src/codegen/props/border.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ export async function getBorderRadiusProps(
4444

4545
// Apply same CSS shorthand optimization as fourValueShortcut
4646
if (tl === tr && tr === br && br === bl) {
47-
if (tl === '0') return
4847
return { borderRadius: tl }
4948
}
5049
if (tl === br && tr === bl) return { borderRadius: `${tl} ${tr}` }

src/codegen/responsive/__tests__/mergePropsToResponsive.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,29 @@ describe('responsive grouping helpers', () => {
357357
expect(result.__textShadowToken).toBeUndefined()
358358
})
359359

360+
it('replaces collapsed textShadow string with token after multi-breakpoint merge', () => {
361+
const result = mergePropsToResponsive(
362+
new Map([
363+
[
364+
'mobile' as BreakpointKey,
365+
{
366+
textShadow: '0 4px 8px $shadow',
367+
__textShadowToken: '$titleShadow',
368+
},
369+
],
370+
[
371+
'pc' as BreakpointKey,
372+
{
373+
textShadow: '0 4px 8px $shadow',
374+
},
375+
],
376+
]),
377+
)
378+
379+
expect(result.textShadow).toBe('$titleShadow')
380+
expect(result.__textShadowToken).toBeUndefined()
381+
})
382+
360383
it('groups nodes by name across breakpoints', () => {
361384
const breakpointNodes = new Map<BreakpointKey, SceneNode[]>([
362385
[

0 commit comments

Comments
 (0)