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
- Local Development
- Widget Version Control
- Connecting Local Widget to Portal
- 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.tsxas entry point - Uses
src/local-widget.tsxfor 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:
- Create an initial merge request with a semantic tag in the title
- Ensure the MR title begins with
feat:orfix: - 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:
- Open the portal page in the ICP (Invent Control Panel)
- Navigate to the "Micro Apps Versioning" page
- 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:orfix:) - If issues persist, contact your INVENT technical manager
- If your widget is not listed, verify:
- Click the cog icon (⚙️) next to your widget
- Select the desired version from the "Change Current Version" dropdown
- 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
-
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"
-
Start Local Development Server
npm run start_federationRun this command in the root of your widget repository
-
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
-
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:
-
Dashboard Requests Widget Metadata
- Portal requests dashboard configuration from BfF
- Response includes list of widgets with their IDs and settings
-
Check Widget Cache
- Platform checks if the widget code has been loaded before
- If cached, skip to step 6
-
Insert Script Tag
<script
type="text/javascript"
data-webpack="VENDOR_WidgetName"
async
src="https://{STORE_BASE_URL}/{widgetId}/remoteEntry.js"
></script> -
Load Widget Bundle
- Browser downloads the widget's
remoteEntry.jsfile - Widget bundle is stored at:
{STORE_BASE_URL}/{widgetId}/remoteEntry.js
- Browser downloads the widget's
-
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
- Webpack runtime locates the widget's scope (e.g.,
-
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
-
Never Include Shared Dependencies in Widget Bundle
- Mark shared dependencies as
externalsin webpack config - Let @invent/webpack-config handle shared dependencies
- Mark shared dependencies as
-
Keep Widget Bundles Small
- Only include widget-specific code
- Use tree-shaking for unused code elimination
- Lazy load heavy dependencies when possible
-
Test Federation Locally
- Use
npm run start_federationto test federation mode - Verify widget works with shared dependencies from host
- Use
-
Monitor Bundle Size
- Run
npm run analyzeto visualize bundle composition - Keep main bundle under 200KB (gzipped)
- Run
Learn more: Module Federation Documentation
Next Steps
Continue to: Deployment →