Virtual Agent feature bubble - ServiceNow Community
Mark Roethof
Tera Patron
Tera Patron

Hi there,

Noticed the presentation of the Virtual Agent widget on the HI Service Portal? A nice 'feature bubble' is presented when hovering over the Virtual Agent icon, the hover showing Topics the Virtual Agent can help you with. A nice small addition, which makes the Virtual Agent more attractive. Unfortunately, not available in New York on customer instances or personal developer instances. Who knows, maybe Orlando.

So let's give it a go and see if we can create our own 🙂

HI service Portal

When hovering over the Virtual Agent icon, this is how the feature bubble looks like:

find_real_file.png

Custom Service Portal widget

Our starting point will be our previous Virtual Agent SlideNav Service Portal widget which we've set up a few weeks ago.
Basically, with this custom Service Portal widget the Virtual Agent is not presented through a pop-up though through a SlideNav from the right instead.

Body HTML Template

1) Adding a div element for the bubble

Within the conversation-button-container, add:

find_real_file.png

2) Adding ng mouse elements to display/hide the bubble

On the button tab, add:

ng-mouseenter="c.showConversationBubble()" ng-mouseleave="c.hideConversationBubble()" ng-mousedown="c.showConversationWindow()"

3) Full code:

<!-- Virtual Agent button -->
<div ng-if="user.logged_in" class="conversation-button-container">
  <button data-toggle="modal" data-target="#vaModal" class="help-button" style="background-color: {{::c.options.button_color}}" ng-attr-tabindex="0" ng-mouseenter="c.showConversationBubble()" ng-mouseleave="c.hideConversationBubble()" ng-mousedown="c.showConversationWindow()">
    <div class="hover-overlay"></div>
    <span aria-hidden="true" ng-class="'help-icon icon-open sn-va-widget-icon'" style="background-image: url('{{::c.options.background_image}}')"></span>
  </button>  
  <!-- Conversation bubble-->
  <div ng-if="c.enableConversationBubble" class="conversation-bubble">
    <font class="conversation_bubble_question">{{::data.conversationBubbleQuestion}}</font><br />
    <font class="conversation_bubble_answer">{{c.conversationBubbleAnswer}}</font>
  </div>
</div>

<!-- Modal -->
<div class="modal right fade" id="vaModal" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <iframe class="agent" ng-src="{{c.widget_source}}"></iframe>
  </div>
</div>

 

CSS

1) Adding styling for the bubble:

// Conversation Bubble
.conversation-bubble {
  border: 1px solid #d4d4d4;
  border-radius: $button-dimensions;
  display: block;
  width:285px;
  height: $button-dimensions;
  box-shadow: 0px 2px 11px #ababab;
  -moz-box-shadow: 0px 2px 11px #ababab;
  -o-box-shadow: 0px 2px 11px #ababab;
  padding: 8px 12px;
}

2) Adding styling for the bubble question:

.conversation_bubble_question {
  font-weight: bold;
}

3) Adding styling for the bubble answer:

.conversation_bubble_answer {
  font-style: oblique;
}

4) Full code:

$window-width: 375px;
$button-dimensions: 60px;

// Conversation Bubble
.conversation-bubble {
  border: 1px solid #d4d4d4;
  border-radius: $button-dimensions;
  display: block;
  width:285px;
  height: $button-dimensions;
  box-shadow: 0px 2px 11px #ababab;
  -moz-box-shadow: 0px 2px 11px #ababab;
  -o-box-shadow: 0px 2px 11px #ababab;
  padding: 8px 12px;
}

.conversation_bubble_question {
  font-weight: bold;
}

.conversation_bubble_answer {
  font-style: oblique;
}

// Modal
.modal.right .modal-dialog {
  position: fixed;
  margin: auto;
  width: $window-width;
  height: 100%;
  -webkit-transform: translate3d(0%, 0, 0);
  -ms-transform: translate3d(0%, 0, 0);
  -o-transform: translate3d(0%, 0, 0);
  transform: translate3d(0%, 0, 0);
}

.modal.right .modal-content {
  height: 100%;
  overflow-y: auto;
}

