Skip to content

Commit

Permalink
Improve feedback widget (#197)
Browse files Browse the repository at this point in the history
- widget moved to the left, and is part of the html output rather than
being a floating js object. Because the right ToC has `position: sticky`
and scrolls itself as you progress through a page, I was unable to leave
the feedback on the right _and_ avoid it shadowing some content. I've
made the nav on the left slightly smaller so people can't think there's
anything underneath the feedback. It gets expanded to full height when
the feedback vanishes.
- after leaving feedback, politely thanks you and vanishes within a
couple seconds
- you _can't_ leave negative feedback without adding a message
- you _can_ leave a message to a positive feedback (but may also leave
it empty)
- the user journey through docs pages is registered and submitted as
part of their feedback
- taken away the `Edit this page` links, and improved style/wording
overall

----

Depends on 

neo4j-labs/neo4j-labs.github.io#3
neo4j-contrib/dx-feedback-form#2
  • Loading branch information
stefano-ottolenghi authored Nov 3, 2023
1 parent 23a2d0f commit 5303ef8
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 64 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
"brace-style": ["warn", "1tbs", { "allowSingleLine": true }],
"max-len": [1, 160, 2],
"spaced-comment": "off"
},
"env": {
"browser": true
}
}
15 changes: 12 additions & 3 deletions src/css/feedback.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.feedback {
position: fixed;
bottom: 0;
right: 2rem;
left: 1rem;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
background: var(--feedback-background-color);
color: var(--feedback-color);
padding: 0.5rem 1rem;
width: 320px;
width: calc(var(--nav-width) - 2rem);
font-size: 0.8rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
z-index: 1000;
Expand Down Expand Up @@ -74,6 +74,10 @@
font-size: 0.8rem;
}

.feedback div.more-information {
margin: 10px 0;
}

.feedback textarea {
border-radius: 0.25rem;
border: 1px solid var(--color-blue-300);
Expand Down Expand Up @@ -105,6 +109,7 @@
font-weight: 600;
margin-right: 0.5rem;
margin-bottom: 1rem;
cursor: pointer;
}

.feedback .primary:focus,
Expand Down Expand Up @@ -151,6 +156,10 @@
margin-bottom: 0.5rem;
}

.feedback .error {
font-size: 0.8rem;
}

@media all and (max-width: 1024px) {
.feedback {
position: relative;
Expand All @@ -159,7 +168,7 @@
max-width: var(--doc-max-width);
min-width: 0;
right: auto;
width: auto;
width: calc(var(--nav-width) - 2rem);
box-shadow: none;
margin-bottom: 2rem;
}
Expand Down
6 changes: 4 additions & 2 deletions src/css/vars.css
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,9 @@
--nav-panel-height: calc(var(--nav-height) - var(--drawer-height));
--nav-panel-height--desktop:
calc(
var(--nav-height--desktop) - var(--drawer-height)
);
var(--nav-height--desktop) - var(--drawer-height) - var(--feedback-height) - 1rem
); /* 1rem is feedback padding */

--nav-width: 18rem;
--toc-top: calc(var(--body-top) + var(--toolbar-height));
--kb-metadata-top: calc(var(--body-top) + var(--toolbar-height));
Expand All @@ -392,6 +393,7 @@
--doc-max-width: calc(720 / var(--rem-base) * 1rem);
--doc-max-width--desktop: calc(980 / var(--rem-base) * 1rem);
--cheat-sheet-max-width--desktop: calc(1100 / var(--rem-base) * 1rem);
--feedback-height: 2.75rem;

/* stacking */
--z-index-nav: 1;
Expand Down
180 changes: 128 additions & 52 deletions src/js/11-feedback.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
const { getCookie } = require('./modules/cookies')
const URL = 'https://uglfznxroe.execute-api.us-east-1.amazonaws.com/dev/Feedback'

