@dataverse-kit/form-runtime
v0.1.0
Published
Shared React runtime for Dynamics 365 form rendering. Mounted by the form-builder designer canvas, preview, and generated host code so all three render identically. v8 default entry, v9 sibling at /v9, FluentUI-agnostic hooks at /hooks. Consumes @datavers
Maintainers
Readme
@dataverse-kit/form-runtime
Shared React runtime for Dynamics 365 form rendering. Mounted by the form-builder designer canvas, the live preview, and code generated for export targets (Web Resource, PCF, Static Web App, Standalone Grid Customizer PCF, PA Grid Control Customizer). All three paths render the same React tree, so what you see in the canvas is what users ship.
Entries
| Entry | Purpose |
|-------|---------|
| @dataverse-kit/form-runtime | v8 default — <FormRuntime> rendered with Fluent UI v8 (@fluentui/react). The designer canvas always mounts this. |
| @dataverse-kit/form-runtime/v9 | v9 sibling — <FormRuntime> rendered with Fluent UI v9 (@fluentui/react-components). Used by generated v9-target projects. Canvas does not mount this (ROADMAP-locked). |
| @dataverse-kit/form-runtime/hooks | FluentUI-agnostic hooks: useSubgridData, useChartData, useLookupSearch, useFormState, useDirtyTracker. |
| @dataverse-kit/form-runtime/utils | gridLayoutUtils — shared by designer/preview/generator for dashboard-layout math. |
| @dataverse-kit/form-runtime/theme | FormThemeProvider + useFormTheme. |
Dependencies
Every consumed @dataverse-kit/runtime and @dataverse-kit/api-service
import flows through this package directly — no re-exports, no wrappers.
Per the dynamics-toolkit v1.0 migration preference (full eradication of
duplicated logic, not wrapper delegation).
| Peer dep | Why |
|----------|-----|
| react (^16.14 || ^17 || ^18) | Canvas PCF compatibility caps React at 16.14. |
| react-dom | Same. |
| @fluentui/react (^8.115) | v8 default entry. |
| @fluentui/react-components (^9) | v9 entry (optional). |
| @dataverse-kit/runtime (^1) | FetchXmlParser, ConditionEvaluator, DirtyTracker. |
| @dataverse-kit/api-service (^0.2) | IApiService, ServiceFactory. |
Status
WYSIWYG-canvas refactor complete (2026-05-19). Every editor
surface — main canvas, callout editor, and live preview — and every
generator-emitted host renders through <FormRuntime>. The legacy
parallel rendering pipeline (ControlRenderer.tsx, SectionBlock,
CellBlock, CalloutPreview, MainFormPreview etc.) is fully
retired. ~6,000 LOC of duplicate rendering code deleted across the
refactor; ~3,000 LOC of feature-parity runtime + tests added.
Phase summary
| Phase | Outcome |
|-------|---------|
| Phase 1 | Extract this package (@dataverse-kit/form-runtime). <FormRuntime> dispatcher, 22 per-type v8 controls, capability boundary (FormRuntimeContext), instrumentation attrs, live-data hooks. |
| Phase 2 (m1–m9) | Designer overlay layer (selection, drop zones, drag handles, inline rename, badges, context menu, affordances, empty state, column drops). m9 flipped the runtime canvas + overlay default-on. |
| Legacy canvas deletion | 7 dead editor-side files removed (TabBar, HeaderBlock, CommandBarBlock, DialogHeaderBlock, DialogButtonsBlock, PanelButtonsBlock, GridFormCanvas) — ~1,700 LOC. |
| Phase 3 (m1–m6) | Runtime gained parity for the legacy preview's features: SectionShell variant/dims/background/collapsible/labels, per-control callouts (CalloutAnchor/CalloutShell/CalloutsProvider), dialog/panel FooterShell, business-rule integration (RuleStatesProvider), header field summary flyout, tab-content slots (TabContentSlotsProvider). m6 deleted the legacy MainFormPreview/DialogFormPreview/PanelFormPreview/CalloutFormPreview/GridFormPreview paths and the runtimeCanvasFlag gate. |
| Phase 4 | Callout editor moved to runtime (CalloutEditCanvas mounts SectionShell for section bodies + CalloutShell for the preview popup). Cascade deletion of ControlRenderer.tsx (2,971 LOC), SectionBlock.tsx, CellBlock.tsx, CalloutPreview.tsx — ~4,100 LOC. |
What this guarantees
- Single source of truth for rendering. Every form / section / cell / control render path runs through the shells in this package. No parity-drift risk between editor canvas, preview, and generated output.
- Designer overlay is feature-complete for the runtime canvas
(Phase 2 m1–m8 features all active). Pivot section drops on the
overlay remain deferred — they need a
SectionShellpivot-grouping pass before they can wire in. Editor-side callout pivot grouping works via the form-builder'sDraggablePivotBar. - Tests: 405 in form-runtime, 352 in form-builder, 1041 in export-engine — all green at the close of Phase 4.
Known deferred items (not blocking)
- Pivot section drops on the runtime overlay (waits on
SectionShellpivot-grouping support). - Subgrid focused-view / card-list / grid-customizer variants stay editor-inline pending follow-up.
- Live Dataverse entity-image fetch +
PersonaPresencefor systemuser lookups in the header flyout — would need aFormRuntimeCapabilitiesextension. Initials render fine without it. - The four hooks declared but not extracted in Phase 1
(
useFormState,useSubgridData,useChartData,useLookupSearch,useDirtyTracker) — the existing editor versions are still in use. - Package.json
exportsmap for/v8+/types/*subpaths (works today via Vite alias; matters for the published-package consumer).
Build
npm run typecheck # tsc --noEmit + tests config
npm run build # tsup → dist/{index,v9,hooks,utils,theme}.{cjs,mjs,d.ts}
npm run test # vitest run (jsdom environment)