Skip to main content
The FDO side panel is a persistent navigation menu in the FDO application sidebar. Your plugin can add an entry — with an icon, a label, and a list of submenu items — using SidePanelMixin. When a user selects a submenu item, FDO sends a UI_MESSAGE to your plugin with the item’s message_type. You register a handler for each type in init().

The SidePanelConfig type

type SidePanelConfig = {
    icon: string;   // icon identifier for the panel entry
    label: string;  // display label for the panel entry
    submenu_list: {
        id: string;           // unique identifier for the submenu item
        name: string;         // display name shown in the panel
        message_type: string; // handler name triggered when the item is selected
    }[];
};

Adding SidePanelMixin

Apply SidePanelMixin to your plugin class to unlock the defineSidePanel() method:
import {
    FDO_SDK,
    FDOInterface,
    PluginMetadata,
    SidePanelMixin,
    SidePanelConfig,
} from "@anikitenko/fdo-sdk";

const MyPluginBase = SidePanelMixin(FDO_SDK);

export default class MyPlugin extends MyPluginBase implements FDOInterface {
    // ...
}
Chain SidePanelMixin with QuickActionMixin if you need both:
const MyPluginBase = SidePanelMixin(QuickActionMixin(FDO_SDK));

Defining the side panel

Override defineSidePanel() and return a SidePanelConfig object. FDO calls this during plugin initialization.
defineSidePanel(): SidePanelConfig {
    try {
        return {
            icon: "panel.png",
            label: "UI Extensions",
            submenu_list: [
                {
                    id: "dashboard",
                    name: "Dashboard",
                    message_type: "showDashboard"
                },
                {
                    id: "reports",
                    name: "Reports",
                    message_type: "showReports"
                },
                {
                    id: "settings",
                    name: "Settings",
                    message_type: "showSettings"
                }
            ]
        };
    } catch (error) {
        this.error(error as Error);
        return { icon: "panel.png", label: "UI Extensions", submenu_list: [] };
    }
}
Never throw from defineSidePanel(). Return a SidePanelConfig with an empty submenu_list if an error occurs — a thrown exception will prevent the plugin from initializing properly.

Registering handlers

Each message_type in your submenu_list needs a matching handler registered in init(). When the user selects a submenu item, FDO dispatches a UI_MESSAGE to the handler with that name.
init(): void {
    try {
        PluginRegistry.registerHandler("showDashboard", (data: any) => {
            this.log("Dashboard view triggered from side panel");
            return { success: true, view: "dashboard" };
        });

        PluginRegistry.registerHandler("showReports", (data: any) => {
            this.log("Reports view triggered from side panel");
            return { success: true, view: "reports" };
        });

        PluginRegistry.registerHandler("showSettings", (data: any) => {
            this.log("Settings view triggered from side panel");
            return { success: true, view: "settings" };
        });
    } catch (error) {
        this.error(error as Error);
    }
}
A missing handler for a submenu message_type will result in an error response when the user selects that item. Register all submenu handlers in init() before the plugin finishes loading.

Icon field

The icon field on SidePanelConfig is a string identifier for the icon displayed alongside your plugin’s label in the FDO side panel. Refer to the FDO host documentation for the list of supported icon identifiers.

Full example

The following is adapted from examples/04-ui-extensions-plugin.ts and shows both QuickActionMixin and SidePanelMixin used together:
import {
    FDO_SDK,
    FDOInterface,
    PluginMetadata,
    PluginRegistry,
    QuickActionMixin,
    SidePanelMixin,
    QuickAction,
    SidePanelConfig,
    DOMText,
    DOMNested,
} from "@anikitenko/fdo-sdk";

const UIExtensionsPluginBase = SidePanelMixin(QuickActionMixin(FDO_SDK));

export default class UIExtensionsPlugin extends UIExtensionsPluginBase implements FDOInterface {
    private readonly _metadata: PluginMetadata = {
        name: "UI Extensions Plugin Example",
        version: "1.0.0",
        author: "FDO SDK Team",
        description: "Demonstrates quick actions and side panel integration using mixins",
        icon: "panel-table"
    };

    private currentView: string = "default";

    get metadata(): PluginMetadata { return this._metadata; }

    init(): void {
        try {
            this.log("UIExtensionsPlugin initialized!");

            // Side panel handlers
            PluginRegistry.registerHandler("showDashboard", (data: any) => {
                this.currentView = "dashboard";
                this.log("Dashboard view triggered from side panel");
                return { success: true, view: "dashboard", message: "Dashboard view activated" };
            });

            PluginRegistry.registerHandler("showReports", (data: any) => {
                this.currentView = "reports";
                this.log("Reports view triggered from side panel");
                return { success: true, view: "reports", message: "Reports view activated" };
            });

            PluginRegistry.registerHandler("showSettings", (data: any) => {
                this.currentView = "settings";
                this.log("Settings view triggered from side panel");
                return { success: true, view: "settings", message: "Settings view activated" };
            });
        } catch (error) {
            this.error(error as Error);
        }
    }

    defineSidePanel(): SidePanelConfig {
        try {
            return {
                icon: "panel.png",
                label: "UI Extensions",
                submenu_list: [
                    {
                        id: "dashboard",
                        name: "Dashboard",
                        message_type: "showDashboard"
                    },
                    {
                        id: "reports",
                        name: "Reports",
                        message_type: "showReports"
                    },
                    {
                        id: "settings",
                        name: "Settings",
                        message_type: "showSettings"
                    }
                ]
            };
        } catch (error) {
            this.error(error as Error);
            return { icon: "panel.png", label: "UI Extensions", submenu_list: [] };
        }
    }

    render(): string {
        const domText = new DOMText();
        const domNested = new DOMNested();

        return domNested.createBlockDiv([
            domText.createHText(1, this._metadata.name),
            domText.createPText(this._metadata.description),
            domNested.createBlockDiv([
                domText.createHText(3, "Side Panel"),
                domText.createPText([
                    domText.createStrongText("Side Panel:"),
                    " Access features from the side panel menu"
                ].join('')),
                domNested.createList([
                    "Dashboard",
                    "Reports",
                    "Settings"
                ])
            ], {
                style: {
                    marginTop: "20px",
                    padding: "15px",
                    backgroundColor: "#fff3cd",
                    borderRadius: "5px"
                }
            })
        ], {
            style: { padding: "20px", fontFamily: "Arial, sans-serif" }
        });
    }
}

new UIExtensionsPlugin();

Checklist

1

Apply SidePanelMixin

Wrap FDO_SDK (or a chained mixin) with SidePanelMixin before defining your class.
2

Override defineSidePanel()

Return a SidePanelConfig object with icon, label, and submenu_list. Wrap the body in try/catch and return a config with an empty submenu_list on error.
3

Register a handler for each message_type

In init(), call PluginRegistry.registerHandler(message_type, handler) for every message_type in your submenu_list.
4

Respond to submenu selections

Your handler receives data from the FDO host. Return a result to confirm the action and optionally update plugin state to change the rendered view.