LG-9848: Add analytics to onbeforeunload#8512
Conversation
7eef1d2 to
9aad622
Compare
Set an onbeforeunload handler, do some analytics, manage cleanup.
Avoid strange-looking `NodeJS.Timeout` type
Don't leave onbeforeunload handlers hanging around
e47f560 to
0b84e23
Compare
Sinon's useFakeTimers covers global.setTimeout (which is callable as plain `setTimeout(...)`, but does not cover `window.setTimeout`.
| sandbox.clock.restore(); | ||
| } | ||
|
|
||
| // useFakeTimers overrides global.setTimeout, etc. (callable as setTimeout()), but does not |
There was a problem hiding this comment.
FYI @aduth this is fun fallout from the setTimeout vs window.setTimeout issue. Sinon replaces global.setTimeout but NOT window.setTimeout. There is a config option to set the global it uses, but I think that would break any existing "bare" references to setTimeout.
There was a problem hiding this comment.
Gotcha, this makes sense then 👍
Alternatively, I wonder what might happen if we changed the "window" setTimeout to point to the Node global in the test setup?
window.setTimeout = setTimeout;| const cleanUp = this.cleanUpPromptOnNavigate ?? (() => {}); | ||
| this.cleanUpPromptOnNavigate = undefined; | ||
| cleanUp(); |
There was a problem hiding this comment.
personally I love the null-safe navigator for stuff like this but this is just style
| const cleanUp = this.cleanUpPromptOnNavigate ?? (() => {}); | |
| this.cleanUpPromptOnNavigate = undefined; | |
| cleanUp(); | |
| this.cleanUpPromptOnNavigate = undefined; | |
| this.cleanUpPromptOnNavigate?.(); |
There was a problem hiding this comment.
oh wait I just realized that we can't do this because we're clearing 🤦
| const isAlreadyBound = !!this.cleanUpPromptOnNavigate; | ||
|
|
||
| if (shouldPrompt && !isAlreadyBound) { | ||
| this.cleanUpPromptOnNavigate = promptOnNavigate(); | ||
| } else if (!shouldPrompt && isAlreadyBound) { | ||
| const cleanUp = this.cleanUpPromptOnNavigate ?? (() => {}); | ||
| this.cleanUpPromptOnNavigate = undefined; | ||
| cleanUp(); |
There was a problem hiding this comment.
Kinda unfortunate that TypeScript isn't able to follow the type narrowing through the variable assignment. It could if we avoided the variable, but maybe that sacrifices readability too much?
| const isAlreadyBound = !!this.cleanUpPromptOnNavigate; | |
| if (shouldPrompt && !isAlreadyBound) { | |
| this.cleanUpPromptOnNavigate = promptOnNavigate(); | |
| } else if (!shouldPrompt && isAlreadyBound) { | |
| const cleanUp = this.cleanUpPromptOnNavigate ?? (() => {}); | |
| this.cleanUpPromptOnNavigate = undefined; | |
| cleanUp(); | |
| if (shouldPrompt && !this.cleanUpPromptOnNavigate) { | |
| this.cleanUpPromptOnNavigate = promptOnNavigate(); | |
| } else if (!shouldPrompt && this.cleanUpPromptOnNavigate) { | |
| this.cleanUpPromptOnNavigate(); | |
| this.cleanUpPromptOnNavigate = undefined; |
There was a problem hiding this comment.
I prefer the readability of isAlreadyBound but yeah I wish TS could deduce that cleanUpPromptOnNavigate is there when isAlreadyBound is true.
|
|
||
| ## Analytics | ||
|
|
||
| By default, `promptOnNavigate` will call `trackEvent` to log a `User prompted before navigation` event when the onbeforeunload handler is called. It will then log `User prompted before navigation and still on page` events at 5, 15, and 30 seconds after the onbeforeunload handler is called (the `seconds` property on the event will contain the number of seconds since the initial prompt). |
There was a problem hiding this comment.
Just to confirm my understanding of the behavior: When I tested this, as long as the confirmation prompt was visible, there was no logging happening. It was only after I dismissed the confirmation prompt that it started logging the next events. Is that expected?
There was a problem hiding this comment.
Yeah, I don't think there's a good way for us to know when the prompt is dismissed. That was part of the motivation for doing the follow-up logging at intervals.
There was a problem hiding this comment.
Well it was more that nothing was logged while the prompt was visible. So as far as how it's described here:
It will then log
User prompted before navigation and still on pageevents at 5, 15, and 30 seconds after the onbeforeunload handler is called
In reality it was more like:
It will then log
User prompted before navigation and still on pageevents at 5, 15, and 30 seconds after the onbeforeunload handler dialog is dismissed
🎫 Ticket
LG-9848
🛠 Summary of changes
Team Ada is considering removing some
onbeforeunloadprompts (the ones that say "If you navigate away from this page, your data may not be saved" or something to that effect) from the IdV flow on the grounds that they are annoying. But before we do that, we want actual data about how they are used and if they are doing their job.To that end, this PR refactors
onbeforeunloadcode out into its own package and adds some analytics calls:User prompted before navigationfires when theonbeforeunloadevent handler is calledUser prompted before navigation and still on pagefires at 5s, 15s, and 30s after to indicate the user stayed on the page