added signin button color experiment. closes #1306 (#1320)

This commit is contained in:
Danny Coates 2019-05-03 10:10:56 -07:00 committed by Donovan Preston
parent b82177dc44
commit 23ecb632eb
9 changed files with 157 additions and 82 deletions

View File

@ -1,6 +1,22 @@
import hash from 'string-hash'; import hash from 'string-hash';
import Account from './ui/account';
const experiments = {}; const experiments = {
signin_button_color: {
eligible: function() {
return true;
},
variant: function() {
return ['white-blue', 'blue', 'white-violet', 'violet'][
Math.floor(Math.random() * 4)
];
},
run: function(variant, state) {
const account = state.cache(Account, 'account');
account.buttonClass = variant;
}
}
};
//Returns a number between 0 and 1 //Returns a number between 0 and 1
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -25,23 +41,12 @@ export default function initialize(state, emitter) {
xp.run(+state.query.v, state, emitter); xp.run(+state.query.v, state, emitter);
} }
}); });
const enrolled = state.storage.enrolled;
if (!state.storage.get('testpilot_ga__cid')) { // single experiment per session for now
// first ever visit. check again after cid is assigned. const id = Object.keys(enrolled)[0];
emitter.on('DOMContentLoaded', () => { if (Object.keys(experiments).includes(id)) {
checkExperiments(state, emitter); experiments[id].run(enrolled[id], state, emitter);
});
} else { } else {
const enrolled = state.storage.enrolled.filter(([id, variant]) => { checkExperiments(state, emitter);
const xp = experiments[id];
if (xp) {
xp.run(variant, state, emitter);
}
return !!xp;
});
// single experiment per session for now
if (enrolled.length === 0) {
checkExperiments(state, emitter);
}
} }
} }

View File

@ -8,6 +8,14 @@
user-select: none; user-select: none;
} }
:root {
--violet-gradient: linear-gradient(
-180deg,
rgba(144, 89, 255, 0.8) 0%,
rgba(144, 89, 255, 0.4) 100%
);
}
a { a {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
@ -300,3 +308,66 @@ select {
.word-break-all { .word-break-all {
word-break: break-all; word-break: break-all;
} }
.signin {
border-radius: 6px;
transition-property: transform, background-color;
transition-duration: 250ms;
transition-timing-function: cubic-bezier(0.07, 0.95, 0, 1);
}
.signin:hover,
.signin:focus {
@apply shadow-btn;
transform: scale(1.0625);
}
.signin:hover:active {
transform: scale(0.9375);
}
/* begin signin button color experiment */
.white-blue {
@apply border-blue-dark;
@apply border-2;
@apply text-blue-dark;
}
.white-blue:hover,
.white-blue:focus {
@apply bg-blue-dark;
@apply text-white;
}
.blue {
@apply bg-blue-dark;
@apply text-white;
}
.white-violet {
@apply border-violet;
@apply border-2;
@apply text-violet;
}
.white-violet:hover,
.white-violet:focus {
@apply bg-violet;
@apply text-white;
background-image: var(--violet-gradient);
}
.violet {
@apply bg-violet;
@apply text-white;
}
.violet:hover,
.violet:focus {
background-image: var(--violet-gradient);
}
/* end signin button color experiment */

View File

@ -63,10 +63,10 @@ if (process.env.NODE_ENV === 'production') {
const app = routes(choo()); const app = routes(choo());
window.app = app; window.app = app;
app.use(experiments);
app.use(metrics); app.use(metrics);
app.use(controller); app.use(controller);
app.use(dragManager); app.use(dragManager);
app.use(experiments);
app.use(pasteManager); app.use(pasteManager);
app.mount('body'); app.mount('body');
})(); })();

View File