.modal.right .modal-body {
  padding: 15px 15px 80px;
}

.modal.right.fade .modal-dialog {
  right: -$window-width;
  -webkit-transition: opacity 0.3s linear, right 0.3s ease-out;
  -moz-transition: opacity 0.3s linear, right 0.3s ease-out;
  -o-transition: opacity 0.3s linear, right 0.3s ease-out;
  transition: opacity 0.3s linear, right 0.3s ease-out;
}

.modal.right.fade.in .modal-dialog {
  right: 0;
}

// Virtual Agent button
.sn-card-component_records {
  display: block !important;
}

.conversation-button-container {
  position: fixed;
  right: 30px;
  bottom: 15px;
  z-index: 20;
}

.help-button {
  position: relative;
  width: $button-dimensions;
  color: #fff;
  float: right;
  border: none;
  height: $button-dimensions;
  border-radius: $button-dimensions;
  box-shadow: 0px 2px 11px #ababab;
  -moz-box-shadow: 0px 2px 11px #ababab;
  -o-box-shadow: 0px 2px 11px #ababab;
  padding: 0;

  &::before {
    content: "";
    width: $button-dimensions;
    height: $button-dimensions;
    border-radius: 50%;
    position: absolute;
    top: 0;
    left: 0;
  }

  &:hover::before {
    background-color: rgba(0,0,0,0.2);
  }

  .help-icon {
    pointer-events: none;
    position: relative;
  }

  .sn-va-widget-icon {
    height: 32px;
    top: 1px;
    left: 12px;
    width: 36px;
    display: block
  }
}

// Agent Frame
.agent {
  border: 0px;
  height: 100vh;
  width: 100%;
}

 

Server Script

Using the Server Script is new if we look at our previous version of the Virtual Agent Service Portal SlideNav widget. All Server Script is supposed to retrieve the Virtual Agent topics which will be displayed within the feature bubble.

1) Full code:

(function() {
	
	data.conversationBubbleQuestion = gs.getMessage(gs.getProperty('qt_va_conversation_bubble_question'));
	
	var topicArr = [];
	
	var grTopic = new GlideRecord('sys_cs_topic');
grTopic.addEncodedQuery('active=true^design_category!=5628e7f27373230025d032e954f6a7de^ORdesign_category=NULL^design_category!=23242db273603300c1dc32e954f6a79f^ORdesign_category=^name!=system^nameNOT LIKE_');
	grTopic._query();
	
	while(grTopic._next()) {
		topicArr.push(gs.getMessage(grTopic.getDisplayValue()));
	}
	topicArr.join();
	
	data.topicArr = topicArr;
	
})();

 

Client Controller

Within the Client Controller, we need to add controllers that will be used called upon within the Body HTML Template.

1) Full code:

function() {

	var c = this;

	var topicArr = c.data.topicArr;
	c.widget_source = '';
	
	c.showConversationBubble = function() {
		if(!c.conversationBubbleAnswer) {
			var topicStr = topicArr[Math.floor(Math.random() * topicArr.length)];
			c.conversationBubbleAnswer = topicStr;
		}
		c.enableConversationBubble = true;
	}
	c.hideConversationBubble = function() {
		c.enableConversationBubble = false;
		c.expiredConversationBubble = true;
		c.conversationBubbleAnswer = '';
	}
	
	c.showConversationWindow = function() {
		c.widget_source = c.options.url;
	}

}

 

Option schema

The Option schema will be untouched. Though, just in case you don't have the code for this the full code below.

1) Full code:

[ {
  "name" : "background_image",
  "section" : "Presentation",
  "default_value" : "/images/sn-va-sp-widget/sn-va-sp-widget-icon.svg",
  "label" : "Background imeage",
  "type" : "string"
}, {
  "name" : "button_color",
  "section" : "Presentation",
  "default_value" : "#8D8DE0",
  "label" : "Button color",
  "type" : "string"
}, {
  "name" : "url",
  "section" : "Behavior",
  "default_value" : "$sn-va-web-client-app.do?sysparm_skip_load_history=true",
  "label" : "URL",
  "type" : "string"
} ]



