Skip to content

Commit 752c21c

Browse files
authored
fix(landing): reset unmeasurable preview stages (#7153)
1 parent 27b97cb commit 752c21c

2 files changed

Lines changed: 97 additions & 3 deletions

File tree

apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.test.ts

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,63 @@
1-
import { describe, expect, it } from 'vitest'
2-
import { calculateFitScale } from '@/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage'
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
import { act, createElement } from 'react'
5+
import { createRoot, type Root } from 'react-dom/client'
6+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
7+
import {
8+
calculateFitScale,
9+
ResponsiveDesignStage,
10+
} from '@/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage'
11+
12+
let resizeObserver: ResizeObserverMock | null = null
13+
14+
class ResizeObserverMock implements ResizeObserver {
15+
private readonly callback: ResizeObserverCallback
16+
private target: Element | null = null
17+
18+
constructor(callback: ResizeObserverCallback) {
19+
this.callback = callback
20+
resizeObserver = this
21+
}
22+
23+
observe(target: Element) {
24+
this.target = target
25+
}
26+
27+
unobserve() {
28+
this.target = null
29+
}
30+
31+
disconnect() {
32+
this.target = null
33+
}
34+
35+
deliver(width: number, height: number) {
36+
if (!this.target) throw new Error('ResizeObserver has no observed target')
37+
this.callback(
38+
[{ target: this.target, contentRect: { width, height } } as ResizeObserverEntry],
39+
this
40+
)
41+
}
42+
}
43+
44+
let container: HTMLDivElement
45+
let root: Root
46+
47+
beforeEach(() => {
48+
resizeObserver = null
49+
vi.stubGlobal('CSS', { supports: vi.fn(() => true) })
50+
vi.stubGlobal('ResizeObserver', ResizeObserverMock)
51+
container = document.createElement('div')
52+
document.body.appendChild(container)
53+
root = createRoot(container)
54+
})
55+
56+
afterEach(() => {
57+
act(() => root.unmount())
58+
container.remove()
59+
vi.unstubAllGlobals()
60+
})
361

462
describe('calculateFitScale', () => {
563
it('fits the design surface to the limiting host dimension', () => {
@@ -54,3 +112,34 @@ describe('calculateFitScale', () => {
54112
).toBe(0)
55113
})
56114
})
115+
116+
describe('ResponsiveDesignStage', () => {
117+
it('hides an already visible surface until a measurable size returns', () => {
118+
act(() => {
119+
root.render(
120+
createElement(
121+
ResponsiveDesignStage,
122+
{ width: 1000, height: 500 },
123+
createElement('span', null, 'Preview')
124+
)
125+
)
126+
})
127+
128+
const surface = container.firstElementChild?.firstElementChild
129+
if (!(surface instanceof HTMLElement) || !resizeObserver) {
130+
throw new Error('responsive stage did not mount')
131+
}
132+
const observer = resizeObserver
133+
134+
act(() => observer.deliver(500, 250))
135+
expect(surface.style.opacity).toBe('1')
136+
expect(surface.style.zoom).toBe('0.5')
137+
138+
act(() => observer.deliver(0, 250))
139+
expect(surface.style.opacity).toBe('0')
140+
141+
act(() => observer.deliver(500, 250))
142+
expect(surface.style.opacity).toBe('1')
143+
expect(surface.style.zoom).toBe('0.5')
144+
})
145+
})

apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ export function ResponsiveDesignStage({
9191
inset,
9292
maxScale,
9393
})
94-
if (scale === 0 || Math.abs(scale - appliedScale) < SCALE_EPSILON) return
94+
if (scale === 0) {
95+
surface.style.opacity = '0'
96+
appliedScale = -1
97+
return
98+
}
99+
if (Math.abs(scale - appliedScale) < SCALE_EPSILON) return
95100

96101
if (supportsZoom) {
97102
surface.style.zoom = String(scale)

0 commit comments

Comments
 (0)