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));
+ }
+ });
};