Skip to content

Commit

Permalink
feat(survey): Support identifying iterations of a repeatable survey (#…
Browse files Browse the repository at this point in the history
…1200)

feat(survey): Support identifying iterations of a repeatable survey

1. Change the localStorage key used to store the seenSurvey key to have the iteration as the suffix.
2. Send the current_iteration and current_iteration_start_date properties on the event
3. Moved closeSurveyPopup into the survey-utils.tsx file.
4. Rename closeSurveyPopup to dismissedSurveyEvent to match sentSurveyEvent
  • Loading branch information
Phanatic authored Jun 6, 2024
1 parent 4f64d8e commit 61350f5
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 21 deletions.
95 changes: 95 additions & 0 deletions cypress/e2e/surveys.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,39 @@ describe('Surveys', () => {
})
})

it('captures survey sent event with iteration', () => {
cy.visit('./playground/cypress')
cy.intercept('GET', '**/surveys/*', {
surveys: [
{
id: '123',
name: 'Test survey',
description: 'description',
type: 'popover',
start_date: '2021-01-01T00:00:00Z',
questions: [openTextQuestion],
current_iteration: 2,
current_iteration_start_date: '12-12-2004',
},
],
}).as('surveys')
cy.intercept('POST', '**/e/*').as('capture-assertion')
onPageLoad()
cy.get('.PostHogSurvey123').shadow().find('textarea').type('experiments is awesome!')
cy.get('.PostHogSurvey123').shadow().find('.form-submit').click()
cy.wait('@capture-assertion')
cy.wait('@capture-assertion').then(async ({ request }) => {
const captures = await getPayload(request)
expect(captures.map(({ event }) => event)).to.deep.equal(['survey shown', 'survey sent'])
expect(captures[1].properties).to.contain({
$survey_id: '123',
$survey_response: 'experiments is awesome!',
$survey_iteration: 2,
$survey_iteration_start_date: '12-12-2004',
})
})
})

it('captures survey shown event', () => {
cy.visit('./playground/cypress')
cy.intercept('GET', '**/surveys/*', {
Expand All @@ -761,6 +794,36 @@ describe('Surveys', () => {
})
})

it('captures survey shown event with iteration', () => {
cy.visit('./playground/cypress')
cy.intercept('GET', '**/surveys/*', {
surveys: [
{
id: '123',
name: 'Test survey',
description: 'description',
type: 'popover',
start_date: '2021-01-01T00:00:00Z',
questions: [openTextQuestion],
current_iteration: 2,
current_iteration_start_date: '12-12-2004',
},
],
}).as('surveys')
cy.intercept('POST', '**/e/*').as('capture-assertion')
onPageLoad()
cy.wait('@capture-assertion')
cy.wait('@capture-assertion').then(async ({ request }) => {
const captures = await getPayload(request)
expect(captures[0].event).to.equal('survey shown')
expect(captures[0].properties).to.contain({
$survey_id: '123',
$survey_iteration: 2,
$survey_iteration_start_date: '12-12-2004',
})
})
})

it('captures survey dismissed event', () => {
cy.visit('./playground/cypress')
cy.intercept('GET', '**/surveys/*', {
Expand All @@ -784,5 +847,37 @@ describe('Surveys', () => {
expect(captures.map(({ event }) => event)).to.contain('survey dismissed')
})
})

it('captures survey dismissed event with iteration', () => {
cy.visit('./playground/cypress')
cy.intercept('GET', '**/surveys/*', {
surveys: [
{
id: '123',
name: 'Test survey',
description: 'description',
type: 'popover',
start_date: '2021-01-01T00:00:00Z',
questions: [openTextQuestion],
current_iteration: 2,
current_iteration_start_date: '12-12-2004',
},
],
}).as('surveys')
cy.intercept('POST', '**/e/*').as('capture-assertion')
onPageLoad()
cy.get('.PostHogSurvey123').shadow().find('.cancel-btn-wrapper').click()
cy.wait('@capture-assertion')
cy.wait('@capture-assertion').then(async ({ request }) => {
const captures = await getPayload(request)
const dismissedEvent = captures.filter(({ event }) => event == 'survey dismissed')[0]
expect(dismissedEvent).to.not.be.null
expect(dismissedEvent.properties).to.contain({
$survey_id: '123',
$survey_iteration: 2,
$survey_iteration_start_date: '12-12-2004',
})
})
})
})
})
25 changes: 6 additions & 19 deletions src/extensions/surveys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
style,
defaultSurveyAppearance,
sendSurveyEvent,
dismissedSurveyEvent,
createShadow,
getContrastingTextColor,
SurveyContext,
getDisplayOrderQuestions,
getSurveySeenKey,
} from './surveys/surveys-utils'
import * as Preact from 'preact'
import { createWidgetShadow, createWidgetStyle } from './surveys-widget'
Expand Down Expand Up @@ -96,7 +98,7 @@ export const callSurveys = (posthog: PostHog, forceReload: boolean = false) => {
}
}