/* global fetch */
;(function () {
'use strict'

var url = 'https://uglfznxroe.execute-api.us-east-1.amazonaws.com/dev/Feedback'
const updateUserJourney = function () {
var journey = JSON.parse(localStorage.getItem('userJourney'))
if (journey == null) journey = []
journey.push({
url: window.location.href,
title: document.title,
landTime: Math.round(Date.now() / 1000),
})
localStorage.setItem('userJourney', JSON.stringify(journey))
}
updateUserJourney()

var feedback = document.querySelector('.feedback')
if (!feedback) return

var original = feedback.innerHTML

var edit = ''
var editLink = document.querySelector('.edit-this-page a')
/*var editLink = document.querySelector('.edit-this-page a')
if (editLink) {
edit = ' <a href="' + editLink.getAttribute('href') + '" target="_blank">Edit this page</a>'
}
}*/

var sendFeedback = function (helpful, reason, moreInformation) {
var sendRequest = function (parameters) {
const identity = getCookie('neo_identity')
const gid = getCookie('_gid')
const uetsid = getCookie('_uetsid')
Expand All @@ -30,7 +42,6 @@ const { getCookie } = require('./modules/cookies')

var body = 'project=' + encodeURIComponent(project)
body += '&url=' + encodeURIComponent(window.location.href)
body += '&helpful=' + helpful.toString()

if (identity) {
body += '&identity=' + identity
Expand All @@ -42,72 +53,125 @@ const { getCookie } = require('./modules/cookies')
body += '&uetsid=' + uetsid
}

if (!helpful) {
body += '&reason=' + encodeURIComponent(reason)
for (const [paramKey, paramVal] of Object.entries(parameters)) {
body += '&' + paramKey + '=' + encodeURIComponent(paramVal)
}

if (moreInformation) {
body += '&moreInformation=' + encodeURIComponent(moreInformation)
}
var userJourney = localStorage.getItem('userJourney')
if (JSON.parse(userJourney).length > 1) {
body += '&userJourney=' + encodeURIComponent(userJourney)
}

fetch(url, {
//console.log(body)
fetch(URL, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
})
localStorage.removeItem('userJourney')
}

var isHelpful = function () {
sendFeedback(true)

feedback.classList.add('positive')
feedback.style.height = null
feedback.innerHTML = `<form class="form">
<div class="header">
<p><strong>Thank you! Would you like to share some feedback?</strong></p>
<svg width="14px" height="22px" viewBox="0 0 22 22" role="button" class="cancel" aria-label="Cancel Feedback">
<line x1="19.5833333" y1="0.416666667" x2="0.416666667" y2="19.5833333"></line>
<line x1="19.5833333" y1="19.5833333" x2="0.416666667" y2="0.416666667"></line>
</svg>
</div>
<div class="more-information">
<label for="more-information"><strong>More information</strong></label>
<textarea id="more-information" type="text" rows="3" cols="50" name="more-information" style="resize:none"></textarea>
</div>
<div class="buttons">
<input type="button" class="primary" data-submit="submit" value="Submit feedback">
<!--<input type="button" class="secondary" data-submit="skip" value="Skip">-->
</div>
</form>
`

feedback.querySelector('.cancel').addEventListener('click', function (e) {
e.preventDefault()
sendRequest({ helpful: true }) // get positive feedback even if thet bail out before completion
setTimeout(() => { fadeOut(feedback, 50) }, 0)

if (window.mixpanel) {
window.mixpanel.track('DOCS_FEEDBACK_POSITIVE', {
pathname: window.location.origin + window.location.pathname,
search: window.location.search,
hash: window.location.hash,
})
}
})

feedback.querySelector('.primary').addEventListener('click', function (e) {
e.preventDefault()

var moreInformation = feedback.querySelector('textarea[name="more-information"]').value

sendRequest({
helpful: true,
moreInformation: moreInformation,
})
feedback.innerHTML = '<div class="header thank-you-positive"><p><strong>Thank you for your feedback!</strong></p></div>'
setTimeout(() => { fadeOut(feedback, 50) }, 2000)

feedback.innerHTML = '<div class="header thank-you-positive"><p><strong>Thank you for your feedback!</strong></p></div>'
if (window.mixpanel) {
window.mixpanel.track('DOCS_FEEDBACK_POSITIVE', {
pathname: window.location.origin + window.location.pathname,
search: window.location.search,
hash: window.location.hash,
})
}
})
}

var isUnhelpful = function () {
feedback.classList.add('negative')
feedback.style.height = null
feedback.innerHTML = `<form class="form">
<div class="header">
<p><strong>We&rsquo;re sorry to hear that. How could we improve this page?</strong></p>
<p><strong>How is this page unhelpful?</strong></p>
<svg width="14px" height="22px" viewBox="0 0 22 22" role="button" class="cancel" aria-label="Cancel Feedback">
<line x1="19.5833333" y1="0.416666667" x2="0.416666667" y2="19.5833333"></line>
<line x1="19.5833333" y1="19.5833333" x2="0.416666667" y2="0.416666667"></line>
</svg>
</div>
<div>
<input id="missing" type="radio" class="feedback-option" data-reason="missing" name="specific" value="missing" checked="true"><label
for="missing">It has missing information</label>
for="missing">Missing information</label>
</div>
<div>
<input id="hard-to-follow" type="radio" class="feedback-option" data-reason="hard-to-follow" name="specific" value="hard-to-follow">
<label for="hard-to-follow">It&rsquo;s hard to follow or confusing</label>
<label for="hard-to-follow">Hard to follow or confusing</label>
</div>
<div>
<input id="inaccurate" type="radio" class="feedback-option" data-reason="inaccurate" name="specific" value="inaccurate">
<label for="inaccurate">It&rsquo;s inaccurate, out of date, or doesn&rsquo;t work</label>
<label for="inaccurate">Inaccurate, out of date, or doesn&rsquo;t work</label>
</div>
<div><input id="other" type="radio" class="feedback-option" data-reason="other" name="specific" value="other"><label for="other">Something else
${edit}</label></div>
<div class="more-information"><label for="more-information"><strong>More information</strong></label><textarea
<div class="more-information"><label for="more-information"><strong>More information *</strong></label><textarea
id="more-information" type="text" rows="3" cols="50" name="more-information" style="resize:none"></textarea>
</div>
<div class="buttons"><input type="button" class="primary" data-submit="submit" value="Submit feedback"><input
type="button" class="secondary" data-submit="skip" value="Skip"></div>
<div class="buttons"><input type="button" class="primary" data-submit="submit" value="Submit feedback"></div>
</div>
</form>
`

var thankyou = `<div class="header thank-you-positive">
<p><strong>Thank you for your feedback!</strong></p>
<p>We will take this information into account while updating our documentation.</p>
<p><strong>Thank you!</strong></p>
<p>We will consider your feedback while updating our documentation.</p>
`

if (editLink) {
/*if (editLink) {
thankyou += '<p>You can also help us by ' + edit.replace('Edit', 'editing') + '.</p></div>'
}
}*/

feedback.querySelector('.cancel').addEventListener('click', function (e) {
e.preventDefault()
Expand All @@ -124,37 +188,35 @@ const { getCookie } = require('./modules/cookies')
})

feedback.querySelector('.primary').addEventListener('click', function (e) {
feedback.classList.remove('negative')
feedback.classList.add('positive')

e.preventDefault()

var reason = feedback.querySelector('input[name="specific"]:checked').value
var moreInformation = feedback.querySelector('textarea[name="more-information"]').value

sendFeedback(false, reason, moreInformation)
feedback.innerHTML = thankyou

if (window.mixpanel) {
window.mixpanel.track('DOCS_FEEDBACK_POSITIVE', {
pathname: window.location.origin + window.location.pathname,
search: window.location.search,
hash: window.location.hash,
})
// check more information not empty
var moreInformation = feedback.querySelector('textarea[name="more-information"]')
if (moreInformation.value === '') {
if (feedback.querySelector('p[class="error"]')) return //stubborn people
const error = document.createElement('p')
error.classList.add('error')
error.innerHTML = 'Please elaborate on your feedback.'
feedback.querySelector('div[class="more-information"]')
.insertAdjacentElement('afterend', error)
return
}
})

feedback.querySelector('.secondary').addEventListener('click', function (e) {
e.preventDefault()

var reason = feedback.querySelector('input[name="specific"]:checked').value
var moreInformation = feedback.querySelector('textarea[name="more-information"]').value
var reason = feedback.querySelector('input[name="specific"]:checked')

sendFeedback(false, reason, moreInformation)
sendRequest({
helpful: false,
reason: reason.value,
moreInformation: moreInformation.value,
})
feedback.innerHTML = thankyou

feedback.classList.remove('negative')
feedback.classList.add('positive')
setTimeout(() => { fadeOut(feedback, 50) }, 2000)

if (window.mixpanel) {
window.mixpanel.track('DOCS_FEEDBACK_SKIP', {
window.mixpanel.track('DOCS_FEEDBACK_POSITIVE', {
pathname: window.location.origin + window.location.pathname,
search: window.location.search,
hash: window.location.hash,
Expand All @@ -167,6 +229,7 @@ const { getCookie } = require('./modules/cookies')
feedback.classList.remove('negative')
feedback.classList.remove('positive')
feedback.innerHTML = original
feedback.style.height = 'var(--feedback-height)'

var yes = feedback.querySelector('.yes')
var no = feedback.querySelector('.no')
Expand All @@ -191,14 +254,27 @@ const { getCookie } = require('./modules/cookies')

if (position + windowHeight > footerOffset) {
feedback.classList.add('absolute')
feedback.style.top = (footerOffset - feedback.clientHeight) + 'px'
feedback.style.bottom = 'auto'
} else {
feedback.classList.remove('absolute')
feedback.style.top = 'auto'
feedback.style.bottom = '0px'
}
})

reset()
})()

function fadeOut (element, speed) {
var op = 1 // initial opacity
var timer = setInterval(function () {
if (op <= 0.1) {
clearInterval(timer)
element.style.display = 'none'
element.style.opacity = null
}
element.style.opacity = op
element.style.filter = 'alpha(opacity=' + op * 100 + ')'
op -= op * 0.1
}, speed)

// enlarge navigation to fill hole left by feedback widget faded out
document.documentElement.style.setProperty('--feedback-height', '0rem')
}
4 changes: 0 additions & 4 deletions src/partials/article.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,5 @@ If you typed the URL of this page manually, please double check that you entered
{{> comments}}
{{#if (eq page.layout 'training')}}
{{> training-help}}
{{else}}
{{#unless page.attributes.disablefeedback}}
{{> feedback}}
{{/unless}}
{{/if}}
</article>
Loading

0 comments on commit 5303ef8

Please sign in to comment.