Skip to main content

Development Workflow

This guide covers the essential workflow for developing, debugging, and managing versions of your custom widgets. You'll learn how to run your widget in standalone mode for quick iteration, connect it to a live portal environment for integration testing, and manage widget versions across your deployments.

Table of Contents

  1. Local Development
  2. Widget Version Control
  3. Connecting Local Widget to Portal
  4. Understanding Module Federation

Local Development

Dev Mode Launch (Standalone)

Reference: Dev Launch

Notice: Local dev mode primarily used for the initial widget setup and initial UI development stage. For further implementation stages deploy the widget to the platform and continue the development process in a portal environment.

Run widget standalone without platform integration using the Host application (wlabel):

npm run dev
  • Uses src/main.tsx as entry point
  • Uses src/local-widget.tsx for mocked data
  • Runs on configured port (e.g., localhost:5210)

main.tsx Example

import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "react-query";
import { ThemeProvider } from "styled-components";
import KpiTracker from "./widget/widget";
import { mockTheme, mockProps } from "./local-widget";

const queryClient = new QueryClient();

const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);

root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={mockTheme}>
<KpiTracker {...mockProps} />
</ThemeProvider>
</QueryClientProvider>
</React.StrictMode>
);

local-widget.tsx Example

import type { IWidgetProps } from "./types/widget-props";

export const mockTheme = {
name: "core-light",
colors: {
primary: "rgb(19, 183, 209)",
secondary: "rgb(32, 78, 110)",
surface: "rgb(255, 255, 255)",
onSurface: "rgb(45, 56, 66)",
// ... more colors
},
sizeUnit: 4,
borderRadius: 3,
// ... more theme properties
};

export const mockProps: IWidgetProps = {
platformMeta: {
storeWidgetsById: {},
platformWidgetsById: {},
currentDashboardId: "mock-dashboard-123",
currentDashboard: undefined,
},
httpClient: async (url: string, options: any) => {
console.log("Mock HTTP Client:", url, options);
// Return mock data
return {
revenue: 125000,
users: 5280,
conversion: 3.42,
timestamp: Date.now(),
};
},
useShareValue: () => (path: string[], value: any) => {
console.log("Share value:", path, value);
},
useSelectSharedValue: (selector: any) => null,
useDeleteSharedValue: () => (path: string[]) => {
console.log("Delete shared value:", path);
},
title: "KPI Tracker (Dev Mode)",
apiUrl: "http://localhost:3000/api",
refreshInterval: 30,
showTrend: true,
enableNotifications: true,
remoteModule: () => null,
showNotification: (type, content, options) => {
console.log("Notification:", type, content, options);
},
useQueryData: (key: string[]) => null,
useDashboardNavigationInfo: (dashboardId: string) => ({ data: null }),
getExtension: (name: string) => null,
installedExtensions: [],
dashboardWidgetInstanceId: "mock-widget-instance-123",
};

Widget Version Control

Reference: Semantic Release

The INVENT platform supports widget versioning. Each widget can have multiple versions, and portals can use either the latest version or a specific fixed version.

Releasing Your First Version

To release the first version of your widget:

  1. Create an initial merge request with a semantic tag in the title
  2. Ensure the MR title begins with feat: or fix:
  3. After the MR is merged, the first version will be automatically released and added to the portal

Example MR titles:

feat: initial KPI Tracker widget ACME-123
feat: add KPI tracking functionality ACME-123

For more information about semantic tags, see the Semantic Release documentation.

Version Management

Latest Version (Default)

When a widget is added to a portal, the default version is set to "latest". This means:

  • Each time a new MR with a semantic tag is merged, a new version is released
  • The portal automatically loads the latest version
  • No manual intervention is required for updates

Fixed Version

To use a specific version of a widget:

  1. Open the portal page in the ICP (Invent Control Panel)
  2. Navigate to the "Micro Apps Versioning" page
  3. Search for your widget by name
    • If your widget is not listed, verify:
      • The initial MR has been merged
      • The MR title included the required semantic tag (feat: or fix:)
      • If issues persist, contact your INVENT technical manager
  4. Click the cog icon (⚙️) next to your widget
  5. Select the desired version from the "Change Current Version" dropdown
  6. Click "Apply" to save the changes

The portal will now use the fixed version you selected instead of automatically updating to the latest version.


Connecting Local Widget to Portal

You can debug your local widget development directly in a portal environment.

