- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 01-07-2021 06:16 AM
Note: This is part four of a series of articles where we will develop Custom Components with the Now Experience UI Framework. This series started with two articles about custom GraphQL APIs. We are going to use these to fetch data from the platform for our component. In the third article, we created the first component and displayed it in the Contextual Side Panel. Custom Components - Custom GraphQL API Part1 | Custom Components - Custom GraphQL API Part2 | Custom Components - Use GraphQL and Contextual Side Panel
Now that we have deployed our first component to the instance and it is visible in the Agent Workspace, we need to talk about one important point, User Experience. Technically our component does what it should do. It shows the needed information to the agent. But in reality, this component does not provide any value. An agent would not be satisfied with the use of this component and the acceptance to use it would tend towards zero.
What do we need to do?
The agent should be able to quickly see the request of the contact. There should be no overhead and the component should look like everything else in the Workspace. But we don't need to do everything on our own.
ServiceNow wants to enable everyone to build cohesive, consumer-grade user experience on the Now Platform. That’s why they created the Now Experience Design System with the Now Experience Components as one major part of it. These components come with a lot of built-in capabilities that we can leverage without much effort, like:
- Accessibility (WCAG 2.0)
- Desktop and Mobile-Web Ready
- Support for Internationalization
- Compatibility with multiple Browser
And that all with a cohesive design and great documentation. Besides the technical documentation, so that we can use the components in our projects, we also get a documentation of the intended usage of the component so that we can use them in the right way.
In this case, the usage of now-card would make sense.
“Use a card to help the user make a decision or quickly view information.”
That’s what we want to do. It also tells us what we'd better not do. This would be one example:
Visite the documentation for Now Experience Component for more information.
Another Component that we are going to use, to support the agents to quickly identify issues, is the now-highlighted-value.
The Highlighted Value is a label, with or without an Icon, that has a background color as a highlight. This can be used to either highlight a status, to quickly separate the important things from the less important things, or it can be used as an indication of a category to group similar content.
One important point is that you are aware that certain colors are used to convey a specific message. Don’t use a critical status color to group non-critical stuff. Better pick a category color.
Whit that in mind, let's make our component pretty.
Each Request should be one card that contains a separate card for every Requested Item. We use the now-label-value-tabbed Component to display the attributes and the values. First, we need to install the packages and import them into our project.
npm install @servicenow/now-card
npm install @servicenow/now-highlighted-value
npm install @servicenow/now-label-value
import "@servicenow/now-card";
import "@servicenow/now-highlighted-value";
import "@servicenow/now-label-value";
There are three pieces of information that we want to show in the header of the request card. A shopping cart icon, to indicate that this is a request, the number of the request, and a Highlighted Value for the status of the request.
<now-card>
<now-card-header tagline={{ "label": request.number, "icon": "shopping-cart-outline" }} >
The card header provides a slot called “metadata” which we can use for the Highlighted Value.
{request.state == "Closed Incomplete" ?
<now-highlighted-value slot="metadata" color="critical" label={request.state}></now-highlighted-value> : request.state == "Closed Complete" ?
<now-highlighted-value slot="metadata" color="positive" label={request.state}></now-highlighted-value> :
<now-highlighted-value slot="metadata" color="info" label={request.state}></now-highlighted-value>
}
</now-card-header >
<now-card-divider></now-card-divider>
Inside the request card, we place our cards for the Requested Items. Here we change the icon to something different. To keep the card small we only show the Catalog Item and the Stage using the now-label-value-tabbed.
{request.items.map(item =>
<now-card>
<now-card-header tagline={{ "label": item.number, "icon": "configuration-item-outline" }} > </now-card-header>
<now-card-divider></now-card-divider>
<now-label-value-tabbed size="sm" items={[{ label: "Catalog Item", value: item.cat_item }, { label: "Stage", value: item.stage }]} />
</now-card>
)}
</now-card>
This is what it should look like:
Because the variables are not always necessary, we don’t want to show them directly to keep the card small. So we use a now-popover together with a now-button-stateful. If the Requested Item has no variables, we disable the Button.
<now-popover>
<now-button-stateful slot="trigger" icon="ellipsis-h-outline" disabled={item.variables.length == 0} />
<now-card slot="content">
<now-card-header tagline={{ "label": "Variables" }} > </now-card-header>
<now-card-divider></now-card-divider>
<now-label-value-tabbed size="sm" items={item.variables.map(variable => { return { label: variable.question_text, value: variable.value } })} />
</now-card>
</now-popover>
This is how the end result looks like when we deploy it to our instance.
In the next article, we will look at scripted modals.
Custom Components - Scripted Modal
Complete index.js
import snabbdom from '@servicenow/ui-renderer-snabbdom';
import styles from './styles.scss';
import { createCustomElement, actionTypes } from '@servicenow/ui-core';
const { COMPONENT_BOOTSTRAPPED } = actionTypes;
import { createGraphQLEffect } from '@servicenow/ui-effect-graphql';
import "@servicenow/now-card";
import "@servicenow/now-highlighted-value";
import "@servicenow/now-label-value";
import "@servicenow/now-button";
import "@servicenow/now-popover";
const DATA_FETCH_SUCCEEDED = 'DATA_FETCH_SUCCEEDED';
const DATA_FETCH_FAILED = 'DATA_FETCH_FAILED';
const requestQuery = `
query($contactID: ID!){
x413019 {
agineo {
contactRequest(contactID: $contactID) {
number
state
items{
number
stage
cat_item
variables{
question_text
value
}
}
}
}
}
}`;
const dataFetchHandler = createGraphQLEffect(requestQuery, {
variableList: ['contactID'],
successActionType: DATA_FETCH_SUCCEEDED,
errorActionType: DATA_FETCH_FAILED
});
const view = (state, { updateState }) => {
return (
<div>
{state.requests.map(request =>
<now-card>
<now-card-header tagline={{ "label": request.number, "icon": "shopping-cart-outline" }} >
{request.state == "Closed Incomplete" ?
<now-highlighted-value slot="metadata" color="critical" label={request.state}></now-highlighted-value> : request.state == "Closed Complete" ?
<now-highlighted-value slot="metadata" color="positive" label={request.state}></now-highlighted-value> :
<now-highlighted-value slot="metadata" color="info" label={request.state}></now-highlighted-value>}
</now-card-header>
<now-card-divider></now-card-divider>
{request.items.map(item =>
<now-card>
<now-card-header tagline={{ "label": item.number, "icon": "configuration-item-outline" }} > </now-card-header>
<now-card-divider></now-card-divider>
<now-label-value-tabbed size="sm" items={[{ label: "Catalog Item", value: item.cat_item }, { label: "Stage", value: item.stage }]} />
<now-popover>
<now-button-stateful slot="trigger" icon="ellipsis-h-outline" disabled={item.variables.length == 0} />
<now-card slot="content">
<now-card-header tagline={{ "label": "Variables" }} > </now-card-header>
<now-card-divider></now-card-divider>
<now-label-value-tabbed size="sm" items={item.variables.map(variable => { return { label: variable.question_text, value: variable.value } })} />
</now-card>
</now-popover>
</now-card>
)}
</now-card>
)}
</div>
);
};
createCustomElement('x-413019-contact-req', {
renderer: { type: snabbdom },
view,
styles,
initialState: {
requests: []
},
properties: {
sysId: { default: "9951981c2f996410e1717f572799b6df" }
},
actionHandlers: {
"FETCH_DATA_REQUESTED": dataFetchHandler,
[COMPONENT_BOOTSTRAPPED]: (coeffects) => {
coeffects.dispatch("FETCH_DATA_REQUESTED", { contactID: coeffects.properties.sysId });
},
[DATA_FETCH_SUCCEEDED]: (coeffects) => { console.log(coeffects.action.payload); coeffects.updateState({ requests: coeffects.action.payload.data.x413019.agineo.contactRequest }) }
}
});
- 1,619 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
hey, so i need something more simple, only fetch current incident number into the component. how can i do that?