Skip to main content

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

TargetNotes
ViewLayoutMost common — embedded list in a view tab
DashboardLayoutCan also be used as a count/grid dashboard tile

Pattern Structure

  1. Call useTypeLayoutConfig(typeName, layoutType) to fetch the dashboard widget configuration for the target type
  2. Extract the specific widget config from dashboardConfig.data.widgets by name
  3. Merge with the type-level metadata (typeName, typePluralName, canCreateInstance)
  4. Pass the merged config to <InstanceGridForCustomWidget configuration={...} />
  5. (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

PropTypeDescription
configurationobjectMerged widget + type metadata from useTypeLayoutConfig
isStandardGridbooleanfalse = custom program-driven grid; true = standard instance list
canCreateInstancebooleanControls whether the "Create" button shows
isFilterVisiblebooleanShow/hide the search filter bar
baseUrlstringThe API or program endpoint for the grid's data source
programDatastringInstance ID or payload passed to the program endpoint
otherUIComponents() => JSXRender 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 — Symmera
  • BatchCertificateCreate.js — Symmera (useTypeLayoutConfig for create layout)
  • ConfigurationArtifactReservedWidget.js — Symmera (useTypeLayoutConfig for create layout)
  • MemberDeviceLocation.js — Symmera (pageContext.layoutConfig for display names)