Skip to content

Instantly share code, notes, and snippets.

@heldrida
Created October 31, 2017 16:30
Show Gist options
  • Save heldrida/5c77f6c525cb14914c669bb3a9afb5c0 to your computer and use it in GitHub Desktop.
Save heldrida/5c77f6c525cb14914c669bb3a9afb5c0 to your computer and use it in GitHub Desktop.
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