Last updated December 4, 2020
Introduction to the frame building library used to create custom, interactive interfaces.
Create custom interfaces using our modular frame building library. These custom interfaces can be displayed in many places across the application: task panel, order overview, dashboard and more.
This is implemented as an iframe that, as of writing, an implementation manager must setup using internal tools. The iframe runs a hook that both performs any API actions required by the hook and returns a configuration object, which is rendered by the front-end library. The configuration object has an array of sections composed of one or many display elements.
Sections are the top-level structural component of the configuration object.
module.exports = async (ctx) => {
return {
body: {
sections: [{
// sections formatted here
}]
}
};
}
Every section returned requires a title and an array of elements.
type Section = {
title: string;
width?: string; // recommended as a percentage
elements: AnyElement[];
};
export type DescriptionElement = {
key: string;
type: 'description';
text: string;
displayOptions?: DisplayOptions;
};
Use Cases: Great for displaying contextual information and links
Break: Introduce a line break - useful to visually breakup elements within a section
export type BreakElement = {
type: 'break';
};
These components either write data to the model or trigger an action.
Note: The model is included in the payload sent to the hook for button clicks, which allows for configuration of the action before clicking a button.
Input: Simple input field to write to the model. Placeholder is an option field to display to the user when the input is empty.
export type InputElement = {
key: string;
type: 'input';
placeholder?: string;
};
Dropdown: Single-select dropdown for short lists. When an option is selected, the dropdown value will be written to the model with its key in the model matching the key of the element.
export type DropdownElement = {
key: string;
type: 'dropdown';
options: {
value: string;
title: string;
displayOptions?: DisplayOptions;
}[];
allowNone?: boolean;
placeholder?: string;
};
Options:
- See Display Options for how to show or hide options in the dropdown via the option display options element
allowNone
will permit the user to unselect a value from the dropdown - good for a default valueplaceholder
value presented to the user as a default value isallowNone
is true or as a message to the user to display
Table: Table component with selectable rows, similar to the dropdown only writing the row object to the model rather than a single value
export type TableElement = {
key: string;
type: 'table';
perPage?: number;
schema: {
fields: {
name: string;
key: string;
type: 'string' | 'number' | 'date';
}[];
sort: {
ascending: boolean;
key: string;
}
};
values: any[];
};
Button: Simple button for users to trigger actions.
export type ButtonElement = {
key: string;
type: 'button';
text: string;
displayOptions?: DisplayOptions;
};
If many buttons are listed in a single section, they will arrange neatly inline. When a button is clicked, the frame will make a request to the provided hook indicating that a specific button was pressed. While this request to the hook is pending, the button will remain in a loading state, and all other interactive elements will be disabled.
When a button is clicked it passes the key of the clicked button back to the hook, so the same hook must also handle the click by key.
const handleClick = async (ctx) => {
if (ctx.body.key === 'buttonKey1') {
return {
body: {} // Action
};
}
if (ctx.body.key === 'buttonKey2') {
return {
body: {} // Action
};
}
return {
status: 400,
body: {
message: 'Unrecognized click',
},
};
}
export type RedirectAction = {
action: 'redirect';
url: string;
inApp?: boolean;
target?: '_blank'|'_top';
}
export type MessageAction = {
action: 'message';
text: string;
}
export type ModalAction = {
action: 'modal';
url: string;
title?: string;
}
export type FormAction = {
action: 'form';
shouldRefresh?: boolean;
formId: string;
}
export type NewForm = {
action: 'new_form';
shouldRefresh?: boolean;
schemaId: string;
}