Skip to content

Commit

Permalink
Add forced variation example
Browse files Browse the repository at this point in the history
  • Loading branch information
ptessier committed Jan 10, 2024
1 parent cc9f165 commit 1e29c13
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 19 deletions.
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"@typescript-eslint/ban-types": "off",

// allow function overloads
"no-redeclare": "off"
"no-redeclare": "off",

// allow to force re-render hooks deps
"react-hooks/exhaustive-deps": "off"
}
}
20 changes: 13 additions & 7 deletions packages/react/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,27 @@ h1 {
line-height: 1.1;
}

input {
border-radius: 8px;
border: 1px solid #646cff;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
transition: all 250ms ease-out;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
background-color: #646cff;
cursor: pointer;
transition: border-color 0.25s;
transition: all 250ms ease-out;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
background-color: #646cffdd;
}
13 changes: 13 additions & 0 deletions packages/react/src/pages/home.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,17 @@
to {
transform: rotate(360deg);
}
}

.row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 1rem;
margin: 1rem 0;
justify-content: center;
}

.row > input {
width: 8em;
}
89 changes: 81 additions & 8 deletions packages/react/src/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,71 @@
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { getInstance } from "sdk";
import styles from "./home.module.css";
import mxLogo from "/mx.svg";
import reactLogo from "/react.svg";
import viteLogo from "/vite.svg";

export function HomePage() {
/**
* Force re-rendering of the component when forced variations or attributes changes.
* In a real world scenario, we wouldn't need to do this.
*/
const [renderCount, setRenderCount] = useState(0);

const assignment = useMemo(() => {
const experiments = getInstance();
return experiments.getAssignment("experiment-a");
}, []);
}, [renderCount]);

const attributes = useMemo(() => {
const experiments = getInstance();
return experiments.getAttributes();
}, []);
}, [renderCount]);

const experiments = useMemo(() => {
const experiments = getInstance();
const record = experiments.getExperiments();
return Object.values(record);
return experiments.getExperiments();
}, []);

const forcedVariations = useMemo(() => {
const experiments = getInstance();
return experiments.getForcedVariations();
}, [renderCount]);

// State to update subject id
const [id, setId] = useState<string>("");

const handleUpdateId = () => {
if (!id) {
console.warn("subject id is empty");
return;
}

getInstance().setAttributes({ id });
setRenderCount((count) => count + 1);
};

// State for forcing variations
const [experimentKey, setExperimentKey] = useState<string>("experiment-a");
const [variationKey, setVariationKey] = useState<string>("");

const handleForceVariation = () => {
if (!experimentKey || !variationKey) {
console.warn("experiment key or variation key is empty");
return;
}

getInstance().setForcedVariation(experimentKey, variationKey);
setExperimentKey("");
setVariationKey("");
setRenderCount((count) => count + 1);
};

const handleClearForcedVariations = () => {
getInstance().clearForcedVariations();
setRenderCount((count) => count + 1);
};

return (
<div className={styles.home}>
<div>
Expand All @@ -41,18 +85,47 @@ export function HomePage() {
<h1>A/B Testing on React</h1>
<div>
<h3>Experiments</h3>
{experiments.map((experiment) => (
<div key={experiment.key}>{JSON.stringify(experiment, null, 2)}</div>
))}
{JSON.stringify(Object.values(experiments))}
</div>
<div>
<h3>Subject Attributes</h3>
{JSON.stringify(attributes, null, 2)}
<div className={styles.row}>
<input
type="input"
placeholder="id"
value={id}
onChange={(e) => setId(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleUpdateId()}
/>
<button onClick={handleUpdateId}>Update</button>
</div>
</div>
<div>
<h3>Assignment</h3>
{JSON.stringify(assignment, null, 2)}
</div>
<div>
<h3>Forced Variations</h3>
{JSON.stringify(forcedVariations, null, 2)}
<div className={styles.row}>
<input
type="input"
placeholder="experiment key"
value={experimentKey}
onChange={(e) => setExperimentKey(e.target.value)}
/>
<input
type="input"
placeholder="variation key"
value={variationKey}
onChange={(e) => setVariationKey(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleForceVariation()}
/>
<button onClick={handleForceVariation}>Force Variation</button>
<button onClick={handleClearForcedVariations}>Clear</button>
</div>
</div>
</div>
);
}
10 changes: 7 additions & 3 deletions packages/sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,17 @@ export class Client {
}

public getForcedVariations() {
this.store.forcedVariations.getEntries();
return this.store.forcedVariations.getEntries();
}

public setForcedVariations(experimentKey: string, variationKey: string) {
public setForcedVariation(experimentKey: string, variationKey: string) {
this.store.forcedVariations.set(experimentKey, variationKey);
}

public clearForcedVariations() {
this.store.forcedVariations.clear();
}

public getAssignment(experimentKey: string): IAssignment {
// Exclude if experiment has no key
if (experimentKey === null || experimentKey === undefined || experimentKey === "") {
Expand Down Expand Up @@ -197,5 +201,5 @@ export class Client {
function getTrackingKey(props: IAssignmentEvent): string {
const { experimentKey, variationKey, subjectAttribute, subjectKey } = props;

return [experimentKey, variationKey, stringify(subjectAttribute), subjectKey].filter((value) => !!value).join(":");
return [experimentKey, variationKey, stringify(subjectAttribute), subjectKey].join(":");
}

0 comments on commit 1e29c13

Please sign in to comment.