Open a Tab Using Web API
Introduction
This how-to shows you how to open a tab in Studio Pro from an extension. This tab will contain your web content.
Prerequisites
This how-to uses the results of Get Started with the Web Extensibility API. Please complete that how-to before starting this one. You should also be familiar with creating menus as described in Create a Menu Using Web API.
Opening a Tab
Firstly, create a menu item to open the tab. This is done inside the loaded event in Main. For more information see Create a Menu Using Web API.
In a listener event called menuItemActivated the studioPro.ui.tabs.open(<tabinfo>, <uispec>) call opens a new tab where:
-
<TabInfo>is an object containing thetitleof the tab, which will be shown in the title bar of your tab in Studio Pro. -
<uispec>is an object containing two required properties:componentNamewhich is the name of the extension prefixed with "extension/". For example "extension/myextension" in the following example.uiEntryPointwhich is the name mapped from themanifest.jsonfile. See below for examples with multiple tabs.
open method is called, the TabHandle returned must be tracked by the extension so that it can be closed later by calling the close method.
An example of the class Main to open a tab called My Extension Tab looks similar to the following:
import { IComponent, studioPro, TabHandle } from "@mendix/extensions-api";
class Main implements IComponent {
tabs: { [menuId: string]: Promise<TabHandle> } = {};
async loaded() {
// Add menu items to the Extensions menu to open and close our tab
await studioPro.ui.extensionsMenu.add({
menuId: "myextension.MainMenu",
caption: "MyExtension Menu",
subMenus: [
{ menuId: "myextension.ShowTabMenuItem", caption: "Show tab" },
{
menuId: "myextension.CloseTabMenuItem",
caption: "Close tab",
},
],
});
studioPro.ui.extensionsMenu.addEventListener(
"menuItemActivated",
async (args) => {
// Open a tab when the menu item is clicked
if (args.menuId === "myextension.ShowTabMenuItem") {
const handle = studioPro.ui.tabs.open(
{
title: "My Extension Tab",
},
{
componentName: "extension/myextension",
uiEntrypoint: "tab",
}
);
// Track the open tab
this.tabs["myextension.MainMenu"] = handle;
}
// Close the tab opened previously
if (args.menuId === "myextension.CloseTabMenuItem") {
studioPro.ui.tabs.close(await this.tabs["myextension.MainMenu"]);
}
}
);
}
}
export const component: IComponent = new Main();TabHandle.
Filling the Tabs With Content
In the previous example, the uiEntryPoint property of the <uispec> object had the value "tab". This value must match the one from the manifest.
If you want to have multiple tabs in your extension, you need to structure the folders and set up the manifest file correctly.
To do this, follow these steps:
-
Add a new method
createTabSpecin yourMainclass.createTabSpec(tab: string, title: string): { info: TabInfo, ui: UISpec} { const info: TabInfo = { title }; const ui: UISpec = { componentName: "extension/myextension", uiEntrypoint: tab, }; return {info, ui}; } -
Add three folders inside the
uifolder, one for each tab you want to display contents for. -
Create an
index.tsxfile in each folder. -
Put the following code in each
index.tsxfile (this example is for tab3):import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; createRoot(document.getElementById("root")!).render( <StrictMode> <h1>tab3</h1> </StrictMode> );In this example, we'll add 3 tabs: tab1, tab2, and tab3.
-
Create listener events in the
Mainclass to open each of the three tabs. TheMainclass will then look like this:import { IComponent, studioPro, TabInfo, UISpec } from "@mendix/extensions-api"; class Main implements IComponent { async loaded() { // Add a menu item to the Extensions menu await studioPro.ui.extensionsMenu.add({ menuId: "myextension.MainMenu", caption: "Show Tabs", subMenus: [ { menuId: "myextension.ShowTab1", caption: "Show tab 1" }, { menuId: "myextension.ShowTab2", caption: "Show tab 2" }, { menuId: "myextension.ShowTab3", caption: "Show tab 3" }, ], }); // Open a tab when the menu item is clicked studioPro.ui.extensionsMenu.addEventListener( "menuItemActivated", async (args) => { if (args.menuId === "myextension.ShowTab1") { const tab1Spec = this.createTabSpec("tab1", "Tab 1 Title"); studioPro.ui.tabs.open(tab1Spec.info, tab1Spec.ui); } if (args.menuId === "myextension.ShowTab2") { const tab2Spec = this.createTabSpec("tab2", "Tab 2 Title"); studioPro.ui.tabs.open(tab2Spec.info, tab2Spec.ui); } if (args.menuId === "myextension.ShowTab3") { const tab3Spec = this.createTabSpec("tab3", "Tab 3 Title"); studioPro.ui.tabs.open(tab3Spec.info, tab3Spec.ui); } } ); } createTabSpec(tab: string, title: string): { info: TabInfo; ui: UISpec } { const info: TabInfo = { title }; const ui: UISpec = { componentName: "extension/myextension", uiEntrypoint: tab, }; return { info, ui }; } } export const component: IComponent = new Main(); -
Ensure the tabs are added to the
manifest.jsonfile. Here is an example of three tabs under theuiproperty.{ "mendixComponent": { "entryPoints": { "main": "main.js", "ui": { "tab1": "tab1.js", "tab2": "tab2.js", "tab3": "tab3.js" } } } } -
Update
vite.configto match the manifest with an entry for each tab. For example:import { defineConfig, ResolvedConfig, UserConfig } from "vite"; export default defineConfig({ build: { lib: { formats: ["es"], entry: { main: "src/main/index.ts", tab1: "src/ui/tab1/index.tsx", tab2: "src/ui/tab2/index.tsx", tab3: "src/ui/tab3/index.tsx", }, }, rollupOptions: { external: ["@mendix/component-framework", "@mendix/model-access-sdk"], }, outDir: "./dist/myextension", }, } satisfies UserConfig);
After building and installing the extension in our Studio Pro app, each tab will display the content specified in the related index.tsx file.
Conclusion
You now know how to create tabs and populate them with content.
Extensibility Feedback
If you would like to provide us with some additional feedback you can complete a small Survey
Any feedback is much appreciated.