Skip to content

Commit 409d67b

Browse files
committed
feat(interaction tracking): Define library structure
- Define BaseResource structure for user analytics event - Define resource for User interactions - Tracker callback returns UserInteractionResource
1 parent bc84a27 commit 409d67b

File tree

11 files changed

+22121
-83
lines changed

11 files changed

+22121
-83
lines changed

package-lock.json

Lines changed: 21869 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/contexts/withTracking.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/components/elements/Button/index.tsx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,23 @@ import {
66

77
import 'antd/lib/button/style/css';
88

9-
import {
10-
withDataContext,
11-
} from '../../contexts/withDataContext';
12-
139
import {
1410
withTracking,
15-
} from '../../contexts/withTracking';
11+
} from '../../../library/user-analytics/react/components/withTracking';
1612

1713
interface ButtonStateProps extends AntButtonProps {
1814
label: string;
1915
context?: any;
2016
}
2117

2218
export interface ButtonActionProps {
23-
customCallback?: (data: any) => void;
19+
2420
}
2521

2622
export type ButtonProps = ButtonStateProps & ButtonActionProps
2723

2824
function Button(props: ButtonProps) {
29-
const { label, context, onClick, customCallback, ...rest } = props;
30-
31-
// function handleClick(e: React.MouseEvent) {
32-
// customCallback && customCallback(context);
33-
// }
25+
const { label, onClick, ...rest } = props;
3426

3527
return (
3628
<AntButton
@@ -44,5 +36,4 @@ function Button(props: ButtonProps) {
4436

4537
export default Button;
4638

47-
export const ButtonWithContext = withDataContext(Button)
48-
export const ButtonWithTracking = withTracking(Button)
39+
export const ButtonWithTracking = withTracking(Button);

src/components/elements/Input/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'antd/lib/input/style/css';
55

66
import {
77
withTracking,
8-
} from '../../contexts/withTracking';
8+
} from '../../../library/user-analytics/react/components/withTracking';
99

1010
export interface InputProps extends AntInputProps {
1111

src/components/templates/LoginForm/index.tsx

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,44 @@
1-
import { DataContext } from '../../contexts/withDataContext';
2-
import Button, { ButtonWithContext, ButtonWithTracking } from '../../elements/Button';
3-
4-
import Input from '../../elements/Input';
1+
import
2+
UserInteractionResource,
3+
{
4+
UserInteraction,
5+
}
6+
from '../../../library/user-analytics/lib/resources/userInteractionResource';
7+
import { DataContext } from '../../../library/user-analytics/react/contexts/dataContext';
58

9+
import Button, { ButtonWithTracking } from '../../elements/Button';
10+
import Input, { InputWithTracking } from '../../elements/Input';
611
import Card from '../../widgets/Card';
712

813
export interface LoginFormProps {
914

1015
}
1116

1217
const data = {
13-
page: "Login page",
14-
company: "My company",
15-
}
18+
context: "Login Page",
19+
app: {
20+
version: "1",
21+
},
22+
} as UserInteraction.DataContext;
1623

1724
function LoginForm(props: LoginFormProps) {
1825

19-
function verifyUsernameAndPassword (e: React.MouseEvent<HTMLElement, MouseEvent>) {
26+
function verifyUsernameAndPassword(e: React.MouseEvent<HTMLElement, MouseEvent>) {
2027
// app logic goes here
28+
console.log("verifyUsernameAndPassword");
2129
}
2230

23-
function logMouseEvent (event: React.MouseEvent<HTMLElement, MouseEvent>, data: any) {
31+
function logEvent(
32+
event: React.MouseEvent<HTMLElement, MouseEvent>,
33+
interactionResource: UserInteractionResource
34+
) {
2435
// tracking logic goes here
36+
console.log("logEvent");
37+
console.log(interactionResource);
38+
/*
39+
do whatever you want with the resource,
40+
like save it to IndexedDB, compress it, save it via API, etc
41+
*/
2542
}
2643

2744
return (
@@ -32,34 +49,50 @@ function LoginForm(props: LoginFormProps) {
3249
<ButtonWithTracking
3350
type="primary"
3451
label="Login"
35-
onClick={(e: any) => verifyUsernameAndPassword(e.value)}
52+
onClick={verifyUsernameAndPassword}
53+
3654
trackers={[{
37-
context: "Login and Signup",
38-
type: "onClick",
39-
callback: logMouseEvent,
55+
action: "onClick",
56+
track: logEvent,
57+
58+
// pass optional custom data
4059
data: {
41-
element: "Login button",
42-
...data,
60+
color: "blue",
4361
}
4462
}]}
63+
64+
// optional props
65+
origin="Login Button"
66+
// context="AntD Card"
4567
/>,
4668
<Button
4769
type="ghost"
4870
label="Sign Up"
49-
onClick={verifyUsernameAndPassword}
71+
onClick={verifyUsernameAndPassword}
5072
/>
5173
]}>
52-
<Input
74+
<InputWithTracking
5375
type="text"
5476
placeholder="email"
77+
78+
trackers={[{
79+
action: "onClick",
80+
track: logEvent,
81+
data: {
82+
customKey1: "input",
83+
}
84+
}]}
85+
86+
// optional props
87+
origin="Email Input"
5588
/>
5689
<Input
5790
type="password"
5891
placeholder="password"
5992
/>
6093
</Card>
6194
</DataContext.Provider>
62-
95+
6396
)
6497
}
6598

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export function getUserOS() {
2+
const userAgent = window.navigator.userAgent;
3+
const platform = window.navigator.platform;
4+
const macosPlatforms = ["Macintosh", "MacIntel", "MacPPC", "Mac68K"];
5+
const windowsPlatforms = ["Win32", "Win64", "Windows", "WinCE"];
6+
const iosPlatforms = ["iPhone", "iPad", "iPod"];
7+
8+
let name = "";
9+
10+
if (macosPlatforms.includes(platform)) {
11+
name = "Mac OS";
12+
} else if (iosPlatforms.includes(platform)) {
13+
name = "iOS";
14+
} else if (windowsPlatforms.includes(platform)) {
15+
name = "Windows";
16+
} else if (/Android/.test(userAgent)) {
17+
name = "Android";
18+
} else if (!name && /Linux/.test(platform)) {
19+
name = "Linux";
20+
}
21+
22+
return {
23+
name,
24+
version: ""
25+
};
26+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export default interface BaseResource {
2+
app: {
3+
version: string,
4+
};
5+
date: Date;
6+
browser: {
7+
name: string,
8+
version: string,
9+
userAgent: string,
10+
platform: string,
11+
window: {
12+
width: number,
13+
height: number,
14+
}
15+
};
16+
os: {
17+
name: string,
18+
version: string,
19+
};
20+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import BaseResource from './baseResource';
2+
3+
import { getUserOS } from '../interaction-tracking/browser';
4+
5+
export type Object<T> = {
6+
[P in keyof T]: T[P]
7+
};
8+
9+
export default interface UserInteractionResource extends BaseResource {
10+
type: typeof UserInteraction.TYPE;
11+
action: UserInteraction.Action;
12+
source: {
13+
context: string;
14+
origin: string;
15+
component: string;
16+
element: {
17+
currentTarget: string;
18+
target: string;
19+
innerHTML?: string;
20+
innerText?: string;
21+
value?: string;
22+
};
23+
};
24+
data?: Object<any>;
25+
}
26+
27+
export namespace UserInteraction {
28+
export interface DataContext {
29+
app: BaseResource["app"],
30+
context: UserInteractionResource["source"]["context"],
31+
}
32+
33+
export const TYPE = "UserInteraction";
34+
35+
export type Action =
36+
| "onClick";
37+
38+
export interface Tracker {
39+
action: Action;
40+
data?: UserInteractionResource["data"];
41+
track: (e: any, interactionResource: UserInteractionResource) => void;
42+
}
43+
44+
export function generateResource(
45+
app: BaseResource["app"],
46+
action: UserInteraction.Action,
47+
source: UserInteractionResource["source"],
48+
data: UserInteractionResource["data"],
49+
) : UserInteractionResource {
50+
return {
51+
type: UserInteraction.TYPE,
52+
app,
53+
date: new Date(),
54+
browser: {
55+
name: navigator.appName,
56+
version: navigator.appVersion,
57+
userAgent: navigator.userAgent,
58+
platform: navigator.platform,
59+
window: {
60+
width: window.innerWidth,
61+
height: window.innerHeight,
62+
}
63+
},
64+
os: getUserOS(),
65+
action,
66+
source,
67+
data,
68+
}
69+
}
70+
}

src/components/contexts/withDataContext.tsx renamed to src/library/user-analytics/react/components/withDataContext.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import React, { createContext } from 'react';
2-
3-
const data = {} as any;
4-
export const DataContext = createContext(data);
1+
import { DataContext } from '../contexts/dataContext';
52

63
export const withDataContext = <P extends object>(Component: React.ComponentType<P>) => {
74
return function fn(props: P) {

0 commit comments

Comments
 (0)