Skip to content

Commit

Permalink
Add support for custom events (#29)
Browse files Browse the repository at this point in the history
* Expose `track` function to track custom events

* Work in feedback

Co-Authored-By: Chris <7249920+chriswdmr@users.noreply.github.com>

* Check if track function successfully queues events into vaq

* Add react tests

Co-Authored-By: Chris <7249920+chriswdmr@users.noreply.github.com>

* Use eslint disable instead of ts-expect-error for tests

* Minor style improvments

* Respect `mode` attribute

* Refactor reading `mode`

* Refactor detectEnvironment

* Add tests for development mode

* Publish 0.1.7-beta.1

* Fix evaluating the mode; adding tests (#31)

* Fix version of tests

* Update apps/nextjs/package.json

Co-authored-by: Douglas Parsons <dglsparsons@users.noreply.github.com>

* Merge branch 'main' into add-custom-events

* Update pnpm-lock.yaml

* Add missing default exports

* Fix default exports

* Push beta version

* Use `event` instead of `track` inside of `vaq` (#35)

The analytics-script listens for `event` and not `track`

* Only run when window is defined (#38)

* Push package version

* Add log when server calls track (#40)

* Bump package version for new beta release

* Fix lint errors with custom events branch (#52)

* Address eslint errors with custom events branch

* Make CI workflow run regardless of target branch

* Bump package version

* Update pnpm-lock.yaml

* Revert "Update pnpm-lock.yaml"

This reverts commit 024f2c8.

* Fix build

* Update naming

Analytics -> Web Analytics

* Fix Development tests

* Remove beta badge from banner

---------

Co-authored-by: Chris <7249920+chriswdmr@users.noreply.github.com>
Co-authored-by: Douglas Parsons <dglsparsons@users.noreply.github.com>
Co-authored-by: Douglas Parsons <dglsparsons@gmail.com>
Co-authored-by: Timo Lins <me@timo.sh>
  • Loading branch information
5 people authored Apr 19, 2023
1 parent affbbc1 commit 6fd786a
Show file tree
Hide file tree
Showing 16 changed files with 535 additions and 136 deletions.
Binary file modified .github/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: ci
on:
pull_request:
branches:
- main
push:
branches:
- main
Expand Down
5 changes: 4 additions & 1 deletion apps/nextjs/e2e/development/beforeSend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ test.describe('beforeSend', () => {
page.on('console', (msg) => {
const message = msg.text();

if (message.includes('[Vercel Analytics]')) {
if (
message.includes('[Vercel Web Analytics]') ||
message.includes('[Vercel Analytics]')
) {
messages.push(message);
}
});
Expand Down
5 changes: 4 additions & 1 deletion apps/nextjs/e2e/development/pageview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ test.describe('pageview', () => {
page.on('console', (msg) => {
const message = msg.text();

if (message.includes('[Vercel Analytics]')) {
if (
message.includes('[Vercel Web Analytics]') ||
message.includes('[Vercel Analytics]')
) {
messages.push(message);
}
});
Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"test:e2e:production": "NEXT_PUBLIC_ANALYTICS_MODE=production pnpm --filter nextjs... build && playwright test production"
},
"dependencies": {
"@vercel/analytics": "workspace:^0.1.5",
"@vercel/analytics": "workspace:*",
"next": "latest",
"react": "18.2.0",
"react-dom": "18.2.0"
Expand Down
6 changes: 3 additions & 3 deletions packages/web/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![Analytics](https://github.com/vercel/analytics/blob/main/.github/banner.png)

<div align="center"><strong>Vercel Analytics for Audiences</strong></div>
<div align="center"><strong>Vercel Web Analytics</strong></div>
<div align="center">Privacy-friendly, real-time traffic insights</div>
<br />
<div align="center">
Expand All @@ -21,13 +21,13 @@ This package does **not** track data in development mode.

## Quickstart

1. Enable Vercel Analytics for a project in the [Vercel Dashboard](https://vercel.com/dashboard).
1. Enable Vercel Web Analytics for a project in the [Vercel Dashboard](https://vercel.com/dashboard).
2. Add the `@vercel/analytics` package to your project
3. Inject the Analytics script to your app

- If you are using **Next.js** or **React**, you can use the `<Analytics />` component to inject the script into your app.
- For other frameworks, you can use the `inject` function add the tracking script to your app.
- If you want to use Vercel Analytics on a static site without npm, follow the instructions in the [documentation](https://vercel.com/docs/concepts/analytics/audiences/quickstart).
- If you want to use Vercel Web Analytics on a static site without npm, follow the instructions in the [documentation](https://vercel.com/docs/concepts/analytics/audiences/quickstart).

4. Deploy your app to Vercel and see data flowing in.

Expand Down
4 changes: 2 additions & 2 deletions packages/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vercel/analytics",
"version": "0.1.11",
"version": "1.0.0",
"keywords": [
"analytics",
"vercel"
Expand Down Expand Up @@ -62,7 +62,7 @@
"jest-environment-jsdom": "^29.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tsup": "^6.3.0"
"tsup": "6.5.0"
},
"peerDependencies": {
"react": "^16.8||^17||^18"
Expand Down
147 changes: 146 additions & 1 deletion packages/web/src/generic.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { inject } from './generic';
import { inject, track } from './generic';

describe('inject', () => {
describe('in development mode', () => {
Expand Down Expand Up @@ -39,3 +39,148 @@ describe('inject', () => {
});
});
});

describe('track custom events', () => {
beforeEach(() => {
// reset the internal queue before every test
window.vaq = [];
});

describe('in production mode', () => {
beforeEach(() => {
inject({
mode: 'production',
});
});

describe('queue custom events', () => {
it('should track event with name only', () => {
track('my event');

expect(window.vaq).toBeDefined();

if (!window.vaq) throw new Error('window.vaq is not defined');

expect(window.vaq[0]).toEqual([
'event',
{
name: 'my event',
},
]);
});

it('should allow custom data to be tracked', () => {
track('custom event', {
string: 'string',
number: 1,
});

expect(window.vaq).toBeDefined();

if (!window.vaq) throw new Error('window.vaq is not defined');

expect(window.vaq[0]).toEqual([
'event',
{
name: 'custom event',
data: {
string: 'string',
number: 1,
},
},
]);
});

it('should strip data for nested objects', () => {
track('custom event', {
string: 'string',
number: 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
nested: {
object: '',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
});

expect(window.vaq).toBeDefined();

if (!window.vaq) throw new Error('window.vaq is not defined');

expect(window.vaq[0]).toEqual([
'event',
{
name: 'custom event',
data: {
string: 'string',
number: 1,
},
},
]);
});
});
});

describe('in development mode', () => {
beforeEach(() => {
inject({
mode: 'development',
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
jest.spyOn(global.console, 'error').mockImplementation(() => {});
});

describe('queue custom events', () => {
it('should track event with name only', () => {
track('my event');

expect(window.vaq).toBeDefined();

if (!window.vaq) throw new Error('window.vaq is not defined');

expect(window.vaq[0]).toEqual([
'event',
{
name: 'my event',
},
]);
});

it('should allow custom data to be tracked', () => {
track('custom event', {
string: 'string',
number: 1,
});

expect(window.vaq).toBeDefined();

if (!window.vaq) throw new Error('window.vaq is not defined');

expect(window.vaq[0]).toEqual([
'event',
{
name: 'custom event',
data: {
string: 'string',
number: 1,
},
},
]);
});

it('should log an error when nested properties are sent', () => {
track('custom event', {
string: 'string',
number: 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
nested: {
object: '',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
});

// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledTimes(1);
});
});
});
});
69 changes: 57 additions & 12 deletions packages/web/src/generic.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,84 @@
import { name, version } from '../package.json';
import { name as packageName, version } from '../package.json';
import { initQueue } from './queue';
import type { AnalyticsProps } from './types';
import { isBrowser, getMode } from './utils';
import type { AllowedPropertyValues, AnalyticsProps } from './types';
import {
isBrowser,
parseProperties,
setMode,
isDevelopment,
isProduction,
} from './utils';

export const inject = (
export function inject(
props: AnalyticsProps = {
debug: true,
},
): void => {
): void {
if (!isBrowser()) return;

const mode = getMode(props.mode);
setMode(props.mode);

initQueue();

if (props.beforeSend) {
window.va?.('beforeSend', props.beforeSend);
}

const src =
mode === 'development'
? 'https://va.vercel-scripts.com/v1/script.debug.js'
: '/_vercel/insights/script.js';
const src = isDevelopment()
? 'https://va.vercel-scripts.com/v1/script.debug.js'
: '/_vercel/insights/script.js';

if (document.head.querySelector(`script[src*="${src}"]`)) return;

const script = document.createElement('script');
script.src = src;
script.defer = true;
script.setAttribute('data-sdkn', name);
script.setAttribute('data-sdkn', packageName);
script.setAttribute('data-sdkv', version);

if (mode === 'development' && props.debug === false) {
if (isDevelopment() && props.debug === false) {
script.setAttribute('data-debug', 'false');
}

document.head.appendChild(script);
}

export function track(
name: string,
properties?: Record<string, AllowedPropertyValues>,
): void {
if (!isBrowser()) {
// eslint-disable-next-line no-console
console.warn(
'[Vercel Web Analytics] Server-side execution of `track()` is currently not supported.',
);
return;
}

if (!properties) {
window.va?.('event', { name });
return;
}

try {
const props = parseProperties(properties, {
strip: isProduction(),
});

window.va?.('event', {
name,
data: props,
});
} catch (err) {
if (err instanceof Error && isDevelopment()) {
// eslint-disable-next-line no-console
console.error(err);
}
}
}

// eslint-disable-next-line import/no-default-export
export default {
inject,
track,
};
2 changes: 0 additions & 2 deletions packages/web/src/index.ts

This file was deleted.

Loading

0 comments on commit 6fd786a

Please # to comment.