@ -3,7 +3,7 @@ import { platform, locale } from './utils';
import { sendMetrics } from './api'; import { sendMetrics } from './api';
let appState = null; let appState = null;
// let experiment = null; let experiment = null;
const HOUR = 1000 * 60 * 60; const HOUR = 1000 * 60 * 60;
const events = []; const events = [];
let session_id = Date.now(); let session_id = Date.now();
@ -11,11 +11,13 @@ const lang = locale();
export default function initialize(state, emitter) { export default function initialize(state, emitter) {
appState = state; appState = state;
if (!appState.user.firstAction) {
appState.user.firstAction = appState.route === '/' ? 'upload' : 'download';
}
emitter.on('DOMContentLoaded', () => { emitter.on('DOMContentLoaded', () => {
// experiment = storage.enrolled[0]; experiment = storage.enrolled;
if (!appState.user.firstAction) {
appState.user.firstAction =
appState.route === '/' ? 'upload' : 'download';
}
const query = appState.query; const query = appState.query;
addEvent('client_visit', { addEvent('client_visit', {
entrypoint: appState.route === '/' ? 'upload' : 'download', entrypoint: appState.route === '/' ? 'upload' : 'download',
@ -59,6 +61,11 @@ function submitEvents() {
async function addEvent(event_type, event_properties) { async function addEvent(event_type, event_properties) {
const user_id = await appState.user.metricId(); const user_id = await appState.user.metricId();
const device_id = await appState.user.deviceId(); const device_id = await appState.user.deviceId();
const ab_id = Object.keys(experiment)[0];
if (ab_id) {
event_properties.experiment = ab_id;
event_properties.variant = experiment[ab_id];
}
events.push({ events.push({
device_id, device_id,
event_properties, event_properties,

View File

@ -86,16 +86,13 @@ class Storage {
this.engine.setItem('referrer', str); this.engine.setItem('referrer', str);
} }
get enrolled() { get enrolled() {
return JSON.parse(this.engine.getItem('experiments') || '[]'); return JSON.parse(this.engine.getItem('ab_experiments') || '{}');
} }
enroll(id, variant) { enroll(id, variant) {
const enrolled = this.enrolled; const enrolled = {};
// eslint-disable-next-line no-unused-vars enrolled[id] = variant;
if (!enrolled.find(([i, v]) => i === id)) { this.engine.setItem('ab_experiments', JSON.stringify(enrolled));
enrolled.push([id, variant]);
this.engine.setItem('experiments', JSON.stringify(enrolled));
}
} }
get files() { get files() {

View File

@ -8,6 +8,7 @@ class Account extends Component {
this.emit = emit; this.emit = emit;
this.enabled = state.capabilities.account; this.enabled = state.capabilities.account;
this.local = state.components[name] = {}; this.local = state.components[name] = {};
this.buttonClass = '';
this.setState(); this.setState();
} }
@ -62,7 +63,8 @@ class Account extends Component {
return html` return html`
<send-account> <send-account>
<button <button
class="p-2 md:p-4 border rounded-lg text-blue-dark border-blue-dark hover:text-white hover:bg-blue-dark focus:outline" class="px-4 py-2 md:px-8 md:py-4 focus:outline signin ${this
.buttonClass}"
onclick="${e => this.login(e)}" onclick="${e => this.login(e)}"
title="${translate('signInOnlyButton')}" title="${translate('signInOnlyButton')}"
> >

82
package-lock.json generated
View File

@ -1320,16 +1320,8 @@
"dev": true, "dev": true,
"requires": { "requires": {
"@dannycoates/elliptic": "^6.4.2", "@dannycoates/elliptic": "^6.4.2",
"asmcrypto.js": "^0.22.0" "asmcrypto.js": "^0.22.0",
}, "webcrypto-core": "github:dannycoates/webcrypto-core#8e0152a66d3ae6329cf080ccb3085eb06637070f"
"dependencies": {
"webcrypto-core": {
"version": "github:dannycoates/webcrypto-core#8e0152a66d3ae6329cf080ccb3085eb06637070f",
"from": "github:dannycoates/webcrypto-core#8e0152a66d3ae6329cf080ccb3085eb06637070f",
"requires": {
"tslib": "^1.7.1"
}
}
} }
}, },
"@fullhuman/postcss-purgecss": { "@fullhuman/postcss-purgecss": {
@ -5818,16 +5810,8 @@
"version": "github:dannycoates/express-ws#d0910a43b1802b22476362113557e20b18e185ba", "version": "github:dannycoates/express-ws#d0910a43b1802b22476362113557e20b18e185ba",
"from": "github:dannycoates/express-ws", "from": "github:dannycoates/express-ws",
"requires": { "requires": {
"esm": "^3.0.84" "esm": "^3.0.84",
}, "ws": "github:dannycoates/ws#c83cbb3bce478122cedcb8c475d9e86e1112824a"
"dependencies": {
"ws": {
"version": "github:dannycoates/ws#c83cbb3bce478122cedcb8c475d9e86e1112824a",
"from": "github:dannycoates/ws#c83cbb3bce478122cedcb8c475d9e86e1112824a",
"requires": {
"async-limiter": "~1.0.0"
}
}
} }
}, },
"extend": { "extend": {
@ -6373,11 +6357,8 @@
"resolved": "https://registry.npmjs.org/fluent-intl-polyfill/-/fluent-intl-polyfill-0.1.0.tgz", "resolved": "https://registry.npmjs.org/fluent-intl-polyfill/-/fluent-intl-polyfill-0.1.0.tgz",
"integrity": "sha1-ETOUSrJHeINHOZVZaIPg05z4hc8=", "integrity": "sha1-ETOUSrJHeINHOZVZaIPg05z4hc8=",
"dev": true, "dev": true,
"dependencies": { "requires": {
"intl-pluralrules": { "intl-pluralrules": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b"
"version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b",
"from": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b"
}
} }
}, },
"fluent-langneg": { "fluent-langneg": {
@ -6583,15 +6564,13 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": false, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": false, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -6608,22 +6587,19 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"resolved": false, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -6754,8 +6730,7 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": false, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -6769,7 +6744,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -6786,7 +6760,6 @@
"resolved": false, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -6795,15 +6768,13 @@
"version": "0.0.8", "version": "0.0.8",
"resolved": false, "resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"resolved": false, "resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -6824,7 +6795,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -6913,8 +6883,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -6928,7 +6897,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -7066,7 +7034,6 @@
"resolved": false, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -8292,6 +8259,11 @@
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
"dev": true "dev": true
}, },
"intl-pluralrules": {
"version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b",
"from": "github:projectfluent/IntlPluralRules#module",
"dev": true
},
"invariant": { "invariant": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -15766,7 +15738,8 @@
"tslib": { "tslib": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
}, },
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
@ -16587,6 +16560,14 @@
"object.assign": "^4.0.3" "object.assign": "^4.0.3"
} }
}, },
"webcrypto-core": {
"version": "github:dannycoates/webcrypto-core#8e0152a66d3ae6329cf080ccb3085eb06637070f",
"from": "github:dannycoates/webcrypto-core",
"dev": true,
"requires": {
"tslib": "^1.7.1"
}
},
"webdriverio": { "webdriverio": {
"version": "4.14.4", "version": "4.14.4",
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-4.14.4.tgz", "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-4.14.4.tgz",
@ -17819,6 +17800,13 @@
"integrity": "sha1-wlLXx8WxtAKJdjDjRTx7/mkNnKE=", "integrity": "sha1-wlLXx8WxtAKJdjDjRTx7/mkNnKE=",
"dev": true "dev": true
}, },
"ws": {
"version": "github:dannycoates/ws#c83cbb3bce478122cedcb8c475d9e86e1112824a",
"from": "github:dannycoates/ws",
"requires": {
"async-limiter": "~1.0.0"
}
},
"x-is-string": { "x-is-string": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",

View File

@ -118,7 +118,9 @@ function clientEvent(event, ua, language, session_id, deltaT, platform, ip) {
utm_content: ep.utm_content, utm_content: ep.utm_content,
utm_medium: ep.utm_medium, utm_medium: ep.utm_medium,
utm_source: ep.utm_source, utm_source: ep.utm_source,
utm_term: ep.utm_term utm_term: ep.utm_term,
experiment: ep.experiment,
variant: ep.variant
}; };
const user_properties = { const user_properties = {
active_count: up.active_count, active_count: up.active_count,

View File

@ -127,7 +127,8 @@ const colors = {
'pink-light': '#fa7ea8', 'pink-light': '#fa7ea8',
'pink-lighter': '#ffbbca', 'pink-lighter': '#ffbbca',
'pink-lightest': '#ffebef', 'pink-lightest': '#ffebef',
cloud: 'rgba(255, 255, 255, 0.8)' cloud: 'rgba(255, 255, 255, 0.8)',
violet: 'hsl(258, 57%, 35%)'
}; };
module.exports = { module.exports = {
@ -737,7 +738,9 @@ module.exports = {
inner: 'inset 0 2px 4px 0 rgba(0,0,0,0.06)', inner: 'inset 0 2px 4px 0 rgba(0,0,0,0.06)',
outline: '0 0 0 3px rgba(52,144,220,0.5)', outline: '0 0 0 3px rgba(52,144,220,0.5)',
none: 'none', none: 'none',
cloud: '0 0 5rem 5rem white' cloud: '0 0 5rem 5rem white',
btn:
'inset 0 -6px 12px 0 rgba(0,70,144,0.25), 0 4px 6px 0 rgba(34,0,51,0.04), 0 1px 10px 0 rgba(7,48,114,0.12), 0 2px 8px -1px rgba(14,13,26,0.08)'
}, },
/* /*