-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(store-sdk): custom events, tests and docs (#937)
* fix: general wrapper types for analytics and removed unecessary code from unwrap function * chore: renaming analytics index file to wrap * chore: adding wrap and unwrap functions tests * chore: adding tests to analytics layer * feat: allows sending custom analytics events * feat: add docs to analytics * chore: add test for custom event * docs: adding link to GA4 spec * chore: removing tsdx incompatible code * fix: change title name to analytics * docs: correct grammar Co-authored-by: Emerson Laurentino <emersonlaurentino@hotmail.com> * docs: improve readability and fix erros on code Co-authored-by: Larícia Mota <laricia.mota@vtex.com.br> * chore: improve error message from useAnalyticsEvent Co-authored-by: Larícia Mota <laricia.mota@vtex.com.br> * chore: moving event samples to fixture file Co-authored-by: Emerson Laurentino <emersonlaurentino@hotmail.com> Co-authored-by: Larícia Mota <laricia.mota@vtex.com.br>
- Loading branch information
1 parent
dcc05e9
commit c268542
Showing
9 changed files
with
342 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
## Analytics | ||
|
||
The analytics module lets you manage analytics events based on [Google Analytics 4 (GA4) data model](https://developers.google.com/analytics/devguides/collection/ga4/reference/events). The events are wrapped and then sent over standard `postMessage` calls, which share the event only with the website's origin. The events are received via event listeners. It also supports sending and receiving custom events as the types on the helper functions can be overridden. | ||
|
||
### Sending events | ||
|
||
Analytics events can be sent by using the `sendAnalyticsEvent` function and it's especially useful to send common ecommerce events such as `add_to_cart`. It enforces standard GA4 events via type check and IntelliSense suggestions, but this behavior can be altered via overriding the function's types. | ||
|
||
To fire a standard GA4 event: | ||
```tsx | ||
import { useCallback } from 'react' | ||
import { sendAnalyticsEvent } from '@vtex/store-sdk' | ||
|
||
const MyComponent = () => { | ||
const addToCartCallback = useCallback(() => { | ||
/* ... */ | ||
|
||
const addToCartEvent = { | ||
type: 'add_to_cart', | ||
data: { | ||
items: [ | ||
/* ... */ | ||
] | ||
} | ||
} | ||
|
||
sendAnalyticsEvent(addToCartEvent) | ||
}, []) | ||
|
||
return <button onClick={addToCartCallback}>Add to cart</button> | ||
} | ||
``` | ||
|
||
For custom events, define the type of the event and override the default type via `sendAnalyticsEvent` generics. Your custom event has to have a type and a data field of any kind. | ||
|
||
To fire a custom event: | ||
|
||
```tsx | ||
import { useCallback } from 'react' | ||
import { sendAnalyticsEvent } from '@vtex/store-sdk' | ||
|
||
interface CustomEvent { | ||
type: 'custom_event', | ||
data: { | ||
customProperty?: string | ||
} | ||
} | ||
|
||
const MyComponent = () => { | ||
const customEventCallback = useCallback(() => { | ||
/* ... */ | ||
|
||
const customEvent = { | ||
type: 'custom_event', | ||
data: { | ||
customProperty: 'value' | ||
} | ||
} | ||
|
||
sendAnalyticsEvent<CustomEvent>(customEvent) | ||
}, []) | ||
|
||
return <button onClick={customEventCallback}>Press here</button> | ||
} | ||
``` | ||
|
||
### Receiving events | ||
|
||
It's possible to receive analytics events by using the `useAnalyticsEvent` hook. It accepts a handler that will be called every time an event sent by `sendAnalyticsEvent` arrives. For that reason, it can fire both for standard GA4 events and for custom events that a library or a component might be sending. To help users be aware of that possibility, the event received by the handler is, by default, typed as `UnknownEvent`. You can assume it has another type by simply typing the callback function as you wish, but be careful with the unexpected events that might come to this handler. | ||
|
||
To use the `useAnalyticsEvent` hook: | ||
|
||
```tsx | ||
import { useAnalyticsEvent } from '@vtex/store-sdk' | ||
import type { AnalyticsEvent } from '@vtex/store-sdk' | ||
|
||
/** | ||
* Notice that we typed it as AnalyticsEvent, but there may be events that are not from this type. | ||
* | ||
* Since we're dealing with it on a switch and we are providing an empty default clause, | ||
* we're not gonna have issues receiving custom events sent by other components or libraries. | ||
*/ | ||
function handler(event: AnalyticsEvent) { | ||
switch(event.type) { | ||
case 'add_to_cart': { | ||
/* ... */ | ||
} | ||
|
||
/* ... */ | ||
|
||
default: { | ||
/* ... */ | ||
} | ||
} | ||
} | ||
|
||
// In your component: | ||
const MyComponent = () => { | ||
useAnalyticsEvent(handler) | ||
|
||
/* ... */ | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
packages/store-sdk/test/analytics/__fixtures__/EventSamples.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { AddToCartEvent } from '../../../src/analytics/events/add_to_cart' | ||
import type { WrappedAnalyticsEvent } from '../../../src/analytics/wrap' | ||
|
||
export interface CustomEvent { | ||
type: 'custom_event' | ||
data: { | ||
customDataProperty: string | ||
} | ||
customProperty: string | ||
} | ||
|
||
export const CUSTOM_EVENT_SAMPLE: CustomEvent = { | ||
type: 'custom_event', | ||
data: { | ||
customDataProperty: 'value', | ||
}, | ||
customProperty: 'value', | ||
} | ||
|
||
export const WRAPPED_CUSTOM_EVENT_SAMPLE: WrappedAnalyticsEvent<CustomEvent> = { | ||
type: 'AnalyticsEvent', | ||
data: { | ||
type: 'store:custom_event', | ||
data: { | ||
customDataProperty: 'value', | ||
}, | ||
customProperty: 'value', | ||
}, | ||
} | ||
|
||
export const ADD_TO_CART_SAMPLE: AddToCartEvent = { | ||
type: 'add_to_cart', | ||
data: { | ||
items: [{ item_id: 'PRODUCT_ID' }], | ||
}, | ||
} | ||
|
||
export const WRAPPED_ADD_TO_CART_SAMPLE: WrappedAnalyticsEvent<AddToCartEvent> = { | ||
type: 'AnalyticsEvent', | ||
data: { | ||
type: 'store:add_to_cart', | ||
data: { | ||
items: [{ item_id: 'PRODUCT_ID' }], | ||
}, | ||
}, | ||
} |
55 changes: 55 additions & 0 deletions
55
packages/store-sdk/test/analytics/sendAnalyticsEvent.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { sendAnalyticsEvent } from '../../src/analytics/sendAnalyticsEvent' | ||
import type { CustomEvent } from './__fixtures__/EventSamples' | ||
import { | ||
CUSTOM_EVENT_SAMPLE, | ||
ADD_TO_CART_SAMPLE, | ||
WRAPPED_CUSTOM_EVENT_SAMPLE, | ||
WRAPPED_ADD_TO_CART_SAMPLE, | ||
} from './__fixtures__/EventSamples' | ||
|
||
const noop = () => {} | ||
const origin = 'http://localhost:8080/' | ||
|
||
describe('sendAnalyticsEvent', () => { | ||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
it('window.postMessage is called with correct params', () => { | ||
const postMessageSpy = jest | ||
.spyOn(window, 'postMessage') | ||
.mockImplementation(noop) | ||
|
||
Object.defineProperty(window, 'origin', { | ||
writable: false, | ||
value: origin, | ||
}) | ||
|
||
sendAnalyticsEvent(ADD_TO_CART_SAMPLE) | ||
|
||
expect(postMessageSpy).toHaveBeenCalled() | ||
expect(postMessageSpy).toHaveBeenCalledWith( | ||
WRAPPED_ADD_TO_CART_SAMPLE, | ||
origin | ||
) | ||
}) | ||
|
||
it('sendAnalyticsEvent is able to send custom events', () => { | ||
const postMessageSpy = jest | ||
.spyOn(window, 'postMessage') | ||
.mockImplementation(noop) | ||
|
||
Object.defineProperty(window, 'origin', { | ||
writable: false, | ||
value: origin, | ||
}) | ||
|
||
sendAnalyticsEvent<CustomEvent>(CUSTOM_EVENT_SAMPLE) | ||
|
||
expect(postMessageSpy).toHaveBeenCalled() | ||
expect(postMessageSpy).toHaveBeenCalledWith( | ||
WRAPPED_CUSTOM_EVENT_SAMPLE, | ||
origin | ||
) | ||
}) | ||
}) |
44 changes: 44 additions & 0 deletions
44
packages/store-sdk/test/analytics/useAnalyticsEvent.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { renderHook } from '@testing-library/react-hooks' | ||
|
||
import { useAnalyticsEvent } from '../../src/analytics/useAnalyticsEvent' | ||
import { wrap } from '../../src/analytics/wrap' | ||
import { ADD_TO_CART_SAMPLE } from './__fixtures__/EventSamples' | ||
|
||
describe('useAnalyticsEvent', () => { | ||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
it('useAnalyticsEvent calls handler with correct params when an AnalyticsEvent is fired', async () => { | ||
const handler = jest.fn() | ||
|
||
jest.spyOn(window, 'addEventListener').mockImplementation((_, fn) => { | ||
if (typeof fn === 'function') { | ||
fn(new MessageEvent('message', { data: wrap(ADD_TO_CART_SAMPLE) })) | ||
} | ||
}) | ||
|
||
renderHook(() => useAnalyticsEvent(handler)) | ||
|
||
expect(handler).toHaveBeenCalled() | ||
expect(handler).toHaveBeenCalledWith(ADD_TO_CART_SAMPLE) | ||
}) | ||
|
||
it('useAnalyticsEvent ignores events that are not AnalyticsEvent', async () => { | ||
const handler = jest.fn() | ||
|
||
jest.spyOn(window, 'addEventListener').mockImplementation((_, fn) => { | ||
if (typeof fn === 'function') { | ||
fn( | ||
new MessageEvent('message', { | ||
data: { ...wrap(ADD_TO_CART_SAMPLE), type: 'OtherEventType' }, | ||
}) | ||
) | ||
} | ||
}) | ||
|
||
renderHook(() => useAnalyticsEvent(handler)) | ||
|
||
expect(handler).not.toHaveBeenCalled() | ||
}) | ||
}) |
Oops, something went wrong.