This guest blog post was written by Rona Kwestel, the head of technical communication at Axoni. She transitioned from software development to technical writing almost 20 years ago and hasn’t looked back since. Flare has been her tool of choice since first testing it out in the late aughts, and she especially likes to play with the interactive features that enhance the user experience.
An important feature of any published documentation is the ability to gather feedback from readers. Not only does it enable the writer to improve the presentation and content of the documentation, but it gives readers a direct line to the people tasked with making the product comprehensible and usable. That feedback loop empowers users and gives the writer a better understanding of what users need and how they use the product.
The Challenge
Without benefit of a web development team or a third-party add-on, it can be difficult to add a feedback mechanism to your documentation. Initially, I created my own from scratch - a button at the bottom of every page, a form that appeared upon clicking the button, and a script to capture both the form contents and the page URL and populate an email message with that information - but it required invoking the user's email client to send the message since we have no backend server. This is not only clunky, but it precludes the ability to send anonymous feedback and can also be a point of failure if the user's email client is not configured to allow it.
The Aha Moment
While viewing another company's documentation that I knew was also built using MadCap Flare, I noticed their seamless interface for sending feedback, so I sent a message hoping the writer would be willing to share with me how they set it up. Not only did I hear back right away - fellow technical writers really like to help each other! - but it opened up a whole new option I had never heard of before: the Jira Issue Collector. If you're already using Jira for issue tracking, this well-hidden feature turns Jira into the missing backend piece of the puzzle at no extra cost!
The Solution
It turns out that you can set up a Jira project with one or more issue collectors that allow both internal and external users to create tickets in that project. When users populate and submit the issue collector form that gets triggered by the click of a button, it produces a ticket in the corresponding Jira project complete with the user's message, their environment, including the URL from which they sent the feedback, and, optionally, their name and email address if they want you to respond back to them. You can then set up either Jira notifications or a Slack integration to be notified whenever a ticket is added to the project, and then use that ticket to track updates to the documentation accordingly.
It also turns out that it can be pretty tricky to set this up even when following the Jira documentation. My design was to have a Feedback button on the right side of every page, which, when clicked, would expose a set of buttons indicating the helpfulness of the content. Clicking on one of those buttons would raise the correspondingly tailored Jira form for users to submit their feedback. Here's what it looks like in action:
How to Do It
The rest of this article describes how I developed and put all the pieces together.
The work was done in a MadCap Flare project, but it should work in any tool where you have control over the HTML, CSS, and JavaScript.
The issue collectors described here were created in Jira Server, but word on the street indicates that no substantive changes are needed when migrating this solution to Jira Cloud.
Creating the Doc Feedback Project
In order to keep the feedback tickets separate from other tickets, we (I needed the help of a full Jira Admin) created a new Doc Feedback (DF) project in Jira. The project creates only tickets of type Task and has the typical fields of Summary, Description, and Reporter along with a few others.
Most importantly, it has the Environment field added to its Create Issue and Edit/View Issue screens for Task tickets, since this is where the user's environment will be captured when they submit feedback. This data includes the URL from which they invoked the form, which is how we know exactly which page they were viewing when they submitted the feedback.
Additionally, since I wanted to support the option of users specifying whether they found the documentation helpful, we created a Helpful? custom field with values Yes, Somewhat, and Not really, and assigned it to the Create Issue and Edit/View Issue screens for Task tickets in the DF project. This would allow us to get a quick summary of how we're doing with the documentation, though admittedly people are most likely to comment when they find the documentation unhelpful.
The Jira issue collector has a Got Feedback? template that I opted not to use because I preferred to use my own wording and styling around those options and wanted to tailor the form with verbiage reflecting the user's selection.
Creating the Issue Collectors
Because I wanted users to first specify whether they found the content helpful so I could tailor the Jira pop-up form and set the value of the Helpful? field in the underlying JavaScript code accordingly, I needed to create 3 issue collectors - one for each value. I opened the Doc Feedback project in Jira, selected the Issue Collectors tab at the bottom of the Project Settings page (you need project-level admin privileges to do this) and clicked on Add issue collector to create the first one. Then I copy/pasted that one to create the other two, modifying the following settings in each:
Field |
Value |
Notes |
---|---|---|
Name |
|
a name for each of the issue collectors based on its Helpful? value |
Description |
|
a general description of the issue collector |
Issue Type |
Task |
the default issue type the project defines for creation; in our case, this is a Task |
Reporter |
|
the default issue reporter when the issue is created by an external user; in our case, I specified myself |
Match reporter? |
Attempt to match submitter email address |
this allows internal Jira users to be reflected as the reporter when they submit feedback |
Collect browser info |
this collects the environment data of the user (per their consent), including the browser type, the screen resolution, the referral header, and the URL of the page from which the feedback was collected | |
Trigger text |
|
the text that will appear on the trigger button |
Trigger style |
Custom |
this allows creating a custom JavaScript function to control the trigger behavior |
Template |
Custom |
this allows creating a custom form |
Custom fields |
|
this includes a list of fields enabled for the Create Issue screen; as you check each one you want (the only one I checked was Description), it appears in the preview of the form below it; leave ones you don't want to include there unchecked I could have checked the Helpful? field to add it to the form and then would only have needed one issue collector, but it includes the option of None, which I didn't want to expose, and a single issue collector wouldn't allow me to tailor the message at the top of the form based on the user's Helpful? selection. |
Message |
|
a message for each of the issue collectors based on the selected value of Helpful? |
Generated Issue Collector Embedding Code
Once the issue collector fields are set and you click on Submit, it provides the code you'll need to add to your project. The code for each issue collector is the same except for the unique collectorId
that distinguishes one from the other. You can always find the code again by going back to the Issue Collectors tab, selecting the desired issue collector, and scrolling down to the Embedding this issue collector section. You can choose between Embed in HTML and Embed in JavaScript; I initially started with HTML, but later moved it to a script to keep the master page clean.
The HTML Embedding code appears as follows (identifying IDs are replaced here with <!--relevant-comment-->
):
<script type="text/javascript" src="https://jira.<!--company-->.com/s/<!--hex-->-CDN/kcgegi/813004/<!--hex-->/2.2.4.7/_/download/batch/com.atlassian.plugins.jquery:jquery/com.atlassian.plugins.jquery:jquery.js?collectorId=<!--collectorID-->"></script>
<script type="text/javascript" src="https://jira.<!--company-->.com/s/<!--hex-->-T/kcgegi/<!--hex-->/4.0.4/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=<!--collectorID-->"></script>
If you paste the embedded URLs in the generated code into a browser, you can see what the code looks like. The first one contains the jQuery JavaScript library with some Atlassian modifications and the second one contains all the form-related code.
Important note on these scripts:
- The first script, which downloads a jQuery library, is not only not necessary in the context of Flare, it causes a conflict that breaks other Flare features in the output, like the display of the search results and the smooth operation of the dropdown menus in the left navigation pane. This is because Flare already loads a later version of a jQuery library and the loading of the one from Jira effectively downgrades the version. To avoid this issue, do not include the first script in your code.
- The second script contains an
&
character in the URL, which is a special character in HTML but not in JavaScript, so if the script is embedded in:- a JavaScript file, leave the code as is:
...?locale=en-US&collectorId...
- an HTML file, replace the
&
character with the HTML character code & to avoid build errors:...?locale=en-US&collectorId...
- a JavaScript file, leave the code as is:
<a href="#" id="atlwdg-trigger" class="atlwdg-trigger atlwdg-RIGHT">Provide feedback</a>
and applied the following CSS to the anchor tag's classes:
.atlwdg-trigger {
position: fixed;
background: #205081;
padding: 5px;
border: 2px solid white;
border-top: none;
font-weight: bold;
color: white !important;
display: block;
white-space: nowrap;
text-decoration: none !important;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
-webkit-box-shadow: 0 0 5px rgb(0 0 0 / 50%);
box-shadow: 0 0 5px rgb(0 0 0 / 50%);
border-radius: 0 0 5px 5px;
}
.atlwdg-trigger.atlwdg-RIGHT {
left: 100%;
top: 40%;
-webkit-transform: rotate(90deg);
-webkit-transform-origin: top left;
-moz-transform: rotate(90deg);
-moz-transform-origin: top left;
-ms-transform: rotate(90deg);
-ms-transform-origin: top left;
transform: rotate(90deg);
transform-origin: top left;
}
Customized Trigger Code
To customize the look and feel of the Feedback button, I switched the issue collector's Trigger style to Custom and added the following anchor tag inside the <body>
tag of the master page:
<!-- Feedback button -->
<a href="#" id="feedback-button" class="feedback-button feedback-RIGHT">Feedback</a>
The anchor's id of feedback-button
is needed to set a click
function on that element in the script, while the classes feedback-button
and feedback-RIGHT
are used for styling in the CSS.
Then I added the following CSS to the stylesheet using the feedback-button
and feedback-RIGHT
classes defined on the Feedback button:
.feedback-button,
.feedback-button:link,
.feedback-button:visited {
position: fixed;
background: #ff5722;
padding: 5px;
border: 2px solid white;
border-top: none;
color: white;
display: block;
white-space: nowrap;
text-decoration: none;
font-size: 0.8em;
-webkit-box-shadow: 0 0 5px rgb(0 0 0 / 50%);
box-shadow: 0 0 5px rgb(0 0 0 / 50%);
border-radius: 0 0 5px 5px;
}
.feedback-button:hover {
color: black;
text-decoration: none;
}
.feedback-button.feedback-RIGHT {
left: 100%;
top: 40%;
-webkit-transform: rotate(90deg);
-webkit-transform-origin: top left;
-moz-transform: rotate(90deg);
-moz-transform-origin: top left;
-ms-transform: rotate(90deg);
-ms-transform-origin: top left;
transform: rotate(90deg);
transform-origin: top left;
}
The :link
and :visited
selectors eliminate the need to add the !important
qualifier on some of the attributes, and the attributes defined in the :hover
selector turn off the default underline for hyperlinks and change the color instead so the user recognizes that the button is active.
Creating the Feedback Helpful Container
To create a container for the Was this page helpful? buttons that would then invoke their corresponding issue collector form, I added the following code inside the <body>
tag of the master page:
<!-- Feedback helpful container -->
<div class="feedback-helpful-container">
<p class="feedback-title">Was this page helpful?</p>
<a href="" class="feedback-trigger" id="feedback-trigger-yes">Yes</a>
<a href="" class="feedback-trigger" id="feedback-trigger-somewhat">Somewhat</a>
<a href="" class="feedback-trigger" id="feedback-trigger-not-really">Not really</a>
</div>
The buttons are again defined as anchor tags, with the common feedback-trigger
class used to style the buttons generally, and the unique element id
s used to target each button individually for its background color in the CSS and its click
function in the script.
Then I added the following code to the stylesheet, where the :link
, :visited
, and :hover
selectors serve the same purpose as was described for the Feedback button:
.feedback-helpful-container {
display: none;
position: fixed;
top: 35%;
right: 2.5em;
padding: 1em;
background-color: white;
border: 1px solid #d0d3d5;
border-radius: 0.8em;
box-shadow: 0px 0px 5px rgb(0 0 0 / 50%);
}
.feedback-title {
margin-bottom: 0.5em;
white-space: nowrap;
font-weight: bold;
}
.feedback-trigger,
.feedback-trigger:link,
.feedback-trigger:visited {
color: white;
text-decoration: none;
text-align: center;
margin: 0.5em;
padding: 0.1em 0.4em;
border-radius: .25em;
font-size: .9em;
white-space: nowrap;
display: block;
width: 11em;
}
.feedback-trigger:hover {
color: black;
text-decoration: none;
}
#feedback-trigger-yes {
background: #4caf50;
}
#feedback-trigger-somewhat {
background: #ffc107;
}
#feedback-trigger-not-really {
background: #ff5722;
}
Creating the Feedback Collector Script
The feedback-collector.js
script is where all the pieces are tied together.
- When the page is ready, it loads all the Jira issue collector scripts and sets a
click
function on thefeedback-button
element. It also defines atriggerFunction
for each uniquecollectorId
that in turn defines aclick
function for the correspondingfeedback-trigger
button id, which invokes Jira'sshowCollectorDialog
function and toggles off thefeedback-helpful-container
. - Any field values enabled for the Create Issue screen in the Doc Feedback project can be manually set in the
fieldValues
attribute. Here I setrecordWebInfo
andrecordWebInfoConsent
to1
to default the checkbox for collecting environment data to checked (the user still has the option to uncheck it), and I setcustomfield_12509
(the custom field ID of the Helpful? field) to the custom value corresponding to the selectedfeedback-trigger
button (see the Tip below for how to find these IDs).
feedback-collector.js
/* Doc Feedback via Jira Issue Collector */
/* When the user clicks on the Feedback button, a "Was this page helpful" container with responses is displayed. Based on the user's selection, the corresponding Jira Issue Collector form with that "Helpful" value preset is raised for the user to populate and submit. This generates a Jira ticket in the Doc Feedback project. */
/* Some resources:
https://confluence.atlassian.com/adminjiraserver089/using-the-issue-collector-1005346596.html
https://confluence.atlassian.com/adminjiraserver089/advanced-use-of-the-jira-issue-collector-1005346607.html
https://community.atlassian.com/t5/Confluence-questions/Issue-collector-How-do-I-always-collect-the-URL-of-the-source/qaq-p/1030542
*/
function loadScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
$('head').append(script);
}
function loadJiraScripts() {
/* Helpful issue collector */
loadScript('https://jira.<!--company-->.com/s/<!--hex-->-T/kcgegi/813004/<!--hex-->/4.0.4/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=<!--collector ID-->');
/* Somewhat Helpful issue collector */
loadScript('https://jira.<!--company-->.com/s/<!--hex-->-T/kcgegi/813004/<!--hex-->/4.0.4/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=<!--collector ID-->');
/* Not Helpful issue collector */
loadScript('https://jira.<!--company-->.com/s/<!--hex-->-T/kcgegi/813004/<!--hex-->/4.0.4/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=<!--collector ID-->');
}
function toggleFeedbackHelpful() {
$('.feedback-helpful-container').toggle(500); // toggle the feedback helpful container
}
window.ATL_JQ_PAGE_PROPS = {
/* Each of these feedback form trigger functions specifies the Helpful field - customfield_12509 - according to the button the user clicks. In order for this to work, the Helpful field must be added to the Create Issue screen for the Doc Feedback project in Jira. */
/* Helpful */
'f3b701a3': {
"triggerFunction": function(showCollectorDialog) {
jQuery("#feedback-trigger-yes").click(function(e) {
e.preventDefault();
showCollectorDialog();
toggleFeedbackHelpful();
});
},
fieldValues: {
// Default the checkbox to include environment data to checked
recordWebInfo: '1',
recordWebInfoConsent: ['1'],
// Set the "Helpful?" field to "Yes"
customfield_12509: '12523'
}
},
/* Somewhat helpful */
'2e8f0107': {
"triggerFunction": function(showCollectorDialog) {
jQuery("#feedback-trigger-somewhat").click(function(e) {
e.preventDefault();
showCollectorDialog();
toggleFeedbackHelpful();
});
},
fieldValues: {
// Default the checkbox to include environment data to checked
recordWebInfo: '1',
recordWebInfoConsent: ['1'],
// Set the "Helpful?" field to "Somewhat"
customfield_12509: '12524'
}
},
/* Not helpful */
'db5583b4': {
"triggerFunction": function(showCollectorDialog) {
jQuery("#feedback-trigger-not-really").click(function(e) {
e.preventDefault();
showCollectorDialog();
toggleFeedbackHelpful();
});
},
fieldValues: {
// Default the checkbox to include environment data to checked
recordWebInfo: '1',
recordWebInfoConsent: ['1'],
// Set the "Helpful?" field to "Not really"
customfield_12509: '12525'
}
}
};
$(document).ready(function() {
/* Load all the Jira issue collector scripts */
loadJiraScripts();
/* Set a click function on the feedback button to toggle the feedback helpful container in/out each time it's clicked */
$('#feedback-button').click(function() {
toggleFeedbackHelpful();
})
});
How to Find a Custom Field ID
To get the ID of any custom field along with the IDs of its values, click on the Create button in that project in Jira to start creating a new ticket, then right click on that field, select Inspect to view it in the Google Chrome Inspector, and find the HTML code that looks like this:
<select class="select cf-select" name="customfield_12509" id="customfield_12509">
<option value="-1">None</option>
<option value="12523">Yes</option>
<option value="12524">Somewhat</option>
<option value="12525">Not really</option>
</select>
Here, customfield_12509
is the ID of the field, and 12523
, 12524
, and 12525
are the IDs of the values for that field.
Including the Script in the Master Page
The last step is to add the feedback-collector.js
script to the Content/Resources/Scripts
folder of the project and include it in the <head>
tag of the master page.
<!-- Feedback Collector -->
<script type="text/javascript" src="../Scripts/feedback-collector.js"></script>
Integrating with Slack
Because I'm not a fan of Jira notification emails, our Jira Admin set up a Slack integration so that any time new tickets are created in the Doc Feedback project, a notice appears in a Slack channel named doc-feedback
. This allows me and any other channel members to be aware of feedback without the need to manage email messages. Now we wait and hope for helpful feedback!