Steps to Connect

  1. Set Widget to Dev Version

    • Open the ICP (Invent Control Panel)
    • Navigate to "Micro Apps Versioning" page
    • Find your widget in the list
    • Click the cog icon (⚙️) next to your widget
    • Select 'dev-version' in the "Change Current Version" dropdown
    • Click "Apply"
  2. Start Local Development Server

    npm run start_federation

    Run this command in the root of your widget repository

  3. Load Widget in Portal

    • Open the portal in your browser
    • Navigate to a dashboard
    • Add your widget from the Micro App Library
    • Your locally running dev build will be loaded into the portal
  4. See Changes

    • Make changes to your widget code
    • Reload the portal page to see the changes
    • Debug using browser DevTools

Benefits

  • Test widget in real portal environment
  • Debug with actual platform APIs and data
  • Verify integration with other widgets
  • Test with production-like configuration
  • Faster development iteration

Understanding Module Federation

Reference: Module Federation

The INVENT platform uses Webpack Module Federation to load and manage widgets at runtime. This section provides an overview of how widgets are loaded into the platform.

What is Module Federation?

Module Federation is a Webpack 5 feature that allows JavaScript applications to dynamically load code from another application at runtime. In the INVENT platform:

  • Host Application (wlabel) is the main platform application
  • Remote Applications (widgets) are loaded dynamically from the Widget Store
  • Shared Dependencies (React, React Query, styled-components) are reused from the host

How Widgets Are Loaded

When a dashboard requests a widget, the following steps occur:

  1. Dashboard Requests Widget Metadata

    • Portal requests dashboard configuration from BfF
    • Response includes list of widgets with their IDs and settings
  2. Check Widget Cache

    • Platform checks if the widget code has been loaded before
    • If cached, skip to step 6
  3. Insert Script Tag

    <script
    type="text/javascript"
    data-webpack="VENDOR_WidgetName"
    async
    src="https://{STORE_BASE_URL}/{widgetId}/remoteEntry.js"
    ></script>
  4. Load Widget Bundle

    • Browser downloads the widget's remoteEntry.js file
    • Widget bundle is stored at: {STORE_BASE_URL}/{widgetId}/remoteEntry.js
  5. Webpack Runtime Resolution

    • Webpack runtime locates the widget's scope (e.g., VENDOR_WidgetName)
    • Resolves the exposed module (e.g., ./Widget)
    • Creates a React component instance
  6. Widget Rendering

    • Widget is rendered with platform-provided props
    • Widget can now access shared dependencies from the host
    • Widget appears on the dashboard

Widget Configuration for Federation

webpack.config.js (handled by @invent/webpack-config):

new ModuleFederationPlugin({
name: "VENDOR_WidgetName",
filename: "remoteEntry.js",
exposes: {
"./Widget": "./src/widget/index",
"./Preview": "./src/widget/preview",
},
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true, requiredVersion: "^18.0.0" },
"styled-components": { singleton: true },
// ... more shared dependencies
},
});

Shared Dependencies

The host application exposes shared dependencies that all widgets can use:

  • React & React DOM - UI library
  • React Query - Data fetching and caching
  • styled-components - CSS-in-JS styling
  • @invent/wl-ui-kit - Platform UI components
  • @invent/shared-types - TypeScript types

This approach:

  • Reduces widget bundle sizes
  • Ensures version consistency across widgets
  • Prevents multiple React instances
  • Improves loading performance

Development vs Production

Development (npm run start_federation)

  • Widget runs on localhost with dev port (e.g., localhost:5123)
  • Hot Module Replacement (HMR) enabled
  • Source maps available for debugging
  • Portal configured to load from localhost:{port}

Production (after deployment)

  • Widget bundled and deployed to Widget Store
  • Served from CDN ({STORE_BASE_URL}/{widgetId}/remoteEntry.js)
  • Minified and optimized
  • Versioned for cache control

Best Practices

  1. Never Include Shared Dependencies in Widget Bundle

    • Mark shared dependencies as externals in webpack config
    • Let @invent/webpack-config handle shared dependencies
  2. Keep Widget Bundles Small

    • Only include widget-specific code
    • Use tree-shaking for unused code elimination
    • Lazy load heavy dependencies when possible
  3. Test Federation Locally

    • Use npm run start_federation to test federation mode
    • Verify widget works with shared dependencies from host
  4. Monitor Bundle Size

    • Run npm run analyze to visualize bundle composition
    • Keep main bundle under 200KB (gzipped)

Learn more: Module Federation Documentation


Next Steps

Continue to: Deployment