Embedded Instance Grid Pattern
Overview
A widget that embeds a full Protrak instance grid (with search, sort, pagination, row actions) inside a View Layout tab. The widget uses useTypeLayoutConfig to load the dashboard configuration for the target type, then passes it to InstanceGridForCustomWidget which renders the grid. An optional otherUIComponents prop injects extra buttons into the grid toolbar.
When to Use
- A View Layout tab needs to display a list of related instances (e.g., show all Member Devices that match a dynamic rule)
- The list requires the same full-featured grid (sort, search, pagination, row click) as the native instance list
- The widget needs to inject a custom action button into the grid toolbar (e.g., "Evaluate Rule")
Applicable Layout Types
| Target | Notes |
|---|---|
ViewLayout | Most common — embedded list in a view tab |
DashboardLayout | Can also be used as a count/grid dashboard tile |
Pattern Structure
- Call
useTypeLayoutConfig(typeName, layoutType)to fetch the dashboard widget configuration for the target type - Extract the specific widget config from
dashboardConfig.data.widgetsby name - Merge with the type-level metadata (
typeName,typePluralName,canCreateInstance) - Pass the merged config to
<InstanceGridForCustomWidget configuration={...} /> - (Optional) Pass
otherUIComponents— a render function — to inject toolbar buttons
Code Example
function DynamicGroupsInstanceGridForViewLayout(pageContext) {
const { protrakUtils, protrakComponents } =
React.useContext(customWidgetContext);
const {
InstanceGridForCustomWidget,
Button,
ButtonEnums,
Container,
Spinner,
} = protrakComponents;
const { useTypeLayoutConfig } = protrakUtils;
// State for programmatic filtering or refresh triggers
const [baseUrl, setBaseUrl] = React.useState(
'programs/SolrConnectorCommonProgram/executeCommonProgram'
);
const [programData, setProgramData] = React.useState(
'00000000-0000-0000-0000-000000000000'
);
// Load the dashboard layout config for the target type
const dashboardConfig = useTypeLayoutConfig('MemberDevice', 'dashboard');
if (dashboardConfig.isLoading && !dashboardConfig.data) {
return (
<Container>
<Spinner />
</Container>
);
}
let configuration = {};
if (dashboardConfig.isFulfilled && dashboardConfig.data) {
// Find the specific widget inside the dashboard layout
const widget = dashboardConfig.data.widgets.find(
(w) => w.name === 'MemberDeviceDashboard'
);
// Merge widget config with type metadata
configuration = {
...widget,
canCreateInstance: dashboardConfig.data.canCreateInstance,
typeName: dashboardConfig.data.typeName,
typePluralName: dashboardConfig.data.typePluralName,
typeSingularName: dashboardConfig.data.typeSingularName,
};
}
// Optional: extra button in the grid toolbar
const renderOtherUIComponents = () => (
<Button
text="Evaluate Rule"
outline
appearance={ButtonEnums.Appearance.Transparent}
size={ButtonEnums.Size.Small}
onClick={() => {
setBaseUrl('programs/SolrConnectorCommonProgram/executeCommonProgram');
setProgramData(pageContext.instanceId);
}}
style={{ marginLeft: '1rem' }}
/>
);
return (
<InstanceGridForCustomWidget
isStandardGrid={false}
configuration={configuration}
canCreateInstance={pageContext.layoutConfig.canCreateInstance}
isFilterVisible={false}
baseUrl={baseUrl}
programData={programData}
otherUIComponents={renderOtherUIComponents}
widgetView="GridV2"
/>
);
}
useTypeLayoutConfig(typeName, layoutType) — Reference
Fetches the layout configuration for a given type and layout kind. Returns a standard API state object:
const { useTypeLayoutConfig } = protrakUtils;
// layoutType: 'dashboard' | 'create' | 'view' | 'edit'
const config = useTypeLayoutConfig('MemberDevice', 'dashboard');
Return value shape:
{
isLoading: boolean,
isFulfilled: boolean,
isError: boolean,
error: any,
loading: boolean, // alias for isLoading (older naming)
data: {
typeName: string,
typePluralName: string,
typeSingularName: string,
canCreateInstance: boolean,
widgets: Array<{
name: string,
// widget-specific config
}>,
// full layout config including sections, fields, etc.
}
}
Common usages:
// For embedded grid — get dashboard config
const dashboardConfig = useTypeLayoutConfig('MyType', 'dashboard');
// For form widgets — check which fields are visible in the create layout
const createConfig = useTypeLayoutConfig(pageContext.instanceType, 'create');
if (createConfig.isFulfilled) {
const sections = createConfig.data.sections; // form sections and fields
}
InstanceGridForCustomWidget — Key Props
| Prop | Type | Description |
|---|---|---|
configuration | object | Merged widget + type metadata from useTypeLayoutConfig |
isStandardGrid | boolean | false = custom program-driven grid; true = standard instance list |
canCreateInstance | boolean | Controls whether the "Create" button shows |
isFilterVisible | boolean | Show/hide the search filter bar |
baseUrl | string | The API or program endpoint for the grid's data source |
programData | string | Instance ID or payload passed to the program endpoint |
otherUIComponents | () => JSX | Render function for extra toolbar components |
widgetView | 'GridV2' | Grid version — use 'GridV2' |
pageContext.layoutConfig
Available in all layout contexts. Contains the full layout configuration from admin:
pageContext.layoutConfig = {
canCreateInstance: boolean,
typeName: string,
sections: Array<{
name: string,
widgets: Array<{
name: string,
fields: Array<{
attributeName: string,
label: string,
options: Array<{ name: string, displayName: string }>,
// ...
}>,
}>,
}>,
}
Used in MemberDeviceLocation.js to look up picklist display names:
const getDisplayName = (name, attributeName) => {
const fieldSections =
pageContext?.layoutConfig?.sections?.[0]?.widgets?.[2]?.fields;
const field = fieldSections?.find((f) => f.attributeName === attributeName);
const match = field?.options?.find((o) => o.name === name);
return match ? match.displayName : name;
};
Real Examples
DynamicGroupsInstanceGridForViewLayout.js— SymmeraBatchCertificateCreate.js— Symmera (useTypeLayoutConfigfor create layout)ConfigurationArtifactReservedWidget.js— Symmera (useTypeLayoutConfigfor create layout)MemberDeviceLocation.js— Symmera (pageContext.layoutConfigfor display names)