Created
October 21, 2022 10:17
-
-
Save kant01ne/639f3539f24c89f8573f5eda729e38e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
// We use lodash to transform the label name, if you don't have lodash, you could either install it, remove that part, or implement your own version of camel case. | |
import camelCase from 'lodash/camelCase' | |
export type AnalyticsListenerContextValue = { | |
// When manually calling stopPropagation, we want to make sure we still capture analytics so prefer using stopPropagation from this provider instead of calling event.stopPropagation directly. | |
stopPropagation: ( | |
event: | |
| React.KeyboardEvent | |
| MouseEvent | |
| React.MouseEvent<HTMLElement, MouseEvent> | |
) => void | |
trackEvent: (event: string, properties?: Record<string, unknown>) => void | |
} | |
const AnalyticsListenerContext = | |
React.createContext<AnalyticsListenerContextValue>({ | |
stopPropagation: () => {}, | |
trackEvent: () => {}, | |
}) | |
export const AnalyticsListenerProvider: React.FC<{ | |
children: React.ReactNode | |
track?: (name: string, properties?: Record<string, unknown>) => void | |
}> = ({ children, track }) => { | |
const processEvent: AnalyticsListenerContextValue['stopPropagation'] = | |
React.useCallback( | |
(event) => { | |
if (!event || !event.target) { | |
return | |
} | |
const target = event.target as Element | |
const attr = getTargetAttr(target) | |
if (attr) { | |
track?.('click', attr) | |
} | |
}, | |
[track] | |
) | |
React.useEffect(() => { | |
document.addEventListener('click', processEvent) | |
return () => { | |
document.removeEventListener('click', processEvent) | |
} | |
}, [processEvent]) | |
const trackEvent = React.useCallback( | |
(event: string, properties?: Record<string, unknown>) => { | |
track?.(event, properties) | |
}, | |
[track] | |
) | |
const stopPropagation: AnalyticsListenerContextValue['stopPropagation'] = | |
React.useCallback( | |
(event) => { | |
processEvent(event) | |
event.preventDefault() | |
event.stopPropagation() | |
}, | |
[processEvent] | |
) | |
return ( | |
<AnalyticsListenerContext.Provider value={{ stopPropagation, trackEvent }}> | |
{children} | |
</AnalyticsListenerContext.Provider> | |
) | |
} | |
const getTargetAttr = ( | |
target: Element, | |
depth = 3 | |
): { label: string; type: string } | undefined => { | |
let label = '' | |
let type: string | |
if (target.getAttribute('data-analytics-label')) { | |
label = | |
target.getAttribute('data-analytics-label') || | |
target.ariaLabel || | |
(target as HTMLButtonElement).innerText | |
type = target.nodeName.toLowerCase() | |
return { | |
label: camelCase(label), | |
type, | |
} | |
} | |
switch (target.nodeName) { | |
case 'BUTTON': { | |
label = | |
target.getAttribute('data-analytics-label') || | |
target.ariaLabel || | |
(target as HTMLButtonElement).innerText | |
type = 'button' | |
break | |
} | |
case 'SELECT': { | |
label = target.getAttribute('data-analytics-label') || target.ariaLabel | |
type = 'select' | |
break | |
} | |
case 'A': { | |
label = | |
target.getAttribute('data-analytics-label') || | |
target.ariaLabel || | |
(target as HTMLButtonElement).innerText | |
type = 'a' | |
break | |
} | |
case 'INPUT': { | |
if ((target as HTMLInputElement).type !== 'checkbox') { | |
return undefined | |
} | |
label = target.ariaLabel || (target as HTMLButtonElement).innerText | |
type = 'checkbox' | |
break | |
} | |
default: { | |
return depth === 0 || target.parentElement === null | |
? undefined | |
: getTargetAttr(target.parentElement, depth - 1) | |
} | |
} | |
return { | |
label: camelCase(label), | |
type, | |
} | |
} | |
export const useAnalyticsListener = (): AnalyticsListenerContextValue => | |
React.useContext(AnalyticsListenerContext) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment