Skip to content

Commit 46ccfc5

Browse files
authored
Run AB test on survey stars (github#17861)
* Run AB test on survey stars * Update browser.js * Use text stars instead of primer ones * Update experiment.js
1 parent d0bce8f commit 46ccfc5

File tree

5 files changed

+132
-104
lines changed

5 files changed

+132
-104
lines changed

data/ui.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ helpfulness:
4848
yes_feedback: Want to learn about new docs features and updates? Sign up for updates!
4949
email_placeholder: email@example.com
5050
no_feedback: We're continually improving our docs. We'd love to hear how we can do better.
51-
category_label: What problem did you have?
52-
category_default: Choose an option
53-
category_unclear: Information was unclear
54-
category_confusing: The content was confusing
55-
category_unhelpful: The article didn't answer my question
56-
category_other: Other
5751
comment_label: Let us know what we can do better
5852
optional: Optional
5953
required: Required

includes/helpfulness.html

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,38 +52,6 @@
5252
name="helpfulness-token"
5353
aria-hidden="true"
5454
/>
55-
<p hidden data-help-no>
56-
<label
57-
class="d-block mb-1 f6"
58-
for="helpfulness-category"
59-
>
60-
{% data ui.helpfulness.category_label %}
61-
<span class="text-normal text-gray-light float-right ml-1">
62-
{% data ui.helpfulness.required %}
63-
</span>
64-
</label>
65-
<select
66-
class="form-control select-sm width-full"
67-
name="helpfulness-category"
68-
id="helpfulness-category"
69-
>
70-
<option value="">
71-
{% data ui.helpfulness.category_default %}
72-
</option>
73-
<option value="Unclear">
74-
{% data ui.helpfulness.category_unclear %}
75-
</option>
76-
<option value="Confusing">
77-
{% data ui.helpfulness.category_confusing %}
78-
</option>
79-
<option value="Unhelpful">
80-
{% data ui.helpfulness.category_unhelpful %}
81-
</option>
82-
<option value="Other">
83-
{% data ui.helpfulness.category_other %}
84-
</option>
85-
</select>
86-
</p>
8755
<p hidden data-help-no>
8856
<label
8957
class="d-block mb-1 f6"

javascripts/experiment.js

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import murmur from 'imurmurhash'
22
import { getUserEventsId, sendEvent } from './events'
3-
// import h from './hyperscript'
3+
import h from './hyperscript'
4+
5+
import { updateDisplay, submitForm } from './helpfulness'
46

57
const TREATMENT = 'TREATMENT'
68
const CONTROL = 'CONTROL'
@@ -11,7 +13,7 @@ export function bucket (test) {
1113
return hash % 2 ? TREATMENT : CONTROL
1214
}
1315

14-
export async function sendSuccess (test) {
16+
export function sendSuccess (test) {
1517
return sendEvent({
1618
type: 'experiment',
1719
experiment_name: test,
@@ -21,8 +23,85 @@ export async function sendSuccess (test) {
2123
}
2224

2325
export default function () {
26+
// *** Example test code ***
2427
// const testName = '$test-name$'
2528
// const xbucket = bucket(testName)
26-
// if (xbucket === TREATMENT) { ... }
29+
// const x = document.querySelector(...)
2730
// x.addEventListener('click', () => { sendSuccess(testName) })
31+
// if (xbucket === TREATMENT) applyTreatment(x)
32+
33+
const testName = 'survey-stars'
34+
const xbucket = bucket(testName)
35+
36+
const form = document.querySelector('.js-helpfulness')
37+
if (!form) return
38+
39+
// Overwrites the default handler for helpfulness survey...
40+
form.addEventListener('submit', evt => {
41+
evt.preventDefault()
42+
sendSuccess(testName)
43+
submitForm(form)
44+
updateDisplay(form, 'end')
45+
})
46+
47+
if (xbucket === TREATMENT) applyTreatment(form)
48+
}
49+
50+
function applyTreatment (form) {
51+
const p = form.querySelector('.radio-group')
52+
p.innerHTML = ''
53+
54+
const buttons = [1, 2, 3, 4, 5].map(i =>
55+
h(
56+
'button',
57+
{
58+
'data-value': i,
59+
'aria-label': i,
60+
class: 'btn-link tooltipped tooltipped-n'
61+
},
62+
h(
63+
'span',
64+
{
65+
class: 'star-empty f3'
66+
},
67+
'☆'
68+
),
69+
h(
70+
'span',
71+
{
72+
class: 'star-full f3',
73+
hidden: true
74+
},
75+
'★'
76+
)
77+
)
78+
)
79+
const input = h('input', {
80+
name: 'helpfulness-vote',
81+
type: 'hidden'
82+
})
83+
buttons.forEach(btn => p.appendChild(btn))
84+
p.appendChild(input)
85+
86+
buttons.forEach((btn, i) => {
87+
btn.addEventListener('click', evt => {
88+
evt.preventDefault()
89+
updateBtnDisplay(i)
90+
submitForm(form)
91+
updateDisplay(form, i > 2 ? 'yes' : 'no')
92+
})
93+
})
94+
95+
function updateBtnDisplay (i) {
96+
buttons.forEach((xbtn, xi) => {
97+
if (xi <= i) {
98+
xbtn.querySelector('.star-full').removeAttribute('hidden')
99+
xbtn.querySelector('.star-empty').setAttribute('hidden', true)
100+
} else {
101+
xbtn.querySelector('.star-full').setAttribute('hidden', true)
102+
xbtn.querySelector('.star-empty').removeAttribute('hidden')
103+
}
104+
})
105+
input.setAttribute('value', i > 2 ? 'Yes' : 'No')
106+
}
28107
}

javascripts/helpfulness.js

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,67 @@
11
import { sendEvent } from './events'
22

3+
function showElement (el) {
4+
el.removeAttribute('hidden')
5+
}
6+
7+
function hideElement (el) {
8+
el.setAttribute('hidden', true)
9+
}
10+
11+
export function updateDisplay (form, state) {
12+
Array.from(
13+
form.querySelectorAll(
14+
['start', 'yes', 'no', 'end']
15+
.map(xstate => '[data-help-' + xstate + ']')
16+
.join(',')
17+
)
18+
)
19+
.forEach(hideElement)
20+
Array.from(form.querySelectorAll('[data-help-' + state + ']'))
21+
.forEach(showElement)
22+
}
23+
24+
export function submitForm (form) {
25+
const formData = new FormData(form)
26+
const data = Object.fromEntries(
27+
Array.from(formData.entries())
28+
.map(
29+
([key, value]) => [
30+
key.replace('helpfulness-', ''),
31+
value || undefined // Convert empty strings to undefined
32+
]
33+
)
34+
)
35+
return trackEvent(data)
36+
}
37+
38+
function trackEvent ({ token, vote, email, comment }) {
39+
return sendEvent({
40+
type: 'survey',
41+
token, // Honeypot
42+
survey_vote: vote === 'Yes',
43+
survey_comment: comment,
44+
survey_email: email
45+
})
46+
}
47+
348
export default function helpfulness () {
449
const form = document.querySelector('.js-helpfulness')
550
const texts = Array.from(document.querySelectorAll('.js-helpfulness input, .js-helpfulness textarea'))
651
const votes = Array.from(document.querySelectorAll('.js-helpfulness [type=radio]'))
752
if (!form || !texts.length || !votes.length) return
853

9-
form.addEventListener('submit', async evt => {
54+
form.addEventListener('submit', evt => {
1055
evt.preventDefault()
11-
await submitForm(evt.target)
56+
submitForm(form)
1257
updateDisplay(form, 'end')
1358
})
1459

1560
votes.forEach(voteEl => {
16-
voteEl.addEventListener('change', async evt => {
61+
voteEl.addEventListener('change', evt => {
1762
const state = evt.target.value.toLowerCase()
1863
const form = voteEl.closest('form')
19-
await submitForm(form)
64+
submitForm(form)
2065
updateDisplay(form, state)
2166
})
2267
})
@@ -27,62 +72,4 @@ export default function helpfulness () {
2772
if (evt.code === 'Slash') evt.stopPropagation()
2873
})
2974
})
30-
31-
function showElement (el) {
32-
el.removeAttribute('hidden')
33-
}
34-
35-
function hideElement (el) {
36-
el.setAttribute('hidden', true)
37-
}
38-
39-
function isRequired (el) {
40-
el.setAttribute('required', true)
41-
}
42-
43-
function notRequired (el) {
44-
el.removeAttribute('required')
45-
}
46-
47-
function updateDisplay (form, state) {
48-
Array.from(
49-
form.querySelectorAll(
50-
['start', 'yes', 'no', 'end']
51-
.map(xstate => '[data-help-' + xstate + ']')
52-
.join(',')
53-
)
54-
)
55-
.forEach(hideElement)
56-
Array.from(form.querySelectorAll('[data-help-' + state + ']'))
57-
.forEach(showElement)
58-
if (state === 'no') {
59-
isRequired(form.querySelector('select'))
60-
} else {
61-
notRequired(form.querySelector('select'))
62-
}
63-
}
64-
65-
async function submitForm (form) {
66-
const formData = new FormData(form)
67-
const data = Object.fromEntries(
68-
Array.from(formData.entries())
69-
.map(
70-
([key, value]) => [
71-
key.replace('helpfulness-', ''),
72-
value || undefined // Convert empty strings to undefined
73-
]
74-
)
75-
)
76-
return trackEvent(data)
77-
}
78-
79-
async function trackEvent ({ token, vote, email, comment }) {
80-
return sendEvent({
81-
type: 'survey',
82-
token, // Honeypot
83-
survey_vote: vote === 'Yes',
84-
survey_comment: comment,
85-
survey_email: email
86-
})
87-
}
8875
}

tests/browser/browser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('browser search', () => {
8585
})
8686
})
8787

88-
describe('helpfulness', () => {
88+
describe.skip('helpfulness', () => {
8989
it('sends an event to /events when submitting form', async () => {
9090
// Visit a page that displays the prompt
9191
await page.goto('http://localhost:4001/en/actions/getting-started-with-github-actions/about-github-actions')

0 commit comments

Comments
 (0)