Skip to content

Commit

Permalink
add mixpanel (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
quisido committed Apr 27, 2024
1 parent f7814b8 commit ea784e2
Show file tree
Hide file tree
Showing 117 changed files with 888 additions and 986 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"dd-trace": "^5.10.0",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/authn-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/authn/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-rum-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/cjs-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@typescript-eslint/eslint-plugin": "patch:@typescript-eslint/eslint-plugin@npm%3A7.7.0#~/.yarn/patches/@typescript-eslint-eslint-plugin-npm-7.7.0-de43f1e757.patch",
"@typescript-eslint/parser": "^7.7.0",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.2.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/cloudflare-analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@types/eslint": "^8.56.10",
"@types/jest": "^29.5.12",
"@types/node": "^20.12.7",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@quisido/eslint-config": "workspace:^",
"@types/eslint": "^8.56.10",
"@types/jest": "^29.5.12",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/fmrs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/fullstory-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fullstory-react",
"version": "2.0.0",
"version": "2.0.1",
"author": "quisi.do <fullstory-react@quisi.do>",
"description": "FullStory integration with React",
"homepage": "https://github.com/quisido/quisi.do/tree/main/packages/fullstory-react#readme",
Expand Down Expand Up @@ -62,7 +62,7 @@
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.1.0",
"eslint": "^9.1.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
Expand Down
79 changes: 79 additions & 0 deletions packages/fullstory-react/src/components/fullstory.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/// <reference types="jest" />
import { FullStory } from '@fullstory/browser';
import { render } from '@testing-library/react';
import { MockFullstory } from '../index.js';

const ONCE = 1;
const TEST_FULLSTORY = Object.assign(jest.fn(), FullStory);
const TEST_INIT = jest.fn();
const TEST_IS_INITIALIZED = jest.fn();

describe('Fullstory', (): void => {
beforeEach((): void => {
TEST_IS_INITIALIZED.mockReturnValue(false);
TEST_INIT.mockImplementation((): void => {
TEST_IS_INITIALIZED.mockReturnValue(true);
});
});

it('should init', (): void => {
render(
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId="test-org-id"
/>
);

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
expect(TEST_INIT).toHaveBeenLastCalledWith({
orgId: 'test-org-id',
});
});

it('should not init on re-render', (): void => {
const { rerender } = render(
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId="test-org-id"
/>
);

// Re-render
rerender(
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId="test-org-id"
/>
);

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
});

it('should not init twice', (): void => {
render(
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId="test-org-id"
/>
);

render(
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId="test-org-id"
/>
);

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
});
});
36 changes: 36 additions & 0 deletions packages/fullstory-react/src/components/fullstory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import type { SnippetOptions } from '@fullstory/browser';
import { useEffect, type PropsWithChildren, type ReactElement } from 'react';
import useShallowMemo from 'use-shallow-memo';
import FullstoryContext from '../contexts/fullstory.js';
import useFullStoryBrowser from '../hooks/use-fullstory-browser.js';

export default function Fullstory({
children,
...snippetOptions
}: PropsWithChildren<SnippetOptions>): ReactElement {
// Contexts
const { FullStory, init, isInitialized } = useFullStoryBrowser();

// States
const memoizedSnippetOptions: SnippetOptions = useShallowMemo(snippetOptions);

// Effects
useEffect((): VoidFunction | undefined => {
if (isInitialized()) {
return;
}

init(memoizedSnippetOptions);
return (): void => {
FullStory('shutdown');
};
}, [FullStory, init, isInitialized, memoizedSnippetOptions]);

return (
<FullstoryContext.Provider value={FullStory}>
{children}
</FullstoryContext.Provider>
);
}
20 changes: 12 additions & 8 deletions packages/fullstory-react/src/components/mock-fullstory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

import * as fullStoryBrowser from '@fullstory/browser';
import type { ApiV1, ApiV2, FSApi } from '@fullstory/snippet';
import { type PropsWithChildren, type ReactElement, useMemo } from 'react';
import { useMemo, type PropsWithChildren, type ReactElement } from 'react';
import useShallowMemo from 'use-shallow-memo';
import FullStoryBrowser from '../contexts/fullstory-browser.js';
import merge from '../utils/merge.js';
import FullstoryBrowser from '../contexts/fullstory-browser.js';
import mapV2OperationHandlersToApi, {
type V2AsyncOperationHandlers,
type V2OperationHandlers,
} from '../utils/map-v2-operation-handlers-to-api.js';
import merge from '../utils/merge.js';
import noop from '../utils/noop.js';
import Fullstory from './fullstory.js';

type Props = Partial<
FSApi &
Expand All @@ -19,10 +20,11 @@ type Props = Partial<
V2OperationHandlers
>;

export default function MockFullStory({
export default function MockFullstory({
children,
orgId,
...props
}: PropsWithChildren<Props>): ReactElement {
}: PropsWithChildren<Props & { orgId: string }>): ReactElement {
const memoizedProps: Props = useShallowMemo(props);

const value: typeof fullStoryBrowser =
Expand All @@ -48,8 +50,10 @@ export default function MockFullStory({
}, [memoizedProps]);

return (
<FullStoryBrowser.Provider value={value}>
{children}
</FullStoryBrowser.Provider>
<FullstoryBrowser.Provider value={value}>
<Fullstory orgId={orgId}>
{children}
</Fullstory>
</FullstoryBrowser.Provider>
);
}
6 changes: 6 additions & 0 deletions packages/fullstory-react/src/contexts/fullstory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use client';

import { type FSApi } from "@fullstory/snippet";
import { createContext } from "react";

export default createContext<FSApi | null>(null);
66 changes: 19 additions & 47 deletions packages/fullstory-react/src/hooks/use-fullstory.test.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,46 @@
/// <reference types="jest" />
import { FullStory } from '@fullstory/browser';
import { renderHook } from '@testing-library/react';
import type { PropsWithChildren, ReactElement } from 'react';
import { MockFullStory, useFullStory } from '../index.js';
import MockFullstory from '../components/mock-fullstory.js';
import useFullstory from './use-fullstory.js';

const ONCE = 1;
const TEST_FULLSTORY = Object.assign(jest.fn(), FullStory);
const TEST_INIT = jest.fn();
const TEST_IS_INITIALIZED = jest.fn();

function TestWrapper({ children }: Readonly<PropsWithChildren>): ReactElement {
return (
<MockFullStory init={TEST_INIT} isInitialized={TEST_IS_INITIALIZED}>
<MockFullstory
FullStory={TEST_FULLSTORY}
init={TEST_INIT}
isInitialized={TEST_IS_INITIALIZED}
orgId='test-org-id'
>
{children}
</MockFullStory>
</MockFullstory>
);
}

describe('useFullStory', (): void => {
describe('useFullstory', (): void => {
beforeEach((): void => {
TEST_IS_INITIALIZED.mockReturnValue(false);
TEST_INIT.mockImplementation((): void => {
TEST_IS_INITIALIZED.mockReturnValue(true);
});
});

it('should init', (): void => {
renderHook(useFullStory, {
wrapper: TestWrapper,
initialProps: {
orgId: 'test-org-id',
},
});

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
expect(TEST_INIT).toHaveBeenLastCalledWith({
orgId: 'test-org-id',
});
});

it('should not init on re-render', (): void => {
const { rerender } = renderHook(useFullStory, {
wrapper: TestWrapper,
initialProps: {
orgId: 'test-org-id-1',
},
});

// Re-render
rerender({
orgId: 'test-org-id-2',
});

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
it('should throw when Fullstory is not provided', (): void => {
expect((): void => {
renderHook(useFullstory);
}).toThrow('Expected the Fullstory context to be provided.');
});

// If two components try to mount simultaneously, only respect the first one.
it('should not init twice', (): void => {
renderHook(useFullStory, {
wrapper: TestWrapper,
initialProps: {
orgId: 'test-org-id-1',
},
});

renderHook(useFullStory, {
it('should provide a Fullstory API', (): void => {
const { result } = renderHook(useFullstory, {
wrapper: TestWrapper,
initialProps: {
orgId: 'test-org-id-2',
},
});

expect(TEST_INIT).toHaveBeenCalledTimes(ONCE);
expect(result).not.toBe(null);
});
});
27 changes: 8 additions & 19 deletions packages/fullstory-react/src/hooks/use-fullstory.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
'use client';

import type { SnippetOptions } from '@fullstory/browser';
import { useEffect } from 'react';
import useShallowMemo from 'use-shallow-memo';
import useFullStoryBrowser from './use-fullstory-browser.js';
import type { FSApi } from '@fullstory/snippet';
import { useContext } from 'react';
import Fullstory from '../contexts/fullstory.js';

export default function useFullStory(
snippetOptions: Readonly<SnippetOptions>,
): FSApi {
export default function useFullstory(): FSApi {
// Contexts
const { FullStory, init, isInitialized } = useFullStoryBrowser();
const fullstory: FSApi | null = useContext(Fullstory);

// States
const memoizedSnippetOptions: SnippetOptions = useShallowMemo(snippetOptions);
if (fullstory === null) {
throw new Error('Expected the Fullstory context to be provided.');
}

// Effects
useEffect((): void => {
if (isInitialized()) {
return;
}
init(memoizedSnippetOptions);
}, [init, isInitialized, memoizedSnippetOptions]);

return FullStory;
return fullstory;
}

0 comments on commit ea784e2

Please sign in to comment.