Skip to content

Commit 91ad2ad

Browse files
committed
Fix side effects and Storybook act() wrap warnings
Signed-off-by: handreyrc <handrey.cunha@gmail.com>
1 parent d9178e8 commit 91ad2ad

5 files changed

Lines changed: 50 additions & 11 deletions

File tree

packages/open-workflow-diagram-editor/.storybook/preview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const preview: Preview = {
2828
color: /(background|color)$/i,
2929
date: /Date$/i,
3030
},
31+
disableSaveFromUI: true, // Disable modifiy story popup. Stories mustn't be editable from Storybook UI.
3132
},
3233

3334
backgrounds: {

packages/open-workflow-diagram-editor/src/core/workflowSdk.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,10 @@ export function serializeWorkflow(
295295
model: sdk.Specification.Workflow,
296296
format: ContentFormat,
297297
): string {
298-
const workflow = model instanceof sdk.Classes.Workflow ? model : new sdk.Classes.Workflow(model);
299-
if (format === "json") return workflow.serialize("json");
300-
// SDK bug (v1.0.3-alpha6): instance.serialize("yaml") fails because normalize()
301-
// returns a Workflow class instance and js-yaml rejects non-plain objects.
302-
// Workaround: serialize to JSON first to get a plain object, then dump as YAML.
303-
// TODO: Remove this workaround once the SDK is fixed.
304-
return dump(JSON.parse(workflow.serialize("json")));
298+
// The SDK validates the model before serializing it and it may cause validation exceptions
299+
// Even if we have a model with validation errors we want it to be serialized
300+
const json = JSON.stringify(model);
301+
if (format === "json") return json;
302+
// dump only works with plain objects.
303+
return dump(JSON.parse(json));
305304
}

packages/open-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ export const Diagram = ({ divRef, colorMode = "light" }: DiagramProps) => {
124124

125125
// Debounce layout calculation to avoid excessive CPU usage on rapid changes.
126126
const debounceTimeoutId = setTimeout(() => {
127+
// Clear any previous layout error when starting a new layout cycle
128+
// so the editor can recover if the new layout succeeds.
129+
setLayoutError(null);
127130
abortController = new AbortController();
128131

129132
const graph = buildDiagramElements(model, errors);
@@ -186,7 +189,7 @@ export const Diagram = ({ divRef, colorMode = "light" }: DiagramProps) => {
186189
}
187190
setLayoutError(error instanceof Error ? error : new Error(String(error)));
188191
});
189-
}, 100);
192+
}, 300);
190193

191194
return () => {
192195
isActive = false;

packages/open-workflow-diagram-editor/src/react-flow/hooks/useWorkflowHistory.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,11 @@ export function useWorkflowHistory(isReadOnly: boolean): UseWorkflowHistoryRetur
123123
// No-op if model content is unchanged.
124124
if (structuralEqual(present.model, newModel)) return;
125125

126-
// Content changed externally (e.g. props.content updated by host).
127-
// Push new snapshot — future is discarded by reducer.
128-
push({ model: newModel, viewport, selectedNodeId });
126+
// Content changed externally (e.g. props.content updated by host or addon panel).
127+
// Preserve the current viewport so undo restores to where the user was looking,
128+
// rather than the placeholder {x:0,y:0,zoom:1} passed by the caller.
129+
// The real viewport will be updated by submitModel after layout settles.
130+
push({ model: newModel, viewport: present.viewport, selectedNodeId });
129131
},
130132
[push, setPresent],
131133
);

packages/open-workflow-diagram-editor/vitest.config.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,40 @@ export default defineConfig({
3232
globals: true,
3333
environment: "jsdom",
3434
setupFiles: ["./tests/setupTests.ts"],
35+
// Suppress React's "not wrapped in act()" warnings emitted as stderr during
36+
// Storybook (Chromium) tests. These all originate from @xyflow/react internal
37+
// components, not from our own code. They cannot be fixed here because:
38+
//
39+
// • BatchProvider / FlowRenderer / GraphView — React Flow's root store
40+
// provider uses a Zustand-backed context whose subscriptions fire outside
41+
// of React's event-loop batching. The store dispatches are inside React
42+
// Flow's own source and are not exposed for us to wrap.
43+
//
44+
// • NodeWrapper / NodeRenderer — React Flow wraps every node in an internal
45+
// component that attaches a ResizeObserver. ResizeObserver callbacks always
46+
// run outside React's scheduler; the resulting setState is therefore always
47+
// flagged by the act() warning. This is a known React Flow limitation.
48+
//
49+
// • EdgeWrapper / EdgeRenderer / ConnectionLineWrapper — same pattern as
50+
// NodeWrapper: React Flow drives edge visibility and z-index updates via
51+
// internal effects that fire from ResizeObserver and IntersectionObserver
52+
// callbacks, both of which run asynchronously outside act().
53+
//
54+
// • MarkerDefinitions — React Flow maintains an internal SVG <defs> registry
55+
// for arrowhead markers. It updates that registry in a useLayoutEffect that
56+
// re-runs whenever edges change, which cascades a second setState call outside
57+
// the act() boundary that triggered the initial render.
58+
//
59+
// • ForwardRef (ReactFlowProvider) / Controls / Background — React Flow's
60+
// viewport-tracking hooks (useResizeObserver, useViewport) attach native
61+
// DOM event listeners and ResizeObservers at mount time. Their first-paint
62+
// state updates all land outside act() for the same reason as NodeWrapper.
63+
//
64+
// The filter matches only the exact React warning phrase so any act() warning
65+
// from our own application code will still surface.
66+
onConsoleLog(log) {
67+
if (log.includes("not wrapped in act")) return false;
68+
},
3569
projects: [
3670
{
3771
extends: true,

0 commit comments

Comments
 (0)