Result

With our previous Service Portal widget in mind, we didn't had to add that much code. Basically just a div which presents the feature bubble, adding Server Script to collect the topic names, and adding Client Controller which is called upon in the Body HTML Template to show/hide the div.

The result of this all:

find_real_file.png

Note: Attached to this article is an XML export of this Service Portal widget.

Future enhancement

Having this custom Service Portal widget makes the presentation of the Virtual Agent widget more attractive. Though still… a nice enhancement we came up with would be to have the feature bubble pop-up after N seconds. In the coming days/weeks we will work on this and share an updated version of this Virtual Agent Service Portal widget on the Community.

---

And that's it actually. Hope you like it. If any questions or remarks, let me know!

👍
If this post helped you in any way, I would appreciate it if you hit bookmark or mark it as helpful.

Interested in more articles, blogs, videos, and Share projects on Virtual Agent I published?
Virtual Agent


Kind regards,
Mark

---

LinkedIn

Comments
Brian S5
Kilo Sage

I set this up on our portal, but where it should say "Need Help ? You can ask me about" it says NULL. Do you know what setting controls this ? 

 

find_real_file.png

Mark Roethof
Tera Patron
Tera Patron

Hi there,

I think this has to do with the Server Script and specifically:

data.conversationBubbleQuestion = gs.getMessage(gs.getProperty('qt_va_conversation_bubble_question'));

Did you setup a System Property?

If my answer helped you in any way, please then mark it as helpful.

Kind regards,
Mark

---

LinkedIn
Community article list

Brian S5
Kilo Sage

That did the trick, creating the string property and setting the value as the text worked! Thank you!! 

Chris D
Kilo Sage
Kilo Sage

Great work on this and the timeout functionality - had this on my backlog for a bit and finally got around to implementing it!

Had a few small tweaks to adjust to our VA widget (based off the OOTB one, not using your simplified SideNav one) but got it working all good. The main thing is that the OOTB controller is $ctrl but yours is c, so I just had to change all your c references to $ctrl.

Oh and you had me going a little crazy until I downloaded your XML and saw it correct there... you're missing the variable data inputs in your HTML in these instructions (note the data in curly braces): 

<!-- Conversation bubble-->
<div ng-if="$ctrl.enableConversationBubble" class="conversation-bubble">
  	<font class="conversation_bubble_question">{{::data.conversationBubbleQuestion}}</font><br />
  	<font class="conversation_bubble_answer">{{$ctrl.conversationBubbleAnswer}}</font>
</div>

*edit: ahhhh I see what happened - the HTML code snippet here hides those curly braces. You can see them now since I changed the language to JS. 

I noticed too that a few of our topics with (near-)max length names will break onto a second line at your width (285px). Although it looks slightly worse with the extra whitespace, I did have to increase this width to 325px to account for the longest topic names. Because of this, I also added margin-left:auto; so that the Feature Bubble stays right-aligned when a user opens up the OOTB-style chat window.

Mark Roethof
Tera Patron
Tera Patron

Good finding, tnx!

Somehow if I look at the source code of the article, the ::data and c. are in, though looking at the article... don't see it 😞 I replaced the Conversation bubble code with an image for now.

Kind regards,
Mark
---

LinkedIn
Community article list

Eric M
Giga Expert

Does anyone know if they are adding this into the product yet? it would be good to also have a pop-up type message like you see on allot of public marketing pages. 

Luiz Lucena
Mega Sage

Hi Mark,

This is really nice! Awesome job!

Quick question, if needed to be in pop-up instead of SlideNav from the right, where do we change?

Thanks, in advance,

Mark Roethof
Tera Patron
Tera Patron

Hi Luiz,

I also so your post of yesterday. Is it only about having a pop-up, or is it more? Like after N seconds, only on a specific page, dynamic text, etc?

If it's only a pop-up, don't clone/edit the widget. Just go for overriding the CSS.

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Luiz Lucena
Mega Sage

Hi Mark, 

Thanks for reply. 
Initially, was just a matter of adding a static text to the icon, but then, our community friend, Chris D, presented me to all your posts and my head is still spinning, so many wonderful features and possibilities!

