Problem/Motivation

The modeler API provides export functions for recipes and archives. They are currently not available in the UI but should be. In addition, we also want an export as JSON or as SVG.

In addition, we want the modeler to be usable standalone to view the JSON format without having to have a Drupal backend available. That can then be used in the ECA Guide. For that, there should be an option to also include replay data in the export.

Steps to reproduce

Proposed resolution

There should be a single export icon in the toolbar. This should first check if there are unsaved changes and ask the user to save first or cancel.

The confirmation dialog for the export should then ask for the export format:

  • Recipe: this should leverage the export from Modeler API
  • Archive: this should leverage the export from Modeler API
  • JSON: this one should further ask if available replay data should be exported as well, but only if replay data is available. In addition, it should include a list of required Drupal modules which can be derived from the provider property of all the contained nodes and conditions
  • SVG: this one should export as visible, i.e. respect annotation status and order indicators

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork modeler-3574470

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jurgenhaas created an issue. See original summary.

jurgenhaas’s picture

Issue summary: View changes

jurgenhaas’s picture

Status: Active » Fixed

Export, Standalone Viewer & View Modes

This branch adds three major features to the ECA Modeler: a model export system, a standalone read-only viewer for embedding in any web page, and a view mode system (fullscreen / restored / minimized).

1. Export Feature

Four export formats are available from a new toolbar button (download icon):

Format Type Output
Recipe Backend Opens the Drupal recipe export form in a new tab
Archive Backend Downloads a .tar.gz via CSRF-authenticated fetch
JSON Client-side Full model data including nodes, edges, metadata, config forms, component registry, and optionally replay data
SVG Client-side Native SVG rendering of the canvas (no foreignObject — works in Inkscape and system image viewers)

Recipe and Archive require backend URLs (export_url, export_recipe_url) injected by modeler_api/src/Api.php. JSON and SVG are always available when the model has at least one node.

The export dialog is a modal with radio-button format selection, full keyboard accessibility (focus trapping, Escape to close), and WCAG AA compliance. If there are unsaved changes, a save-first confirmation dialog appears automatically before opening the export dialog.

Key files: useExport.ts (hook, ~880 lines), ExportDialog.tsx (component), Flow.tsx (save-then-export integration with pendingExportAfterSaveRef).

2. Standalone Viewer

A self-contained viewer that displays exported JSON models without a Drupal backend. It can be embedded in any HTML page.

<div id="eca-viewer" style="height: 600px;"></div>
<script src="modeler-viewer.bundle.js"></script>
<script>
  EcaModelerViewer.init('#eca-viewer', {
    modelUrl: 'my-workflow.json'
  });
</script>

Supports both URL-based loading (modelUrl) and inline model data (model). Returns a Promise<{ destroy() }> for lifecycle management.

The standalone entry point (src/standalone.tsx) synthesises a Settings object with standalone: true. This flag propagates through the existing component tree:

  • Flow.tsx forces read-only mode and skips the unsaved-changes dialog
  • Toolbar.tsx hides Close, Minimize, and Save buttons
  • useConfigurationLoader returns pre-baked config forms from the exported JSON instead of fetching from the backend
  • The property panel, replay panel, and search all work normally in read-only mode

Build produces an IIFE bundle (dist/modeler-viewer.bundle.js + .css) via a --standalone flag on the existing esbuild pipeline. New npm scripts: build:standalone and build:standalone:production.

Key files: src/standalone.tsx (entry point), build.sh (standalone build target), standalone.html (example embedding page).

3. View Modes

Three view modes managed by the new useViewMode hook:

Mode Drupal (regular) Standalone
Fullscreen (default for Drupal) position: fixed, covers entire viewport Same — takes over the viewport
Restored (default for standalone) Floating window: draggable via toolbar, resizable via corner handle, 80% viewport centred, min 480×360 Fills parent container, no drag/resize
Minimized Existing behaviour: hides wrapper, shows restore bar N/A — button hidden

The toolbar shows a fullscreen/restore toggle button. Double-clicking the toolbar title area also toggles. In Drupal restored mode, the toolbar centre acts as a drag handle (cursor: grab); in standalone restored mode, this cursor is suppressed since the window is not draggable.

The Drupal restored-mode window position and size are persisted to localStorage and restored on next use, clamped to the current viewport to prevent off-screen placement after browser resize.

Key files: useViewMode.ts (hook, ~250 lines), modeler.css (restored/fullscreen/standalone CSS), Flow.tsx (dynamic className, resize handle), Toolbar.tsx (toggle button, drag handle).

4. Edge Order Indicators in Read-Only Mode

Edge order number badges are now always visible when the "show edge order numbers" option is enabled, including in standalone, read-only, and locked modes. However, drag-to-reorder is disabled: the badges are rendered with draggable="false" and all drag/drop handlers are guarded. This is achieved by passing a globalLocked flag from FlowCanvas through the edge data, separate from the per-edge isLocked flag.

5. Flaky E2E Focus-Trap Test Fix

The export dialog's focus-trap E2E test was intermittently failing because Tab presses fired before the useFocusTrap hook registered its keyboard handler. Applied the same toPass() polling pattern used in the accessibility spec's settings-modal test.

Quality Gates

  • ESLint: 0 errors, 0 warnings
  • TypeScript: 0 errors (strict mode)
  • Unit tests: 90 suites, 2605 tests — all passing
  • E2E tests: 18 export tests — all passing
  • Storybook a11y: 33 suites, 203 tests, 0 violations

Files Changed

32 files, +4860 / −41 lines across production code, tests, stories, documentation, and build configuration.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.