Skip to content

Commit 638259a

Browse files
feat(EventBuilder): support overriding timestamp at user property and event level (#2246)
* add timestamp_micros to user properties * update style and include event update * fix request level timestamp_micros not being sent as number * fix tests * add timestamp picker component * add package.json changes * rename isUserProperty prop to be more clear * fix type in ValidateEvent/index.spec.tsx * Merge remote-tracking branch 'origin/main' into overwrite-timestamp * resolve conflicts * use iso-8601 format, add helper text, simplify value updates * remove unused import
1 parent 8bd6d19 commit 638259a

18 files changed

Lines changed: 592 additions & 134 deletions

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
"@mui/material": "^5.13.0",
1414
"@mui/styles": "^5.14.15",
1515
"@mui/x-data-grid": "^6.4.0",
16+
"@mui/x-date-pickers": "^8.11.2",
1617
"@typescript-eslint/eslint-plugin": "^4.0.0",
1718
"@typescript-eslint/parser": "^4.0.0",
1819
"babel-eslint": "^10.0.0",
1920
"classnames": "^2.3.2",
2021
"copy-to-clipboard": "^3.3.3",
22+
"dayjs": "^1.11.18",
2123
"eslint": "^7.0.0",
2224
"gatsby": "^5.9.1",
2325
"gatsby-plugin-emotion": "^8.9.0",
@@ -44,8 +46,8 @@
4446
"react-helmet": "^6.1.0",
4547
"react-icons": "^4.8.0",
4648
"react-json-view": "^1.21.3",
47-
"react-markdown": "^9.0.1",
4849
"react-loader-spinner": "^6.1.6",
50+
"react-markdown": "^9.0.1",
4951
"react-redux": "^8.0.5",
5052
"react-syntax-highlighter": "^15.5.0",
5153
"redux": "^4.2.1",
@@ -104,7 +106,7 @@
104106
"react": "^18.2.0"
105107
}
106108
},
107-
"resolutions": {
109+
"resolutions": {
108110
"@types/react": "^18.2.0",
109111
"@types/react-dom": "^18.2.0"
110112
},

src/components/ExternalLink.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,25 @@ import { PropsWithChildren } from "react"
1111
const PREFIX = "ExternalLink"
1212

1313
const classes = {
14-
link: `${PREFIX}-link`,
1514
icon: `${PREFIX}-icon`,
1615
hover: `${PREFIX}-hover`,
1716
}
1817

19-
const Root = styled("span")(() => ({
20-
[`& .${classes.link}`]: {
21-
display: "inline-flex",
22-
alignItems: "center",
23-
},
24-
18+
const StyledLink = styled("a")({
19+
display: "inline-flex",
20+
alignItems: "center",
2521
[`& .${classes.icon}`]: {
2622
marginLeft: "0.5ch",
2723
color: "inherit",
2824
},
2925

30-
[`& .${classes.hover}`]: {
26+
[`&.${classes.hover}`]: {
3127
"&:hover": {
3228
opacity: 1.0,
3329
},
3430
opacity: 0.3,
3531
},
36-
}))
32+
})
3733

3834
type Props = {
3935
href: string
@@ -48,23 +44,21 @@ const ExternalLink: React.FC<PropsWithChildren<Props>> = ({
4844
hover,
4945
}) => {
5046
return (
51-
<Root>
52-
<Tooltip title={title || ""}>
53-
<a
54-
className={clsx({ [classes.hover]: hover }, classes.link)}
55-
href={href}
56-
target="_blank"
57-
rel="noreferrer"
58-
>
59-
{children}
60-
<Launch
61-
className={clsx({ [classes.icon]: children !== undefined })}
62-
color="action"
63-
fontSize={children === undefined ? undefined : "inherit"}
64-
/>
65-
</a>
66-
</Tooltip>
67-
</Root>
47+
<Tooltip title={title || ""}>
48+
<StyledLink
49+
className={clsx({ [classes.hover]: hover })}
50+
href={href}
51+
target="_blank"
52+
rel="noreferrer"
53+
>
54+
{children}
55+
<Launch
56+
className={clsx({ [classes.icon]: children !== undefined })}
57+
color="action"
58+
fontSize={children === undefined ? undefined : "inherit"}
59+
/>
60+
</StyledLink>
61+
</Tooltip>
6862
)
6963
}
7064

src/components/LinkedTextField.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ export interface LinkedTextFieldProps {
88
value: string | undefined
99
label: string
1010
onChange: (e: string) => void
11+
onBlur?: () => void
1112
helperText: string | JSX.Element
1213
extraAction?: JSX.Element
1314
required?: true
1415
disabled?: boolean
1516
id?: string
17+
error?: boolean
18+
size?: "small" | "medium"
1619
}
1720

1821
const LinkedTextField: React.FC<LinkedTextFieldProps> = ({
@@ -21,11 +24,14 @@ const LinkedTextField: React.FC<LinkedTextFieldProps> = ({
2124
label,
2225
value,
2326
onChange,
27+
onBlur,
2428
required,
2529
helperText,
2630
disabled,
2731
extraAction,
2832
id,
33+
error,
34+
size = "small",
2935
}) => {
3036
return (
3137
<TextField
@@ -42,15 +48,17 @@ const LinkedTextField: React.FC<LinkedTextFieldProps> = ({
4248
),
4349
}}
4450
id={id}
45-
size="small"
51+
size={size}
4652
variant="outlined"
4753
fullWidth
4854
label={label}
4955
value={value === undefined ? "" : value}
5056
onChange={e => onChange(e.target.value)}
57+
onBlur={onBlur}
5158
required={required}
5259
helperText={helperText}
5360
disabled={disabled}
61+
error={error}
5462
/>
5563
)
5664
}

src/components/ga4/EventBuilder/Items.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ const Items: React.FC<Props> = ({
8282
addNumberParam={() => addItemNumberParam(idx)}
8383
removeParam={(itemIdx: number) => removeItemParam(idx, itemIdx)}
8484
removeItem={() => removeItem(idx)}
85+
setParamTimestamp={() => {}}
86+
allowTimestampOverride={false}
8587
/>
8688
</WithHelpText>
8789
))}
Lines changed: 72 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,106 @@
11
import * as React from "react"
22

3-
import { styled } from '@mui/material/styles';
43
import TextField from "@mui/material/TextField"
54
import { Parameter as ParameterT } from "./types"
65
import { ShowAdvancedCtx } from "."
7-
import { IconButton, Tooltip } from "@mui/material"
6+
import {
7+
IconButton,
8+
Tooltip,
9+
Grid
10+
} from "@mui/material"
811
import { Delete } from "@mui/icons-material"
9-
10-
const PREFIX = 'Parameter';
11-
12-
const classes = {
13-
parameter: `${PREFIX}-parameter`
14-
};
15-
16-
const Root = styled('section')((
17-
{
18-
theme
19-
}
20-
) => ({
21-
[`&.${classes.parameter}`]: {
22-
display: "flex",
23-
"&> *": {
24-
flexGrow: 1,
25-
},
26-
"&> :not(:first-child)": {
27-
marginLeft: theme.spacing(1),
28-
},
29-
}
30-
}));
12+
import TimestampPicker from "./TimestampPicker"
13+
import { TimestampScope } from "@/constants"
3114

3215
interface Props {
3316
parameter: ParameterT
17+
idx: number
3418
setParamName: (name: string) => void
3519
setParamValue: (value: string) => void
20+
setParamTimestamp: (idx: number, value: number | undefined) => void
3621
removeParam: () => void
22+
allowTimestampOverride: boolean
3723
}
3824

3925
const Parameter: React.FC<Props> = ({
4026
parameter,
27+
idx,
4128
setParamName,
4229
setParamValue,
30+
setParamTimestamp,
4331
removeParam,
32+
allowTimestampOverride,
4433
}) => {
45-
4634
const showAdvanced = React.useContext(ShowAdvancedCtx)
4735

4836
const [name, setName] = React.useState(parameter.name)
4937
const [value, setValue] = React.useState(parameter.value || "")
38+
const [timestamp, setTimestamp] = React.useState(
39+
parameter.timestamp_micros?.toString() || ""
40+
)
41+
42+
React.useEffect(() => {
43+
const num = parseInt(timestamp, 10)
44+
setParamTimestamp(idx, isNaN(num) ? undefined : num)
45+
}, [timestamp, setParamTimestamp, idx])
5046

5147
const inputs = (
52-
<Root className={classes.parameter}>
53-
<TextField
54-
id={`#/events/0/params/${name}`}
55-
variant="outlined"
56-
size="small"
57-
value={name}
58-
onChange={e => setName(e.target.value)}
59-
onBlur={() => setParamName(name)}
60-
label="name"
61-
/>
62-
<TextField
63-
variant="outlined"
64-
size="small"
65-
value={value || ""}
66-
InputLabelProps={{
67-
...(parameter.exampleValue === undefined ? {} : { shrink: true }),
68-
}}
69-
onChange={e => setValue(e.target.value)}
70-
onBlur={() => setParamValue(value)}
71-
label={`${parameter.type} value`}
72-
placeholder={parameter.exampleValue?.toString()}
73-
/>
74-
</Root>
48+
<Grid container spacing={1}>
49+
<Grid item xs>
50+
<TextField
51+
id={`#/events/0/params/${name}`}
52+
variant="outlined"
53+
size="small"
54+
value={name}
55+
onChange={e => setName(e.target.value)}
56+
onBlur={() => setParamName(name)}
57+
label="name"
58+
fullWidth
59+
/>
60+
</Grid>
61+
<Grid item xs>
62+
<TextField
63+
variant="outlined"
64+
size="small"
65+
value={value || ""}
66+
InputLabelProps={{
67+
...(parameter.exampleValue === undefined ? {} : { shrink: true }),
68+
}}
69+
onChange={e => setValue(e.target.value)}
70+
onBlur={() => setParamValue(value)}
71+
label={`${parameter.type} value`}
72+
placeholder={parameter.exampleValue?.toString()}
73+
fullWidth
74+
/>
75+
</Grid>
76+
{allowTimestampOverride && (
77+
<Grid item xs={12}>
78+
<TimestampPicker
79+
timestamp={timestamp}
80+
scope={TimestampScope.USER_PROPERTY}
81+
setTimestamp={setTimestamp}
82+
/>
83+
</Grid>
84+
)}
85+
</Grid>
7586
)
7687
if (showAdvanced) {
7788
return (
78-
<section /* className={formClasses.trashRow} */>
79-
<Tooltip title="remove parameter">
80-
<IconButton onClick={removeParam}>
81-
<Delete />
82-
</IconButton>
83-
</Tooltip>
84-
{inputs}
85-
</section>
89+
<Grid container spacing={1} alignItems="flex-start">
90+
<Grid item>
91+
<Tooltip title="remove parameter">
92+
<IconButton onClick={removeParam}>
93+
<Delete />
94+
</IconButton>
95+
</Tooltip>
96+
</Grid>
97+
<Grid item xs>
98+
{inputs}
99+
</Grid>
100+
</Grid>
86101
)
87102
}
88103
return inputs
89104
}
90105

91-
export default Parameter
106+
export default Parameter

src/components/ga4/EventBuilder/Parameters.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,26 @@ interface Props {
3535
parameters: ParameterT[]
3636
setParamName: (idx: number, name: string) => void
3737
setParamValue: (idx: number, value: string) => void
38+
setParamTimestamp: (idx: number, value: number | undefined) => void
3839
addStringParam: () => void
3940
addNumberParam: () => void
4041
removeParam: (idx: number) => void
4142
removeItem?: () => void
4243
addItemsParam?: () => void
44+
allowTimestampOverride: boolean
4345
}
4446

4547
const Parameters: React.FC<Props> = ({
4648
parameters,
4749
setParamName,
4850
setParamValue,
51+
setParamTimestamp,
4952
addStringParam,
5053
addNumberParam,
5154
removeParam,
5255
addItemsParam,
5356
removeItem,
57+
allowTimestampOverride: allowTimestampOverride,
5458
}) => {
5559
const showAdvanced = React.useContext(ShowAdvancedCtx)
5660

@@ -62,7 +66,10 @@ const Parameters: React.FC<Props> = ({
6266
parameter={parameter}
6367
setParamName={name => setParamName(idx, name)}
6468
setParamValue={value => setParamValue(idx, value)}
69+
setParamTimestamp={setParamTimestamp}
70+
idx={idx}
6571
removeParam={() => removeParam(idx)}
72+
allowTimestampOverride={allowTimestampOverride}
6673
/>
6774
))}
6875
<section className={classes.buttonRow} >

0 commit comments

Comments
 (0)