Created
October 31, 2017 16:30
-
-
Save heldrida/5c77f6c525cb14914c669bb3a9afb5c0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { PropTypes } from 'react' | |
export const insertScriptHelper = (projectId) => { | |
const el = document.querySelector('[data-optimizely-snippet]') | |
if (!isReadyHelper() && !el) { | |
const protocol = `${document.location.protocol}//` | |
const scriptTag = document.createElement('script') | |
scriptTag.type = 'text/javascript' | |
scriptTag.async = true | |
scriptTag.src = `${protocol}cdn.optimizely.com/js/${projectId}.js` | |
scriptTag.setAttribute('data-optimizely-snippet', true) | |
document.getElementsByTagName('head')[0].appendChild(scriptTag) | |
} | |
} | |
export const triggerEventHelper = (eventName) => { | |
try { | |
typeof window.optimizely === 'object' && | |
window.optimizely.push({ | |
type: 'event', | |
eventName: eventName, | |
}) | |
} catch (e) { | |
throw Error('OptimizelyExperiment triggerEventHelper unexpected error!') | |
} | |
} | |
export const isReadyHelper = () => { | |
return typeof window.optimizely === 'object' && | |
typeof window.optimizely.data === 'object' && | |
typeof window.optimizely.allExperiments === 'object' | |
} | |
class OptimizelyExperiment extends React.Component { | |
constructor (props) { | |
super(props) | |
window.optimizely = window.optimizely || [] | |
window.optimizely.push({ | |
type: 'log', | |
level: 'info', | |
}) | |
this.onReadyRecallMs = 10 | |
this.defaultState = { | |
variantName: null, | |
result: null, | |
goalCompleted: false, | |
} | |
this.eventListeners = [] | |
this.state = Object.assign({}, this.defaultState) | |
this.mapVariantToState = this.mapVariantToState.bind(this) | |
this.eventHandler = this.eventHandler.bind(this) | |
} | |
componentWillMount () { | |
if (!isReadyHelper()) { | |
this.insertScript() | |
} | |
} | |
componentDidMount () { | |
this.observeReady(this.mapVariantToState) | |
} | |
componentDidUpdate (prevProps, prevState) { | |
if (isReadyHelper() && prevState.variantName !== this.state.variantName) { | |
this.setResult() | |
} | |
if (prevState.goalCompleted !== this.state.goalCompleted) { | |
this.removeAllEventListeners() | |
} | |
} | |
componentWillUnmount () { | |
this.removeAllEventListeners() | |
} | |
observeReady (cb) { | |
const { allExperiments } = window.optimizely | |
if (allExperiments && typeof allExperiments[this.props.experimentId] !== 'undefined') { | |
this.triggerPageActivation() | |
this.props.customEvents.forEach(({selector, eventType, eventName}) => | |
this.attachEventHandler(selector, eventType, eventName)) | |
if (typeof cb === 'function') cb() | |
} else { | |
const t = setTimeout(() => { | |
clearTimeout(t) | |
this.observeReady(cb) | |
}, this.onReadyRecallMs) | |
} | |
} | |
mapVariantToState () { | |
const { experimentId } = this.props | |
const { variationIdsMap, allVariations } = window.optimizely | |
let variantName = 'Original' | |
if (this.isExperimentActive(experimentId)) { | |
const variationId = variationIdsMap[experimentId][0] // simple AB test, not multiple | |
variantName = allVariations[variationId].name | |
} | |
this.setState(() => { | |
// keep a reference for debugging | |
this.setMiddlemanRefHelper({ | |
variantName, | |
}) | |
return { | |
variantName, | |
} | |
}) | |
} | |
isExperimentActive (experimentId) { | |
const { activeExperiments } = window.optimizely | |
return activeExperiments.find(v => v === experimentId) | |
} | |
insertScript () { | |
const { projectId } = this.props | |
insertScriptHelper(projectId) | |
} | |
runExperiment () { | |
const { variantName } = this.state | |
const { experimentsMap } = this.props | |
let result = experimentsMap[variantName] || false | |
if (typeof result === 'function') { | |
result = result() | |
} | |
return result | |
} | |
setResult () { | |
this.setState({ | |
result: this.runExperiment(), | |
}) | |
} | |
triggerPageActivation () { | |
window.optimizely.push({ | |
type: 'page', | |
pageName: this.props.pageName, | |
}) | |
} | |
attachEventHandler (selector, eventType, eventName) { | |
if (Array.isArray(selector)) { | |
return selector.forEach(v => this.attachEventHandler(v, eventType, eventName)) | |
} | |
const target = document.querySelector(selector) | |
const eventHandler = this.eventHandler.bind(undefined, target, eventType, eventName) | |
if (target) { | |
target.addEventListener(eventType, eventHandler, true) | |
this.eventListeners.push({ | |
target, | |
eventHandler, | |
eventType, | |
}) | |
} | |
} | |
eventHandler (target, eventType, eventName) { | |
this.setState(() => { | |
triggerEventHelper(eventName) | |
return { | |
goalCompleted: true, | |
} | |
}) | |
} | |
removeAllEventListeners () { | |
this.eventListeners.forEach(({target, eventType, eventHandler}) => | |
this.removeEventListener(target, eventType, eventHandler)) | |
} | |
removeEventListener (target, eventType, eventHandler) { | |
target.removeEventListener(eventType, eventHandler, true) | |
} | |
setMiddlemanRefHelper (data) { | |
const propName = '__AB_TEST_STATE_REF__' | |
window[propName] = Object.assign({}, window[propName], data) | |
} | |
render () { | |
return this.state.result | |
} | |
} | |
OptimizelyExperiment.propTypes = { | |
projectId: PropTypes.string.isRequired, | |
experimentId: PropTypes.string.isRequired, | |
name: PropTypes.string.isRequired, | |
experimentsMap: PropTypes.object.isRequired, | |
pageName: PropTypes.string, | |
customEvents: PropTypes.array, | |
} | |
export default OptimizelyExperiment |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment