Skip to main content

Target type EditLayout

  • The custom widget can be rendered in the Edit Layout section or as a widget, allowing developers to create dynamic and interactive UI components tailored to specific business needs.

Coding Guidelines

pageContext

The pageContext provided to the custom widget contains the following properties:

{
attributeValues: object of attributes configured in an edit layout of the instance type,
canConnect: boolean value to check for link permission in relation widgets,
editMode: edit mode configured in the edit layout,
editedValues: object of edited attribute values in the custom widget as well as attributes in Section/Groups,
getAttributeWorkingValue: function to get the working values of attributes,
instanceDetails: object containing details of the instance on which the custom widget is configured,
instanceType: string value representing the type of the instance,
instanceId: string value representing the ID of the instance,
isPromoteInProgress: function called when promote is in progress,
onAttributeEdit: function called on editing attributes in the custom widget to update the `editedValues` object and corresponding attribute values in the standard widgets of the same layout,
onLinkSuccess: function called after linking an instance is successful,
onPromoteSuccess: function called on instance promote success,
onPromoteError: function called on instance promote error,
reloadInstanceDetails: function to reload the instance through the custom widget,
saveInstance: function to save the instance through the custom widget,
saveOperationState: object containing `{ data, isLoading, isError }` for save operations,
}
  • Along with this, pageContext contains additional properties common to all widget types. For details, check: common pageContext.

Typical Use Cases

  • Custom Widgets in Edit Layout: Used to display custom widgets in the edit layout, either as a section or as a widget.
  • Dynamic UI Creation: Allows users to create UI and functionality from scratch, providing a blank canvas for custom business logic.
  • Role and Rule-Based Visibility: Visibility can be controlled based on roles and rules configured in the edit layout.
  • Non-Standard Data Visualization: Ideal for displaying non-standard data visualizations or user interactions requiring data fetching, updates, or additional validations.

Sample Code

Example: Custom Widget for Editing Attributes

This example demonstrates a custom widget that:

  • Fetches instance and type details using the useProtrakApi hook.
  • Updates attributes like "Component" and "Department Name" on user input.
  • Uses the onAttributeEdit function from pageContext to update the "Functional Task Selection" attribute in the same layout.
const FunctionalTaskComponent = 'ComponentTaskAndTaskTemplate';
const FunctionalTaskSelection = 'FunctionalTaskSelection';
const TYPE_DETAILS = 'types/Task';
const component = 'Component';
const departmentName = 'Department/ Application/ Vendor Name';
const addTask = 'Add Functional Task';

function CustomEditWidget(pageContext) {
const { protrakUtils, protrakComponents } = React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;
const { Button, Dropdown, TextBox, ErrorModal } = protrakComponents;

const [attributeValue, setAttributeValue] = React.useState('');
const [departmentNameValue, setDepartmentNameValue] = React.useState('');
const [taskData, setTaskData] = React.useState([]);
const [options, setOptions] = React.useState([]);

const { state: projectResponse } = useProtrakApi({
requestConfig: getProject,
pageContext,
});

const { state: typeResponse } = useProtrakApi({
requestConfig: typeDetailsPromise,
});

React.useEffect(() => {
if (projectResponse.isFulfilled && projectResponse.data?.attributes) {
const previousData = JSON.parse(projectResponse.data.attributes[0]?.textValue || '[]');
setTaskData(previousData.filter((data) => !data.IsDeleted));
} else if (projectResponse.isError) {
return <ErrorModal message="Error while fetching project data" />;
}
}, [projectResponse]);

React.useEffect(() => {
if (typeResponse.isFulfilled && typeResponse.data?.attributes) {
const componentOptions = typeResponse.data.attributes.find(
(attr) => attr.attributeName === FunctionalTaskComponent
)?.options;
setOptions(componentOptions || []);
} else if (typeResponse.isError) {
return <ErrorModal message="Error while fetching type details" />;
}
}, [typeResponse]);

const handleAddTask = () => {
const newTask = { Component: attributeValue.value, DepartmentName: departmentNameValue };
const updatedTaskData = [...taskData, newTask];
setTaskData(updatedTaskData);

pageContext.onAttributeEdit(FunctionalTaskSelection, {
name: FunctionalTaskSelection,
textValue: JSON.stringify(updatedTaskData),
type: 'Text',
canUpdate: true,
});
};

return (
<div>
<div>
<label>{component}</label>
<Dropdown
defaultValues={attributeValue}
picklistOptions={mapPicklistValues(options, 'displayName', 'name')}
onValueChange={(newValue) => setAttributeValue(newValue)}
/>
</div>
<div>
<label>{departmentName}</label>
<TextBox value={departmentNameValue} onEdit={(newValue) => setDepartmentNameValue(newValue)} />
</div>
<Button onClick={handleAddTask} disabled={!attributeValue || !departmentNameValue}>
{addTask}
</Button>
</div>
);
}

function mapPicklistValues(options, labelKey, valueKey) {
return options.map((option) => ({
label: option[labelKey],
value: option[valueKey],
}));
}

const getProject = ({ pageContext }) => ({
endpoint: `instances/${pageContext.instanceId}`,
config: { method: 'GET', params: { 'attributes[0]': FunctionalTaskSelection } },
});

const typeDetailsPromise = () => ({
endpoint: TYPE_DETAILS,
config: { method: 'GET' },
});