@dfosco/storyboard-react 2.0.0 → 2.1.0
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 +2 -2
- package/src/Viewfinder.jsx +46 -215
- package/src/context.jsx +71 -19
- package/src/context.test.jsx +97 -14
- package/src/hooks/useRecord.js +16 -8
- package/src/hooks/useScene.js +19 -10
- package/src/hooks/useScene.test.js +40 -13
- package/src/hooks/useSceneData.js +17 -11
- package/src/hooks/useSceneData.test.js +60 -32
- package/src/index.js +3 -1
- package/src/test-utils.js +8 -5
- package/src/vite/data-plugin.js +136 -18
- package/src/vite/data-plugin.test.js +135 -2
|
@@ -69,10 +69,13 @@ describe('storyboardDataPlugin', () => {
|
|
|
69
69
|
const code = plugin.load(RESOLVED_ID)
|
|
70
70
|
|
|
71
71
|
expect(code).toContain("import { init } from '@dfosco/storyboard-core'")
|
|
72
|
-
expect(code).toContain('init({
|
|
72
|
+
expect(code).toContain('init({ flows, objects, records, prototypes })')
|
|
73
73
|
expect(code).toContain('"Test"')
|
|
74
74
|
expect(code).toContain('"Jane"')
|
|
75
75
|
expect(code).toContain('"First"')
|
|
76
|
+
// Backward-compat alias
|
|
77
|
+
expect(code).toContain('const scenes = flows')
|
|
78
|
+
expect(code).toContain('export { flows, scenes, objects, records, prototypes }')
|
|
76
79
|
})
|
|
77
80
|
|
|
78
81
|
it('load returns null for other IDs', () => {
|
|
@@ -93,7 +96,24 @@ describe('storyboardDataPlugin', () => {
|
|
|
93
96
|
)
|
|
94
97
|
|
|
95
98
|
const plugin = createPlugin()
|
|
96
|
-
expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate
|
|
99
|
+
expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate flow "dup"/)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('duplicate objects show globally-scoped hint', () => {
|
|
103
|
+
mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
|
|
104
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
|
|
105
|
+
writeFileSync(
|
|
106
|
+
path.join(tmpDir, 'src', 'data', 'user.object.json'),
|
|
107
|
+
JSON.stringify({ name: 'Global' }),
|
|
108
|
+
)
|
|
109
|
+
writeFileSync(
|
|
110
|
+
path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'user.object.json'),
|
|
111
|
+
JSON.stringify({ name: 'Local' }),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const plugin = createPlugin()
|
|
115
|
+
expect(() => plugin.load(RESOLVED_ID)).toThrow(/Duplicate object "user"/)
|
|
116
|
+
expect(() => plugin.load(RESOLVED_ID)).toThrow(/globally scoped/)
|
|
97
117
|
})
|
|
98
118
|
|
|
99
119
|
it('handles JSONC files (with comments)', () => {
|
|
@@ -107,6 +127,19 @@ describe('storyboardDataPlugin', () => {
|
|
|
107
127
|
expect(code).toContain('"JSONC Scene"')
|
|
108
128
|
})
|
|
109
129
|
|
|
130
|
+
it('normalizes .scene files into flow category in the index', () => {
|
|
131
|
+
writeFileSync(
|
|
132
|
+
path.join(tmpDir, 'legacy.scene.json'),
|
|
133
|
+
JSON.stringify({ title: 'Legacy Scene' }),
|
|
134
|
+
)
|
|
135
|
+
const plugin = createPlugin()
|
|
136
|
+
const code = plugin.load(RESOLVED_ID)
|
|
137
|
+
|
|
138
|
+
// .scene.json files should be normalized to the flows category
|
|
139
|
+
expect(code).toContain('"Legacy Scene"')
|
|
140
|
+
expect(code).toContain('init({ flows, objects, records, prototypes })')
|
|
141
|
+
})
|
|
142
|
+
|
|
110
143
|
it('buildStart resets the index cache', () => {
|
|
111
144
|
writeDataFiles(tmpDir)
|
|
112
145
|
const plugin = createPlugin()
|
|
@@ -131,3 +164,103 @@ describe('storyboardDataPlugin', () => {
|
|
|
131
164
|
expect(code3).toContain('"Extra"')
|
|
132
165
|
})
|
|
133
166
|
})
|
|
167
|
+
|
|
168
|
+
describe('prototype scoping', () => {
|
|
169
|
+
it('prefixes flows inside src/prototypes/{Name}/ with the prototype name', () => {
|
|
170
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
|
|
171
|
+
writeFileSync(
|
|
172
|
+
path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'default.flow.json'),
|
|
173
|
+
JSON.stringify({ title: 'Dashboard Default' }),
|
|
174
|
+
)
|
|
175
|
+
writeFileSync(
|
|
176
|
+
path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'signup.flow.json'),
|
|
177
|
+
JSON.stringify({ title: 'Dashboard Signup' }),
|
|
178
|
+
)
|
|
179
|
+
// Global flow in src/data/
|
|
180
|
+
mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
|
|
181
|
+
writeFileSync(
|
|
182
|
+
path.join(tmpDir, 'src', 'data', 'default.flow.json'),
|
|
183
|
+
JSON.stringify({ title: 'Global Default' }),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
const plugin = createPlugin()
|
|
187
|
+
const code = plugin.load(RESOLVED_ID)
|
|
188
|
+
|
|
189
|
+
expect(code).toContain('"Dashboard/default"')
|
|
190
|
+
expect(code).toContain('"Dashboard/signup"')
|
|
191
|
+
expect(code).toContain('"default"')
|
|
192
|
+
expect(code).toContain('"Dashboard Default"')
|
|
193
|
+
expect(code).toContain('"Global Default"')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('prefixes records inside src/prototypes/{Name}/ with the prototype name', () => {
|
|
197
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Blog'), { recursive: true })
|
|
198
|
+
writeFileSync(
|
|
199
|
+
path.join(tmpDir, 'src', 'prototypes', 'Blog', 'posts.record.json'),
|
|
200
|
+
JSON.stringify([{ id: '1', title: 'Scoped Post' }]),
|
|
201
|
+
)
|
|
202
|
+
// Global record
|
|
203
|
+
mkdirSync(path.join(tmpDir, 'src', 'data'), { recursive: true })
|
|
204
|
+
writeFileSync(
|
|
205
|
+
path.join(tmpDir, 'src', 'data', 'posts.record.json'),
|
|
206
|
+
JSON.stringify([{ id: '1', title: 'Global Post' }]),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
const plugin = createPlugin()
|
|
210
|
+
const code = plugin.load(RESOLVED_ID)
|
|
211
|
+
|
|
212
|
+
expect(code).toContain('"Blog/posts"')
|
|
213
|
+
expect(code).toContain('"posts"')
|
|
214
|
+
expect(code).toContain('"Scoped Post"')
|
|
215
|
+
expect(code).toContain('"Global Post"')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('does NOT prefix objects inside src/prototypes/{Name}/', () => {
|
|
219
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Dashboard'), { recursive: true })
|
|
220
|
+
writeFileSync(
|
|
221
|
+
path.join(tmpDir, 'src', 'prototypes', 'Dashboard', 'helpers.object.json'),
|
|
222
|
+
JSON.stringify({ util: true }),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
const plugin = createPlugin()
|
|
226
|
+
const code = plugin.load(RESOLVED_ID)
|
|
227
|
+
|
|
228
|
+
// Object should be plain "helpers", NOT "Dashboard/helpers"
|
|
229
|
+
expect(code).toContain('"helpers"')
|
|
230
|
+
expect(code).not.toContain('"Dashboard/helpers"')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('allows same flow name in different prototypes without clash', () => {
|
|
234
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'A'), { recursive: true })
|
|
235
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'B'), { recursive: true })
|
|
236
|
+
writeFileSync(
|
|
237
|
+
path.join(tmpDir, 'src', 'prototypes', 'A', 'default.flow.json'),
|
|
238
|
+
JSON.stringify({ from: 'A' }),
|
|
239
|
+
)
|
|
240
|
+
writeFileSync(
|
|
241
|
+
path.join(tmpDir, 'src', 'prototypes', 'B', 'default.flow.json'),
|
|
242
|
+
JSON.stringify({ from: 'B' }),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
const plugin = createPlugin()
|
|
246
|
+
// Should not throw (no duplicate)
|
|
247
|
+
const code = plugin.load(RESOLVED_ID)
|
|
248
|
+
expect(code).toContain('"A/default"')
|
|
249
|
+
expect(code).toContain('"B/default"')
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('normalizes .scene.json inside prototypes to scoped flow', () => {
|
|
253
|
+
mkdirSync(path.join(tmpDir, 'src', 'prototypes', 'Legacy'), { recursive: true })
|
|
254
|
+
writeFileSync(
|
|
255
|
+
path.join(tmpDir, 'src', 'prototypes', 'Legacy', 'old.scene.json'),
|
|
256
|
+
JSON.stringify({ compat: true }),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
const plugin = createPlugin()
|
|
260
|
+
const code = plugin.load(RESOLVED_ID)
|
|
261
|
+
|
|
262
|
+
// Should be indexed as a scoped flow, not a scene
|
|
263
|
+
expect(code).toContain('"Legacy/old"')
|
|
264
|
+
expect(code).toContain('flows')
|
|
265
|
+
})
|
|
266
|
+
})
|