if (!localStorage.getItem(`seenSurvey_${survey.id}`)) {
if (!localStorage.getItem(getSurveySeenKey(survey))) {
const shadow = createShadow(style(survey?.appearance), survey.id)
Preact.render(<SurveyPopup key={'popover-survey'} posthog={posthog} survey={survey} />, shadow)
}
Expand Down Expand Up @@ -221,6 +223,8 @@ export function SurveyPopup({
posthog.capture('survey shown', {
$survey_name: survey.name,
$survey_id: survey.id,
$survey_iteration: survey.current_iteration,
$survey_iteration_start_date: survey.current_iteration_start_date,
sessionRecordingUrl: posthog.get_session_replay_url?.(),
})
localStorage.setItem(`lastSeenSurveyDate`, new Date().toISOString())
Expand Down Expand Up @@ -248,7 +252,7 @@ export function SurveyPopup({
value={{
isPreviewMode,
previewPageIndex: previewPageIndex,
handleCloseSurveyPopup: () => closeSurveyPopup(survey, posthog, isPreviewMode),
handleCloseSurveyPopup: () => dismissedSurveyEvent(survey, posthog, isPreviewMode),
}}
>
{!shouldShowConfirmation ? (
Expand Down Expand Up @@ -403,23 +407,6 @@ export function Questions({
)
}

const closeSurveyPopup = (survey: Survey, posthog?: PostHog, isPreviewMode?: boolean) => {
if (isPreviewMode || !posthog) {
return
}

posthog.capture('survey dismissed', {
$survey_name: survey.name,
$survey_id: survey.id,
sessionRecordingUrl: posthog.get_session_replay_url?.(),
$set: {
[`$survey_dismissed/${survey.id}`]: true,
},
})
localStorage.setItem(`seenSurvey_${survey.id}`, 'true')
window.dispatchEvent(new Event('PHSurveyClosed'))
}

export function FeedbackWidget({
survey,
forceDisableHtml,
Expand Down
43 changes: 41 additions & 2 deletions src/extensions/surveys/surveys-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -537,21 +537,42 @@ export const sendSurveyEvent = (
posthog?: PostHog
) => {
if (!posthog) return
localStorage.setItem(getSurveySeenKey(survey), 'true')

localStorage.setItem(`seenSurvey_${survey.id}`, 'true')
posthog.capture('survey sent', {
$survey_name: survey.name,
$survey_id: survey.id,
$survey_iteration: survey.current_iteration,
$survey_iteration_start_date: survey.current_iteration_start_date,
$survey_questions: survey.questions.map((question) => question.question),
sessionRecordingUrl: posthog.get_session_replay_url?.(),
...responses,
$set: {
[`$survey_responded/${survey.id}`]: true,
[getSurveyInteractionProperty(survey, 'responded')]: true,
},
})
window.dispatchEvent(new Event('PHSurveySent'))
}

export const dismissedSurveyEvent = (survey: Survey, posthog?: PostHog, readOnly?: boolean) => {
// TODO: state management and unit tests for this would be nice
if (readOnly || !posthog) {
return
}
posthog.capture('survey dismissed', {
$survey_name: survey.name,
$survey_id: survey.id,
$survey_iteration: survey.current_iteration,
$survey_iteration_start_date: survey.current_iteration_start_date,
sessionRecordingUrl: posthog.get_session_replay_url?.(),
$set: {
[getSurveyInteractionProperty(survey, 'dismissed')]: true,
},
})
localStorage.setItem(getSurveySeenKey(survey), 'true')
window.dispatchEvent(new Event('PHSurveyClosed'))
}

// Use the Fisher-yates algorithm to shuffle this array
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
export const shuffle = (array: any[]) => {
Expand Down Expand Up @@ -604,6 +625,24 @@ export const getDisplayOrderQuestions = (survey: Survey): SurveyQuestion[] => {
return reverseIfUnshuffled(survey.questions, shuffle(survey.questions))
}

export const getSurveySeenKey = (survey: Survey): string => {
let surveySeenKey = `seenSurvey_${survey.id}`
if (survey.current_iteration && survey.current_iteration > 0) {
surveySeenKey = `seenSurvey_${survey.id}_${survey.current_iteration}`
}

return surveySeenKey
}

const getSurveyInteractionProperty = (survey: Survey, action: string): string => {
let surveyProperty = `$survey_${action}/${survey.id}`
if (survey.current_iteration && survey.current_iteration > 0) {
surveyProperty = `$survey_${action}/${survey.id}/${survey.current_iteration}`
}

return surveyProperty
}

export const SurveyContext = createContext<{
isPreviewMode: boolean
previewPageIndex: number | undefined
Expand Down
2 changes: 2 additions & 0 deletions src/posthog-surveys-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,6 @@ export interface Survey {
} | null
start_date: string | null
end_date: string | null
current_iteration: number | null
current_iteration_start_date: string | null
}

0 comments on commit 61350f5

Please sign in to comment.