The plan now is to have your custom widget, but as popup (as the ootb) AND as part of the portal, so it would be shown in all pages and user not lose the conversation when switching pages.

My scripting knowledge is still crawling, so if a grab the HTML and CSS from ootb and override on yours, should work? 
Or if I just grab the part where it says "//window sizing" from ootb CSS?

Mark Roethof
Tera Patron
Tera Patron

I did create recently a new widget (cloned from out-of-the-box), which has:
- Pop-up like out-of-the-box
- Can be embedded on header or footer, so it displays on all pages
- Widget visible based on User preferences
- Some additional system properties user for higher maintainability
-- Like a system property for the skip load history, notification sound
- Custom icon when pop-up is closed
- Icon is bigger when pop-up is closed
- Icon is smaller when pop-up is open
- Text balloon with name which is mentioned in the Branding Record and a Static Text which can be changed through UI Messages
- Text balloon which appears after N seconds (system property)
- Text balloon which only appears when opening the Portal through the index page (so not when you would directly open a Catalog Item for example)

Would something like that help?

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Luiz Lucena
Mega Sage

That's really nice, Mark!

Will look into this!

Thanks!

Chris D
Kilo Sage
Kilo Sage

Hey @Mark Roethof,

I thought of this great custom widget (which we're also using) when rereading the Configure the Service Portal Chat Client doc because I noticed that ServiceNow is now officially calling the widget "legacy".

I see the pros of using the new config option over the widget - namely, the easy basic setup to get VA on every portal page* - but at the same time, I think we'd lose all the customization potential of using the (custom) widget.

Have you given this any thought? I don't foresee us switching off the "legacy" widget, but I don't like the formalized title which implies it may be killed off sometime in the future.

*p.s. you mentioned in another comment you updated your widget to "be embedded on header or footer, so it displays on all pages" - I'm definitely intrigued by this but haven't seen you mention/document it before. Was this a simple change?

Mark Roethof
Tera Patron
Tera Patron

Hi Chris,

Since the New York release, there was a switch that Agent Chat configuration is considered the first go to. Agent Chat configuration has some advantages, like the easy configuration, the portal - role combination, etc..

Though Agent Chat configuration also has some huge disadvantages. If you want to have complete different behavior of the Service Portal widget, interaction with other widgets, different presentation which can't be solved by CSS, applying user criteria, dependency on group/department/location... then forget about Agent Chat configuration, you really need to go for a custom Service Portal widget. And for valid reasons.

Also at my most recent VA implementation we have chosen for a custom Virtual Agent widget because of the need to have widgets interacting with each other. So a valid reason to skip Agent Chat configuration.

If my answer helped you in any way, please then mark it as helpful.

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Mark Roethof
Tera Patron
Tera Patron

Embedding on header or footer, is the below what you mean or something else?

find_real_file.png

If my answer helped you in any way, please then mark it as helpful.

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Chris D
Kilo Sage
Kilo Sage

Huh, I feel silly if that's it... 😶

So you just put that (with the proper widget name ofc) in "Body HTML template" of the footer and that's pretty much it, huh? Nice - thanks!

Mark Roethof
Tera Patron
Tera Patron

Yes indeed.

For the Service Portal concerned, locate the Theme used, the Theme contains a Header and Footer. In the Header or Footer (doesn't matter which) you can embed the widget in the Body HTML template. For example at the bottom of the existing code.

Code like:

<widget id="sn-va-sp-widget-chat-client-auto-popup"></widget>

You could also use the widget options:

<widget id="sn-va-sp-widget-chat-client-auto-popup" options='{"va_url_params":"sysparm_skip_load_history=true&sysparm_disable_audio_notifications=true"}'></widget>

If my answer helped you in any way, please then mark it as helpful.

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Ryax
Tera Expert

Hi all,

Does anyone know if there is a similar guide for getting this bubble with the original VA pop-up instead of the SlideNav? I am trying to implement this on our instance and unfortunately do not have a huge amount of experience with CSS or HTML  so struggling to decipher the right bits from what Mark has provided. 

Thanks

Rich

Chris D
Kilo Sage
Kilo Sage

You don't need the SlideNav for this.

I see the confusion in which Mark mentions the SlideNav was the starting point, but it's 100% not a pre-req - I've implemented this here without ever looking into the SlideNav code.

It's been a little bit since I've done so, but I don't think I needed to do anything special adding this to the OOTB widget besides two super minor tweaks mentioned in a different comment: using $ctrl (ootb) instead of $c (Mark's custom) and increasing the width in the CSS.

Just go through his new code blocks and copy and paste them into your widgets into the appropriate spots. Don't get too caught up in the "Full Code" he provided - that's just for reference and perhaps could be confusing if it's referencing SlideNav-unique content and doesn't match the OOTB widget exactly.

Mark Roethof
Tera Patron
Tera Patron

Hi there,

What are you requirements exactly? If it's just a bubble with static text, just plain CSS will do. If it's a bubble a dynamic text, though system property, glide record etc, or a bubble after N seconds, a bubble after interaction with the typeahead search... custom needed widget.

Also see an other article which I wrote, using the out-the-box widget with slight adjustments to communicate with the typeahead search.

Typeahead Search interact with Virtual Agent Service Portal Widget callout 

Kind regards,
Mark
2020 ServiceNow Community MVP
2020 ServiceNow Developer MVP

---

LinkedIn
Community article list

Ryax
Tera Expert

Hi Mark,

Thanks for pointing me towards that article, it wasn't one I had come across before. I was previously trying to pick some of the CSS out of the code above however the new article you linked me to simplified it a bit more and allowed me to get the result I wanted (see below). Thanks again for all the articles, they've been so helpful in assisting with our VA implementation 🙂

find_real_file.png

Rich

S Ram
Tera Contributor

Hi Mark,

Thanks a lot for this really helpful feature addition to the VA widget. When I add it manually to a single page (say the Homepage for ex.), it works like a charm!

But unfortunately, when I add this widget to the Theme footer (to make it available on all portal pages) it wouldn't work. Infact the whole screen freezes and its needs to be reloaded, even clicking other areas doesn't work and the VA widget is not getting rendered properly (cannot see even the default welcome messages, only the top half of the VA chat window header is visible)

Is there something that I'm doing wrong here mate?

harun_isakovic
Mega Guru

I am confused on the part 2, what do you mean on the button tab? I added this to the button element but nothing happens

ng-mouseenter="$ctrl.showConversationBubble()" 
ng-mouseleave="$ctrl.hideConversationBubble()" 
ng-mousedown="$ctrl.showConversationWindow()"
Ryax
Tera Expert

Hi @harun.isakovic 

This is added in the HTML button element, if you take a look at point 3 it shows how the full code for the button should look:

<button data-toggle="modal" data-target="#vaModal" class="help-button" style="background-color: {{::c.options.button_color}}" ng-attr-tabindex="0" ng-mouseenter="c.showConversationBubble()" ng-mouseleave="c.hideConversationBubble()" ng-mousedown="c.showConversationWindow()">
    <div class="hover-overlay"></div>
    <span aria-hidden="true" ng-class="'help-icon icon-open sn-va-widget-icon'" style="background-image: url('{{::c.options.background_image}}')"></span>
  </button>  

Rich

harun_isakovic
Mega Guru

I added this but the feature bubble still isnt popping up. If I force the "$ctrl.enableConversationBubble" to be true then the bubble is visible but it just says "Null" in it, while looking white and square like css isnt even applying to it.

But yeah Im not doing the sidebar either, im just trying to make this work with the OOTB widget look.

I'm confused to what Im missing

Eric Merkle
ServiceNow Employee
ServiceNow Employee

Mark has done some amazing work for our virtual agent and our platform over the years.  In Utah we are introducing a similar, but quite different feature called proactive triggers. Please review the article 

johndoh
Mega Sage

You can also see Enable Virtual Agent Feature Bubble without SlideN... - ServiceNow Community if you would like to implement without the SlideNav

Version history
Last update:
‎12-03-2019 01:50 AM
Updated by: