@posthog/react 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +25 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/surveys/index.js +54 -6
- package/dist/esm/surveys/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/surveys/index.d.ts +3 -1
- package/dist/umd/index.js +25 -0
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/surveys/index.js +52 -4
- package/dist/umd/surveys/index.js.map +1 -1
- package/package.json +5 -3
- package/src/hooks/__tests__/featureFlags.test.tsx +178 -3
- package/src/hooks/__tests__/useThumbSurvey.test.tsx +105 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useFeatureFlagResult.ts +31 -0
- package/src/hooks/useThumbSurvey.ts +38 -5
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { renderHook, act } from '@testing-library/react'
|
|
3
|
+
import { PostHogProvider, PostHog } from '../../context'
|
|
4
|
+
import { useThumbSurvey } from '../useThumbSurvey'
|
|
5
|
+
import { SurveyEventName, SurveyEventProperties } from 'posthog-js'
|
|
6
|
+
import { isUndefined } from '../../utils/type-utils'
|
|
7
|
+
|
|
8
|
+
jest.useFakeTimers()
|
|
9
|
+
|
|
10
|
+
describe('useThumbSurvey hook', () => {
|
|
11
|
+
let posthog: PostHog
|
|
12
|
+
let captureMock: jest.Mock
|
|
13
|
+
let displaySurveyMock: jest.Mock
|
|
14
|
+
let wrapper: React.FC<{ children: React.ReactNode }>
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
captureMock = jest.fn()
|
|
18
|
+
displaySurveyMock = jest.fn()
|
|
19
|
+
|
|
20
|
+
posthog = {
|
|
21
|
+
capture: captureMock,
|
|
22
|
+
get_session_replay_url: () => 'https://app.posthog.com/replay/123',
|
|
23
|
+
surveys: { displaySurvey: displaySurveyMock },
|
|
24
|
+
} as unknown as PostHog
|
|
25
|
+
|
|
26
|
+
wrapper = ({ children }) => <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('survey shown tracking', () => {
|
|
30
|
+
it.each([
|
|
31
|
+
[false, true, false], // disableAutoShownTracking, shouldAutoTrack, shouldExposeTrackShown
|
|
32
|
+
[true, false, true],
|
|
33
|
+
])(
|
|
34
|
+
'disableAutoShownTracking=%s: auto-tracks=%s, exposes trackShown=%s',
|
|
35
|
+
(disableAutoShownTracking, shouldAutoTrack, shouldExposeTrackShown) => {
|
|
36
|
+
const { result } = renderHook(
|
|
37
|
+
() => useThumbSurvey({ surveyId: 'test-survey', disableAutoShownTracking }),
|
|
38
|
+
{ wrapper }
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
expect(captureMock).toHaveBeenCalledTimes(shouldAutoTrack ? 1 : 0)
|
|
42
|
+
expect(!isUndefined(result.current.trackShown)).toBe(shouldExposeTrackShown)
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
it('should only emit survey shown once when trackShown is called multiple times', () => {
|
|
47
|
+
const { result } = renderHook(
|
|
48
|
+
() => useThumbSurvey({ surveyId: 'test-survey', disableAutoShownTracking: true }),
|
|
49
|
+
{ wrapper }
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
act(() => {
|
|
53
|
+
result.current.trackShown?.()
|
|
54
|
+
result.current.trackShown?.()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
expect(captureMock).toHaveBeenCalledTimes(1)
|
|
58
|
+
expect(captureMock).toHaveBeenCalledWith(SurveyEventName.SHOWN, {
|
|
59
|
+
[SurveyEventProperties.SURVEY_ID]: 'test-survey',
|
|
60
|
+
sessionRecordingUrl: 'https://app.posthog.com/replay/123',
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('respond', () => {
|
|
66
|
+
it.each([
|
|
67
|
+
['up', 1],
|
|
68
|
+
['down', 2],
|
|
69
|
+
] as const)('respond("%s") calls displaySurvey with initialResponses: { 0: %d }', (value, expectedResponse) => {
|
|
70
|
+
const { result } = renderHook(() => useThumbSurvey({ surveyId: 'test-survey' }), { wrapper })
|
|
71
|
+
|
|
72
|
+
act(() => {
|
|
73
|
+
result.current.respond(value)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(displaySurveyMock).toHaveBeenCalledWith(
|
|
77
|
+
'test-survey',
|
|
78
|
+
expect.objectContaining({ initialResponses: { 0: expectedResponse } })
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should only allow one response', () => {
|
|
83
|
+
const { result } = renderHook(() => useThumbSurvey({ surveyId: 'test-survey' }), { wrapper })
|
|
84
|
+
|
|
85
|
+
act(() => {
|
|
86
|
+
result.current.respond('up')
|
|
87
|
+
result.current.respond('down')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(displaySurveyMock).toHaveBeenCalledTimes(1)
|
|
91
|
+
expect(result.current.response).toBe('up')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should call onResponse callback', () => {
|
|
95
|
+
const onResponse = jest.fn()
|
|
96
|
+
const { result } = renderHook(() => useThumbSurvey({ surveyId: 'test-survey', onResponse }), { wrapper })
|
|
97
|
+
|
|
98
|
+
act(() => {
|
|
99
|
+
result.current.respond('down')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
expect(onResponse).toHaveBeenCalledWith('down')
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
})
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FeatureFlagResult } from 'posthog-js'
|
|
2
|
+
import { useContext, useEffect, useState } from 'react'
|
|
3
|
+
import { PostHogContext } from '../context'
|
|
4
|
+
import { isUndefined } from '../utils/type-utils'
|
|
5
|
+
|
|
6
|
+
export function useFeatureFlagResult(flag: string): FeatureFlagResult | undefined {
|
|
7
|
+
const { client, bootstrap } = useContext(PostHogContext)
|
|
8
|
+
|
|
9
|
+
const [result, setResult] = useState<FeatureFlagResult | undefined>(() => client.getFeatureFlagResult(flag))
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
return client.onFeatureFlags(() => {
|
|
13
|
+
setResult(client.getFeatureFlagResult(flag))
|
|
14
|
+
})
|
|
15
|
+
}, [client, flag])
|
|
16
|
+
|
|
17
|
+
if (!client?.featureFlags?.hasLoadedFlags && bootstrap?.featureFlags) {
|
|
18
|
+
const bootstrappedValue = bootstrap.featureFlags[flag]
|
|
19
|
+
if (isUndefined(bootstrappedValue)) {
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
key: flag,
|
|
24
|
+
enabled: typeof bootstrappedValue === 'string' ? true : !!bootstrappedValue,
|
|
25
|
+
variant: typeof bootstrappedValue === 'string' ? bootstrappedValue : undefined,
|
|
26
|
+
payload: bootstrap.featureFlagPayloads?.[flag],
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result
|
|
31
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useMemo, type RefCallback } from 'react'
|
|
1
|
+
import { useState, useCallback, useRef, useMemo, type RefCallback, useEffect } from 'react'
|
|
2
2
|
import { usePostHog } from './usePostHog'
|
|
3
|
-
import { DisplaySurveyType, SurveyPosition } from 'posthog-js'
|
|
3
|
+
import { DisplaySurveyType, SurveyEventName, SurveyEventProperties, SurveyPosition } from 'posthog-js'
|
|
4
4
|
|
|
5
5
|
export interface UseThumbSurveyOptions {
|
|
6
6
|
/** ID of the target PostHog survey */
|
|
@@ -11,6 +11,8 @@ export interface UseThumbSurveyOptions {
|
|
|
11
11
|
properties?: Record<string, any>
|
|
12
12
|
/** Callback on thumb button click */
|
|
13
13
|
onResponse?: (response: 'up' | 'down') => void
|
|
14
|
+
/** Disable automatically emitting `survey shown` on hook mount. Defaults to false. */
|
|
15
|
+
disableAutoShownTracking?: boolean
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface UseThumbSurveyResult {
|
|
@@ -20,6 +22,8 @@ export interface UseThumbSurveyResult {
|
|
|
20
22
|
response: 'up' | 'down' | null
|
|
21
23
|
/** Ref to attach to the trigger element for positioning the followup survey popup */
|
|
22
24
|
triggerRef: RefCallback<HTMLElement>
|
|
25
|
+
/** Method to manually trigger a `survey shown` event. Only available when disableAutoShownTracking is true. */
|
|
26
|
+
trackShown?: () => void
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
const TRIGGER_ATTR = 'data-ph-thumb-survey-trigger'
|
|
@@ -60,6 +64,8 @@ const TRIGGER_ATTR = 'data-ph-thumb-survey-trigger'
|
|
|
60
64
|
* Notes:
|
|
61
65
|
* - The thumbs up/down response will ALWAYS be recorded, whether your survey is set to collect partial responses or not.
|
|
62
66
|
* - By default, followup questions will be displayed as a pop-up next to the triggerRef. Use options.position to change the position.
|
|
67
|
+
* - By default, `survey shown` is emitted automatically on hook mount. To prevent this behavior, set `disableAutoShownTracking: true`,
|
|
68
|
+
* and manually call `trackShown()` when you want to emit this event.
|
|
63
69
|
*
|
|
64
70
|
* @param options UseThumbSurveyOptions
|
|
65
71
|
* @returns UseThumbSurveyResult
|
|
@@ -69,6 +75,7 @@ export function useThumbSurvey({
|
|
|
69
75
|
displayPosition = SurveyPosition.NextToTrigger,
|
|
70
76
|
properties,
|
|
71
77
|
onResponse,
|
|
78
|
+
disableAutoShownTracking,
|
|
72
79
|
}: UseThumbSurveyOptions): UseThumbSurveyResult {
|
|
73
80
|
const posthog = usePostHog()
|
|
74
81
|
const [responded, setResponded] = useState<'up' | 'down' | null>(null)
|
|
@@ -89,9 +96,29 @@ export function useThumbSurvey({
|
|
|
89
96
|
[triggerValue]
|
|
90
97
|
)
|
|
91
98
|
|
|
99
|
+
const shownRef = useRef(false)
|
|
100
|
+
const respondedRef = useRef(false)
|
|
101
|
+
|
|
102
|
+
const trackShown = useCallback(() => {
|
|
103
|
+
if (shownRef.current || !posthog) return
|
|
104
|
+
shownRef.current = true
|
|
105
|
+
posthog.capture(SurveyEventName.SHOWN, {
|
|
106
|
+
[SurveyEventProperties.SURVEY_ID]: surveyId,
|
|
107
|
+
sessionRecordingUrl: posthog.get_session_replay_url?.(),
|
|
108
|
+
...properties,
|
|
109
|
+
})
|
|
110
|
+
}, [posthog, surveyId, properties])
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!disableAutoShownTracking) {
|
|
114
|
+
trackShown()
|
|
115
|
+
}
|
|
116
|
+
}, [trackShown, disableAutoShownTracking])
|
|
117
|
+
|
|
92
118
|
const respond = useCallback(
|
|
93
119
|
(value: 'up' | 'down') => {
|
|
94
|
-
if (!posthog?.surveys ||
|
|
120
|
+
if (!posthog?.surveys || respondedRef.current) return
|
|
121
|
+
respondedRef.current = true
|
|
95
122
|
|
|
96
123
|
setResponded(value)
|
|
97
124
|
onResponse?.(value)
|
|
@@ -104,10 +131,16 @@ export function useThumbSurvey({
|
|
|
104
131
|
initialResponses: { 0: value === 'up' ? 1 : 2 },
|
|
105
132
|
position: displayPosition,
|
|
106
133
|
selector: `[${TRIGGER_ATTR}="${triggerValue}"]`,
|
|
134
|
+
skipShownEvent: true,
|
|
107
135
|
})
|
|
108
136
|
},
|
|
109
|
-
[posthog, surveyId, displayPosition, properties,
|
|
137
|
+
[posthog, surveyId, displayPosition, properties, onResponse, triggerValue]
|
|
110
138
|
)
|
|
111
139
|
|
|
112
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
respond,
|
|
142
|
+
response: responded,
|
|
143
|
+
triggerRef,
|
|
144
|
+
...(disableAutoShownTracking && { trackShown }),
|
|
145
|
+
}
|
|
113
146
|
}
|