@dfosco/storyboard-react 3.11.0-beta.11 → 3.11.0-beta.12
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
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard-react",
|
|
3
|
-
"version": "3.11.0-beta.
|
|
3
|
+
"version": "3.11.0-beta.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@dfosco/storyboard-core": "3.11.0-beta.
|
|
7
|
-
"@dfosco/tiny-canvas": "3.11.0-beta.
|
|
6
|
+
"@dfosco/storyboard-core": "3.11.0-beta.12",
|
|
7
|
+
"@dfosco/tiny-canvas": "3.11.0-beta.12",
|
|
8
8
|
"@neodrag/react": "^2.3.1",
|
|
9
9
|
"glob": "^11.0.0",
|
|
10
10
|
"jsonc-parser": "^3.3.1"
|
package/src/context.jsx
CHANGED
|
@@ -22,6 +22,11 @@ function matchCanvasRoute(pathname) {
|
|
|
22
22
|
return canvasRouteMap.get(normalized) || null
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function isCanvasPath(pathname) {
|
|
26
|
+
const normalized = pathname.replace(/\/+$/, '') || '/'
|
|
27
|
+
return normalized === '/canvas' || normalized.startsWith('/canvas/')
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
/**
|
|
26
31
|
* Derives the top-level prototype name from a pathname.
|
|
27
32
|
* "/Dashboard" → "Dashboard", "/Dashboard/sub" → "Dashboard"
|
|
@@ -62,6 +67,10 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
62
67
|
|
|
63
68
|
// Canvas route detection — matches current URL against registered canvas routes
|
|
64
69
|
const canvasName = useMemo(() => matchCanvasRoute(location.pathname), [location.pathname])
|
|
70
|
+
const isMissingCanvasRoute = useMemo(
|
|
71
|
+
() => isCanvasPath(location.pathname) && !canvasName,
|
|
72
|
+
[location.pathname, canvasName],
|
|
73
|
+
)
|
|
65
74
|
|
|
66
75
|
const searchParams = new URLSearchParams(location.search)
|
|
67
76
|
const sceneParam = searchParams.get('flow') || searchParams.get('scene')
|
|
@@ -70,7 +79,7 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
70
79
|
|
|
71
80
|
// Resolve flow name with prototype scoping (skip for canvas pages)
|
|
72
81
|
const activeFlowName = useMemo(() => {
|
|
73
|
-
if (canvasName) return null
|
|
82
|
+
if (canvasName || isMissingCanvasRoute) return null
|
|
74
83
|
const requested = sceneParam || flowName || sceneName
|
|
75
84
|
if (requested) {
|
|
76
85
|
// Allow fully-scoped flow names from URLs/widgets without re-prefixing
|
|
@@ -94,7 +103,7 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
94
103
|
// 4. Global default — or null if no flow exists at all
|
|
95
104
|
if (flowExists('default')) return 'default'
|
|
96
105
|
return null
|
|
97
|
-
}, [canvasName, sceneParam, flowName, sceneName, prototypeName, pageFlow])
|
|
106
|
+
}, [canvasName, isMissingCanvasRoute, sceneParam, flowName, sceneName, prototypeName, pageFlow])
|
|
98
107
|
|
|
99
108
|
// Auto-install body class sync (sb-key--value classes on <body>)
|
|
100
109
|
useEffect(() => installBodyClassSync(), [])
|
|
@@ -117,7 +126,7 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
117
126
|
|
|
118
127
|
// Skip flow loading for canvas pages and flow-less pages
|
|
119
128
|
const { data, error } = useMemo(() => {
|
|
120
|
-
if (canvasName) return { data: null, error: null }
|
|
129
|
+
if (canvasName || isMissingCanvasRoute) return { data: null, error: null }
|
|
121
130
|
if (!activeFlowName) return { data: {}, error: null }
|
|
122
131
|
try {
|
|
123
132
|
let flowData = loadFlow(activeFlowName)
|
|
@@ -136,7 +145,7 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
136
145
|
} catch (err) {
|
|
137
146
|
return { data: null, error: err.message }
|
|
138
147
|
}
|
|
139
|
-
}, [canvasName, activeFlowName, recordName, recordParam, params, prototypeName])
|
|
148
|
+
}, [canvasName, isMissingCanvasRoute, activeFlowName, recordName, recordParam, params, prototypeName])
|
|
140
149
|
|
|
141
150
|
// Canvas pages get their own rendering path — no flow data needed
|
|
142
151
|
if (canvasName) {
|
|
@@ -157,6 +166,27 @@ export default function StoryboardProvider({ flowName, sceneName, recordName, re
|
|
|
157
166
|
)
|
|
158
167
|
}
|
|
159
168
|
|
|
169
|
+
if (isMissingCanvasRoute) {
|
|
170
|
+
const currentUrl = `${location.pathname}${location.search}`
|
|
171
|
+
const truncatedUrl = currentUrl.length > 60
|
|
172
|
+
? currentUrl.slice(0, 60) + '…'
|
|
173
|
+
: currentUrl
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<main className={styles.container}>
|
|
177
|
+
<div className={styles.banner}>
|
|
178
|
+
<strong>Canvas not found</strong>
|
|
179
|
+
No canvas matches this route.
|
|
180
|
+
</div>
|
|
181
|
+
<p className={styles.meta}>
|
|
182
|
+
Tried to open{' '}
|
|
183
|
+
<a href={currentUrl} title={currentUrl}>{truncatedUrl}</a>
|
|
184
|
+
</p>
|
|
185
|
+
<a className={styles.homeLink} href="/">← Go to index page</a>
|
|
186
|
+
</main>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
160
190
|
const value = {
|
|
161
191
|
data,
|
|
162
192
|
error,
|
package/src/context.test.jsx
CHANGED
|
@@ -280,4 +280,17 @@ describe('StoryboardProvider', () => {
|
|
|
280
280
|
)
|
|
281
281
|
expect(screen.getByTestId('ctx')).toHaveTextContent('Global Default')
|
|
282
282
|
})
|
|
283
|
+
|
|
284
|
+
it('shows a simple 404 for unknown canvas routes with an index link', () => {
|
|
285
|
+
mockUseLocation.mockReturnValue({ pathname: '/canvas/unknown-board', search: '', hash: '' })
|
|
286
|
+
|
|
287
|
+
render(
|
|
288
|
+
<StoryboardProvider>
|
|
289
|
+
<ContextReader />
|
|
290
|
+
</StoryboardProvider>,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
expect(screen.getByText('Canvas not found')).toBeInTheDocument()
|
|
294
|
+
expect(screen.getByRole('link', { name: /go to index page/i })).toHaveAttribute('href', '/')
|
|
295
|
+
})
|
|
283
296
|
})
|