From a26072eb1d80635f90cc249c1eed2cb02ab5f6f1 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 11:55:08 +0200 Subject: [PATCH 01/22] Started with making the context Generic --- .gitignore | 4 +- lib/mixpanel/context.tsx | 88 +++++++------------ lib/mixpanel/index.ts | 3 +- .../tracking/MobileTrackingService.ts | 35 ++++++++ lib/mixpanel/tracking/TrackingService.ts | 7 ++ lib/mixpanel/tracking/WebTrackingService.ts | 52 +++++++++++ lib/mixpanel/types/baseTypes.ts | 19 ++++ lib/mixpanel/types/mobileTypes.ts | 20 +++++ lib/mixpanel/{types.ts => types/webTypes.ts} | 69 ++++++--------- lib/mixpanel/{ => web}/utils.ts | 0 test/mixpanel.test.tsx | 2 +- 11 files changed, 198 insertions(+), 101 deletions(-) create mode 100644 lib/mixpanel/tracking/MobileTrackingService.ts create mode 100644 lib/mixpanel/tracking/TrackingService.ts create mode 100644 lib/mixpanel/tracking/WebTrackingService.ts create mode 100644 lib/mixpanel/types/baseTypes.ts create mode 100644 lib/mixpanel/types/mobileTypes.ts rename lib/mixpanel/{types.ts => types/webTypes.ts} (76%) rename lib/mixpanel/{ => web}/utils.ts (100%) diff --git a/.gitignore b/.gitignore index b60064f..43d450b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ yarn-debug.log* yarn-error.log* /dist -.vscode \ No newline at end of file +.vscode + +.idea diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index 4d57c1e..a4716c4 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -7,25 +7,23 @@ import React, { useEffect, useState, } from 'react'; -import { MixpanelEvent, MixpanelPageViewEvent } from './types'; import { - extractUtmParams, - isStandalonePWA, writeUtmParamsToSessionStorage, -} from './utils.ts'; +} from './web/utils.ts'; +import { TrackingService } from './tracking/TrackingService.ts'; +import { WebMixpanelEvent, WebMixpanelPageViewEvent } from './types/webTypes.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from './types/mobileTypes.ts'; interface MixpanelContextProps { - trackEvent: (event: MixpanelEvent) => void; - trackPageView: (event: MixpanelPageViewEvent) => void; - setEventContext: (context: MixpanelEvent['context']) => void; + trackEvent: (event: WebMixpanelEvent | MobileMixpanelEvent) => void; + trackPageView: (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => void; + setEventContext: (context: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']) => void; } interface MixpanelProviderProps { children: React.ReactNode; - eventApiClient: ( - args: MixpanelEvent | MixpanelPageViewEvent - ) => Promise; - defaultEventContext?: MixpanelEvent['context']; + trackingService: TrackingService; + defaultEventContext?: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']; } const MixpanelContext = createContext(null); @@ -42,58 +40,32 @@ export function useMixpanelContext() { export function MixpanelProvider({ children, - eventApiClient, + trackingService, defaultEventContext, }: MixpanelProviderProps) { - const [eventContext, setEventContext] = useState( + const [eventContext, setEventContext] = useState( defaultEventContext || {} ); - const trackEvent = useCallback( - (event: MixpanelEvent) => { - // only send events on the client - if (typeof window === 'undefined') { - return; - } - - const utmParams = extractUtmParams(window.location.search); - - eventApiClient({ - ...event, - context: { - title: document.title, - pathname: window.location.pathname, - pwa: isStandalonePWA(), - ...eventContext, - ...utmParams, - ...event.context, - }, - }).catch((e) => console.error(e)); - }, - [eventApiClient, eventContext] - ); - - const trackPageView = useCallback( - (event: MixpanelPageViewEvent) => { - // only send events on the client - if (typeof window === 'undefined') { - return; - } - - const utmParams = extractUtmParams(window.location.search); - - eventApiClient({ - ...event, - name: 'Page view', - context: { - pwa: isStandalonePWA(), - ...utmParams, - ...event.context, - }, - }).catch((e) => console.error(e)); - }, - [eventApiClient] - ); + const trackEvent = useCallback((event: WebMixpanelEvent | MobileMixpanelEvent) => { + trackingService.trackEvent({ + ...event, + context: { + ...eventContext, + ...event.context, + }, + }); + }, [trackingService, eventContext]); + + const trackPageView = useCallback((event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => { + trackingService.trackPageView({ + ...event, + context: { + ...eventContext, + ...event.context, + }, + }); + }, [trackingService, eventContext]); useEffect(() => { writeUtmParamsToSessionStorage(window.location.search); diff --git a/lib/mixpanel/index.ts b/lib/mixpanel/index.ts index 629f275..9ecec59 100644 --- a/lib/mixpanel/index.ts +++ b/lib/mixpanel/index.ts @@ -1,2 +1,3 @@ export { MixpanelProvider, useMixpanelContext } from './context'; -export * from './types'; +export * from './types/webTypes.ts'; +export * from './types/mobileTypes.ts'; diff --git a/lib/mixpanel/tracking/MobileTrackingService.ts b/lib/mixpanel/tracking/MobileTrackingService.ts new file mode 100644 index 0000000..de45fc2 --- /dev/null +++ b/lib/mixpanel/tracking/MobileTrackingService.ts @@ -0,0 +1,35 @@ +import { TrackingService } from './TrackingService.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; + + +interface EventApiClient { + (args: MobileMixpanelEvent | MobileMixpanelPageViewEvent): Promise; +} + + +export class MobileTrackingService implements TrackingService { + private eventApiClient: EventApiClient; + + constructor(eventApiClient: EventApiClient) { + this.eventApiClient = eventApiClient; + } + + trackEvent(event: MobileMixpanelEvent): void { + this.eventApiClient({ + ...event, + context: { + ...event.context, + }, + }).catch((e) => console.error('Failed to track event:', e)); + } + + trackPageView(event: MobileMixpanelPageViewEvent): void { + this.eventApiClient({ + ...event, + name: 'Page view', + context: { + ...event.context, + }, + }).catch((e) => console.error('Failed to track page view:', e)); + } +} diff --git a/lib/mixpanel/tracking/TrackingService.ts b/lib/mixpanel/tracking/TrackingService.ts new file mode 100644 index 0000000..17baa61 --- /dev/null +++ b/lib/mixpanel/tracking/TrackingService.ts @@ -0,0 +1,7 @@ +import { WebMixpanelEvent, WebMixpanelPageViewEvent } from '../types/webTypes.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; + +export interface TrackingService { + trackEvent(event: WebMixpanelEvent | MobileMixpanelEvent): void; + trackPageView(event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent): void; +} diff --git a/lib/mixpanel/tracking/WebTrackingService.ts b/lib/mixpanel/tracking/WebTrackingService.ts new file mode 100644 index 0000000..de83891 --- /dev/null +++ b/lib/mixpanel/tracking/WebTrackingService.ts @@ -0,0 +1,52 @@ +import { TrackingService } from './TrackingService.ts'; +import { extractUtmParams, isStandalonePWA } from '../web/utils.ts'; +import { WebMixpanelEvent, WebMixpanelPageViewEvent } from '../types/webTypes.ts'; + +interface EventApiClient { + (args: WebMixpanelEvent | WebMixpanelPageViewEvent): Promise; +} + +export class WebTrackingService implements TrackingService { + private eventApiClient: EventApiClient; + + constructor(eventApiClient: EventApiClient) { + this.eventApiClient = eventApiClient; + } + + trackEvent(event: WebMixpanelEvent): void { + if (typeof window === 'undefined') { + return; + } + + const utmParams = extractUtmParams(window.location.search); + + this.eventApiClient({ + ...event, + context: { + title: document.title, + pathname: window.location.pathname, + pwa: isStandalonePWA(), + ...utmParams, + ...event.context, + }, + }).catch((e) => console.error('Failed to track event:', e)); + } + + trackPageView(event: WebMixpanelPageViewEvent): void { + if (typeof window === 'undefined') { + return; + } + + const utmParams = extractUtmParams(window.location.search); + + this.eventApiClient({ + ...event, + name: 'Page view', + context: { + pwa: isStandalonePWA(), + ...utmParams, + ...event.context, + }, + }).catch((e) => console.error('Failed to track page view:', e)); + } +} diff --git a/lib/mixpanel/types/baseTypes.ts b/lib/mixpanel/types/baseTypes.ts new file mode 100644 index 0000000..53ea7ac --- /dev/null +++ b/lib/mixpanel/types/baseTypes.ts @@ -0,0 +1,19 @@ +export interface BaseEventContext { + utm_source?: string; + utm_medium?: string; + utm_campaign?: string; + utm_content?: string; + utm_term?: string; + [key: string]: unknown; +} + +export interface BaseEventData { + title?: string; + [key: string]: unknown; +} + +export interface BaseMixpanelEvent { + name: string; + context?: BaseEventContext; + data?: BaseEventData; +} diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts new file mode 100644 index 0000000..020ad51 --- /dev/null +++ b/lib/mixpanel/types/mobileTypes.ts @@ -0,0 +1,20 @@ +import { BaseMixpanelEvent, BaseEventContext, BaseEventData } from './baseTypes.ts'; + +interface MobileEventContext extends BaseEventContext { + screen?: string; // e.g., 'Home', 'Settings' +} + +interface MobileEventData extends BaseEventData { + screen: string; + section?: string; +} + +export interface MobileMixpanelEvent extends BaseMixpanelEvent { + context?: MobileEventContext; + data?: MobileEventData; +} + +export interface MobileMixpanelPageViewEvent { + context?: MobileEventContext; + data: MobileEventData; +} diff --git a/lib/mixpanel/types.ts b/lib/mixpanel/types/webTypes.ts similarity index 76% rename from lib/mixpanel/types.ts rename to lib/mixpanel/types/webTypes.ts index 69640c1..dd167cc 100644 --- a/lib/mixpanel/types.ts +++ b/lib/mixpanel/types/webTypes.ts @@ -1,3 +1,24 @@ + +import { BaseMixpanelEvent, BaseEventContext, BaseEventData } from './baseTypes.ts'; + +export interface WebEventContext extends BaseEventContext { + title?: string; + pathname?: string; + href?: string; + route?: string; + audience?: string; + section?: string; + pwa?: boolean; +} + +export interface WebEventData extends BaseEventData { + pathname: string; + href?: string; + route?: string; + audience?: string; +} + + /** * @description * Mixpanel event we will pass this event to our own backend that is used as a proxy for Mixpanel. Almost everything is optional. The only required field is `name`. @@ -24,27 +45,10 @@ * } * } */ -export type MixpanelEvent = { - name: string; - context?: { - title?: string; - pathname?: string; - href?: string; - route?: string; - audience?: string; - section?: string; - pwa?: boolean; - utm_source?: string; - utm_medium?: string; - utm_campaign?: string; - utm_content?: string; - utm_term?: string; - [key: string]: unknown; - }; - data?: { - [key: string]: unknown; - }; -}; +export interface WebMixpanelEvent extends BaseMixpanelEvent { + context?: WebEventContext; + data?: WebEventData; +} /** * @description @@ -70,22 +74,7 @@ export type MixpanelEvent = { * } *} */ -export type MixpanelPageViewEvent = { - context?: { - pwa?: boolean; - utm_source?: string; - utm_medium?: string; - utm_campaign?: string; - utm_content?: string; - utm_term?: string; - [key: string]: unknown; - }; - data?: { - title?: string; - pathname: string; - href?: string; - route?: string; - audience?: string; - [key: string]: unknown; - }; -}; +export interface WebMixpanelPageViewEvent { + context?: WebEventContext; + data: WebEventData; +} diff --git a/lib/mixpanel/utils.ts b/lib/mixpanel/web/utils.ts similarity index 100% rename from lib/mixpanel/utils.ts rename to lib/mixpanel/web/utils.ts diff --git a/test/mixpanel.test.tsx b/test/mixpanel.test.tsx index 09a8d69..3555105 100644 --- a/test/mixpanel.test.tsx +++ b/test/mixpanel.test.tsx @@ -3,7 +3,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { extractUtmParams, writeUtmParamsToSessionStorage, -} from '../lib/mixpanel/utils'; +} from '../lib/mixpanel/web/utils'; import { MixpanelProvider, useMixpanelContext } from '../lib/mixpanel/context'; import { fireEvent, From 9ca2d07535ace5e46c4859ac412ae66be965189d Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 13:35:10 +0200 Subject: [PATCH 02/22] Fixed test --- test/mixpanel.test.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/mixpanel.test.tsx b/test/mixpanel.test.tsx index 3555105..6b3fc72 100644 --- a/test/mixpanel.test.tsx +++ b/test/mixpanel.test.tsx @@ -1,4 +1,6 @@ -// @ts-nocheck test file contains web api's that are not availalbe in node environment for typescript +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck test file contains web api's that are not available in node environment for typescript + import { afterEach, describe, expect, test, vi } from 'vitest'; import { extractUtmParams, @@ -13,6 +15,7 @@ import { } from '@testing-library/react'; import React, { useEffect } from 'react'; import { MixpanelEvent } from '../lib/mixpanel'; +import { WebTrackingService } from '../lib/mixpanel/tracking/WebTrackingService'; describe('UTM tags', () => { const urlContainingUTMParams = new URL( @@ -58,6 +61,7 @@ describe('MixpanelContext', () => { }) => ( {children} @@ -76,11 +80,7 @@ describe('MixpanelContext', () => { }); }; - function TrackEventTestingComponent({ - defaultEventContext, - }: { - defaultEventContext?: MixpanelEvent['context']; - }) { + function TrackEventTestingComponent({defaultEventContext}: { defaultEventContext?: MixpanelEvent['context']; }) { const { trackEvent, setEventContext } = useMixpanelContext(); useEffect(() => { From 7eec439734132992afdcfcfee78b6a865232aee7 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 15:48:15 +0200 Subject: [PATCH 03/22] Reworked Tests --- .../mixpanel/context.test.tsx | 52 ++--------- lib/mixpanel/context.tsx | 74 +++++++++------ .../tracking/MobileTrackingService.test.ts | 49 ++++++++++ .../tracking/MobileTrackingService.ts | 7 +- lib/mixpanel/tracking/TrackingService.ts | 14 ++- .../tracking/WebTrackingService.test.ts | 89 +++++++++++++++++++ lib/mixpanel/types/baseTypes.ts | 11 ++- lib/mixpanel/types/mobileTypes.ts | 31 ++++--- lib/mixpanel/types/webTypes.ts | 61 +++++++------ lib/mixpanel/web/utils.test.ts | 34 +++++++ 10 files changed, 298 insertions(+), 124 deletions(-) rename test/mixpanel.test.tsx => lib/mixpanel/context.test.tsx (75%) create mode 100644 lib/mixpanel/tracking/MobileTrackingService.test.ts create mode 100644 lib/mixpanel/tracking/WebTrackingService.test.ts create mode 100644 lib/mixpanel/web/utils.test.ts diff --git a/test/mixpanel.test.tsx b/lib/mixpanel/context.test.tsx similarity index 75% rename from test/mixpanel.test.tsx rename to lib/mixpanel/context.test.tsx index 6b3fc72..4410556 100644 --- a/test/mixpanel.test.tsx +++ b/lib/mixpanel/context.test.tsx @@ -1,12 +1,7 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck test file contains web api's that are not available in node environment for typescript +// @ts-nocheck test file contains web apis that are not available in node environment for typescript -import { afterEach, describe, expect, test, vi } from 'vitest'; -import { - extractUtmParams, - writeUtmParamsToSessionStorage, -} from '../lib/mixpanel/web/utils'; -import { MixpanelProvider, useMixpanelContext } from '../lib/mixpanel/context'; +import { describe, expect, test, vi } from 'vitest'; import { fireEvent, render, @@ -14,40 +9,8 @@ import { RenderOptions, } from '@testing-library/react'; import React, { useEffect } from 'react'; -import { MixpanelEvent } from '../lib/mixpanel'; -import { WebTrackingService } from '../lib/mixpanel/tracking/WebTrackingService'; - -describe('UTM tags', () => { - const urlContainingUTMParams = new URL( - 'https://example.com?utm_source=source&utm_medium=medium&utm_campaign=campaign&utm_content=content&utm_term=term' - ); - - test('extracting utm tags from url', () => { - const result = extractUtmParams(urlContainingUTMParams.search); - - expect(result).toEqual({ - utm_source: 'source', - utm_medium: 'medium', - utm_campaign: 'campaign', - utm_content: 'content', - utm_term: 'term', - }); - }); - - test('utm tags are saved in session storage', () => { - writeUtmParamsToSessionStorage(urlContainingUTMParams.search); - - expect(sessionStorage.getItem('utm_source')).toBe('source'); - expect(sessionStorage.getItem('utm_medium')).toBe('medium'); - expect(sessionStorage.getItem('utm_campaign')).toBe('campaign'); - expect(sessionStorage.getItem('utm_content')).toBe('content'); - expect(sessionStorage.getItem('utm_term')).toBe('term'); - }); - - afterEach(() => { - sessionStorage.clear(); - }); -}); +import { MixpanelProvider, useMixpanelContext } from './context.tsx'; +import { WebTrackingService } from './tracking/WebTrackingService.ts'; describe('MixpanelContext', () => { const eventApiClient = vi.fn(() => Promise.resolve()); @@ -60,7 +23,6 @@ describe('MixpanelContext', () => { defaultEventContext: MixpanelEvent; }) => ( @@ -80,7 +42,11 @@ describe('MixpanelContext', () => { }); }; - function TrackEventTestingComponent({defaultEventContext}: { defaultEventContext?: MixpanelEvent['context']; }) { + function TrackEventTestingComponent({ + defaultEventContext, + }: { + defaultEventContext?: MixpanelEvent['context']; + }) { const { trackEvent, setEventContext } = useMixpanelContext(); useEffect(() => { diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index a4716c4..0b115e4 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -7,23 +7,33 @@ import React, { useEffect, useState, } from 'react'; -import { - writeUtmParamsToSessionStorage, -} from './web/utils.ts'; +import { writeUtmParamsToSessionStorage } from './web/utils.ts'; import { TrackingService } from './tracking/TrackingService.ts'; -import { WebMixpanelEvent, WebMixpanelPageViewEvent } from './types/webTypes.ts'; -import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from './types/mobileTypes.ts'; +import { + WebMixpanelEvent, + WebMixpanelPageViewEvent, +} from './types/webTypes.ts'; +import { + MobileMixpanelEvent, + MobileMixpanelPageViewEvent, +} from './types/mobileTypes.ts'; interface MixpanelContextProps { trackEvent: (event: WebMixpanelEvent | MobileMixpanelEvent) => void; - trackPageView: (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => void; - setEventContext: (context: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']) => void; + trackPageView: ( + event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent + ) => void; + setEventContext: ( + context: WebMixpanelEvent['context'] | MobileMixpanelEvent['context'] + ) => void; } interface MixpanelProviderProps { children: React.ReactNode; trackingService: TrackingService; - defaultEventContext?: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']; + defaultEventContext?: + | WebMixpanelEvent['context'] + | MobileMixpanelEvent['context']; } const MixpanelContext = createContext(null); @@ -43,29 +53,35 @@ export function MixpanelProvider({ trackingService, defaultEventContext, }: MixpanelProviderProps) { - const [eventContext, setEventContext] = useState( - defaultEventContext || {} - ); + const [eventContext, setEventContext] = useState< + WebMixpanelEvent['context'] | MobileMixpanelEvent['context'] + >(defaultEventContext || {}); - const trackEvent = useCallback((event: WebMixpanelEvent | MobileMixpanelEvent) => { - trackingService.trackEvent({ - ...event, - context: { - ...eventContext, - ...event.context, - }, - }); - }, [trackingService, eventContext]); + const trackEvent = useCallback( + (event: WebMixpanelEvent | MobileMixpanelEvent) => { + trackingService.trackEvent({ + ...event, + context: { + ...eventContext, + ...event.context, + }, + }); + }, + [trackingService, eventContext] + ); - const trackPageView = useCallback((event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => { - trackingService.trackPageView({ - ...event, - context: { - ...eventContext, - ...event.context, - }, - }); - }, [trackingService, eventContext]); + const trackPageView = useCallback( + (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => { + trackingService.trackPageView({ + ...event, + context: { + ...eventContext, + ...event.context, + }, + }); + }, + [trackingService, eventContext] + ); useEffect(() => { writeUtmParamsToSessionStorage(window.location.search); diff --git a/lib/mixpanel/tracking/MobileTrackingService.test.ts b/lib/mixpanel/tracking/MobileTrackingService.test.ts new file mode 100644 index 0000000..eb66291 --- /dev/null +++ b/lib/mixpanel/tracking/MobileTrackingService.test.ts @@ -0,0 +1,49 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { MobileTrackingService } from './MobileTrackingService'; +import { + MobileMixpanelEvent, + MobileMixpanelPageViewEvent, +} from '../types/mobileTypes'; + +describe('MobileTrackingService', () => { + const mockEventApiClient = vi.fn(() => Promise.resolve()); + let service: MobileTrackingService; + let mockEvent: MobileMixpanelEvent; + let mockPageViewEvent: MobileMixpanelPageViewEvent; + + beforeEach(() => { + service = new MobileTrackingService(mockEventApiClient); + // Setup mock data + mockEvent = { + name: 'Test Event', + context: { screenName: 'HomeScreen', route: '/home' }, + data: { info: 'test' }, + }; + + mockPageViewEvent = { + context: { utm_source: 'google' }, + data: { title: 'Home Page', route: '/home', audience: 'users' }, + }; + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should successfully track an event', async () => { + await service.trackEvent(mockEvent); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...mockEvent, + context: { ...mockEvent.context }, + }); + }); + + it('should successfully track a page view', async () => { + await service.trackPageView(mockPageViewEvent); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...mockPageViewEvent, + name: 'Page view', + context: { ...mockPageViewEvent.context }, + }); + }); +}); diff --git a/lib/mixpanel/tracking/MobileTrackingService.ts b/lib/mixpanel/tracking/MobileTrackingService.ts index de45fc2..2d077df 100644 --- a/lib/mixpanel/tracking/MobileTrackingService.ts +++ b/lib/mixpanel/tracking/MobileTrackingService.ts @@ -1,12 +1,13 @@ import { TrackingService } from './TrackingService.ts'; -import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; - +import { + MobileMixpanelEvent, + MobileMixpanelPageViewEvent, +} from '../types/mobileTypes.ts'; interface EventApiClient { (args: MobileMixpanelEvent | MobileMixpanelPageViewEvent): Promise; } - export class MobileTrackingService implements TrackingService { private eventApiClient: EventApiClient; diff --git a/lib/mixpanel/tracking/TrackingService.ts b/lib/mixpanel/tracking/TrackingService.ts index 17baa61..28dc2bf 100644 --- a/lib/mixpanel/tracking/TrackingService.ts +++ b/lib/mixpanel/tracking/TrackingService.ts @@ -1,7 +1,15 @@ -import { WebMixpanelEvent, WebMixpanelPageViewEvent } from '../types/webTypes.ts'; -import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; +import { + WebMixpanelEvent, + WebMixpanelPageViewEvent, +} from '../types/webTypes.ts'; +import { + MobileMixpanelEvent, + MobileMixpanelPageViewEvent, +} from '../types/mobileTypes.ts'; export interface TrackingService { trackEvent(event: WebMixpanelEvent | MobileMixpanelEvent): void; - trackPageView(event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent): void; + trackPageView( + event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent + ): void; } diff --git a/lib/mixpanel/tracking/WebTrackingService.test.ts b/lib/mixpanel/tracking/WebTrackingService.test.ts new file mode 100644 index 0000000..ff539cf --- /dev/null +++ b/lib/mixpanel/tracking/WebTrackingService.test.ts @@ -0,0 +1,89 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck test file contains web apis that are not available in node environment for typescript + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { WebTrackingService } from './WebTrackingService'; +import { WebMixpanelEvent } from '../types/webTypes'; +import { extractUtmParams, isStandalonePWA } from '../web/utils'; + +vi.mock('../web/utils', () => ({ + extractUtmParams: vi.fn(), + isStandalonePWA: vi.fn(), +})); + +describe('WebTrackingService', () => { + const mockEventApiClient = vi.fn(() => Promise.resolve()); + let service: WebTrackingService; + + // Setup to simulate browser environment + global.window = Object.create(window); + const url = 'http://example.com?utm_source=test_source'; + Object.defineProperty(window, 'location', { + value: { + search: url.split('?')[1], + pathname: '/test', + }, + }); + + global.document = { + title: 'Test Page', + } as any; + + beforeEach(() => { + service = new WebTrackingService(mockEventApiClient); + (extractUtmParams as vi.Mock).mockReturnValue({ + utm_source: 'test_source', + }); + (isStandalonePWA as vi.Mock).mockReturnValue(true); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should track an event successfully', async () => { + const event: WebMixpanelEvent = { + name: 'Test Event', + context: { additional: 'data' }, + }; + + await service.trackEvent(event); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...event, + context: { + title: 'Test Page', + pathname: '/test', + pwa: true, + utm_source: 'test_source', + additional: 'data', + }, + }); + }); + + it('should not track an event if window is undefined', async () => { + delete global.window; + const event: WebMixpanelEvent = { name: 'Test Event' }; + await service.trackEvent(event); + expect(mockEventApiClient).not.toHaveBeenCalled(); + }); + + it('should track an event successfully', async () => { + const event: WebMixpanelEvent = { + name: 'Test Event', + context: { additional: 'data' }, + }; + + await service.trackEvent(event); + + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...event, + context: { + title: 'Test Page', + pathname: '/test', + pwa: true, + utm_source: 'test_source', + additional: 'data', + }, + }); + }); +}); diff --git a/lib/mixpanel/types/baseTypes.ts b/lib/mixpanel/types/baseTypes.ts index 53ea7ac..582e440 100644 --- a/lib/mixpanel/types/baseTypes.ts +++ b/lib/mixpanel/types/baseTypes.ts @@ -8,10 +8,19 @@ export interface BaseEventContext { } export interface BaseEventData { - title?: string; [key: string]: unknown; } +export interface MixpanelEventContext extends BaseEventContext { + title?: string; + audience?: string; + section?: string; +} + +export interface MixpanelBaseEventData extends BaseEventData { + audience: string; +} + export interface BaseMixpanelEvent { name: string; context?: BaseEventContext; diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index 020ad51..d74ce71 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -1,20 +1,23 @@ -import { BaseMixpanelEvent, BaseEventContext, BaseEventData } from './baseTypes.ts'; - -interface MobileEventContext extends BaseEventContext { - screen?: string; // e.g., 'Home', 'Settings' -} - -interface MobileEventData extends BaseEventData { - screen: string; - section?: string; -} +import { + BaseEventContext, + BaseEventData, + BaseMixpanelEvent, + MixpanelBaseEventData, + MixpanelEventContext, +} from './baseTypes.ts'; export interface MobileMixpanelEvent extends BaseMixpanelEvent { - context?: MobileEventContext; - data?: MobileEventData; + context?: { + screenName?: string; + route?: string; + } & MixpanelEventContext; + data?: BaseEventData; } export interface MobileMixpanelPageViewEvent { - context?: MobileEventContext; - data: MobileEventData; + context?: BaseEventContext; + data: { + title: string; + route: string; + } & MixpanelBaseEventData; } diff --git a/lib/mixpanel/types/webTypes.ts b/lib/mixpanel/types/webTypes.ts index dd167cc..690f47b 100644 --- a/lib/mixpanel/types/webTypes.ts +++ b/lib/mixpanel/types/webTypes.ts @@ -1,23 +1,10 @@ - -import { BaseMixpanelEvent, BaseEventContext, BaseEventData } from './baseTypes.ts'; - -export interface WebEventContext extends BaseEventContext { - title?: string; - pathname?: string; - href?: string; - route?: string; - audience?: string; - section?: string; - pwa?: boolean; -} - -export interface WebEventData extends BaseEventData { - pathname: string; - href?: string; - route?: string; - audience?: string; -} - +import { + BaseEventContext, + BaseEventData, + BaseMixpanelEvent, + MixpanelBaseEventData, + MixpanelEventContext, +} from './baseTypes.ts'; /** * @description @@ -46,8 +33,13 @@ export interface WebEventData extends BaseEventData { * } */ export interface WebMixpanelEvent extends BaseMixpanelEvent { - context?: WebEventContext; - data?: WebEventData; + context?: { + pathname?: string; + href?: string; + route?: string; + pwa?: boolean; + } & MixpanelEventContext; + data?: BaseEventData; } /** @@ -57,13 +49,6 @@ export interface WebMixpanelEvent extends BaseMixpanelEvent { * * @example * const event: MixpanelPageViewEvent = { - * data: { - * title: 'Product Page', // What page is the event triggered on - * pathname: '/product/123', // Make sure there aren't any personal info in the path - * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href - * route: '/product/:id', - * audience: 'Freelancer', // The audience that is viewing the page - * }, * context: { // This is optional and will be mostly provided by the MixpanelProvider * pwa: true, // Is the event triggered in a PWA * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser @@ -72,9 +57,23 @@ export interface WebMixpanelEvent extends BaseMixpanelEvent { * utm_content: 'cta button', //track the specific link within in an ad that a user clicked * utm_term: 'tv sale', // track keywords associated with campaigns * } + * data: { + * title: 'Product Page', // What page is the event triggered on + * pathname: '/product/123', // Make sure there aren't any personal info in the path + * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href + * route: '/product/:id', + * audience: 'Freelancer', // The audience that is viewing the page + * }, *} */ export interface WebMixpanelPageViewEvent { - context?: WebEventContext; - data: WebEventData; + context?: { + pwa?: boolean; + } & BaseEventContext; + data: { + title: string; + pathname: string; + href: string; + route: string; + } & MixpanelBaseEventData; } diff --git a/lib/mixpanel/web/utils.test.ts b/lib/mixpanel/web/utils.test.ts new file mode 100644 index 0000000..c381e22 --- /dev/null +++ b/lib/mixpanel/web/utils.test.ts @@ -0,0 +1,34 @@ +import { afterEach, describe, expect, test } from 'vitest'; +import { extractUtmParams, writeUtmParamsToSessionStorage } from './utils.ts'; + +describe('UTM tags', () => { + const urlContainingUTMParams = new URL( + 'https://example.com?utm_source=source&utm_medium=medium&utm_campaign=campaign&utm_content=content&utm_term=term' + ); + + test('extracting utm tags from url', () => { + const result = extractUtmParams(urlContainingUTMParams.search); + + expect(result).toEqual({ + utm_source: 'source', + utm_medium: 'medium', + utm_campaign: 'campaign', + utm_content: 'content', + utm_term: 'term', + }); + }); + + test('utm tags are saved in session storage', () => { + writeUtmParamsToSessionStorage(urlContainingUTMParams.search); + + expect(sessionStorage.getItem('utm_source')).toBe('source'); + expect(sessionStorage.getItem('utm_medium')).toBe('medium'); + expect(sessionStorage.getItem('utm_campaign')).toBe('campaign'); + expect(sessionStorage.getItem('utm_content')).toBe('content'); + expect(sessionStorage.getItem('utm_term')).toBe('term'); + }); + + afterEach(() => { + sessionStorage.clear(); + }); +}); From c791bd647bf08d2b2e071b6c36d69cac429116ec Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 15:56:00 +0200 Subject: [PATCH 04/22] 1.1.0-beta.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13e977f..99a09e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.0.0", + "version": "1.1.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.0.0", + "version": "1.1.0-beta.0", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 8c52c68..12cc143 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.0.0", + "version": "1.1.0-beta.0", "keywords": [ "Analytics", "Tag Manager", From 5d862a03e80f77947af1b4ff5dbd976921fe1013 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 16:49:03 +0200 Subject: [PATCH 05/22] Added exports & remove any's --- lib/mixpanel/context.test.tsx | 2 +- lib/mixpanel/index.ts | 6 ++++++ lib/mixpanel/tracking/WebTrackingService.test.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/mixpanel/context.test.tsx b/lib/mixpanel/context.test.tsx index 4410556..78181fc 100644 --- a/lib/mixpanel/context.test.tsx +++ b/lib/mixpanel/context.test.tsx @@ -35,7 +35,7 @@ describe('MixpanelContext', () => { options?: Omit ) => { return render(ui, { - wrapper: (props: any) => ( + wrapper: (props) => ( ), ...options?.testingLibraryOptions, diff --git a/lib/mixpanel/index.ts b/lib/mixpanel/index.ts index 9ecec59..4e240e8 100644 --- a/lib/mixpanel/index.ts +++ b/lib/mixpanel/index.ts @@ -1,3 +1,9 @@ export { MixpanelProvider, useMixpanelContext } from './context'; + +export * from './types/baseTypes.ts'; export * from './types/webTypes.ts'; export * from './types/mobileTypes.ts'; + +export type { TrackingService } from './tracking/TrackingService.ts'; +export { MobileTrackingService } from './tracking/MobileTrackingService.ts'; +export { WebTrackingService } from './tracking/WebTrackingService.ts'; diff --git a/lib/mixpanel/tracking/WebTrackingService.test.ts b/lib/mixpanel/tracking/WebTrackingService.test.ts index ff539cf..eadc14e 100644 --- a/lib/mixpanel/tracking/WebTrackingService.test.ts +++ b/lib/mixpanel/tracking/WebTrackingService.test.ts @@ -27,7 +27,7 @@ describe('WebTrackingService', () => { global.document = { title: 'Test Page', - } as any; + }; beforeEach(() => { service = new WebTrackingService(mockEventApiClient); From b807a1a9d4fccb9d1578fbf1262ff7f555789c86 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 16:49:17 +0200 Subject: [PATCH 06/22] 1.2.0-beta.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99a09e6..278bf55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.0", + "version": "1.2.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.0", + "version": "1.2.0-beta.0", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 12cc143..9ba488e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.0", + "version": "1.2.0-beta.0", "keywords": [ "Analytics", "Tag Manager", From 8f719a6932f3eaeff7c4123dad74e243f8232daf Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 16:50:09 +0200 Subject: [PATCH 07/22] beta-1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 278bf55..ca28d9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.2.0-beta.0", + "version": "1.1.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 9ba488e..43fa08f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.2.0-beta.0", + "version": "1.1.0-beta.1", "keywords": [ "Analytics", "Tag Manager", From 9489c30d7315abde952dd7118659472658d73275 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 17:02:20 +0200 Subject: [PATCH 08/22] Added window check for writeUtmParamsToSessionStorage --- lib/mixpanel/context.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index 0b115e4..99a1950 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -84,6 +84,11 @@ export function MixpanelProvider({ ); useEffect(() => { + // Only run on Web / Client + if (typeof window === 'undefined') { + return; + } + writeUtmParamsToSessionStorage(window.location.search); }, []); From ee0990b679d91ee81d33a45e20baeefb20c76d72 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 17:06:57 +0200 Subject: [PATCH 09/22] Fix test --- .../tracking/WebTrackingService.test.ts | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/mixpanel/tracking/WebTrackingService.test.ts b/lib/mixpanel/tracking/WebTrackingService.test.ts index eadc14e..8f023d0 100644 --- a/lib/mixpanel/tracking/WebTrackingService.test.ts +++ b/lib/mixpanel/tracking/WebTrackingService.test.ts @@ -68,22 +68,36 @@ describe('WebTrackingService', () => { }); it('should track an event successfully', async () => { - const event: WebMixpanelEvent = { + const event = { name: 'Test Event', context: { additional: 'data' }, }; - await service.trackEvent(event); - - expect(mockEventApiClient).toHaveBeenCalledWith({ + // Assume service modifies context + await service.trackEvent({ ...event, context: { + ...event.context, title: 'Test Page', pathname: '/test', pwa: true, utm_source: 'test_source', - additional: 'data', }, }); + + // Expected object structure that should be sent to the mock + const expectedEvent = { + name: 'Test Event', + context: { + title: 'Test Page', + pathname: '/test', + pwa: true, + utm_source: 'test_source', + additional: 'data', + }, + }; + + // Check if mock was called with the correct data + expect(mockEventApiClient).toHaveBeenCalledWith(expectedEvent); }); }); From 87db776329d7fbb6f30babac6ff592ea988b4e30 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 17:08:30 +0200 Subject: [PATCH 10/22] Fix test - rm/f --- .../tracking/WebTrackingService.test.ts | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/lib/mixpanel/tracking/WebTrackingService.test.ts b/lib/mixpanel/tracking/WebTrackingService.test.ts index 8f023d0..9c30b91 100644 --- a/lib/mixpanel/tracking/WebTrackingService.test.ts +++ b/lib/mixpanel/tracking/WebTrackingService.test.ts @@ -66,38 +66,4 @@ describe('WebTrackingService', () => { await service.trackEvent(event); expect(mockEventApiClient).not.toHaveBeenCalled(); }); - - it('should track an event successfully', async () => { - const event = { - name: 'Test Event', - context: { additional: 'data' }, - }; - - // Assume service modifies context - await service.trackEvent({ - ...event, - context: { - ...event.context, - title: 'Test Page', - pathname: '/test', - pwa: true, - utm_source: 'test_source', - }, - }); - - // Expected object structure that should be sent to the mock - const expectedEvent = { - name: 'Test Event', - context: { - title: 'Test Page', - pathname: '/test', - pwa: true, - utm_source: 'test_source', - additional: 'data', - }, - }; - - // Check if mock was called with the correct data - expect(mockEventApiClient).toHaveBeenCalledWith(expectedEvent); - }); }); From 0e666b2484888129f870eefab64579af73dd3504 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Tue, 23 Apr 2024 17:09:13 +0200 Subject: [PATCH 11/22] Update version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca28d9c..c40f5b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.1", + "version": "1.1.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 43fa08f..10b13fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.1", + "version": "1.1.0-beta.2", "keywords": [ "Analytics", "Tag Manager", From d52c18568e312871eece295e481cd95f9945b79e Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 10:24:58 +0200 Subject: [PATCH 12/22] Update Mobile types --- lib/mixpanel/types/baseTypes.ts | 4 ++-- lib/mixpanel/types/mobileTypes.ts | 3 +-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/mixpanel/types/baseTypes.ts b/lib/mixpanel/types/baseTypes.ts index 582e440..fd37b2d 100644 --- a/lib/mixpanel/types/baseTypes.ts +++ b/lib/mixpanel/types/baseTypes.ts @@ -18,11 +18,11 @@ export interface MixpanelEventContext extends BaseEventContext { } export interface MixpanelBaseEventData extends BaseEventData { - audience: string; + audience?: string; } export interface BaseMixpanelEvent { - name: string; + name?: string; context?: BaseEventContext; data?: BaseEventData; } diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index d74ce71..3d3f293 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -17,7 +17,6 @@ export interface MobileMixpanelEvent extends BaseMixpanelEvent { export interface MobileMixpanelPageViewEvent { context?: BaseEventContext; data: { - title: string; - route: string; + pathname?: string; } & MixpanelBaseEventData; } diff --git a/package-lock.json b/package-lock.json index c40f5b8..338efe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.2", + "version": "1.1.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.2.0-beta.0", + "version": "1.1.0-beta.3", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 10b13fe..97233d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.2", + "version": "1.1.0-beta.3", "keywords": [ "Analytics", "Tag Manager", From 91a8bd379cd904aaec168606c64a94f39a0f32b8 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 11:37:53 +0200 Subject: [PATCH 13/22] Export * --- lib/mixpanel/index.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/mixpanel/index.ts b/lib/mixpanel/index.ts index 4e240e8..b98f54d 100644 --- a/lib/mixpanel/index.ts +++ b/lib/mixpanel/index.ts @@ -1,4 +1,4 @@ -export { MixpanelProvider, useMixpanelContext } from './context'; +export * from './context'; export * from './types/baseTypes.ts'; export * from './types/webTypes.ts'; diff --git a/package-lock.json b/package-lock.json index 338efe1..e276303 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.3", + "version": "1.1.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.3", + "version": "1.1.0-beta.4", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 97233d7..d000509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.3", + "version": "1.1.0-beta.4", "keywords": [ "Analytics", "Tag Manager", From d17e5fb402415cbf009b6d90a34ccafa3978d5de Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 11:48:17 +0200 Subject: [PATCH 14/22] Update .prettierrc --- .prettierrc | 6 +- lib/mixpanel/context.test.tsx | 317 ++++++++---------- lib/mixpanel/context.tsx | 137 ++++---- .../tracking/MobileTrackingService.test.ts | 71 ++-- .../tracking/MobileTrackingService.ts | 49 ++- lib/mixpanel/tracking/TrackingService.ts | 16 +- .../tracking/WebTrackingService.test.ts | 94 +++--- lib/mixpanel/tracking/WebTrackingService.ts | 76 ++--- lib/mixpanel/types/baseTypes.ts | 28 +- lib/mixpanel/types/mobileTypes.ts | 28 +- lib/mixpanel/types/webTypes.ts | 42 +-- lib/mixpanel/web/utils.test.ts | 46 +-- lib/mixpanel/web/utils.ts | 55 +-- 13 files changed, 442 insertions(+), 523 deletions(-) diff --git a/.prettierrc b/.prettierrc index e698378..290db2d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,8 @@ { "trailingComma": "es5", - "tabWidth": 4, + "bracketSameLine": true, "semi": true, - "singleQuote": true + "singleQuote": true, + "endOfLine": "lf", + "printWidth": 120 } diff --git a/lib/mixpanel/context.test.tsx b/lib/mixpanel/context.test.tsx index 78181fc..bda2403 100644 --- a/lib/mixpanel/context.test.tsx +++ b/lib/mixpanel/context.test.tsx @@ -2,196 +2,173 @@ // @ts-nocheck test file contains web apis that are not available in node environment for typescript import { describe, expect, test, vi } from 'vitest'; -import { - fireEvent, - render, - renderHook, - RenderOptions, -} from '@testing-library/react'; +import { fireEvent, render, renderHook, RenderOptions } from '@testing-library/react'; import React, { useEffect } from 'react'; import { MixpanelProvider, useMixpanelContext } from './context.tsx'; import { WebTrackingService } from './tracking/WebTrackingService.ts'; describe('MixpanelContext', () => { - const eventApiClient = vi.fn(() => Promise.resolve()); - - const ContextWrapper = ({ - children, - defaultEventContext, - }: { - children: React.ReactNode; - defaultEventContext: MixpanelEvent; - }) => ( - - {children} - - ); - - const renderWithMixpanelProvider = ( - ui: React.ReactElement, - options?: Omit - ) => { - return render(ui, { - wrapper: (props) => ( - - ), - ...options?.testingLibraryOptions, - }); - }; - - function TrackEventTestingComponent({ - defaultEventContext, - }: { - defaultEventContext?: MixpanelEvent['context']; - }) { - const { trackEvent, setEventContext } = useMixpanelContext(); - - useEffect(() => { - if (defaultEventContext) { - setEventContext(defaultEventContext); - } - }, [defaultEventContext]); - - return ( - - ); - } - - function TrackPageView() { - const { trackPageView } = useMixpanelContext(); - - useEffect(() => { - trackPageView({ - data: { - title: 'Example', - pathname: '/product/1', - route: '/product/:id', - }, - }); - }, []); - - return null; - } - - test('provides expected context with trackEvent function', () => { - const { result } = renderHook(() => useMixpanelContext(), { - wrapper: ContextWrapper, - }); - - expect(result.current).toHaveProperty('trackEvent'); - expect(typeof result.current.trackEvent).toBe('function'); - - expect(result.current).toHaveProperty('trackPageView'); - expect(typeof result.current.trackPageView).toBe('function'); + const eventApiClient = vi.fn(() => Promise.resolve()); + + const ContextWrapper = ({ + children, + defaultEventContext, + }: { + children: React.ReactNode; + defaultEventContext: MixpanelEvent; + }) => ( + + {children} + + ); + + const renderWithMixpanelProvider = (ui: React.ReactElement, options?: Omit) => { + return render(ui, { + wrapper: (props) => , + ...options?.testingLibraryOptions, }); + }; - test('trackEvent sends correct data to api client', () => { - const { getByText } = renderWithMixpanelProvider( - - ); + function TrackEventTestingComponent({ defaultEventContext }: { defaultEventContext?: MixpanelEvent['context'] }) { + const { trackEvent, setEventContext } = useMixpanelContext(); - fireEvent.click(getByText('button')); + useEffect(() => { + if (defaultEventContext) { + setEventContext(defaultEventContext); + } + }, [defaultEventContext]); - expect(eventApiClient).toHaveBeenCalledWith({ + return ( + + ); + } + + function TrackPageView() { + const { trackPageView } = useMixpanelContext(); + + useEffect(() => { + trackPageView({ + data: { + title: 'Example', + pathname: '/product/1', + route: '/product/:id', + }, + }); + }, []); + + return null; + } + + test('provides expected context with trackEvent function', () => { + const { result } = renderHook(() => useMixpanelContext(), { + wrapper: ContextWrapper, }); - test('provider can extend the default context for event tracking with provider prop', () => { - const defaultEventContext = { - href: 'https://example.com', - pathname: '/example', - audience: 'Consumer', - }; + expect(result.current).toHaveProperty('trackEvent'); + expect(typeof result.current.trackEvent).toBe('function'); - const { getByText } = renderWithMixpanelProvider( - , - { - contextWrapperProps: { defaultEventContext }, - } - ); + expect(result.current).toHaveProperty('trackPageView'); + expect(typeof result.current.trackPageView).toBe('function'); + }); - fireEvent.click(getByText('button')); + test('trackEvent sends correct data to api client', () => { + const { getByText } = renderWithMixpanelProvider(); - expect(eventApiClient).toHaveBeenCalledWith({ - name: 'event name', - context: { - title: 'Page title', - href: 'https://example.com', - pathname: '/example', - pwa: false, - audience: 'Consumer', - }, - data: { - productId: '123', - }, - }); - }); + fireEvent.click(getByText('button')); - test('Default event context can be extended from a child component', () => { - const defaultEventContext = { - href: 'https://example.com', - pathname: '/example', - audience: 'Consumer', - }; + expect(eventApiClient).toHaveBeenCalledWith({ + name: 'event name', + context: { + title: 'Page title', + pathname: '/', + pwa: false, + }, + data: { + productId: '123', + }, + }); + }); - const { getByText } = renderWithMixpanelProvider( - - ); + test('provider can extend the default context for event tracking with provider prop', () => { + const defaultEventContext = { + href: 'https://example.com', + pathname: '/example', + audience: 'Consumer', + }; - fireEvent.click(getByText('button')); + const { getByText } = renderWithMixpanelProvider(, { + contextWrapperProps: { defaultEventContext }, + }); - expect(eventApiClient).toHaveBeenCalledWith({ - name: 'event name', - context: { - title: 'Page title', - href: 'https://example.com', - pathname: '/example', - pwa: false, - audience: 'Consumer', - }, - data: { - productId: '123', - }, - }); + fireEvent.click(getByText('button')); + + expect(eventApiClient).toHaveBeenCalledWith({ + name: 'event name', + context: { + title: 'Page title', + href: 'https://example.com', + pathname: '/example', + pwa: false, + audience: 'Consumer', + }, + data: { + productId: '123', + }, }); + }); + + test('Default event context can be extended from a child component', () => { + const defaultEventContext = { + href: 'https://example.com', + pathname: '/example', + audience: 'Consumer', + }; + + const { getByText } = renderWithMixpanelProvider( + + ); - test('trackPageView sends correct data to api client', () => { - renderWithMixpanelProvider(); - - expect(eventApiClient).toHaveBeenCalledWith({ - name: 'Page view', - context: { - pwa: false, - }, - data: { - title: 'Example', - pathname: '/product/1', - route: '/product/:id', - }, - }); + fireEvent.click(getByText('button')); + + expect(eventApiClient).toHaveBeenCalledWith({ + name: 'event name', + context: { + title: 'Page title', + href: 'https://example.com', + pathname: '/example', + pwa: false, + audience: 'Consumer', + }, + data: { + productId: '123', + }, + }); + }); + + test('trackPageView sends correct data to api client', () => { + renderWithMixpanelProvider(); + + expect(eventApiClient).toHaveBeenCalledWith({ + name: 'Page view', + context: { + pwa: false, + }, + data: { + title: 'Example', + pathname: '/product/1', + route: '/product/:id', + }, }); + }); }); diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index 99a1950..cab2404 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -1,106 +1,83 @@ 'use client'; -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react'; +import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { writeUtmParamsToSessionStorage } from './web/utils.ts'; import { TrackingService } from './tracking/TrackingService.ts'; -import { - WebMixpanelEvent, - WebMixpanelPageViewEvent, -} from './types/webTypes.ts'; -import { - MobileMixpanelEvent, - MobileMixpanelPageViewEvent, -} from './types/mobileTypes.ts'; +import { WebMixpanelEvent, WebMixpanelPageViewEvent } from './types/webTypes.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from './types/mobileTypes.ts'; interface MixpanelContextProps { - trackEvent: (event: WebMixpanelEvent | MobileMixpanelEvent) => void; - trackPageView: ( - event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent - ) => void; - setEventContext: ( - context: WebMixpanelEvent['context'] | MobileMixpanelEvent['context'] - ) => void; + trackEvent: (event: WebMixpanelEvent | MobileMixpanelEvent) => void; + trackPageView: (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => void; + setEventContext: (context: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']) => void; } interface MixpanelProviderProps { - children: React.ReactNode; - trackingService: TrackingService; - defaultEventContext?: - | WebMixpanelEvent['context'] - | MobileMixpanelEvent['context']; + children: React.ReactNode; + trackingService: TrackingService; + defaultEventContext?: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']; } const MixpanelContext = createContext(null); export function useMixpanelContext() { - const context = useContext(MixpanelContext); + const context = useContext(MixpanelContext); - if (!context) { - throw new Error(' not found'); - } + if (!context) { + throw new Error(' not found'); + } - return context; + return context; } -export function MixpanelProvider({ - children, - trackingService, - defaultEventContext, -}: MixpanelProviderProps) { - const [eventContext, setEventContext] = useState< - WebMixpanelEvent['context'] | MobileMixpanelEvent['context'] - >(defaultEventContext || {}); +export function MixpanelProvider({ children, trackingService, defaultEventContext }: MixpanelProviderProps) { + const [eventContext, setEventContext] = useState( + defaultEventContext || {} + ); - const trackEvent = useCallback( - (event: WebMixpanelEvent | MobileMixpanelEvent) => { - trackingService.trackEvent({ - ...event, - context: { - ...eventContext, - ...event.context, - }, - }); + const trackEvent = useCallback( + (event: WebMixpanelEvent | MobileMixpanelEvent) => { + trackingService.trackEvent({ + ...event, + context: { + ...eventContext, + ...event.context, }, - [trackingService, eventContext] - ); + }); + }, + [trackingService, eventContext] + ); - const trackPageView = useCallback( - (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => { - trackingService.trackPageView({ - ...event, - context: { - ...eventContext, - ...event.context, - }, - }); + const trackPageView = useCallback( + (event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent) => { + trackingService.trackPageView({ + ...event, + context: { + ...eventContext, + ...event.context, }, - [trackingService, eventContext] - ); + }); + }, + [trackingService, eventContext] + ); - useEffect(() => { - // Only run on Web / Client - if (typeof window === 'undefined') { - return; - } + useEffect(() => { + // Only run on Web / Client + if (typeof window === 'undefined') { + return; + } - writeUtmParamsToSessionStorage(window.location.search); - }, []); + writeUtmParamsToSessionStorage(window.location.search); + }, []); - return ( - - {children} - - ); + return ( + + {children} + + ); } diff --git a/lib/mixpanel/tracking/MobileTrackingService.test.ts b/lib/mixpanel/tracking/MobileTrackingService.test.ts index eb66291..27d4630 100644 --- a/lib/mixpanel/tracking/MobileTrackingService.test.ts +++ b/lib/mixpanel/tracking/MobileTrackingService.test.ts @@ -1,49 +1,46 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { MobileTrackingService } from './MobileTrackingService'; -import { - MobileMixpanelEvent, - MobileMixpanelPageViewEvent, -} from '../types/mobileTypes'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes'; describe('MobileTrackingService', () => { - const mockEventApiClient = vi.fn(() => Promise.resolve()); - let service: MobileTrackingService; - let mockEvent: MobileMixpanelEvent; - let mockPageViewEvent: MobileMixpanelPageViewEvent; + const mockEventApiClient = vi.fn(() => Promise.resolve()); + let service: MobileTrackingService; + let mockEvent: MobileMixpanelEvent; + let mockPageViewEvent: MobileMixpanelPageViewEvent; - beforeEach(() => { - service = new MobileTrackingService(mockEventApiClient); - // Setup mock data - mockEvent = { - name: 'Test Event', - context: { screenName: 'HomeScreen', route: '/home' }, - data: { info: 'test' }, - }; + beforeEach(() => { + service = new MobileTrackingService(mockEventApiClient); + // Setup mock data + mockEvent = { + name: 'Test Event', + context: { screenName: 'HomeScreen', route: '/home' }, + data: { info: 'test' }, + }; - mockPageViewEvent = { - context: { utm_source: 'google' }, - data: { title: 'Home Page', route: '/home', audience: 'users' }, - }; - }); + mockPageViewEvent = { + context: { utm_source: 'google' }, + data: { title: 'Home Page', route: '/home', audience: 'users' }, + }; + }); - afterEach(() => { - vi.restoreAllMocks(); - }); + afterEach(() => { + vi.restoreAllMocks(); + }); - it('should successfully track an event', async () => { - await service.trackEvent(mockEvent); - expect(mockEventApiClient).toHaveBeenCalledWith({ - ...mockEvent, - context: { ...mockEvent.context }, - }); + it('should successfully track an event', async () => { + await service.trackEvent(mockEvent); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...mockEvent, + context: { ...mockEvent.context }, }); + }); - it('should successfully track a page view', async () => { - await service.trackPageView(mockPageViewEvent); - expect(mockEventApiClient).toHaveBeenCalledWith({ - ...mockPageViewEvent, - name: 'Page view', - context: { ...mockPageViewEvent.context }, - }); + it('should successfully track a page view', async () => { + await service.trackPageView(mockPageViewEvent); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...mockPageViewEvent, + name: 'Page view', + context: { ...mockPageViewEvent.context }, }); + }); }); diff --git a/lib/mixpanel/tracking/MobileTrackingService.ts b/lib/mixpanel/tracking/MobileTrackingService.ts index 2d077df..efaa695 100644 --- a/lib/mixpanel/tracking/MobileTrackingService.ts +++ b/lib/mixpanel/tracking/MobileTrackingService.ts @@ -1,36 +1,33 @@ import { TrackingService } from './TrackingService.ts'; -import { - MobileMixpanelEvent, - MobileMixpanelPageViewEvent, -} from '../types/mobileTypes.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; interface EventApiClient { - (args: MobileMixpanelEvent | MobileMixpanelPageViewEvent): Promise; + (args: MobileMixpanelEvent | MobileMixpanelPageViewEvent): Promise; } export class MobileTrackingService implements TrackingService { - private eventApiClient: EventApiClient; + private eventApiClient: EventApiClient; - constructor(eventApiClient: EventApiClient) { - this.eventApiClient = eventApiClient; - } + constructor(eventApiClient: EventApiClient) { + this.eventApiClient = eventApiClient; + } - trackEvent(event: MobileMixpanelEvent): void { - this.eventApiClient({ - ...event, - context: { - ...event.context, - }, - }).catch((e) => console.error('Failed to track event:', e)); - } + trackEvent(event: MobileMixpanelEvent): void { + this.eventApiClient({ + ...event, + context: { + ...event.context, + }, + }).catch((e) => console.error('Failed to track event:', e)); + } - trackPageView(event: MobileMixpanelPageViewEvent): void { - this.eventApiClient({ - ...event, - name: 'Page view', - context: { - ...event.context, - }, - }).catch((e) => console.error('Failed to track page view:', e)); - } + trackPageView(event: MobileMixpanelPageViewEvent): void { + this.eventApiClient({ + ...event, + name: 'Page view', + context: { + ...event.context, + }, + }).catch((e) => console.error('Failed to track page view:', e)); + } } diff --git a/lib/mixpanel/tracking/TrackingService.ts b/lib/mixpanel/tracking/TrackingService.ts index 28dc2bf..b908587 100644 --- a/lib/mixpanel/tracking/TrackingService.ts +++ b/lib/mixpanel/tracking/TrackingService.ts @@ -1,15 +1,7 @@ -import { - WebMixpanelEvent, - WebMixpanelPageViewEvent, -} from '../types/webTypes.ts'; -import { - MobileMixpanelEvent, - MobileMixpanelPageViewEvent, -} from '../types/mobileTypes.ts'; +import { WebMixpanelEvent, WebMixpanelPageViewEvent } from '../types/webTypes.ts'; +import { MobileMixpanelEvent, MobileMixpanelPageViewEvent } from '../types/mobileTypes.ts'; export interface TrackingService { - trackEvent(event: WebMixpanelEvent | MobileMixpanelEvent): void; - trackPageView( - event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent - ): void; + trackEvent(event: WebMixpanelEvent | MobileMixpanelEvent): void; + trackPageView(event: WebMixpanelPageViewEvent | MobileMixpanelPageViewEvent): void; } diff --git a/lib/mixpanel/tracking/WebTrackingService.test.ts b/lib/mixpanel/tracking/WebTrackingService.test.ts index 9c30b91..6a75af4 100644 --- a/lib/mixpanel/tracking/WebTrackingService.test.ts +++ b/lib/mixpanel/tracking/WebTrackingService.test.ts @@ -7,63 +7,63 @@ import { WebMixpanelEvent } from '../types/webTypes'; import { extractUtmParams, isStandalonePWA } from '../web/utils'; vi.mock('../web/utils', () => ({ - extractUtmParams: vi.fn(), - isStandalonePWA: vi.fn(), + extractUtmParams: vi.fn(), + isStandalonePWA: vi.fn(), })); describe('WebTrackingService', () => { - const mockEventApiClient = vi.fn(() => Promise.resolve()); - let service: WebTrackingService; + const mockEventApiClient = vi.fn(() => Promise.resolve()); + let service: WebTrackingService; - // Setup to simulate browser environment - global.window = Object.create(window); - const url = 'http://example.com?utm_source=test_source'; - Object.defineProperty(window, 'location', { - value: { - search: url.split('?')[1], - pathname: '/test', - }, - }); + // Setup to simulate browser environment + global.window = Object.create(window); + const url = 'http://example.com?utm_source=test_source'; + Object.defineProperty(window, 'location', { + value: { + search: url.split('?')[1], + pathname: '/test', + }, + }); - global.document = { - title: 'Test Page', - }; + global.document = { + title: 'Test Page', + }; - beforeEach(() => { - service = new WebTrackingService(mockEventApiClient); - (extractUtmParams as vi.Mock).mockReturnValue({ - utm_source: 'test_source', - }); - (isStandalonePWA as vi.Mock).mockReturnValue(true); + beforeEach(() => { + service = new WebTrackingService(mockEventApiClient); + (extractUtmParams as vi.Mock).mockReturnValue({ + utm_source: 'test_source', }); + (isStandalonePWA as vi.Mock).mockReturnValue(true); + }); - afterEach(() => { - vi.restoreAllMocks(); - }); + afterEach(() => { + vi.restoreAllMocks(); + }); - it('should track an event successfully', async () => { - const event: WebMixpanelEvent = { - name: 'Test Event', - context: { additional: 'data' }, - }; + it('should track an event successfully', async () => { + const event: WebMixpanelEvent = { + name: 'Test Event', + context: { additional: 'data' }, + }; - await service.trackEvent(event); - expect(mockEventApiClient).toHaveBeenCalledWith({ - ...event, - context: { - title: 'Test Page', - pathname: '/test', - pwa: true, - utm_source: 'test_source', - additional: 'data', - }, - }); + await service.trackEvent(event); + expect(mockEventApiClient).toHaveBeenCalledWith({ + ...event, + context: { + title: 'Test Page', + pathname: '/test', + pwa: true, + utm_source: 'test_source', + additional: 'data', + }, }); + }); - it('should not track an event if window is undefined', async () => { - delete global.window; - const event: WebMixpanelEvent = { name: 'Test Event' }; - await service.trackEvent(event); - expect(mockEventApiClient).not.toHaveBeenCalled(); - }); + it('should not track an event if window is undefined', async () => { + delete global.window; + const event: WebMixpanelEvent = { name: 'Test Event' }; + await service.trackEvent(event); + expect(mockEventApiClient).not.toHaveBeenCalled(); + }); }); diff --git a/lib/mixpanel/tracking/WebTrackingService.ts b/lib/mixpanel/tracking/WebTrackingService.ts index de83891..71f728b 100644 --- a/lib/mixpanel/tracking/WebTrackingService.ts +++ b/lib/mixpanel/tracking/WebTrackingService.ts @@ -3,50 +3,50 @@ import { extractUtmParams, isStandalonePWA } from '../web/utils.ts'; import { WebMixpanelEvent, WebMixpanelPageViewEvent } from '../types/webTypes.ts'; interface EventApiClient { - (args: WebMixpanelEvent | WebMixpanelPageViewEvent): Promise; + (args: WebMixpanelEvent | WebMixpanelPageViewEvent): Promise; } export class WebTrackingService implements TrackingService { - private eventApiClient: EventApiClient; + private eventApiClient: EventApiClient; - constructor(eventApiClient: EventApiClient) { - this.eventApiClient = eventApiClient; - } + constructor(eventApiClient: EventApiClient) { + this.eventApiClient = eventApiClient; + } - trackEvent(event: WebMixpanelEvent): void { - if (typeof window === 'undefined') { - return; - } - - const utmParams = extractUtmParams(window.location.search); - - this.eventApiClient({ - ...event, - context: { - title: document.title, - pathname: window.location.pathname, - pwa: isStandalonePWA(), - ...utmParams, - ...event.context, - }, - }).catch((e) => console.error('Failed to track event:', e)); + trackEvent(event: WebMixpanelEvent): void { + if (typeof window === 'undefined') { + return; } - trackPageView(event: WebMixpanelPageViewEvent): void { - if (typeof window === 'undefined') { - return; - } - - const utmParams = extractUtmParams(window.location.search); - - this.eventApiClient({ - ...event, - name: 'Page view', - context: { - pwa: isStandalonePWA(), - ...utmParams, - ...event.context, - }, - }).catch((e) => console.error('Failed to track page view:', e)); + const utmParams = extractUtmParams(window.location.search); + + this.eventApiClient({ + ...event, + context: { + title: document.title, + pathname: window.location.pathname, + pwa: isStandalonePWA(), + ...utmParams, + ...event.context, + }, + }).catch((e) => console.error('Failed to track event:', e)); + } + + trackPageView(event: WebMixpanelPageViewEvent): void { + if (typeof window === 'undefined') { + return; } + + const utmParams = extractUtmParams(window.location.search); + + this.eventApiClient({ + ...event, + name: 'Page view', + context: { + pwa: isStandalonePWA(), + ...utmParams, + ...event.context, + }, + }).catch((e) => console.error('Failed to track page view:', e)); + } } diff --git a/lib/mixpanel/types/baseTypes.ts b/lib/mixpanel/types/baseTypes.ts index fd37b2d..8775adf 100644 --- a/lib/mixpanel/types/baseTypes.ts +++ b/lib/mixpanel/types/baseTypes.ts @@ -1,28 +1,28 @@ export interface BaseEventContext { - utm_source?: string; - utm_medium?: string; - utm_campaign?: string; - utm_content?: string; - utm_term?: string; - [key: string]: unknown; + utm_source?: string; + utm_medium?: string; + utm_campaign?: string; + utm_content?: string; + utm_term?: string; + [key: string]: unknown; } export interface BaseEventData { - [key: string]: unknown; + [key: string]: unknown; } export interface MixpanelEventContext extends BaseEventContext { - title?: string; - audience?: string; - section?: string; + title?: string; + audience?: string; + section?: string; } export interface MixpanelBaseEventData extends BaseEventData { - audience?: string; + audience?: string; } export interface BaseMixpanelEvent { - name?: string; - context?: BaseEventContext; - data?: BaseEventData; + name?: string; + context?: BaseEventContext; + data?: BaseEventData; } diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index 3d3f293..ed9ec3a 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -1,22 +1,22 @@ import { - BaseEventContext, - BaseEventData, - BaseMixpanelEvent, - MixpanelBaseEventData, - MixpanelEventContext, + BaseEventContext, + BaseEventData, + BaseMixpanelEvent, + MixpanelBaseEventData, + MixpanelEventContext, } from './baseTypes.ts'; export interface MobileMixpanelEvent extends BaseMixpanelEvent { - context?: { - screenName?: string; - route?: string; - } & MixpanelEventContext; - data?: BaseEventData; + context?: { + screenName?: string; + route?: string; + } & MixpanelEventContext; + data?: BaseEventData; } export interface MobileMixpanelPageViewEvent { - context?: BaseEventContext; - data: { - pathname?: string; - } & MixpanelBaseEventData; + context?: BaseEventContext; + data: { + pathname?: string; + } & MixpanelBaseEventData; } diff --git a/lib/mixpanel/types/webTypes.ts b/lib/mixpanel/types/webTypes.ts index 690f47b..d5259c4 100644 --- a/lib/mixpanel/types/webTypes.ts +++ b/lib/mixpanel/types/webTypes.ts @@ -1,9 +1,9 @@ import { - BaseEventContext, - BaseEventData, - BaseMixpanelEvent, - MixpanelBaseEventData, - MixpanelEventContext, + BaseEventContext, + BaseEventData, + BaseMixpanelEvent, + MixpanelBaseEventData, + MixpanelEventContext, } from './baseTypes.ts'; /** @@ -33,13 +33,13 @@ import { * } */ export interface WebMixpanelEvent extends BaseMixpanelEvent { - context?: { - pathname?: string; - href?: string; - route?: string; - pwa?: boolean; - } & MixpanelEventContext; - data?: BaseEventData; + context?: { + pathname?: string; + href?: string; + route?: string; + pwa?: boolean; + } & MixpanelEventContext; + data?: BaseEventData; } /** @@ -67,13 +67,13 @@ export interface WebMixpanelEvent extends BaseMixpanelEvent { *} */ export interface WebMixpanelPageViewEvent { - context?: { - pwa?: boolean; - } & BaseEventContext; - data: { - title: string; - pathname: string; - href: string; - route: string; - } & MixpanelBaseEventData; + context?: { + pwa?: boolean; + } & BaseEventContext; + data: { + title: string; + pathname: string; + href: string; + route: string; + } & MixpanelBaseEventData; } diff --git a/lib/mixpanel/web/utils.test.ts b/lib/mixpanel/web/utils.test.ts index c381e22..abb30c2 100644 --- a/lib/mixpanel/web/utils.test.ts +++ b/lib/mixpanel/web/utils.test.ts @@ -2,33 +2,33 @@ import { afterEach, describe, expect, test } from 'vitest'; import { extractUtmParams, writeUtmParamsToSessionStorage } from './utils.ts'; describe('UTM tags', () => { - const urlContainingUTMParams = new URL( - 'https://example.com?utm_source=source&utm_medium=medium&utm_campaign=campaign&utm_content=content&utm_term=term' - ); + const urlContainingUTMParams = new URL( + 'https://example.com?utm_source=source&utm_medium=medium&utm_campaign=campaign&utm_content=content&utm_term=term' + ); - test('extracting utm tags from url', () => { - const result = extractUtmParams(urlContainingUTMParams.search); + test('extracting utm tags from url', () => { + const result = extractUtmParams(urlContainingUTMParams.search); - expect(result).toEqual({ - utm_source: 'source', - utm_medium: 'medium', - utm_campaign: 'campaign', - utm_content: 'content', - utm_term: 'term', - }); + expect(result).toEqual({ + utm_source: 'source', + utm_medium: 'medium', + utm_campaign: 'campaign', + utm_content: 'content', + utm_term: 'term', }); + }); - test('utm tags are saved in session storage', () => { - writeUtmParamsToSessionStorage(urlContainingUTMParams.search); + test('utm tags are saved in session storage', () => { + writeUtmParamsToSessionStorage(urlContainingUTMParams.search); - expect(sessionStorage.getItem('utm_source')).toBe('source'); - expect(sessionStorage.getItem('utm_medium')).toBe('medium'); - expect(sessionStorage.getItem('utm_campaign')).toBe('campaign'); - expect(sessionStorage.getItem('utm_content')).toBe('content'); - expect(sessionStorage.getItem('utm_term')).toBe('term'); - }); + expect(sessionStorage.getItem('utm_source')).toBe('source'); + expect(sessionStorage.getItem('utm_medium')).toBe('medium'); + expect(sessionStorage.getItem('utm_campaign')).toBe('campaign'); + expect(sessionStorage.getItem('utm_content')).toBe('content'); + expect(sessionStorage.getItem('utm_term')).toBe('term'); + }); - afterEach(() => { - sessionStorage.clear(); - }); + afterEach(() => { + sessionStorage.clear(); + }); }); diff --git a/lib/mixpanel/web/utils.ts b/lib/mixpanel/web/utils.ts index c11f6f1..e178ee3 100644 --- a/lib/mixpanel/web/utils.ts +++ b/lib/mixpanel/web/utils.ts @@ -1,49 +1,26 @@ export const isStandalonePWA = () => - typeof window !== 'undefined' - ? window.matchMedia('(display-mode: standalone)').matches - : false; + typeof window !== 'undefined' ? window.matchMedia('(display-mode: standalone)').matches : false; export const extractUtmParams = (paramsString: string) => { - const searchParams = new URLSearchParams(paramsString); + const searchParams = new URLSearchParams(paramsString); - return { - utm_source: - searchParams.get('utm_source') || - sessionStorage.getItem('utm_source') || - undefined, - utm_medium: - searchParams.get('utm_medium') || - sessionStorage.getItem('utm_medium') || - undefined, - utm_campaign: - searchParams.get('utm_campaign') || - sessionStorage.getItem('utm_campaign') || - undefined, - utm_content: - searchParams.get('utm_content') || - sessionStorage.getItem('utm_content') || - undefined, - utm_term: - searchParams.get('utm_term') || - sessionStorage.getItem('utm_term') || - undefined, - }; + return { + utm_source: searchParams.get('utm_source') || sessionStorage.getItem('utm_source') || undefined, + utm_medium: searchParams.get('utm_medium') || sessionStorage.getItem('utm_medium') || undefined, + utm_campaign: searchParams.get('utm_campaign') || sessionStorage.getItem('utm_campaign') || undefined, + utm_content: searchParams.get('utm_content') || sessionStorage.getItem('utm_content') || undefined, + utm_term: searchParams.get('utm_term') || sessionStorage.getItem('utm_term') || undefined, + }; }; export const writeUtmParamsToSessionStorage = (paramsString: string) => { - const searchParams = new URLSearchParams(paramsString); + const searchParams = new URLSearchParams(paramsString); - const utmSourceKeys = [ - 'utm_source', - 'utm_medium', - 'utm_campaign', - 'utm_content', - 'utm_term', - ]; + const utmSourceKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; - utmSourceKeys.forEach((key) => { - if (searchParams.has(key)) { - sessionStorage.setItem(key, searchParams.get(key)); - } - }); + utmSourceKeys.forEach((key) => { + if (searchParams.has(key)) { + sessionStorage.setItem(key, searchParams.get(key)); + } + }); }; From e6b9bc92769dd051636787fc462cff821e605f21 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 11:51:59 +0200 Subject: [PATCH 15/22] Added disableSessionStorage --- lib/mixpanel/context.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index cab2404..9c3fa8d 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -16,6 +16,7 @@ interface MixpanelProviderProps { children: React.ReactNode; trackingService: TrackingService; defaultEventContext?: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']; + disableSessionStorage?: boolean; } const MixpanelContext = createContext(null); @@ -30,7 +31,12 @@ export function useMixpanelContext() { return context; } -export function MixpanelProvider({ children, trackingService, defaultEventContext }: MixpanelProviderProps) { +export function MixpanelProvider({ + children, + trackingService, + defaultEventContext, + disableSessionStorage = false, +}: MixpanelProviderProps) { const [eventContext, setEventContext] = useState( defaultEventContext || {} ); @@ -62,13 +68,12 @@ export function MixpanelProvider({ children, trackingService, defaultEventContex ); useEffect(() => { - // Only run on Web / Client - if (typeof window === 'undefined') { + if (disableSessionStorage) { return; } writeUtmParamsToSessionStorage(window.location.search); - }, []); + }, [disableSessionStorage]); return ( Date: Wed, 24 Apr 2024 11:53:03 +0200 Subject: [PATCH 16/22] Update Version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e276303..2fbaa45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index d000509..aeaaa1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.4", + "version": "1.1.0-beta.5", "keywords": [ "Analytics", "Tag Manager", From 910cd91a1d4341b9cf6d3a01fd0783f2a7b84e52 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 13:49:18 +0200 Subject: [PATCH 17/22] Added Docs & Added globalSearchParams & localSearchParams --- lib/mixpanel/types/baseTypes.ts | 2 +- lib/mixpanel/types/mobileTypes.ts | 52 ++++++++++++++++++++++- lib/mixpanel/types/webTypes.ts | 69 ++++++++++++++++--------------- package-lock.json | 4 +- package.json | 2 +- 5 files changed, 89 insertions(+), 40 deletions(-) diff --git a/lib/mixpanel/types/baseTypes.ts b/lib/mixpanel/types/baseTypes.ts index 8775adf..0f8cbde 100644 --- a/lib/mixpanel/types/baseTypes.ts +++ b/lib/mixpanel/types/baseTypes.ts @@ -22,7 +22,7 @@ export interface MixpanelBaseEventData extends BaseEventData { } export interface BaseMixpanelEvent { - name?: string; + name: string; context?: BaseEventContext; data?: BaseEventData; } diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index ed9ec3a..ccad9d9 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -6,17 +6,65 @@ import { MixpanelEventContext, } from './baseTypes.ts'; +/** + * @description + * Mixpanel event we will pass this event to our own backend that is used as a proxy for Mixpanel. Almost everything is optional. The only required field is `name`. + * the other properties are suggestions for a "normal" event. + * @example + * const event: MobileMixpanelEvent = { + * name: 'Contact', // e.g. "Update profile", "Add to cart", "Purchase" + * context: { // Give some context to the event. Where is it triggered and by who + * pathname: '/product/123', // Make sure there aren't any personal info in the path + * audience: 'Freelancer', // Who is triggering this event e.g. a role or "new user" + * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser + * utm_medium: 'advertising', // track the advertising medium, including email and banner ads + * utm_campaign: 'Black friday', // track the campaign name associated with the traffic + * utm_content: 'cta button', //track the specific link within in an ad that a user clicked + * utm_term: 'tv sale', // track keywords associated with campaigns + * }, + * data: { // Any other properties that you want to add to the event + * product_id: '123', + * } + * } + */ + export interface MobileMixpanelEvent extends BaseMixpanelEvent { context?: { - screenName?: string; - route?: string; + pathname?: string; } & MixpanelEventContext; data?: BaseEventData; } +/** + * @description + * When sending a page view event to Mixpanel we will pass this event to our own backend that is used as a proxy for Mixpanel. + * It differs from the `MixpanelEvent` in that it has a fixed `name` and information about the page should be provided + * + * For localSearchParams and globalSearchParams, we can use useLocalSearchParams and useGlobalSearchParams from expo-router + * See: https://docs.expo.dev/router/reference/search-parameters/ + * + * @example + * const event: MobileMixpanelPageViewEvent = { + * context: { + * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser + * utm_medium: 'advertising', // track the advertising medium, including email and banner ads + * utm_campaign: 'Black friday', // track the campaign name associated with the traffic + * utm_content: 'cta button', //track the specific link within in an ad that a user clicked + * utm_term: 'tv sale', // track keywords associated with campaigns + * }, + * data: { + * pathname: '/product/detail', // The path of the page where the event is triggered + * localSearchParams: { product_id: '123' }, // The local search params of the page where the event is triggered + * globalSearchParams: { global_id: '12' }, // The global search params of the page where the event is triggered + * audience: 'Freelancer', // The audience that is viewing the page + * }, + * }; + */ export interface MobileMixpanelPageViewEvent { context?: BaseEventContext; data: { pathname?: string; + localSearchParams?: Record; + globalSearchParams?: Record; } & MixpanelBaseEventData; } diff --git a/lib/mixpanel/types/webTypes.ts b/lib/mixpanel/types/webTypes.ts index d5259c4..0e8d99b 100644 --- a/lib/mixpanel/types/webTypes.ts +++ b/lib/mixpanel/types/webTypes.ts @@ -11,25 +11,25 @@ import { * Mixpanel event we will pass this event to our own backend that is used as a proxy for Mixpanel. Almost everything is optional. The only required field is `name`. * the other properties are suggestions for a "normal" event. * @example - * const event: MixpanelEvent = { - * name: 'Contact', // e.g. "Update profile", "Add to cart", "Purchase" - * context: { // Give some context to the event. Where is it triggered and by who - * title: 'Product Page', // What page is the event triggered on - * pathname: '/product/123', // Make sure there aren't any personal info in the path - * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href - * route: '/product/:id', - * audience: 'Freelancer', // Who is triggering this event e.g. a role or "new user" - * section: 'footer', // What section is the event triggered in - * pwa: true, // Is the event triggered in a PWA - * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser - * utm_medium: 'advertising', // track the advertising medium, including email and banner ads - * utm_campaign: 'Black friday', // track the campaign name associated with the traffic - * utm_content: 'cta button', //track the specific link within in an ad that a user clicked - * utm_term: 'tv sale', // track keywords associated with campaigns - * }, - * data: { // Any other properties that you want to add to the event - * product_id: '123', - * } + * const event: WebMixpanelEvent = { + * name: 'Contact', // e.g. "Update profile", "Add to cart", "Purchase" + * context: { // Give some context to the event. Where is it triggered and by who + * title: 'Product Page', // What page is the event triggered on + * pathname: '/product/123', // Make sure there aren't any personal info in the path + * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href + * route: '/product/:id', + * audience: 'Freelancer', // Who is triggering this event e.g. a role or "new user" + * section: 'footer', // What section is the event triggered in + * pwa: true, // Is the event triggered in a PWA + * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser + * utm_medium: 'advertising', // track the advertising medium, including email and banner ads + * utm_campaign: 'Black friday', // track the campaign name associated with the traffic + * utm_content: 'cta button', //track the specific link within in an ad that a user clicked + * utm_term: 'tv sale', // track keywords associated with campaigns + * }, + * data: { // Any other properties that you want to add to the event + * product_id: '123', + * } * } */ export interface WebMixpanelEvent extends BaseMixpanelEvent { @@ -48,23 +48,24 @@ export interface WebMixpanelEvent extends BaseMixpanelEvent { * It differs from the `MixpanelEvent` in that it has a fixed `name` and information about the page should be provided * * @example - * const event: MixpanelPageViewEvent = { - * context: { // This is optional and will be mostly provided by the MixpanelProvider - * pwa: true, // Is the event triggered in a PWA - * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser - * utm_medium: 'advertising', // track the advertising medium, including email and banner ads - * utm_campaign: 'Black friday', // track the campaign name associated with the traffic - * utm_content: 'cta button', //track the specific link within in an ad that a user clicked - * utm_term: 'tv sale', // track keywords associated with campaigns - * } + * const event: WebMixpanelPageViewEvent = { + * context: { + * // This is optional and will be mostly provided by the MixpanelProvider + * pwa: true, // Is the event triggered in a PWA + * utm_source: 'Facebook', // track the source where traffic is coming from, including a website or advertiser + * utm_medium: 'advertising', // track the advertising medium, including email and banner ads + * utm_campaign: 'Black friday', // track the campaign name associated with the traffic + * utm_content: 'cta button', //track the specific link within in an ad that a user clicked + * utm_term: 'tv sale', // track keywords associated with campaigns + * }, * data: { - * title: 'Product Page', // What page is the event triggered on - * pathname: '/product/123', // Make sure there aren't any personal info in the path - * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href - * route: '/product/:id', - * audience: 'Freelancer', // The audience that is viewing the page + * title: 'Product Page', // What page is the event triggered on + * pathname: '/product/123', // Make sure there aren't any personal info in the path + * href: 'https://www.example.com/product/123', // Make sure there aren't any personal info in the href + * route: '/product/:id', + * audience: 'Freelancer', // The audience that is viewing the page * }, - *} + * }; */ export interface WebMixpanelPageViewEvent { context?: { diff --git a/package-lock.json b/package-lock.json index 2fbaa45..ea5c40a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index aeaaa1e..2cc94ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.5", + "version": "1.1.0-beta.6", "keywords": [ "Analytics", "Tag Manager", From 121e5c10212160d140d903a59d3b92510fc4317a Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 13:49:25 +0200 Subject: [PATCH 18/22] Added Docs & Added globalSearchParams & localSearchParams --- lib/mixpanel/types/mobileTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index ccad9d9..b09ed5f 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -40,6 +40,7 @@ export interface MobileMixpanelEvent extends BaseMixpanelEvent { * When sending a page view event to Mixpanel we will pass this event to our own backend that is used as a proxy for Mixpanel. * It differs from the `MixpanelEvent` in that it has a fixed `name` and information about the page should be provided * + * * For localSearchParams and globalSearchParams, we can use useLocalSearchParams and useGlobalSearchParams from expo-router * See: https://docs.expo.dev/router/reference/search-parameters/ * From d60dc85d4e0349a8ee85b233281be50af47aacd3 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Wed, 24 Apr 2024 16:34:44 +0200 Subject: [PATCH 19/22] localSearchParams & globalSearchParams --- lib/mixpanel/types/mobileTypes.ts | 4 ---- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/mixpanel/types/mobileTypes.ts b/lib/mixpanel/types/mobileTypes.ts index b09ed5f..d1b376b 100644 --- a/lib/mixpanel/types/mobileTypes.ts +++ b/lib/mixpanel/types/mobileTypes.ts @@ -55,8 +55,6 @@ export interface MobileMixpanelEvent extends BaseMixpanelEvent { * }, * data: { * pathname: '/product/detail', // The path of the page where the event is triggered - * localSearchParams: { product_id: '123' }, // The local search params of the page where the event is triggered - * globalSearchParams: { global_id: '12' }, // The global search params of the page where the event is triggered * audience: 'Freelancer', // The audience that is viewing the page * }, * }; @@ -65,7 +63,5 @@ export interface MobileMixpanelPageViewEvent { context?: BaseEventContext; data: { pathname?: string; - localSearchParams?: Record; - globalSearchParams?: Record; } & MixpanelBaseEventData; } diff --git a/package-lock.json b/package-lock.json index ea5c40a..e05759b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 2cc94ff..8be4fb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.6", + "version": "1.1.0-beta.7", "keywords": [ "Analytics", "Tag Manager", From 135d2c64cb84546740a03515be48e0006d6b5c69 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Thu, 25 Apr 2024 14:01:46 +0200 Subject: [PATCH 20/22] remove eventcontext from pagetracking: --- lib/mixpanel/context.tsx | 3 +-- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index 9c3fa8d..94779cc 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -59,12 +59,11 @@ export function MixpanelProvider({ trackingService.trackPageView({ ...event, context: { - ...eventContext, ...event.context, }, }); }, - [trackingService, eventContext] + [trackingService] ); useEffect(() => { diff --git a/package-lock.json b/package-lock.json index e05759b..1b010a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "license": "MIT", "devDependencies": { "@testing-library/react": "^14.2.1", diff --git a/package.json b/package.json index 8be4fb1..a6008c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.7", + "version": "1.1.0-beta.8", "keywords": [ "Analytics", "Tag Manager", From 27a85d53e48ea55639adac71132afce32e514d25 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Mon, 29 Apr 2024 09:50:43 +0200 Subject: [PATCH 21/22] Update Documentation --- doc/mixpanel_setup.md | 28 +++++++++++++++++++++++++--- lib/mixpanel/context.tsx | 20 ++++++++++++++++++++ package.json | 2 +- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/mixpanel_setup.md b/doc/mixpanel_setup.md index d3107b4..351a55e 100644 --- a/doc/mixpanel_setup.md +++ b/doc/mixpanel_setup.md @@ -13,7 +13,12 @@ import { MixpanelProvider } from '@freshheads/analytics-essentials'; const App = () => { return ( - + { + return sendTrackEvent(event); + }) + }> ); @@ -83,7 +88,13 @@ const defaultMixpanelEventContext = { const App = () => { return ( - + { + return sendTrackEvent(event); + }) + } + defaultEventContext={defaultMixpanelEventContext}> ); @@ -137,7 +148,12 @@ Then add this component to your app: const App = () => { return ( - + { + return sendTrackEvent(event); + }) + }> {children} @@ -150,6 +166,12 @@ const App = () => { UTM tags are automatically added to the context of the event if they are present in the URL. They will be remembered for the duration of the session. Even if the user navigates to a different page, the UTM tags will be added to new events. +#### Mobile + +On mobile, the UTM tags can not be stored in the session, use `disableSessionStorage` to disable this behaviour. + +```tsx + ## Mixpanel users Mixpanel events can be attached to a user. This is done in the backend on user login, see [FHMixpanelBundle](https://github.com/freshheads/FHMixpanelBundle) for more information. diff --git a/lib/mixpanel/context.tsx b/lib/mixpanel/context.tsx index 94779cc..a8f5afb 100644 --- a/lib/mixpanel/context.tsx +++ b/lib/mixpanel/context.tsx @@ -13,9 +13,29 @@ interface MixpanelContextProps { } interface MixpanelProviderProps { + /** + * Children to render + */ children: React.ReactNode; + /** + * Tracking service to use included in this package are `WebTrackingService` and `MobileTrackingService` + * @see WebTrackingService use for web applications + * @see MobileTrackingService use for mobile applications, NOTE: set disableSessionStorage to true to disable session storage because it is not available in mobile + * + * if you want to use your own tracking service you can implement the `TrackingService` interface + * @see TrackingService + * + */ trackingService: TrackingService; + /** + * Default event context to use for all events + * @see WebMixpanelEvent for web events + * @see MobileMixpanelEvent for mobile events + */ defaultEventContext?: WebMixpanelEvent['context'] | MobileMixpanelEvent['context']; + /** + * Disables session storage for storing utm params + */ disableSessionStorage?: boolean; } diff --git a/package.json b/package.json index a6008c1..a37beb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freshheads/analytics-essentials", - "version": "1.1.0-beta.8", + "version": "1.1.0", "keywords": [ "Analytics", "Tag Manager", From 77daafdec10c91416fa2a9a68db21623a8d20e57 Mon Sep 17 00:00:00 2001 From: Kevin van der Burg Date: Mon, 29 Apr 2024 10:24:48 +0200 Subject: [PATCH 22/22] prettier update --- .prettierrc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.prettierrc b/.prettierrc index 290db2d..0981b7c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,4 @@ { - "trailingComma": "es5", - "bracketSameLine": true, - "semi": true, "singleQuote": true, - "endOfLine": "lf", "printWidth": 120 }