diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-26 21:55:55 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:15:27 +0100 |
| commit | 3ab9f0423e97af63da4bf6a13ffd786955bd5b3b (patch) | |
| tree | 53866337f2e2b0bd47ada82f0f35799595663108 /src/utils | |
| parent | 795b92cc1a168c48c7710ca6e0e1ef5974013d95 (diff) | |
refactor(hooks,providers): rewrite useAckee hook and AckeeProvider
Diffstat (limited to 'src/utils')
| -rw-r--r-- | src/utils/constants.ts | 4 | ||||
| -rw-r--r-- | src/utils/hooks/index.ts | 2 | ||||
| -rw-r--r-- | src/utils/hooks/use-ackee/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/hooks/use-ackee/use-ackee.test.tsx | 44 | ||||
| -rw-r--r-- | src/utils/hooks/use-ackee/use-ackee.ts | 15 | ||||
| -rw-r--r-- | src/utils/hooks/use-update-ackee-options.tsx | 17 | ||||
| -rw-r--r-- | src/utils/providers/ackee-provider/ackee-provider.test.tsx | 46 | ||||
| -rw-r--r-- | src/utils/providers/ackee-provider/ackee-provider.tsx | 95 | ||||
| -rw-r--r-- | src/utils/providers/ackee-provider/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/providers/ackee.tsx | 58 | ||||
| -rw-r--r-- | src/utils/providers/index.ts | 2 |
11 files changed, 208 insertions, 77 deletions
diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e642af9..464db3f 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -30,3 +30,7 @@ export const ROUTES = { } as const; // cSpell:ignore legales thematique developpement + +export const STORAGE_KEY = { + ACKEE: 'ackee-tracking', +} as const; diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 47e90f1..cf8c01c 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -1,3 +1,4 @@ +export * from './use-ackee'; export * from './use-add-classname'; export * from './use-article'; export * from './use-attributes'; @@ -20,4 +21,3 @@ export * from './use-route-change'; export * from './use-scroll-position'; export * from './use-settings'; export * from './use-state-change'; -export * from './use-update-ackee-options'; diff --git a/src/utils/hooks/use-ackee/index.ts b/src/utils/hooks/use-ackee/index.ts new file mode 100644 index 0000000..81cac12 --- /dev/null +++ b/src/utils/hooks/use-ackee/index.ts @@ -0,0 +1 @@ +export * from './use-ackee'; diff --git a/src/utils/hooks/use-ackee/use-ackee.test.tsx b/src/utils/hooks/use-ackee/use-ackee.test.tsx new file mode 100644 index 0000000..230fe0b --- /dev/null +++ b/src/utils/hooks/use-ackee/use-ackee.test.tsx @@ -0,0 +1,44 @@ +import { act, renderHook } from '@testing-library/react'; +import type { FC, ReactNode } from 'react'; +import type { AckeeTrackerValue } from '../../../types'; +import { AckeeProvider, type AckeeProviderProps } from '../../providers'; +import { useAckee } from './use-ackee'; + +const createWrapper = ( + Wrapper: FC<AckeeProviderProps>, + config: AckeeProviderProps +) => + function CreatedWrapper({ children }: { children: ReactNode }) { + return <Wrapper {...config}>{children}</Wrapper>; + }; + +describe('useAckee', () => { + it('should return the default value without provider and prevent update', () => { + const { result } = renderHook(() => useAckee()); + + expect(result.current[0]).toBe('full'); + + act(() => result.current[1]()); + + expect(result.current[0]).toBe('full'); + }); + + it('can update the value', () => { + const defaultValue: AckeeTrackerValue = 'full'; + + const { result } = renderHook(() => useAckee(), { + wrapper: createWrapper(AckeeProvider, { + domainId: 'some-id', + server: 'https://example.com', + storageKey: 'veniam', + tracking: defaultValue, + }), + }); + + expect(result.current[0]).toBe(defaultValue); + + act(() => result.current[1]()); + + expect(result.current[0]).toBe('partial'); + }); +}); diff --git a/src/utils/hooks/use-ackee/use-ackee.ts b/src/utils/hooks/use-ackee/use-ackee.ts new file mode 100644 index 0000000..a89701a --- /dev/null +++ b/src/utils/hooks/use-ackee/use-ackee.ts @@ -0,0 +1,15 @@ +import { useCallback, useContext } from 'react'; +import { AckeeContext } from '../../providers'; + +export const useAckee = () => { + const { tracking, setTracking } = useContext(AckeeContext); + + const toggle = useCallback(() => { + setTracking((prev) => { + if (prev === 'full') return 'partial'; + return 'full'; + }); + }, [setTracking]); + + return [tracking, toggle] as const; +}; diff --git a/src/utils/hooks/use-update-ackee-options.tsx b/src/utils/hooks/use-update-ackee-options.tsx deleted file mode 100644 index f6e5c86..0000000 --- a/src/utils/hooks/use-update-ackee-options.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect } from 'react'; -import { useAckeeTracker } from '../providers'; - -export type AckeeOptions = 'full' | 'partial'; - -/** - * Update Ackee settings with the given choice. - * - * @param {AckeeOptions} value - Either `full` or `partial`. - */ -export const useUpdateAckeeOptions = (value: AckeeOptions) => { - const { setDetailed } = useAckeeTracker(); - - useEffect(() => { - setDetailed(value === 'full'); - }, [value, setDetailed]); -}; diff --git a/src/utils/providers/ackee-provider/ackee-provider.test.tsx b/src/utils/providers/ackee-provider/ackee-provider.test.tsx new file mode 100644 index 0000000..7ba11dc --- /dev/null +++ b/src/utils/providers/ackee-provider/ackee-provider.test.tsx @@ -0,0 +1,46 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { type FC, useContext } from 'react'; +import type { AckeeTrackerValue } from '../../../types'; +import { AckeeContext, AckeeProvider } from './ackee-provider'; + +const bodyPrefix = 'Tracking is set to:'; + +const ComponentTest: FC = () => { + const { tracking } = useContext(AckeeContext); + + return ( + <div> + {bodyPrefix} {tracking} + </div> + ); +}; + +describe('AckeeProvider', () => { + it('uses the default value when the provider is not used', () => { + render(<ComponentTest />); + + expect(rtlScreen.getByText(new RegExp(bodyPrefix))).toHaveTextContent( + `${bodyPrefix} full` + ); + }); + + it('provides the given tracking value to its children', () => { + const tracking: AckeeTrackerValue = 'partial'; + + render( + <AckeeProvider + domainId="some-id" + server="https://example.com" + storageKey="facilis" + tracking={tracking} + > + <ComponentTest /> + </AckeeProvider> + ); + + expect(rtlScreen.getByText(new RegExp(bodyPrefix))).toHaveTextContent( + `${bodyPrefix} ${tracking}` + ); + }); +}); diff --git a/src/utils/providers/ackee-provider/ackee-provider.tsx b/src/utils/providers/ackee-provider/ackee-provider.tsx new file mode 100644 index 0000000..8cd29cd --- /dev/null +++ b/src/utils/providers/ackee-provider/ackee-provider.tsx @@ -0,0 +1,95 @@ +import { useRouter } from 'next/router'; +import { + type FC, + type ReactNode, + createContext, + type Dispatch, + type SetStateAction, + useMemo, +} from 'react'; +import { useAckee } from 'use-ackee'; +import type { AckeeTrackerValue } from '../../../types'; +import { useLocalStorage } from '../../hooks'; + +type AckeeContextProps = { + tracking: AckeeTrackerValue; + setTracking: Dispatch<SetStateAction<AckeeTrackerValue>>; +}; + +export const AckeeContext = createContext<AckeeContextProps>({ + setTracking: (value) => value, + tracking: 'full', +}); + +const validator = (value: unknown): value is AckeeTrackerValue => + value === 'full' || value === 'partial'; + +export type AckeeProviderProps = { + /** + * The provider children. + */ + children?: ReactNode; + /** + * The id given by Ackee for this domain. + */ + domainId: string; + /** + * Should we track visits from localhost? + * + * @default false + */ + isLocalhostTracked?: boolean; + /** + * Should we track our own visits? + * + * @default false + */ + isOwnVisitsTracked?: boolean; + /** + * An URL that points to your Ackee installation (without trailing slash). + */ + server: string; + /** + * The key to use in local storage. + */ + storageKey: string; + /** + * Should the tracking be detailed (full) or not (partial)? + */ + tracking: AckeeTrackerValue; +}; + +export const AckeeProvider: FC<AckeeProviderProps> = ({ + children, + domainId, + isLocalhostTracked = false, + isOwnVisitsTracked = false, + server, + storageKey, + tracking: tracker, +}) => { + const [tracking, setTracking] = useLocalStorage<AckeeTrackerValue>( + storageKey, + tracker, + validator + ); + const { asPath } = useRouter(); + + useAckee( + asPath, + { domainId, server }, + { + detailed: tracking === 'full', + ignoreLocalhost: !isLocalhostTracked, + ignoreOwnVisits: !isOwnVisitsTracked, + } + ); + + const value = useMemo(() => { + return { setTracking, tracking }; + }, [setTracking, tracking]); + + return ( + <AckeeContext.Provider value={value}>{children}</AckeeContext.Provider> + ); +}; diff --git a/src/utils/providers/ackee-provider/index.ts b/src/utils/providers/ackee-provider/index.ts new file mode 100644 index 0000000..10f7a26 --- /dev/null +++ b/src/utils/providers/ackee-provider/index.ts @@ -0,0 +1 @@ +export * from './ackee-provider'; diff --git a/src/utils/providers/ackee.tsx b/src/utils/providers/ackee.tsx deleted file mode 100644 index 0cb0166..0000000 --- a/src/utils/providers/ackee.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useRouter } from 'next/router'; -import { createContext, FC, ReactNode, useContext, useState } from 'react'; -import useAckee from 'use-ackee'; - -export type AckeeProps = { - domain: string; - siteId: string; - detailed?: boolean; - setDetailed: (isDetailed: boolean) => void; -}; - -export type AckeeProviderProps = { - children: ReactNode; - domain: string; - siteId: string; - ignoreLocalhost?: boolean; - ignoreOwnVisits?: boolean; -}; - -export const AckeeContext = createContext<AckeeProps>({ - domain: '', - siteId: '', - setDetailed: (_) => { - // Do nothing. - }, -}); - -export const useAckeeTracker = () => useContext(AckeeContext); - -export const AckeeProvider: FC<AckeeProviderProps> = ({ - domain, - siteId, - children, - ignoreLocalhost = true, - ignoreOwnVisits = true, -}) => { - const [detailed, setDetailed] = useState<boolean>(false); - const { asPath } = useRouter(); - - useAckee( - asPath, - { server: domain, domainId: siteId }, - { detailed, ignoreLocalhost, ignoreOwnVisits } - ); - - return ( - <AckeeContext.Provider - value={{ - domain, - siteId, - detailed, - setDetailed, - }} - > - {children} - </AckeeContext.Provider> - ); -}; diff --git a/src/utils/providers/index.ts b/src/utils/providers/index.ts index 43641a1..640730f 100644 --- a/src/utils/providers/index.ts +++ b/src/utils/providers/index.ts @@ -1,2 +1,2 @@ -export * from './ackee'; +export * from './ackee-provider'; export * from './prism-theme'; |
