@marvelapp/react-ab-test
Advanced tools
Comparing version 2.3.0 to 3.0.0-beta.0
@@ -5,2 +5,3 @@ module.exports = { | ||
emitter: require('./lib/emitter').default, | ||
useExperiment: require('./lib/hook').default, | ||
experimentDebugger: require('./lib/debugger'), | ||
@@ -7,0 +8,0 @@ mixpanelHelper: require('./lib/helpers/mixpanel').default, |
@@ -7,4 +7,2 @@ 'use strict'; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _react = require('react'); | ||
@@ -18,5 +16,5 @@ | ||
var _warning = require('fbjs/lib/warning'); | ||
var _hook = require('./hook'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
var _hook2 = _interopRequireDefault(_hook); | ||
@@ -27,104 +25,37 @@ var _emitter = require('./emitter'); | ||
var _Variant = require('./Variant'); | ||
var _Variant2 = _interopRequireDefault(_Variant); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var filterVariants = function filterVariants(name, children) { | ||
var variants = {}; | ||
_react2.default.Children.forEach(children, function (element) { | ||
if (!_react2.default.isValidElement(element) || element.type.displayName !== "Pushtell.Variant") { | ||
var error = new Error("Pushtell Experiment children must be Pushtell Variant components."); | ||
error.type = "PUSHTELL_INVALID_CHILD"; | ||
throw error; | ||
} | ||
variants[element.props.name] = element; | ||
_emitter2.default.addExperimentVariant(name, element.props.name); | ||
}); | ||
_emitter2.default.emit("variants-loaded", name); | ||
return variants; | ||
}; | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
var CoreExperiment = function CoreExperiment(props) { | ||
var variants = (0, _react.useMemo)(function () { | ||
return filterVariants(props.name, props.children); | ||
}, [props.name, props.children]); | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
var _useExperiment = (0, _hook2.default)(props.name, props.userIdentifier, props.defaultVariantName), | ||
selectVariant = _useExperiment.selectVariant; | ||
var CoreExperiment = function (_Component) { | ||
_inherits(CoreExperiment, _Component); | ||
return selectVariant(variants, []); | ||
}; | ||
function CoreExperiment(props) { | ||
_classCallCheck(this, CoreExperiment); | ||
var _this = _possibleConstructorReturn(this, (CoreExperiment.__proto__ || Object.getPrototypeOf(CoreExperiment)).call(this)); | ||
_this.win = function () { | ||
_emitter2.default.emitWin(_this.props.name); | ||
}; | ||
_this.state = {}; | ||
_this.displayName = "Pushtell.CoreExperiment"; | ||
var children = {}; | ||
_react2.default.Children.forEach(props.children, function (element) { | ||
if (!_react2.default.isValidElement(element) || element.type.displayName !== "Pushtell.Variant") { | ||
var error = new Error("Pushtell Experiment children must be Pushtell Variant components."); | ||
error.type = "PUSHTELL_INVALID_CHILD"; | ||
throw error; | ||
} | ||
children[element.props.name] = element; | ||
_emitter2.default.addExperimentVariant(props.name, element.props.name); | ||
}); | ||
_emitter2.default.emit("variants-loaded", props.name); | ||
_this.state.variants = children; | ||
return _this; | ||
} | ||
_createClass(CoreExperiment, [{ | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
if (nextProps.value !== this.props.value || nextProps.children !== this.props.children) { | ||
var value = typeof nextProps.value === "function" ? nextProps.value() : nextProps.value; | ||
var children = {}; | ||
_react2.default.Children.forEach(nextProps.children, function (element) { | ||
children[element.props.name] = element; | ||
}); | ||
this.setState({ | ||
value: value, | ||
variants: children | ||
}); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
var _this2 = this; | ||
var value = typeof this.props.value === "function" ? this.props.value() : this.props.value; | ||
if (!this.state.variants[value]) { | ||
if ("production" !== process.env.NODE_ENV) { | ||
(0, _warning2.default)(true, 'Experiment “' + this.props.name + '” does not contain variant “' + value + '”'); | ||
} | ||
} | ||
_emitter2.default._incrementActiveExperiments(this.props.name); | ||
_emitter2.default.setActiveVariant(this.props.name, value); | ||
_emitter2.default._emitPlay(this.props.name, value); | ||
this.setState({ | ||
value: value | ||
}); | ||
this.valueSubscription = _emitter2.default.addActiveVariantListener(this.props.name, function (experimentName, variantName) { | ||
_this2.setState({ | ||
value: variantName | ||
}); | ||
}); | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
_emitter2.default._decrementActiveExperiments(this.props.name); | ||
this.valueSubscription.remove(); | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return this.state.variants[this.state.value] || null; | ||
} | ||
}]); | ||
return CoreExperiment; | ||
}(_react.Component); | ||
CoreExperiment.propTypes = { | ||
name: _propTypes2.default.string.isRequired, | ||
value: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.func]).isRequired | ||
userIdentifier: _propTypes2.default.string, | ||
defaultVariantName: _propTypes2.default.string, | ||
children: _propTypes2.default.node | ||
}; | ||
exports.default = CoreExperiment; | ||
; | ||
exports.default = CoreExperiment; |
@@ -133,4 +133,4 @@ 'use strict'; | ||
}, { | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
this.activeSubscription = _emitter2.default.addListener("active", this.updateExperiments); | ||
@@ -137,0 +137,0 @@ this.inactiveSubscription = _emitter2.default.addListener("inactive", this.updateExperiments); |
@@ -7,4 +7,2 @@ "use strict"; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
@@ -32,6 +30,2 @@ | ||
var _calculateActiveVariant = require("./calculateActiveVariant"); | ||
var _calculateActiveVariant2 = _interopRequireDefault(_calculateActiveVariant); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -68,4 +62,2 @@ | ||
_emitter2.default.emitWin(_this.props.name); | ||
}, _this.getActiveVariant = function () { | ||
return (0, _calculateActiveVariant2.default)(_this.props.name, _this.props.userIdentifier, _this.props.defaultVariantName); | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
@@ -77,3 +69,7 @@ } | ||
value: function render() { | ||
return _react2.default.createElement(_CoreExperiment2.default, _extends({}, this.props, { value: this.getActiveVariant })); | ||
return _react2.default.createElement( | ||
_CoreExperiment2.default, | ||
this.props, | ||
this.props.children | ||
); | ||
} | ||
@@ -88,5 +84,6 @@ }]); | ||
defaultVariantName: _propTypes2.default.string, | ||
userIdentifier: _propTypes2.default.string | ||
userIdentifier: _propTypes2.default.string, | ||
children: _propTypes2.default.node | ||
}; | ||
Experiment.displayName = "Pushtell.Experiment"; | ||
exports.default = Experiment; |
@@ -14,3 +14,3 @@ { | ||
"main": "index.js", | ||
"version": "2.3.0", | ||
"version": "3.0.0-beta.0", | ||
"description": "A/B testing React components and debug tools. Isomorphic with a simple, universal interface. Well documented and lightweight. Tested in popular browsers and Node.js. Includes helpers for Mixpanel and Segment.com.", | ||
@@ -21,7 +21,7 @@ "directories": { | ||
"peerDependencies": { | ||
"react": ">=0.14.0 <17.0.0" | ||
"react": ">=16.8.0 <17.0.0" | ||
}, | ||
"dependencies": { | ||
"fbemitter": "^2.0.2", | ||
"fbjs": "^0.8.0", | ||
"fbemitter": "^2.1.1", | ||
"fbjs": "^1.0.0", | ||
"prop-types": "^15.6.0" | ||
@@ -39,4 +39,4 @@ }, | ||
"doctoc": "^1.3.1", | ||
"enzyme": "^3.3.0", | ||
"enzyme-adapter-react-16": "^1.1.1", | ||
"enzyme": "^3.10.0", | ||
"enzyme-adapter-react-16": "^1.14.0", | ||
"enzyme-to-json": "^3.3.1", | ||
@@ -48,8 +48,8 @@ "es6-promise": "^4.2.4", | ||
"eslint-plugin-react": "^7.7.0", | ||
"fbemitter": "^2.0.2", | ||
"fbjs": "^0.8.0", | ||
"fbemitter": "^2.1.1", | ||
"fbjs": "^1.0.0", | ||
"jest": "^22.4.0", | ||
"node-localstorage": "^1.3.0", | ||
"react": "^16.2.0", | ||
"react-dom": "^16.2.0", | ||
"react": "^16.8.0", | ||
"react-dom": "^16.8.0", | ||
"uuid": "^3.2.1" | ||
@@ -60,4 +60,4 @@ }, | ||
"build": "doctoc . --github --title '<h1>Table of Contents</h1>'; babel src --out-dir lib;", | ||
"lint": "eslint .", | ||
"lint:fix": "eslint --fix ." | ||
"lint": "eslint --ignore-path .gitignore .", | ||
"lint:fix": "eslint --ignore-path .gitignore --fix ." | ||
}, | ||
@@ -68,3 +68,4 @@ "jest": { | ||
"enzyme-to-json/serializer" | ||
] | ||
], | ||
"testURL": "http://example.com" | ||
}, | ||
@@ -71,0 +72,0 @@ "repository": { |
@@ -97,14 +97,46 @@ # A/B Testing React Components | ||
Using useExperiment Hook | ||
```js | ||
import React from 'react'; | ||
import { useExperiment } from '@marvelapp/react-ab-test'; | ||
// Hook usage pattern requires registration of experiments | ||
emitter.defineVariants("My Example", ["A", "B"]); | ||
const App = () => { | ||
const { selectVariant, emitWin } = useExperiment("My Example"); | ||
const variant = selectVariant({ | ||
A: <div>Section A</div> | ||
B: <div>Section B</div> | ||
}); | ||
return ( | ||
<div> | ||
{variant} | ||
<button onClick={emitWin}>CTA</button> | ||
</div> | ||
); | ||
}; | ||
``` | ||
Using Experiment Component | ||
```js | ||
import React from 'react'; | ||
import { Experiment, Variant, emitter } from '@marvelapp/react-ab-test'; | ||
class App extends Component { | ||
experimentRef = React.createRef(); | ||
onButtonClick(e) { | ||
this.refs.experiment.win(); | ||
this.experimentRef.current.win(); | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<Experiment ref="experiment" name="My Example"> | ||
<Experiment ref={this.experimentRef} name="My Example"> | ||
<Variant name="A"> | ||
@@ -128,3 +160,3 @@ <div>Section A</div> | ||
// Called when a 'win' is emitted, in this case by this.refs.experiment.win() | ||
// Called when a 'win' is emitted, in this case by this.experimentRef.current.win() | ||
emitter.addWinListener(function(experimentName, variantName) { | ||
@@ -201,3 +233,3 @@ console.log( | ||
// Called when a 'win' is emitted, in this case by this.refs.experiment.win() | ||
// Called when a 'win' is emitted, in this case by emitter.emitWin('My Example') | ||
emitter.addWinListener(function(experimentName, variantName) { | ||
@@ -226,3 +258,3 @@ console.log( | ||
<div> | ||
<Experiment ref="experiment" name="My Example"> | ||
<Experiment name="My Example"> | ||
<Variant name="A"> | ||
@@ -279,3 +311,3 @@ <div>Section A</div> | ||
<div> | ||
<Experiment ref="experiment" name="My Example"> | ||
<Experiment name="My Example"> | ||
<Variant name="A"> | ||
@@ -316,3 +348,2 @@ <div>Section A</div> | ||
<Experiment | ||
ref="experiment" | ||
name="My Example" | ||
@@ -671,4 +702,7 @@ userIdentifier={this.props.userIdentifier} | ||
class App extends React.Component { | ||
experimentRef = React.createRef(); | ||
onButtonClick(e) { | ||
emitter.emitWin('My Example'); | ||
this.experimentRef.current.win(); | ||
// mixpanelHelper sends the 'Experiment Win' event, equivalent to: | ||
@@ -684,3 +718,3 @@ // mixpanel.track('Experiment Win', {Experiment: "My Example", Variant: "A"}) | ||
<div> | ||
<Experiment ref="experiment" name="My Example"> | ||
<Experiment ref={this.experimentRef} name="My Example"> | ||
<Variant name="A"> | ||
@@ -732,4 +766,6 @@ <div>Section A</div> | ||
class App extends React.Component { | ||
experimentRef = React.createRef(); | ||
onButtonClick(e) { | ||
emitter.emitWin('My Example'); | ||
this.experimentRef.current.win(); | ||
// segmentHelper sends the 'Experiment Won' event, equivalent to: | ||
@@ -745,3 +781,3 @@ // segment.track('Experiment Won', {experimentName: "My Example", variationName: "A"}) | ||
<div> | ||
<Experiment ref="experiment" name="My Example"> | ||
<Experiment ref={this.experimentRef} name="My Example"> | ||
<Variant name="A"> | ||
@@ -748,0 +784,0 @@ <div>Section A</div> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14
820
1
68386
785
1
+ Addedcore-js@2.6.12(transitive)
+ Addedfbjs@1.0.0(transitive)
+ Addedfbjs-css-vars@1.0.2(transitive)
Updatedfbemitter@^2.1.1
Updatedfbjs@^1.0.0