feat(FN-3079): add plugin dashboard views and dependency graph plugin

- Add plugin dashboard view registration and hosting across core, dashboard routes, and plugin SDK exports
- Integrate plugin-provided views into app navigation, mobile/header UI, and view state hooks with coverage
- Add fusion-plugin-dependency-graph example plugin with persisted storage, dashboard view UI, and manifest wiring
- Update plugin authoring and architecture docs for dashboard view extension points
- Add a changeset for @runfusion/fusion covering plugin dashboard view support

Fusion-Task-Id: FN-3079
This commit is contained in:
Fusion
2026-05-01 23:32:32 -07:00
committed by gsxdsm
parent a284138167
commit 0597b34eea
38 changed files with 1264 additions and 16 deletions

View File

@@ -1288,6 +1288,42 @@ describe("PluginLoader", () => {
});
});
describe("getPluginDashboardViews", () => {
it("returns empty array when no plugins loaded", async () => {
await pluginStore.init();
const loader = new PluginLoader({ pluginStore, taskStore: mockTaskStore });
expect(loader.getPluginDashboardViews()).toEqual([]);
});
it("aggregates dashboard views from multiple plugins and keeps uiSlots separate", async () => {
await pluginStore.init();
const loader = new PluginLoader({ pluginStore, taskStore: mockTaskStore });
(loader as any).plugins.set("views-a", {
manifest: makeManifest({ id: "views-a" }),
state: "started",
hooks: {},
uiSlots: [{ slotId: "task-detail-tab", label: "Tab", componentPath: "./tab.js" }],
dashboardViews: [{ viewId: "graph", label: "Graph", componentPath: "./graph.js", placement: "more" }],
} as FusionPlugin);
(loader as any).plugins.set("views-b", {
manifest: makeManifest({ id: "views-b" }),
state: "started",
hooks: {},
dashboardViews: [{ viewId: "timeline", label: "Timeline", componentPath: "./timeline.js" }],
} as FusionPlugin);
const views = loader.getPluginDashboardViews();
expect(views).toHaveLength(2);
expect(views.map((entry) => entry.pluginId + ":" + entry.view.viewId)).toEqual([
"views-a:graph",
"views-b:timeline",
]);
expect(loader.getPluginUiSlots()).toHaveLength(1);
});
});
// ── getPluginRuntimes ─────────────────────────────────────────────
describe("getPluginRuntimes", () => {