Skip to content

Commit a895de5

Browse files
committed
Initial commit
1 parent 202c166 commit a895de5

File tree

8 files changed

+215
-0
lines changed

8 files changed

+215
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ typings/
5757
# dotenv environment variables file
5858
.env
5959

60+
dist/

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# react-abtest
2+
3+
Simple React AB test component
4+
5+
## Install
6+
7+
`npm install react-abtest`
8+
9+
## Usage
10+
11+
### ExperimentRandom
12+
13+
Randomly renders a variant.
14+
15+
```js
16+
import { ExperimentUniqueId } from 'react-abtest';
17+
18+
const A = <div>A variant</div>;
19+
const B = <div>B variant</div>;
20+
const C = <div>C variant</div>;
21+
22+
const ExampleTest = () => {
23+
return <ExperimentRandom variants={[A, B, C]} />
24+
}
25+
26+
export default ExampleTest;
27+
```
28+
29+
### ExperimentUniqueId
30+
31+
Renders the same variant based on a unique identifier and experiment name.
32+
33+
```js
34+
import { ExperimentUniqueId } from 'react-abtest';
35+
36+
const A = <div>A variant</div>;
37+
const B = <div>B variant</div>;
38+
const C = <div>C variant</div>;
39+
40+
const ExampleTest = ({ uid }) => {
41+
return <ExperimentUniqueId experimentName={'sample-experiment'} uid={uid} variants={[A, B, C]} />
42+
}
43+
44+
export default ExampleTest;
45+
```
46+
47+
### ExperimentValueGroup
48+
49+
When you already have assigned the users to a group (number), for example in a cookie.
50+
51+
```js
52+
import Cookie from 'cookies';
53+
import { ExperimentValueGroup } from 'react-abtest';
54+
55+
const A = <div>A variant</div>;
56+
const B = <div>B variant</div>;
57+
const C = <div>C variant</div>;
58+
59+
const ExampleTest = () => {
60+
const userGroup = Cookies.get('abTestCookie');
61+
62+
const variants = [
63+
{
64+
group: 1, // Single group
65+
component: A
66+
},
67+
{
68+
group: '2-50', // Range group
69+
component: B,
70+
},
71+
{
72+
group: '51-100',
73+
component: C
74+
}
75+
];
76+
77+
// userGroup = 1, would render A
78+
// userGroup = 33 would render B
79+
// userGroup = 51 would render C
80+
return <ExperimentValueGroup userGroup={userGroup} variants={variants} />;
81+
}
82+
83+
export default ExampleTest;
84+
```

package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "react-abtest",
3+
"version": "1.0.0",
4+
"description": "Simple React AB test component",
5+
"main": "dist/index.js",
6+
"scripts": {
7+
"compile": "babel --presets es2015,stage-0,react -d dist/ src/",
8+
"prepublish": "npm run compile"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/soldotno/react-abtest.git"
13+
},
14+
"bugs": {
15+
"url": "https://github.com/soldotno/react-abtest/issues"
16+
},
17+
"homepage": "https://github.com/soldotno/react-abtest#readme",
18+
"authors": [
19+
"Lars Jansøn Engvik"
20+
],
21+
"license": "MIT",
22+
"devDependencies": {
23+
"babel-cli": "6.24.1",
24+
"babel-preset-es2015": "6.24.1",
25+
"babel-preset-react": "6.24.1",
26+
"babel-preset-stage-0": "6.24.1"
27+
},
28+
"dependencies": {
29+
"prop-types": "15.5.10",
30+
"react": "15.5.4",
31+
"react-dom": "15.5.4"
32+
}
33+
}

src/ExperimentRandom.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import { randomInteger } from './utils';
3+
4+
const ExperimentRandom = ({ variants }) => {
5+
if (variants.length === 0) {
6+
return null;
7+
}
8+
9+
const random = randomInteger(0, variants.length-1);
10+
let VariantComponent = variants[random];
11+
12+
return <VariantComponent />;
13+
}
14+
15+
export default ExperimentRandom;

src/ExperimentUniqueId.jsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
4+
import { createHash } from './utils';
5+
6+
const ExperimentUniqueId = ({ experimentName, uid, variants}) => {
7+
if (variants.length === 0) {
8+
return null;
9+
}
10+
11+
const hash = createHash(uid + experimentName);
12+
const variant = hash % variants.length;
13+
const VariantComponent = variants[variant];
14+
15+
return <VariantComponent />;
16+
};
17+
18+
ExperimentUniqueId.propTypes = {
19+
experimentName: PropTypes.string.isRequired,
20+
uid: PropTypes.string.isRequired,
21+
variants: PropTypes.array.isRequired,
22+
};
23+
24+
export default ExperimentUniqueId;

src/ExperimentValueGroup.jsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
4+
const ExperimentValueGroup = ({ userGroup, variants }) => {
5+
let VariantComponent = null;
6+
7+
variants.some((variant) => {
8+
if (typeof variant.group === 'number') {
9+
if (variant.group === userGroup) {
10+
VariantComponent = variant.component;
11+
return true;
12+
}
13+
}
14+
15+
if (typeof variant.group === 'string') {
16+
const range = variant.group.split('-').map(num => parseInt(num, 10));
17+
18+
if (userGroup >= range[0] && userGroup <= range[1]) {
19+
VariantComponent = variant.component;
20+
return true;
21+
}
22+
}
23+
24+
return false;
25+
});
26+
27+
return VariantComponent ? <VariantComponent /> : null;
28+
}
29+
30+
ExperimentValueGroup.propTypes = {
31+
userGroup: PropTypes.number.isRequired,
32+
variants: PropTypes.array.isRequired,
33+
};
34+
35+
export default ExperimentValueGroup;

src/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import ExperimentRandom from './ExperimentRandom';
2+
import ExperimentUniqueId from './ExperimentUniqueId';
3+
import ExperimentValueGroup from './ExperimentValueGroup';
4+
5+
export {
6+
ExperimentRandom,
7+
ExperimentUniqueId,
8+
ExperimentValueGroup,
9+
};

src/utils.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function randomInteger(min, max) {
2+
return Math.floor(Math.random() * (max - min + 1)) + min;
3+
}
4+
5+
export function createHash(str) {
6+
let i = 0;
7+
let hash = 0;
8+
9+
for (i; i < str.length; i++) {
10+
hash = ((hash << 5) - hash) + str.charCodeAt(i);
11+
}
12+
13+
return hash >>> 0;
14+
}

0 commit comments

Comments
 (0)