diff --git a/src/__tests__/surveys.test.ts b/src/__tests__/surveys.test.ts
index 88b75af9a..11a156080 100644
--- a/src/__tests__/surveys.test.ts
+++ b/src/__tests__/surveys.test.ts
@@ -1,7 +1,7 @@
///
import { SURVEYS_REQUEST_TIMEOUT_MS } from '../constants'
-import { generateSurveys } from '../extensions/surveys'
+import { generateSurveys, getNextSurveyStep } from '../extensions/surveys'
import {
canActivateRepeatedly,
getDisplayOrderChoices,
@@ -927,8 +927,8 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Question A' },
{ type: SurveyQuestionType.Open, question: 'Question B' },
] as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 'Some response')).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 1, 'Some response')).toEqual(SurveyQuestionBranchingType.End)
+ expect(getNextSurveyStep(survey, 0, 'Some response')).toEqual(1)
+ expect(getNextSurveyStep(survey, 1, 'Some response')).toEqual(SurveyQuestionBranchingType.End)
})
it('should branch out to `end`', () => {
@@ -940,7 +940,7 @@ describe('surveys', () => {
},
{ type: SurveyQuestionType.Open, question: 'Question B' },
] as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 'Some response')).toEqual(SurveyQuestionBranchingType.End)
+ expect(getNextSurveyStep(survey, 0, 'Some response')).toEqual(SurveyQuestionBranchingType.End)
})
it('should branch out to a specific question', () => {
@@ -953,7 +953,7 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Question B' },
{ type: SurveyQuestionType.Open, question: 'Question C' },
] as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 'Some response')).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 'Some response')).toEqual(2)
})
// Single-choice
@@ -973,9 +973,9 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Why no?' },
{ type: SurveyQuestionType.Open, question: 'Why maybe?' },
] as unknown[] as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 'Yes')).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 'No')).toEqual(2)
- expect(surveys.getNextSurveyStep(survey, 0, 'Maybe')).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 'Yes')).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 'No')).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 'Maybe')).toEqual(3)
})
// Response-based branching, scale 1-3
@@ -995,9 +995,9 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown[] as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 1)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 2)).toEqual(2)
- expect(surveys.getNextSurveyStep(survey, 0, 3)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 1)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 2)).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 3)).toEqual(3)
})
// Response-based branching, scale 1-5
@@ -1017,9 +1017,9 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 1)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 3)).toEqual(2)
- expect(surveys.getNextSurveyStep(survey, 0, 5)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 1)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 3)).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 5)).toEqual(3)
})
// Response-based branching, scale 1-7
@@ -1039,13 +1039,13 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Great! What did you enjoy the most?' },
] as unknown as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 1)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 2)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 3)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 4)).toEqual(2)
- expect(surveys.getNextSurveyStep(survey, 0, 5)).toEqual(3)
- expect(surveys.getNextSurveyStep(survey, 0, 6)).toEqual(3)
- expect(surveys.getNextSurveyStep(survey, 0, 7)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 1)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 2)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 3)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 4)).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 5)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 6)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 7)).toEqual(3)
})
// Response-based branching, scale 0-10 (NPS)
@@ -1065,9 +1065,9 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown as SurveyQuestion[]
- expect(surveys.getNextSurveyStep(survey, 0, 1)).toEqual(1)
- expect(surveys.getNextSurveyStep(survey, 0, 8)).toEqual(2)
- expect(surveys.getNextSurveyStep(survey, 0, 10)).toEqual(3)
+ expect(getNextSurveyStep(survey, 0, 1)).toEqual(1)
+ expect(getNextSurveyStep(survey, 0, 8)).toEqual(2)
+ expect(getNextSurveyStep(survey, 0, 10)).toEqual(3)
})
it('should display questions in the order AGCEHDFB', () => {
@@ -1121,7 +1121,7 @@ describe('surveys', () => {
for (let i = 0; i < survey.questions.length; i++) {
const currentQuestion = survey.questions[currentStep]
actualOrder.push(currentQuestion.question)
- currentStep = surveys.getNextSurveyStep(survey, currentStep, 'Some response')
+ currentStep = getNextSurveyStep(survey, currentStep, 'Some response')
}
expect(desiredOrder).toEqual(actualOrder)
@@ -1180,7 +1180,7 @@ describe('surveys', () => {
for (const answer of answers) {
const currentQuestion = survey.questions[currentStep]
actualOrder.push(currentQuestion.question)
- currentStep = surveys.getNextSurveyStep(survey, currentStep, answer)
+ currentStep = getNextSurveyStep(survey, currentStep, answer)
}
expect(desiredOrder).toEqual(actualOrder)
expect(currentStep).toEqual(SurveyQuestionBranchingType.End)
@@ -1193,7 +1193,7 @@ describe('surveys', () => {
for (const answer of answers) {
const currentQuestion = survey.questions[currentStep]
actualOrder.push(currentQuestion.question)
- currentStep = surveys.getNextSurveyStep(survey, currentStep, answer)
+ currentStep = getNextSurveyStep(survey, currentStep, answer)
}
expect(desiredOrder).toEqual(actualOrder)
expect(currentStep).toEqual(SurveyQuestionBranchingType.End)
@@ -1206,7 +1206,7 @@ describe('surveys', () => {
for (const answer of answers) {
const currentQuestion = survey.questions[currentStep]
actualOrder.push(currentQuestion.question)
- currentStep = surveys.getNextSurveyStep(survey, currentStep, answer)
+ currentStep = getNextSurveyStep(survey, currentStep, answer)
}
expect(desiredOrder).toEqual(actualOrder)
expect(currentStep).toEqual(SurveyQuestionBranchingType.End)
@@ -1223,7 +1223,7 @@ describe('surveys', () => {
for (const answer of answers) {
const currentQuestion = survey.questions[currentStep]
actualOrder.push(currentQuestion.question)
- currentStep = surveys.getNextSurveyStep(survey, currentStep, answer)
+ currentStep = getNextSurveyStep(survey, currentStep, answer)
}
expect(desiredOrder).toEqual(actualOrder)
expect(currentStep).toEqual(SurveyQuestionBranchingType.End)
@@ -1244,7 +1244,7 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Seems you are not completely happy. Tell us more!' },
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown as SurveyQuestion[]
- expect(() => surveys.getNextSurveyStep(survey, 0, 1)).toThrow('The scale must be one of: 3, 5, 7, 10')
+ expect(() => getNextSurveyStep(survey, 0, 1)).toThrow('The scale must be one of: 3, 5, 7, 10')
})
it('should throw an error for a response value out of the valid range', () => {
@@ -1262,13 +1262,13 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Seems you are not completely happy. Tell us more!' },
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown as SurveyQuestion[]
- expect(() => surveys.getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-3')
+ expect(() => getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-3')
;(survey.questions[0] as RatingSurveyQuestion).scale = 5
- expect(() => surveys.getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-5')
+ expect(() => getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-5')
;(survey.questions[0] as RatingSurveyQuestion).scale = 7
- expect(() => surveys.getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-7')
+ expect(() => getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 1-7')
;(survey.questions[0] as RatingSurveyQuestion).scale = 10
- expect(() => surveys.getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 0-10')
+ expect(() => getNextSurveyStep(survey, 0, 20)).toThrow('The response must be in range 0-10')
})
it('should throw an error for if a response value in a rating question is not an integer', () => {
@@ -1286,10 +1286,8 @@ describe('surveys', () => {
{ type: SurveyQuestionType.Open, question: 'Seems you are not completely happy. Tell us more!' },
{ type: SurveyQuestionType.Open, question: 'Glad to hear that. Tell us more!' },
] as unknown as SurveyQuestion[]
- expect(() => surveys.getNextSurveyStep(survey, 0, '2')).toThrow('The response type must be an integer')
- expect(() => surveys.getNextSurveyStep(survey, 0, 'some_string')).toThrow(
- 'The response type must be an integer'
- )
+ expect(() => getNextSurveyStep(survey, 0, '2')).toThrow('The response type must be an integer')
+ expect(() => getNextSurveyStep(survey, 0, 'some_string')).toThrow('The response type must be an integer')
})
})
diff --git a/src/entrypoints/surveys-preview.es.ts b/src/entrypoints/surveys-preview.es.ts
index f716dcbfc..627e129b4 100644
--- a/src/entrypoints/surveys-preview.es.ts
+++ b/src/entrypoints/surveys-preview.es.ts
@@ -1,2 +1 @@
-export { renderFeedbackWidgetPreview, renderSurveysPreview } from '../extensions/surveys'
-export { getNextSurveyStep } from '../posthog-surveys'
+export { getNextSurveyStep, renderFeedbackWidgetPreview, renderSurveysPreview } from '../extensions/surveys'
diff --git a/src/extensions/surveys.tsx b/src/extensions/surveys.tsx
index ec97061f2..5dc1d1c3c 100644
--- a/src/extensions/surveys.tsx
+++ b/src/extensions/surveys.tsx
@@ -44,6 +44,109 @@ const logger = createLogger('[Surveys]')
const window = _window as Window & typeof globalThis
const document = _document as Document
+function getRatingBucketForResponseValue(responseValue: number, scale: number) {
+ if (scale === 3) {
+ if (responseValue < 1 || responseValue > 3) {
+ throw new Error('The response must be in range 1-3')
+ }
+
+ return responseValue === 1 ? 'negative' : responseValue === 2 ? 'neutral' : 'positive'
+ } else if (scale === 5) {
+ if (responseValue < 1 || responseValue > 5) {
+ throw new Error('The response must be in range 1-5')
+ }
+
+ return responseValue <= 2 ? 'negative' : responseValue === 3 ? 'neutral' : 'positive'
+ } else if (scale === 7) {
+ if (responseValue < 1 || responseValue > 7) {
+ throw new Error('The response must be in range 1-7')
+ }
+
+ return responseValue <= 3 ? 'negative' : responseValue === 4 ? 'neutral' : 'positive'
+ } else if (scale === 10) {
+ if (responseValue < 0 || responseValue > 10) {
+ throw new Error('The response must be in range 0-10')
+ }
+
+ return responseValue <= 6 ? 'detractors' : responseValue <= 8 ? 'passives' : 'promoters'
+ }
+
+ throw new Error('The scale must be one of: 3, 5, 7, 10')
+}
+
+export function getNextSurveyStep(
+ survey: Survey,
+ currentQuestionIndex: number,
+ response: string | string[] | number | null
+) {
+ const question = survey.questions[currentQuestionIndex]
+ const nextQuestionIndex = currentQuestionIndex + 1
+
+ if (!question.branching?.type) {
+ if (currentQuestionIndex === survey.questions.length - 1) {
+ return SurveyQuestionBranchingType.End
+ }
+
+ return nextQuestionIndex
+ }
+
+ if (question.branching.type === SurveyQuestionBranchingType.End) {
+ return SurveyQuestionBranchingType.End
+ } else if (question.branching.type === SurveyQuestionBranchingType.SpecificQuestion) {
+ if (Number.isInteger(question.branching.index)) {
+ return question.branching.index
+ }
+ } else if (question.branching.type === SurveyQuestionBranchingType.ResponseBased) {
+ // Single choice
+ if (question.type === SurveyQuestionType.SingleChoice) {
+ // :KLUDGE: for now, look up the choiceIndex based on the response
+ // TODO: once QuestionTypes.MultipleChoiceQuestion is refactored, pass the selected choiceIndex into this method
+ const selectedChoiceIndex = question.choices.indexOf(`${response}`)
+
+ if (question.branching?.responseValues?.hasOwnProperty(selectedChoiceIndex)) {
+ const nextStep = question.branching.responseValues[selectedChoiceIndex]
+
+ // Specific question
+ if (Number.isInteger(nextStep)) {
+ return nextStep
+ }
+
+ if (nextStep === SurveyQuestionBranchingType.End) {
+ return SurveyQuestionBranchingType.End
+ }
+
+ return nextQuestionIndex
+ }
+ } else if (question.type === SurveyQuestionType.Rating) {
+ if (typeof response !== 'number' || !Number.isInteger(response)) {
+ throw new Error('The response type must be an integer')
+ }
+
+ const ratingBucket = getRatingBucketForResponseValue(response, question.scale)
+
+ if (question.branching?.responseValues?.hasOwnProperty(ratingBucket)) {
+ const nextStep = question.branching.responseValues[ratingBucket]
+
+ // Specific question
+ if (Number.isInteger(nextStep)) {
+ return nextStep
+ }
+
+ if (nextStep === SurveyQuestionBranchingType.End) {
+ return SurveyQuestionBranchingType.End
+ }
+
+ return nextQuestionIndex
+ }
+ }
+
+ return nextQuestionIndex
+ }
+
+ logger.warn('Falling back to next question index due to unexpected branching type')
+ return nextQuestionIndex
+}
+
export class SurveyManager {
private posthog: PostHog
private surveyInFocus: string | null
@@ -663,18 +766,7 @@ export function Questions({
setQuestionsResponses({ ...questionsResponses, [responseKey]: res })
- // Old SDK, no branching
- if (!posthog.getNextSurveyStep) {
- const isLastDisplayedQuestion = displayQuestionIndex === survey.questions.length - 1
- if (isLastDisplayedQuestion) {
- sendSurveyEvent({ ...questionsResponses, [responseKey]: res }, survey, posthog)
- } else {
- setCurrentQuestionIndex(displayQuestionIndex + 1)
- }
- return
- }
-
- const nextStep = posthog.getNextSurveyStep(survey, displayQuestionIndex, res)
+ const nextStep = getNextSurveyStep(survey, displayQuestionIndex, res)
if (nextStep === SurveyQuestionBranchingType.End) {
sendSurveyEvent({ ...questionsResponses, [responseKey]: res }, survey, posthog)
} else {
diff --git a/src/posthog-core.ts b/src/posthog-core.ts
index 707f0ce3d..621bc3599 100644
--- a/src/posthog-core.ts
+++ b/src/posthog-core.ts
@@ -25,7 +25,7 @@ import { PostHogExceptions } from './posthog-exceptions'
import { PostHogFeatureFlags } from './posthog-featureflags'
import { PostHogPersistence } from './posthog-persistence'
import { PostHogSurveys } from './posthog-surveys'
-import { Survey, SurveyCallback, SurveyQuestionBranchingType } from './posthog-surveys-types'
+import { SurveyCallback } from './posthog-surveys-types'
import { RateLimiter } from './rate-limiter'
import { RemoteConfigLoader } from './remote-config'
import { extendURLParams, request, SUPPORTS_REQUEST } from './request'
@@ -1343,15 +1343,6 @@ export class PostHog {
this.surveys.canRenderSurvey(surveyId)
}
- /** Get the next step of the survey: a question index or `end` */
- getNextSurveyStep(
- survey: Survey,
- currentQuestionIndex: number,
- response: string | string[] | number | null
- ): number | SurveyQuestionBranchingType.End {
- return this.surveys.getNextSurveyStep(survey, currentQuestionIndex, response)
- }
-
/**
* Identify a user with a unique ID instead of a PostHog
* randomly generated distinct_id. If the method is never called,
diff --git a/src/posthog-surveys.ts b/src/posthog-surveys.ts
index 0c279fcf7..eb7d0896a 100644
--- a/src/posthog-surveys.ts
+++ b/src/posthog-surveys.ts
@@ -1,13 +1,7 @@
import { SURVEYS } from './constants'
import { getSurveySeenStorageKeys } from './extensions/surveys/surveys-utils'
import { PostHog } from './posthog-core'
-import {
- Survey,
- SurveyCallback,
- SurveyMatchType,
- SurveyQuestionBranchingType,
- SurveyQuestionType,
-} from './posthog-surveys-types'
+import { Survey, SurveyCallback, SurveyMatchType } from './posthog-surveys-types'
import { RemoteConfig } from './types'
import { Info } from './utils/event-utils'
import { assignableWindow, document, userAgent, window } from './utils/globals'
@@ -32,109 +26,6 @@ export const surveyValidationMap: Record targets.every((target) => value !== target),
}
-function getRatingBucketForResponseValue(responseValue: number, scale: number) {
- if (scale === 3) {
- if (responseValue < 1 || responseValue > 3) {
- throw new Error('The response must be in range 1-3')
- }
-
- return responseValue === 1 ? 'negative' : responseValue === 2 ? 'neutral' : 'positive'
- } else if (scale === 5) {
- if (responseValue < 1 || responseValue > 5) {
- throw new Error('The response must be in range 1-5')
- }
-
- return responseValue <= 2 ? 'negative' : responseValue === 3 ? 'neutral' : 'positive'
- } else if (scale === 7) {
- if (responseValue < 1 || responseValue > 7) {
- throw new Error('The response must be in range 1-7')
- }
-
- return responseValue <= 3 ? 'negative' : responseValue === 4 ? 'neutral' : 'positive'
- } else if (scale === 10) {
- if (responseValue < 0 || responseValue > 10) {
- throw new Error('The response must be in range 0-10')
- }
-
- return responseValue <= 6 ? 'detractors' : responseValue <= 8 ? 'passives' : 'promoters'
- }
-
- throw new Error('The scale must be one of: 3, 5, 7, 10')
-}
-
-export function getNextSurveyStep(
- survey: Survey,
- currentQuestionIndex: number,
- response: string | string[] | number | null
-) {
- const question = survey.questions[currentQuestionIndex]
- const nextQuestionIndex = currentQuestionIndex + 1
-
- if (!question.branching?.type) {
- if (currentQuestionIndex === survey.questions.length - 1) {
- return SurveyQuestionBranchingType.End
- }
-
- return nextQuestionIndex
- }
-
- if (question.branching.type === SurveyQuestionBranchingType.End) {
- return SurveyQuestionBranchingType.End
- } else if (question.branching.type === SurveyQuestionBranchingType.SpecificQuestion) {
- if (Number.isInteger(question.branching.index)) {
- return question.branching.index
- }
- } else if (question.branching.type === SurveyQuestionBranchingType.ResponseBased) {
- // Single choice
- if (question.type === SurveyQuestionType.SingleChoice) {
- // :KLUDGE: for now, look up the choiceIndex based on the response
- // TODO: once QuestionTypes.MultipleChoiceQuestion is refactored, pass the selected choiceIndex into this method
- const selectedChoiceIndex = question.choices.indexOf(`${response}`)
-
- if (question.branching?.responseValues?.hasOwnProperty(selectedChoiceIndex)) {
- const nextStep = question.branching.responseValues[selectedChoiceIndex]
-
- // Specific question
- if (Number.isInteger(nextStep)) {
- return nextStep
- }
-
- if (nextStep === SurveyQuestionBranchingType.End) {
- return SurveyQuestionBranchingType.End
- }
-
- return nextQuestionIndex
- }
- } else if (question.type === SurveyQuestionType.Rating) {
- if (typeof response !== 'number' || !Number.isInteger(response)) {
- throw new Error('The response type must be an integer')
- }
-
- const ratingBucket = getRatingBucketForResponseValue(response, question.scale)
-
- if (question.branching?.responseValues?.hasOwnProperty(ratingBucket)) {
- const nextStep = question.branching.responseValues[ratingBucket]
-
- // Specific question
- if (Number.isInteger(nextStep)) {
- return nextStep
- }
-
- if (nextStep === SurveyQuestionBranchingType.End) {
- return SurveyQuestionBranchingType.End
- }
-
- return nextQuestionIndex
- }
- }
-
- return nextQuestionIndex
- }
-
- logger.warn('Falling back to next question index due to unexpected branching type')
- return nextQuestionIndex
-}
-
function defaultMatchType(matchType?: SurveyMatchType): SurveyMatchType {
return matchType ?? 'icontains'
}
@@ -402,7 +293,6 @@ export class PostHogSurveys {
return this.instance.featureFlags.isFeatureEnabled(value)
})
}
- getNextSurveyStep = getNextSurveyStep
// this method is lazily loaded onto the window to avoid loading preact and other dependencies if surveys is not enabled
private _canActivateRepeatedly(survey: Survey) {