JSX in Kustomer
Learn how to use JSX components and helpers available in Kustomer to create advanced custom Klass Views.
Klass Views (or KViews) provide a simple way to extend the Kustomer interface without the need for developers to host any code. Klass Views use JSX-based templates to allow developers an to define lightweight UIs easily. This page covers the JSX components and helpers available in Kustomer.
What is JSX?
JSX is a templating language first built as a part of the React JavaScript Framework. If you are completely new to JSX, we recommend that you start with the React JSX docs to learn JSX syntax.
Important
JSX is neither a subset nor a superset of HTML. Even though the syntax may look similar to HTML syntax, there are many meaningful differences between them. For example, to add a
div
with a class in JSX, the syntax is<div className="test-class">Test</div>
. In HTML, the syntax would be<div class="test-class">Test</div>
.Because the syntax for JSX is different from HTML syntax, make sure to modify any HTML into the appropriate JSX syntax.
Kustomer JSX components
Kustomer provides a number of components that can be used in JSX contexts. You can find the relevant JSX components below grouped based on expected application.
Note
All code samples are in JSX format unless otherwise noted.
Layout JSX components
Use Layout components to add structure to a Klass View.
Segment
Use the Segment
component to separate and to pad content.
<Segment style={{ border: '1px solid #6f5499' }}>
<div style={{ background: '#6f5499', color: '#FFF' }}>
Padded Content goes here
</div>
</Segment>
Prop | Values |
---|---|
style | style object |
Grid
Use the Grid
component to wrap rows of Columns
. Each row consists of columns adding up to 16.
<Grid>
// Columns
</Grid>
Prop | Values |
---|---|
style | style object |
Column
Use the Column
component (must be a child of Grid
) to set the width of a column inside a Grid
. Column values are 1-16. All columns in a Grid must add up to 16.
<Column
size="eight"
style={{ border: "1px solid red", padding: '5px' }}
>
Column A
</Column>
<Column
size="eight"
style={{ border: "1px solid blue", padding: '5px' }}
>
Column B
</Column>
Prop | Values |
---|---|
size | one, two, three, four, ... , fifteen, sixteen |
style | style object |
Container
The Container
wraps its children with standard padding. In order to match our visual styles, most KViews should wrap all of their content in a single <Container usePadding>
. Any KView that does not do this will be responsible for defining all of its own padding; if it defines none, its content will extend all the way to the edge of the KView.
<Container usePadding>
All other KView content goes in here.
</Container>
Prop
Values | |
---|---|
usePadding | true, false |
Expanded Timeline components
Use Expanded Timeline components for Klass Views in the timeline.
BasicField
Use the BasicField
component to render a field in the expanded timeline with a label and value. The component ignores all metadata for the field and renders the passed label and value.
<BasicField
label="Order Number Basic"
value={object.custom.orderNumberStr}
/>
Prop | Values |
---|---|
label | the hardcoded label of the field |
value | the label of the field |
Field
Use the Field
component to render a field in the expanded timeline with a label and a value. This component is associated with the metadata of the object. From the object metadata, the component uses the displayName
for the label (if set) and renders the correct type (for example, dates).
<Field
field="orderNumberStr"
key="orderNumberStr"
type="kobject.order"
value={object.custom.orderNumberStr}
/>
Prop | Values |
---|---|
field | name of the custom field |
key | unique identifier of the field (usually same as the field) |
type | the type of the object the field is for (used for mapping to metadata) |
value | the value of the field |
CustomerField
Use the CustomerField
component to render a label and a Customer relationship card in the expanded timeline.
<Customer label="Rider" id={object.custom.riderRel} />
Prop | Values |
---|---|
id | id of Customer object |
label | label for the field |
ConversationField
Use the ConversationField
component to render a label and a Conversation relationship card in the expanded timeline.
<ConversationField label="Convo" id={object.custom.convoRel} />
Prop | Values |
---|---|
id | id of Conversation object |
label | label for the field |
KobjectField
Use the KobjectField
component to render a label and a KObject relationship card in the expanded timeline.
<KobjectField label="Car" type="car" id={object.custom.carRel} />
Prop | Values |
---|---|
id | id of KObject |
type | type of kObject |
label | label for field |
Images
<Images
style={{ border: "1px solid red", padding: '5px' }}
images={
[
"https://placehold.it/400/ffffff/000000",
"https://placehold.it/400/000000/ffffff"
]
}
/>
Prop | Values |
---|---|
images | array of image URL strings |
style | style object |
Smartbar Detail components
Use Smartbar Detail components to extend the details panel at the top right of the Kustomer UI.
BasicRow
Use the BasicRow
component to render a row in the smartbar details with a label and a value. This component ignores all metadata for the field and renders the passed label and value.
<BasicRow
label="Name"
value={object.custom.nameStr}
/>
Prop | Values |
---|---|
label | the hardcoded label of the field |
value | the value of the field |
Row
Use the Row
component to render a row in the smartbar details with a label and avalue. This component is associated with the metadata of the object. From the object metadata, the component uses the displayName
for the label (if set) and renders the correct type (for example, dates).
Additionally, you can edit row components inline (where supported).
<Row
field={'name'}
value={object.name}
object="customer"
/>
Prop | Values |
---|---|
field | the metadata property name of the field |
object | the type of the object: customer, conversation, kobject |
value | the value of the field |
Row Assignment User
Use the RowAssignmentUser
to render a user assignment picker.
<RowAssignmentUser />
Row Assignment Team
Use the RowAssignmentTeam
component to render a team assignment picker.
<RowAssignmentTeam />
Row Priority
Use the RowPriority
to render a priority picker.
<RowPriority />
Relationship components
Use Relationship components to render relationships in the timeline.
Customer
Use the Customer
component to render a customer relationship card for the given customer ID.
<Customer id={object.custom.riderRel} />
Prop | Values |
---|---|
id | Id of customer |
Conversation
Use the Conversation
component to render a conversation relationship card for the given conversation ID.
<Conversation id={object.custom.riderRel} />
Prop | Values |
---|---|
id | id of conversation |
Kobject
Use the Kobject
component to render a KObject relationship card for the given KObject type and ID.
<Kobject type="car" id={object.custom.carRel} />
Prop | Values |
---|---|
id | id of Kobject |
type | type of Kobject |
Button components
Use Button components to define different types of Buttons that can trigger actions from the UI.
Button
Use the Button
component to render the Kustomer primary button style with the ability to pass a size and to pass either an onClick handler or a link.
<Button
size="small"
onClick={() => { alert('clicked') }}
>
Button Text
</Button>
<Button
size="small"
link="https://www.kustomer.com/"
>
Button Link Text
</Button>
Prop | Values |
---|---|
size | xsmall, small, medium |
onClick | JavaScript executed when button clicked |
link | URL to open on click |
ButtonSecondary
Use the ButtonSecondary
component to render the Kustomer secondary button style with the ability to pass a size and to pass either an onClick handler or a link.
<ButtonSecondary
size="small"
onClick={() => { alert('clicked') }}
>
Button Text
</ButtonSecondary>
<ButtonSecondary
size="small"
link="https://www.kustomer.com/"
>
Button Link Text
</ButtonSecondary>
Prop | Values |
---|---|
size | xsmall, small, medium |
onClick | JavaScript executed when button clicked |
link | URL to open on click |
ButtonDanger
Use the ButtonDanger
component to render the Kustomer danger button style with the ability to pass a size and to pass either an onClick handler or a link.
<ButtonDanger
size="small"
onClick={() => { alert('clicked') }}
>
Button Text
</ButtonDanger>
<ButtonDanger
size="small"
link="https://www.kustomer.com/"
>
Button Link Text
</ButtonDanger>
Prop | Values |
---|---|
size | xsmall, small, medium |
onClick | JavaScript executed when button clicked |
link | URL to open on click |
ButtonGroup
Use the ButtonGroup
component to wrap two or more Button
components to give the same spacing between buttons used in Kustomer.
<ButtonGroup>
<Button onClick={() => {}}>
Save
</Button>
<ButtonSecondary onClick={() => {}}>
Cancel
</Button>
</ButtonGroup>
CardJS components
Use CardJS components with iFrames and the Kustomer CardJS library. These components allow you to extend KViews to create more powerful UI experiences. With CardJS components you can embed content formed from multiple frontend files or embed content that contains a backend server experience. Developers using these CardJS components are responsible for hosting the code referenced in the iFrame.
Card
Use the Card
component to render an iFrame to the provided source with a static size. The default modal size is 300x500. After loading the modal, you can use modalHeight
and modalWidth
as optional properties to set the modal dimensions from within the iFrame.
<Card
src="https://your.url"
modalHeight={300}
modalWidth={500}
context={context}
/>
Prop | Values |
---|---|
src | URL for page to be embedded |
modalHeight | the static height of the frame (optional) |
modalWidth | the static width of the frame (optional) |
context | the context to pass to CardJS |
height | the height of the card, optionally including a unit (px, em, etc.) |
DynamicCard
Use the DynamicCard
component to render an iFrame to the provided source. The component resizes to fit content when used with CardJS Kustomer.resize()
. The default modal size is 300x500. After loading the modal, you can use Kustomer.modal.resize({ height: 1000, width: 1000 })
with optional object that contains a height and a width to resize your modal.
<DynamicCard
src="https://your.url"
context={context}
/>
Prop | Values |
---|---|
src | URL for page to be embedded |
context | the context to pass to CardJS |
Data components
Use Data components to insert additional information into the JSX
WithUser
Use the WithUser
component to provide a user to children as a render prop. Render props are a React concept in which a component takes a function to render and passes the function a set of values.
<WithUser id="your-user-id">
{user => <div>{user.displayName}</div>}
</WithUser>
Prop | Values |
---|---|
id | ID of a user in your org |
Helper functions
In addition to components, Kustomer also provides two groups of helper functions in JSX contexts: a subset of lodash methods and moment.js.
lodash
You can use any lodash methods for JSX code in Kustomer. Some of the more popular ones are:
Example:
<div>{_.get(context, 'custom.accountOwner')}</div>
moment
You can use moment.js for Klass Views to format dates. Example code to show a "relative time" created date, such as "5 minutes ago":
<div> {moment(conversation.createdAt).fromNow()} </div>
KustomerRequest
You can use KustomerRequest
to make requests to the Kustomer API. This helper is similar to Kustomer.request in the Cards SDK. You can optionally add a callback to return a string used for a notification displayed to users after the request is made.
Example code to show a button that creates a Customer:
<Button onClick={async () => {
await KustomerRequest({
url: '/v1/customers',
method: 'POST',
body: { name: 'Foo bar' } },
(err, response) => {
if(err) {
return 'Failed to create Customer';
}
return `Successfully created Customer ${response.name}`;
});
}}>
Create Customer
</Button>
runCommand
You can use runCommand
to run commands that your app has defined.
Data Masking Compatibility
If your organization uses Kustomer's Data Masking feature, agents may be unable to see the contents of some fields if those fields are masked. This masking happens all over the application, including inside Klass Views. When you use JSX to construct your own Klass Views, you may need to handle masked data yourself.
Whenever a field is masked, the code will see an object with this shape instead of the normal value:
{ isMasked: true, maskedPresentation: "********" }
The isMasked
boolean lets you know that the field contents are masked, and the maskedPresentation
string gives a display representation of that field. Depending on your data masking settings, maskedPresentation
might show a partially masked representation of the underlying data, or it might be all asterisks.
When you use our JSX field components and pass a masked value to their value
, they will automatically react to masked values, disabling editing and showing the maskedPresentation
. But if you provide your own logic, you may need to check for masked values yourself.
For instance, if you have a Klass View that uses the customer email to build a link to an internal integration:
<div>
<Button
link={`https://my-internal-integration.example.com/customer?email=${encodeURIComponent(customer?.emails?.[0]?.email)}`}
>
Look up customer on internal system
</Button>
</div>
And you later choose to mask the customer email address field, you would have to change that button to either disable or hide if the email address is masked:
<div>
{(() => {
const email = customer?.emails?.[0]?.email;
if (email?.isMasked) {
return (
<Button disabled>
Can't access internal integration for {email.maskedPresentation}
</Button>
);
} else {
return (
<Button
link={`https://my-internal-integration.example.com/customer?email=${encodeURIComponent(email)}`}
>
Look up customer on internal system
</Button>
);
}
})()}
</div>
On the other hand, if you just use a <Field />
and pass it a masked value:
<Row
field={"externalId"}
value={_.get(customer, 'externalId')}
object="customer"
/>
Then you don't need to make any changes to that Klass View. In this example, if customer externalID is masked, the Klass View will show a masked representation and disable editing that field.
Conditional Attributes and Conditionally Required Attributes
You can show or hide all custom and most standard attributes for all standard and custom klasses based on certain criteria, such as the existence of the value of an attribute, or when the value is updated to one you've specified.
Conditional Attributes
To set an attribute as conditional, add both the dependent and controlling attribute as components in your code. For example, if you wanted to create a certain set of conditions to only show the custom attribute returnReasonStr if contactReasonStr is "return," the code for that would look like:
<CollapsedRows>
<Row
field={"contactReasonStr"}
value={_.get(conversation, 'custom.contactReasonStr')}
object="conversation"
/>
<Row
field={"returnReasonStr"}
value={_.get(conversation, 'custom.returnReasonStr')}
object="conversation"
/>
</CollapsedRows>
Then, in the Conditions tab, you could add conditional rendering for the returnReasonStr row:
{
"conversation_custom_returnReasonStr": {
"condition": {
"op": "or",
"values": [
{
"op": "eq",
"property": "conversation_custom_contactReasonStr",
"values": [
"return"
]
}
]
}
}
}
Let's break this down line by line. First, we have the conversation_custom_returnReasonStr key, a custom attribute on the conversation klass. This dependent attribute points to an object that contains conditions that will cause the dependent attribute to be shown or hidden. For a standard attribute, we'd simply remove the custom word from the key (e.g. conversation_channels). Next, we have a condition option that contains two keys:
- op: Refers to "operator" and is a static value.
- values: This is an array of condition objects. Each object denotes to a separate condition. For example, if you wanted to have multiple conditions that would cause returnReasonStr to be displayed, you could include multiple objects. Building on top of the former example, this would look like:
{
"conversation_custom_returnReasonStr": {
"condition": {
"op": "or",
"values": [
{
"op": "eq",
"property": "conversation_custom_contactReasonStr",
"values": [
"return"
]
},
{
"op": "eq",
"property": "conversation_custom_contactReasonStr",
"values": [
"exchange"
]
}
]
}
}
}
This means the returnReasonStr row would be displayed if contactReasonStr is either "return" or "exchange."
In each condition object, there are three keys:
- op: Refers to "operator." Can either
"eq"
(equals),"exists"
, or"startsWith"
. - property: The controlling attribute.
- values:
- When the op is
"eq"
, this array refers to the possible values the attribute could be equal to in order for the condition to be satisfied. - When the op is
"exists"
, this array should be empty. - When the op is
"startsWith"
, this array refers to the possible values that the attribute could start with for the condition to be satisifed. This is supported for multi-level-lists (where it's called "is within" in the UI), and string values.
- When the op is
Conditionally Required Attributes
On top of conditionally rendering components, you can make any custom attribute conditionally required. This is different from globally required attributes that are required to mark any conversation as done -- instead, if the conditions for a given attribute have been met, the dependent attribute will then be required for a conversation to be marked as done. To configure this, you would add dependentAttributeRequired: true to the condition object:
{
"conversation_custom_returnReasonStr": {
"condition": {
"op": "or",
"values": [
{
"op": "eq",
"property": "conversation_custom_contactReasonStr",
"values": [
"return"
],
"dependentFieldRequired": true
}
]
}
}
}
Now, returnReasonStr will be required, but only when the value of returnReasonStr is set to "return". In the event that a conditionally required attribute has not been filled out, and an agent marks a conversation as done, the required fields modal will launch, prompting the agent to input a value for that field.
Self-Pointing Conditional Attributes
You could also create self-pointing conditional attributes, where the presence of a given component relies either on the existence of a value, or a value you specify. Example:
{
"conversation_brand": {
"condition": {
"op": "or",
"values": [
{
"op": "eq",
"property": "conversation_brand",
"values": [
"abc"
]
}
]
}
}
}
In this case, the conversation brand row would only be displayed if the brand value for a conversation was "abc."
Updated over 1 year ago