-
Notifications
You must be signed in to change notification settings - Fork 112
Table Views
- To quickly build a table instance, use the
TableBuilderfacility exported from Zowe Explorer API. - The
TableBuilderfacility has multiple helper functions to facilitate creating tables. To start, create a new instance of aTableBuilder.
NOTE: It's important to specify Zowe Explorer's extension path when using the table builder, or otherwise the table components are not accessible from your extension when rendering the webview.
const builder = new TableBuilder({
extensionPath: vscode.extensions.getExtension("zowe.vscode-extension-for-zowe").extensionPath,
} as vscode.ExtensionContext);- Properties, sections of the table, and other options can be provided by calling functions directly on the builder:
const builder = new TableBuilder()
.options({ pagination: true, paginationPageSize: 100 })
.rows([
{ place: "(Not) the Apple", apple: 5, banana: 15, orange: 10 },
{ place: "Bananapalooza", apple: 20, banana: 30, orange: 0 },
{ place: "Orange of Fruits", apple: 0, banana: 0, orange: 30 },
])
.columns([
{ field: "place", headerName: "Marketplace" },
{ field: "apple", headerName: "Number of apples", filter: true, sort: "asc" },
{ field: "banana", headerName: "Number of bananas", filter: true },
{ field: "orange", headerName: "Number of oranges", filter: true },
])
.title("Fruits for Sale at Various Marketplaces");- Once you're ready to build the table, you can call either:
-
build: Builds a table instance and returns it to the developer -
buildAndShare: Builds a table instance, adds its view to the mediator, and returns it to the developer
-
- Only use the
buildAndSharecommand if you want to expose a table to the public. Sharing the table allows it to be modified by other extenders, but only the creator of the table can delete the instance. - The tables themselves leverage AG Grid (community edition) for rendering. All supported AG Grid options for the table can be found here: Table.GridProperties
Actions are interactive buttons displayed in the table's action bar that allow users to perform operations on selected rows. They support both static and dynamic titles, conditional visibility, and different callback types.
Use the addAction method to add actions to specific rows or all rows:
// Add actions to all rows
await tableView.addAction("all", {
title: "Delete Item",
command: "delete-item",
type: "secondary",
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Handle deletion logic
console.log("Deleting item:", rowInfo.row);
},
},
});
// Add actions to specific row index
await tableView.addAction(0, {
title: "Edit First Item",
command: "edit-first-item",
type: "primary",
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Handle editing logic
},
},
});Actions support different visual types:
-
"primary": Emphasized button for main actions -
"secondary": Standard button for secondary actions -
"icon": Icon-only button for space-efficient actions
Actions support different callback types based on the selection requirements:
// Single row selection required
{
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// rowInfo contains: { index: number, row: RowData }
}
}
}
// Multiple rows selection supported
{
callback: {
typ: "multi-row",
fn: async (view, rows) => {
// rows is Record<number, RowData> of selected rows
}
}
}
// No selection required
{
callback: {
typ: "no-selection",
fn: async (view) => {
// Operates on the entire table
}
}
}
// Cell-specific actions
{
callback: {
typ: "cell",
fn: async (view, cellValue) => {
// cellValue contains the specific cell data
}
}
}Actions can have dynamic titles that change based on the selected data:
await tableView.addAction("all", {
title: (selectedData) => {
if (Array.isArray(selectedData)) {
return `Process ${selectedData.length} items`;
}
return `Process ${selectedData.name}`;
},
command: "process-items",
callback: {
typ: "multi-row",
fn: async (view, rows) => {
// Process selected rows
},
},
});Controls whether an action is enabled (clickable) based on the selected data:
await tableView.addAction("all", {
title: "Archive Item",
command: "archive-item",
condition: (rowData) => {
// Enable only if status is not already archived
return rowData.status !== "archived";
},
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Archive logic
},
},
});Controls whether an action is visible in the UI based on the selected data:
await tableView.addAction("all", {
title: "Admin Action",
command: "admin-action",
hideCondition: (rowData) => {
// Hide action if user is not admin
return !rowData.userRole?.includes("admin");
},
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Admin-only logic
},
},
});For simple conditions, you can use string expressions:
await tableView.addAction("all", {
title: "Delete",
command: "delete-row",
condition: "data => data.deletable === true",
hideCondition: "data => data.protected === true",
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Delete logic
},
},
});Context menu items appear when users right-click on table rows, providing contextual actions specific to the clicked row.
Use the addContextOption method to add context menu items:
// Add context menu items to all rows
await tableView.addContextOption("all", {
title: "Copy to Clipboard",
command: "copy-to-clipboard",
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
await vscode.env.clipboard.writeText(JSON.stringify(rowInfo.row));
},
},
});
// Add context menu items to specific row
await tableView.addContextOption(0, {
title: "Special Action",
command: "special-action",
condition: (rowData) => rowData.special === true,
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Special action logic
},
},
});Context menu items support the same condition system as actions:
await tableView.addContextOption("all", {
title: "Edit Properties",
command: "edit-properties",
condition: (rowData) => rowData.editable === true,
hideCondition: (rowData) => rowData.locked === true,
callback: {
typ: "single-row",
fn: async (view, rowInfo) => {
// Edit properties logic
},
},
});The table provides built-in context menu items:
- Copy cell: Copies the value of the right-clicked cell
- Copy row: Copies the entire row data as JSON
You can add custom items alongside these built-in options.
The table view provides several methods to manage table content dynamically.
Add new rows to the existing table data:
// Add single row
await tableView.addContent({
name: "New Item",
value: 42,
status: "active",
});
// Add multiple rows
await tableView.addContent({ name: "Item 1", value: 10 }, { name: "Item 2", value: 20 }, { name: "Item 3", value: 30 });Replace all existing table content:
const newData = [
{ name: "Fresh Item 1", value: 100 },
{ name: "Fresh Item 2", value: 200 },
];
await tableView.setContent(newData);Retrieve current table content:
const currentRows = tableView.getContent();
console.log("Current table data:", currentRows);Update or delete specific rows by index:
// Update row at index 0
await tableView.updateRow(0, {
name: "Updated Item",
value: 999,
status: "modified",
});
// Delete row at index 1
await tableView.updateRow(1, null);- Owned tables are represented by the
Table.Instanceclass. - The "View" for a table is a superclass of
Table.Instance, except that itsdisposefunction is protected to prevent other extenders from calling it. - Generally, we recommend building tables using the
TableBuilderclass, but functions are also exposed on the table view to make modifications:
const view = new Table.View(extensionContext);
// Each setter function will send an update to the webview.
// To set data before rendering, pass the data into the constructor as the second argument.
await view.setTitle("Fruits for Sale");
await view.setOptions({ pagination: true, paginationPageSize: 100 });
await view.addColumns([
{ field: "place", headerName: "Marketplace" },
{ field: "apple", headerName: "Number of apples", filter: true, sort: "asc" },
{ field: "banana", headerName: "Number of bananas", filter: true },
{ field: "orange", headerName: "Number of oranges", filter: true },
]);
await view.addContent([
{ place: "(Not) the Apple", apple: 5, banana: 15, orange: 10 },
{ place: "Bananapalooza", apple: 20, banana: 30, orange: 0 },
{ place: "Orange of Fruits", apple: 0, banana: 0, orange: 30 },
]);
// Notice that this logic results in an identical table to the one composed with the TableBuilder.- To dispose of a table instance, call its
disposefunction. This function will also remove the table from the mediator if it was previously added.
- The table mediator is a controller class used for managing shared Zowe Explorer tables. It operates as a singleton and is exposed through the
ZoweExplorerExtenderApi. - Extenders can share a table with the mediator by calling it's
addTablefunction, or the extender can callbuildAndShareon aTableBuilderto build and expose a new table. - At any time, the source extender can remove a table instance by passing it to the mediator's
removeTablefunction. Only developers with access to theTable.Instancewill be able to dispose of the table. - Other extenders can access tables from the mediator by calling its
getTablefunction and providing the ID of the desired table.- To access the table ID, you can call the
getIdfunction on the table view. You can then share this ID with other extenders so that they can explicitly request it from the mediator.
- To access the table ID, you can call the
The table view supports extensive configuration through AG Grid options. Use the setOptions method to configure table behavior.
await tableView.setOptions({
// Pagination
pagination: true,
paginationPageSize: 50,
paginationAutoPageSize: false,
paginationPageSizeSelector: [10, 25, 50, 100],
// Selection
rowSelection: "multiple", // or "single"
suppressRowClickSelection: false,
selectEverything: false,
// Filtering
quickFilterText: "search term",
// Loading state
loading: true,
// Column management
maintainColumnOrder: true,
suppressMovableColumns: false,
// Performance
debug: false,
suppressColumnMoveAnimation: true,
});await tableView.setOptions({
// Auto-sizing strategies
autoSizeStrategy: {
type: "fitGridWidth",
defaultMinWidth: 100,
defaultMaxWidth: 500,
columnLimits: [{ colId: "name", minWidth: 150, maxWidth: 300 }],
},
// Custom tree mode for hierarchical data
customTreeMode: true,
customTreeColumnField: "name",
customTreeInitialExpansionDepth: 2,
// Header configuration
headerHeight: 40,
groupHeaderHeight: 30,
floatingFiltersHeight: 35,
// Localization
localeText: {
noRowsToShow: "No data available",
loading: "Loading...",
},
// Column menu
columnMenu: "new", // or "legacy"
suppressMenuHide: false,
// Drag and drop
suppressDragLeaveHidesColumns: true,
allowDragFromColumnsToolPanel: true,
});Configure individual columns when adding them:
await tableView.addColumns([
{
field: "name",
headerName: "Item Name",
width: 200,
pinned: "left",
sortable: true,
filter: true,
editable: true,
checkboxSelection: true,
headerCheckboxSelection: true,
},
{
field: "value",
headerName: "Value",
type: "numericColumn",
width: 120,
sort: "desc",
filter: true,
valueFormatter: (params) => `$${params.value}`,
},
{
field: "status",
headerName: "Status",
width: 100,
hide: false,
suppressMovable: true,
lockVisible: true,
},
]);The table view provides methods to save and restore grid state, enabling persistence of user preferences like column order, sizing, sorting, and filtering.
Retrieve the current state of the grid:
const gridState = await tableView.getGridState();
// Grid state includes:
// - Column order and widths
// - Sort model
// - Filter model
// - Pinned columns
// - Visible columns
// - Grouped columns
console.log("Current grid state:", gridState);Restore a previously saved grid state:
const savedState = {
columnState: [
{ colId: "name", width: 250, sort: "asc", pinned: "left" },
{ colId: "value", width: 150, sort: null, pinned: null },
],
filterModel: {
name: { type: "contains", filter: "important" },
},
sortModel: [{ colId: "name", sort: "asc" }],
};
await tableView.setGridState(savedState);Manage pagination state separately:
// Get current page information
const currentPage = await tableView.getPage();
const pageSize = await tableView.getPageSize();
console.log(`Currently on page ${currentPage + 1} with ${pageSize} items per page`);
// Set page and page size
await tableView.setPage(2); // Navigate to page 3 (0-indexed)
await tableView.setPageSize(25); // Show 25 items per pageSave and restore table state across sessions:
// Save state when table is modified
tableView.onTableDisplayChanged((displayedRows) => {
const saveState = async () => {
const state = await tableView.getGridState();
const page = await tableView.getPage();
const pageSize = await tableView.getPageSize();
// Save to VS Code workspace state
await context.workspaceState.update("tableState", {
gridState: state,
page,
pageSize,
});
};
saveState().catch(console.error);
});
// Restore state when table is created
const restoreState = async () => {
const savedState = context.workspaceState.get("tableState");
if (savedState) {
await tableView.setGridState(savedState.gridState);
await tableView.setPage(savedState.page);
await tableView.setPageSize(savedState.pageSize);
}
};
// Wait for API to be ready before restoring state
await tableView.waitForAPI();
await restoreState();Manage pinned rows at the top of the grid:
// Pin specific rows to the top
const importantRows = [
{ name: "Critical Item", value: 999, status: "urgent" },
{ name: "High Priority", value: 500, status: "important" },
];
await tableView.pinRows(importantRows);
// Get currently pinned rows
const pinnedRows = await tableView.getPinnedRows();
console.log("Pinned rows:", pinnedRows);
// Replace all pinned rows
await tableView.setPinnedRows([{ name: "New Pinned Item", value: 100, status: "pinned" }]);
// Clear all pinned rows
await tableView.setPinnedRows([]);
// Unpin specific rows
await tableView.unpinRows(rowsToUnpin);Listen for table events to react to user interactions:
// React to data changes
tableView.onTableDataReceived((changedData) => {
console.log("Table data updated:", changedData);
});
// React to display changes (filtering, sorting)
tableView.onTableDisplayChanged((displayedRows) => {
console.log("Displayed rows changed:", displayedRows);
});
// React to data editing
tableView.onTableDataEdited((editEvent) => {
console.log("Cell edited:", editEvent);
// editEvent contains: { rowIndex, field, value, oldValue }
});
// React to custom messages
tableView.onDidReceiveMessage((message) => {
console.log("Received message:", message);
});zowe/vscode-extension-for-zowe
Welcome
Using Zowe Explorer
Roadmaps
- 2025 Zowe Explorer Roadmap
- 2024 Zowe Explorer Roadmap
- 2023 Zowe Explorer Roadmap
- 2022 Zowe Explorer Roadmap
Development Process
- Contributor Guidance
- Developer Setup
- Developer Reference
- Developing for Theia
- File Save Flow
- Menu Commands
Testing Process
Release Process
Backlog Grooming Process
How to Extend Zowe Explorer
- Extending Zowe Explorer
- Using Zowe Explorer Local Storage
- Error Handling for Extenders
- Secure Credentials for Extenders
- Sample Extender Repositories
Conformance Criteria
v3 Features and Information