@dfosco/storyboard-react 4.0.0-beta.36 → 4.0.0-beta.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/Icon.jsx +179 -0
- package/src/ViewfinderNew.jsx +1172 -0
- package/src/ViewfinderNew.module.css +1773 -0
- package/src/canvas/CanvasPage.jsx +14 -0
- package/src/canvas/widgets/LinkPreview.jsx +74 -10
- package/src/canvas/widgets/MarkdownBlock.module.css +2 -2
- package/src/canvas/widgets/PrototypeEmbed.jsx +1 -1
- package/src/canvas/widgets/StoryWidget.jsx +1 -1
- package/src/canvas/widgets/StoryWidget.module.css +3 -3
- package/src/index.js +1 -1
- package/src/vite/data-plugin.js +24 -0
- package/src/Viewfinder.jsx +0 -72
- package/src/Viewfinder.module.css +0 -235
- package/src/canvas/widgets/useSnapshotCapture.test.jsx +0 -164
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for useSnapshotCapture hook — single-capture orchestration.
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
5
|
-
import { renderHook, act } from '@testing-library/react'
|
|
6
|
-
import { useSnapshotCapture } from './useSnapshotCapture.js'
|
|
7
|
-
|
|
8
|
-
vi.mock('../canvasApi.js', () => ({
|
|
9
|
-
uploadImage: vi.fn().mockResolvedValue({ filename: 'snapshot-test-widget.webp' }),
|
|
10
|
-
}))
|
|
11
|
-
|
|
12
|
-
import { uploadImage } from '../canvasApi.js'
|
|
13
|
-
|
|
14
|
-
function createMockIframeRef(contentWindow = null) {
|
|
15
|
-
return { current: contentWindow ? { contentWindow } : null }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function createMockContentWindow() {
|
|
19
|
-
return { postMessage: vi.fn() }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('useSnapshotCapture', () => {
|
|
23
|
-
let listeners = []
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
vi.clearAllMocks()
|
|
27
|
-
listeners = []
|
|
28
|
-
const origAdd = window.addEventListener
|
|
29
|
-
const origRemove = window.removeEventListener
|
|
30
|
-
vi.spyOn(window, 'addEventListener').mockImplementation((type, fn, opts) => {
|
|
31
|
-
if (type === 'message') listeners.push(fn)
|
|
32
|
-
origAdd.call(window, type, fn, opts)
|
|
33
|
-
})
|
|
34
|
-
vi.spyOn(window, 'removeEventListener').mockImplementation((type, fn, opts) => {
|
|
35
|
-
if (type === 'message') listeners = listeners.filter(l => l !== fn)
|
|
36
|
-
origRemove.call(window, type, fn, opts)
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
afterEach(() => { vi.restoreAllMocks() })
|
|
41
|
-
|
|
42
|
-
function dispatchMessage(source, data) {
|
|
43
|
-
const event = new MessageEvent('message', { source, data })
|
|
44
|
-
listeners.forEach(fn => fn(event))
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
it('returns iframeReady=false initially', () => {
|
|
48
|
-
const iframeRef = createMockIframeRef()
|
|
49
|
-
const { result } = renderHook(() =>
|
|
50
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate: vi.fn() })
|
|
51
|
-
)
|
|
52
|
-
expect(result.current.iframeReady).toBe(false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('sets iframeReady=true on snapshot-ready message', () => {
|
|
56
|
-
const cw = createMockContentWindow()
|
|
57
|
-
const iframeRef = createMockIframeRef(cw)
|
|
58
|
-
const { result } = renderHook(() =>
|
|
59
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate: vi.fn() })
|
|
60
|
-
)
|
|
61
|
-
act(() => { dispatchMessage(cw, { type: 'storyboard:embed:snapshot-ready' }) })
|
|
62
|
-
expect(result.current.iframeReady).toBe(true)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('no-ops when onUpdate is null', async () => {
|
|
66
|
-
const cw = createMockContentWindow()
|
|
67
|
-
const iframeRef = createMockIframeRef(cw)
|
|
68
|
-
const { result } = renderHook(() =>
|
|
69
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate: null })
|
|
70
|
-
)
|
|
71
|
-
await act(async () => { await result.current.requestCapture() })
|
|
72
|
-
expect(cw.postMessage).not.toHaveBeenCalled()
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('no-ops when iframe not ready', async () => {
|
|
76
|
-
const cw = createMockContentWindow()
|
|
77
|
-
const iframeRef = createMockIframeRef(cw)
|
|
78
|
-
const { result } = renderHook(() =>
|
|
79
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate: vi.fn() })
|
|
80
|
-
)
|
|
81
|
-
await act(async () => { await result.current.requestCapture() })
|
|
82
|
-
expect(cw.postMessage).not.toHaveBeenCalled()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('sends single capture and calls onUpdate with snapshot', async () => {
|
|
86
|
-
const cw = createMockContentWindow()
|
|
87
|
-
const iframeRef = createMockIframeRef(cw)
|
|
88
|
-
const onUpdate = vi.fn()
|
|
89
|
-
const { result } = renderHook(() =>
|
|
90
|
-
useSnapshotCapture({ iframeRef, widgetId: 'test-widget', onUpdate })
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
act(() => { dispatchMessage(cw, { type: 'storyboard:embed:snapshot-ready' }) })
|
|
94
|
-
|
|
95
|
-
uploadImage.mockResolvedValueOnce({ filename: 'snapshot-test-widget.webp' })
|
|
96
|
-
|
|
97
|
-
await act(async () => {
|
|
98
|
-
const promise = result.current.requestCapture()
|
|
99
|
-
|
|
100
|
-
await new Promise(r => setTimeout(r, 10))
|
|
101
|
-
dispatchMessage(cw, { type: 'storyboard:embed:snapshot', requestId: 1, dataUrl: 'data:image/webp;base64,IMG' })
|
|
102
|
-
|
|
103
|
-
await promise
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
// Single capture, single postMessage
|
|
107
|
-
expect(cw.postMessage).toHaveBeenCalledTimes(1)
|
|
108
|
-
expect(uploadImage).toHaveBeenCalledTimes(1)
|
|
109
|
-
expect(onUpdate).toHaveBeenCalledWith(
|
|
110
|
-
expect.objectContaining({
|
|
111
|
-
snapshot: expect.stringContaining('snapshot-test-widget.webp'),
|
|
112
|
-
})
|
|
113
|
-
)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('guards against concurrent captures', async () => {
|
|
117
|
-
const cw = createMockContentWindow()
|
|
118
|
-
const iframeRef = createMockIframeRef(cw)
|
|
119
|
-
const onUpdate = vi.fn()
|
|
120
|
-
const { result } = renderHook(() =>
|
|
121
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate })
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
act(() => { dispatchMessage(cw, { type: 'storyboard:embed:snapshot-ready' }) })
|
|
125
|
-
uploadImage.mockResolvedValue({ filename: 'snapshot-w1.webp' })
|
|
126
|
-
|
|
127
|
-
await act(async () => {
|
|
128
|
-
const p1 = result.current.requestCapture()
|
|
129
|
-
// Second call while first is in-flight should no-op
|
|
130
|
-
const p2 = result.current.requestCapture()
|
|
131
|
-
|
|
132
|
-
await new Promise(r => setTimeout(r, 10))
|
|
133
|
-
dispatchMessage(cw, { type: 'storyboard:embed:snapshot', requestId: 1, dataUrl: 'data:image/webp;base64,IMG' })
|
|
134
|
-
|
|
135
|
-
await p1
|
|
136
|
-
await p2
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
expect(cw.postMessage).toHaveBeenCalledTimes(1)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('handles capture failure gracefully', async () => {
|
|
143
|
-
const cw = createMockContentWindow()
|
|
144
|
-
const iframeRef = createMockIframeRef(cw)
|
|
145
|
-
const onUpdate = vi.fn()
|
|
146
|
-
const { result } = renderHook(() =>
|
|
147
|
-
useSnapshotCapture({ iframeRef, widgetId: 'w1', onUpdate })
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
act(() => { dispatchMessage(cw, { type: 'storyboard:embed:snapshot-ready' }) })
|
|
151
|
-
uploadImage.mockRejectedValueOnce(new Error('upload failed'))
|
|
152
|
-
|
|
153
|
-
await act(async () => {
|
|
154
|
-
const promise = result.current.requestCapture()
|
|
155
|
-
|
|
156
|
-
await new Promise(r => setTimeout(r, 10))
|
|
157
|
-
dispatchMessage(cw, { type: 'storyboard:embed:snapshot', requestId: 1, dataUrl: 'data:image/webp;base64,FAIL' })
|
|
158
|
-
|
|
159
|
-
await promise
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
expect(onUpdate).not.toHaveBeenCalled()
|
|
163
|
-
})
|
|
164
|
-
})
|