Skip to main content

Target type ViewLayout

The custom widget can be rendered in View layout section or as a widget.

Coding guidelines

  • pageContext The page context provided has following content
{
attributeValues: object of attributes configured in a view layout of instance type
canConnect : boolean value to check for link permission in relation widget
editMode: edit mode configured in view layout
editedValues: object of edited attribute values in custom widget as well as attributes in Section/Groups,
getAttributeWorkingValue: get attributes' values
instanceDetails: object of details of the instance on which custom widget is configured
instanceType: string value,
instanceId: string value,
isPromoteInProgress: function called when promote is in progress
onAttributeEdit: function called on editing attributes in custom widget to update editedValues object & 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 an instance through custom widget
saveInstance: function to save an instance through custom widget,
saveOperationState: saveOperationState : {data, isLoading, isError},
}
  • Along with this, pageContext contains few more properties that are common to all widget types. For details check: common pageContext

Typical use cases

  • Used in displaying custom widgets for the View layout in section or as a widget.
  • View Layout type custom widget when configured as a widget allows user to create UI & functionality from scratch giving a blank canvas.
  • Role and Rule(display conditions) based visibility can be controlled by View layout configurations.
  • Typically used to show non-standard data visualization or user interactions that require data fetching or update requirements with additional business logic or validations

Sample Code

  1. Here's an example of a View layout widget that:
    • fetches instance & type details of a particular instance & type using in-built useProtrakApi hook and a custom promise function
    • On editing 'Component' & 'Department Name' attributes & on 'Add Functional Task' click, 'Fuctional Task Selection' attribute from Project Details group is updated.
    • onAttributeEdit : function from pageContext, with parameters (attributeName, value, errorMessage), used to update the value of the attributes configured in the standard groups of the same layout from the custom widget configured as a group or as a widget 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';

const containerStyle = {
boxSizing: 'border-box',
margin: '12px',
};
const addTaskButtonProperty = {
border: '1px solid #dddddd',
width: '140px',
height: '30px',
};
const attributeNameStyle = {
marginBottom: '7px',
fontSize: '12px',
fontWeight: '600',
};
const inputBoxProperty = {
marginBottom: '10px',
};
function TestViewLayoutWidget(pageContext) {
const { protrakUtils, protrakComponents } =
React.useContext(customWidgetContext);
const { useProtrakApi } = protrakUtils;
const { state: responseForProject } = useProtrakApi({
requestConfig: getProject,
pageContext,
});
const { state: responseForTypeTask } = useProtrakApi({
requestConfig: typeDetailsPromise,
});
const { Button, ErrorModal, Dropdown, TextBox } =
protrakComponents;
const [attributeValue, setComponentPicklistAttributeValue] =
React.useState('');
const [departmentNameValue, setDepartmentNameValue] = React.useState('');
const [taskData, setTaskData] = React.useState([]);
const [options, setPicklistOptions] = React.useState([]);

React.useEffect(() => {
if (
responseForProject.isFulfilled &&
responseForProject.data &&
responseForProject.data.attributes &&
responseForProject.data.attributes[0] &&
responseForProject.data.attributes[0].textValue !== undefined
) {
let previuousFunctionalTaskData = [];
previuousFunctionalTaskData = JSON.parse(
responseForProject.data.attributes[0].textValue
);
let notDeletedData = previuousFunctionalTaskData.filter(
(data) => data.IsDeleted !== true
);
const projectData = notDeletedData.map((data) => {
return {
...data,
IsAdded: false,
};
});
setTaskData(projectData);
} else if (responseForProject.isError) {
return <ErrorModal message="Error while fetching data..." />;
}
}, [responseForProject]);

React.useEffect(() => {
if (
responseForTypeTask.isFulfilled &&
responseForTypeTask.data &&
responseForTypeTask.data.attributes
) {
setPicklistOptions(
responseForTypeTask.data.attributes.find(
(attribute) => attribute.attributeName === FunctionalTaskComponent
).options
);
} else if (responseForTypeTask.isError) {
return <ErrorModal message="Error while fetching data..." />;
}
}, [responseForTypeTask]);

if (taskData !== [] && options !== []) {
return (
<div style={containerStyle}>
<div>
<div>
<div style={attributeNameStyle}> {component} </div>
<div style={inputBoxProperty}>
<Dropdown
id={'Component'}
defaultValues={attributeValue}
isMultiselect={false}
picklistOptions={mapPicklistValues(
options,
'displayName',
'name'
)}
onValueChange={(newValue) => {
setComponentPicklistAttributeValue(newValue);
}}
/>
</div>
</div>
<div>
<div style={attributeNameStyle}> {departmentName} </div>
<div style={inputBoxProperty}>
<TextBox
value={departmentNameValue}
onEdit={(newValue) => {
setDepartmentNameValue(newValue);
}}
/>
</div>
</div>
<div>
<Button
type="button"
style={addTaskButtonProperty}
onClick={() => handleAddTaskClick({})}
disabled={departmentNameValue === '' || attributeValue === ''}
>
{addTask}
</Button>
</div>
</div>
</div>
);
}
function handleAddTaskClick() {
let taskDetails = {
Component: attributeValue.value,
DepartmantName: departmentNameValue
};
let previuosData = taskData;
previuosData.push(taskDetails);
setTaskData(previuosData);
setComponentPicklistAttributeValue('');
setDepartmentNameValue('');
const editedAttribute = {
canUpdate: true,
name: FunctionalTaskSelection,
textValue: JSON.stringify(taskData),
type: 'Text',
};
pageContext.onAttributeEdit(FunctionalTaskSelection, editedAttribute, '');
}
function mapPicklistValues(picklistOptions, labelKey, valueKey) {
if (!picklistOptions || !picklistOptions.length) {
return [];
}
return picklistOptions.map((item) => {
return {
label: labelKey ? item[labelKey] : item,
value: valueKey ? item[valueKey] : item,
};
});
}
}
const getProject = ({ pageContext }) => {
return {
endpoint: 'instances/' + pageContext.instanceId,
config: {
method: 'GET',
params: {
'attributes[0]': FunctionalTaskSelection,
},
},
};
};
const typeDetailsPromise = () => {
const url = TYPE_DETAILS;
return {
endpoint: url,
config: {
method: 'GET',
},
};
};

Here's a preview of how this custom widget looks like.

target_type_view_layout1.png

On 'Add Functional Task' click, 'Fuctional Task Selection' attribute from Project Details group is updated.

target_type_view_layout2.png