Skip to content

Commit

Permalink
feat: Add a onPreviewSubmit callback to preview surveys (#1641)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasheriques authored Jan 7, 2025
1 parent f93a6e6 commit 30f0d49
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 63 deletions.
100 changes: 96 additions & 4 deletions src/__tests__/extensions/surveys.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { act, fireEvent, render, renderHook } from '@testing-library/preact'
import {
generateSurveys,
renderSurveysPreview,
renderFeedbackWidgetPreview,
usePopupVisibility,
renderSurveysPreview,
SurveyManager,
usePopupVisibility,
} from '../../extensions/surveys'
import { createShadow } from '../../extensions/surveys/surveys-utils'
import { Survey, SurveyQuestionType, SurveyType } from '../../posthog-surveys-types'
import { renderHook, act } from '@testing-library/preact'

import { beforeEach } from '@jest/globals'
import '@testing-library/jest-dom'
import { h } from 'preact'
import { useEffect, useRef, useState } from 'preact/hooks'
import { PostHog } from '../../posthog-core'
import { beforeEach } from '@jest/globals'
import { DecideResponse } from '../../types'

declare const global: any
Expand Down Expand Up @@ -59,6 +61,7 @@ describe('survey display logic', () => {
end_date: null,
current_iteration: null,
current_iteration_start_date: null,
feature_flag_keys: [],
},
]

Expand Down Expand Up @@ -301,6 +304,7 @@ describe('SurveyManager', () => {
end_date: null,
current_iteration: null,
current_iteration_start_date: null,
feature_flag_keys: [],
},
]
})
Expand Down Expand Up @@ -408,6 +412,7 @@ describe('SurveyManager', () => {
end_date: null,
current_iteration: null,
current_iteration_start_date: null,
feature_flag_keys: [],
}
document.body.innerHTML = '<div class="widget-selector"></div>'
const handleWidgetSelectorMock = jest
Expand Down Expand Up @@ -794,4 +799,91 @@ describe('preview renders', () => {
expect(root.getElementsByClassName('ph-survey-widget-tab').length).toBe(1)
expect(root.getElementsByClassName('ph-survey-widget-tab')[0].innerHTML).toContain('preview test')
})

test('renderSurveysPreview navigates between questions when submitting answers in preview', async () => {
function TestSurveyPreview() {
const surveyPreviewRef = useRef<HTMLDivElement>(null)
const [currentPageIndex, setCurrentPageIndex] = useState(0)

const survey = {
id: 'test-survey',
name: 'Test Survey',
description: 'Test Survey Description',
type: SurveyType.Popover,
questions: [
{
type: SurveyQuestionType.Open,
question: 'Question 1',
description: 'Description 1',
descriptionContentType: 'text',
originalQuestionIndex: 0,
},
{
type: SurveyQuestionType.Open,
question: 'Question 2',
description: 'Description 2',
descriptionContentType: 'text',
originalQuestionIndex: 1,
},
],
appearance: {
backgroundColor: '#ffffff',
submitButtonText: 'Next',
},
start_date: '2024-01-01T00:00:00.000Z',
end_date: null,
targeting_flag_key: null,
linked_flag_key: null,
conditions: {},
feature_flag_keys: null, // Added this to fix type error
} as Survey

useEffect(() => {
console.log('Render effect triggered with page index:', currentPageIndex)
if (surveyPreviewRef.current) {
renderSurveysPreview({
survey,
parentElement: surveyPreviewRef.current,
previewPageIndex: currentPageIndex,
onPreviewSubmit: () => {
setCurrentPageIndex((prev) => {
console.log('Setting page index from', prev, 'to', prev + 1)
return prev + 1
})
},
})
}
}, [currentPageIndex])

return h('div', { ref: surveyPreviewRef })
}

// Render the test component
const { container } = render(h(TestSurveyPreview, {}))

// Check if we're on the first question
expect(container.textContent).toContain('Question 1')
expect(container.textContent).not.toContain('Question 2')

// Find and fill the textarea
const textarea = container.querySelector('textarea')
console.log('Found textarea:', !!textarea)

await act(async () => {
fireEvent.change(textarea!, { target: { value: 'Test answer' } })
})

// Find and click the submit button (using button type="button" instead of form-submit class)
const submitButton = container.querySelector('button[type="button"]')
console.log('Found submit button:', !!submitButton)
console.log('Submit button text:', submitButton?.textContent)

await act(async () => {
fireEvent.click(submitButton!)
})

// Check if we're on the second question
expect(container.textContent).toContain('Question 2')
expect(container.textContent).not.toContain('Question 1')
})
})
75 changes: 46 additions & 29 deletions src/extensions/surveys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ import {
SurveyType,
} from '../posthog-surveys-types'

import { window as _window, document as _document } from '../utils/globals'
import {
style,
defaultSurveyAppearance,
sendSurveyEvent,
dismissedSurveyEvent,
createShadow,
getContrastingTextColor,
SurveyContext,
getDisplayOrderQuestions,
getSurveySeen,
} from './surveys/surveys-utils'
import * as Preact from 'preact'
import { createWidgetShadow, createWidgetStyle } from './surveys-widget'
import { useState, useEffect, useRef, useContext, useMemo } from 'preact/hooks'
import { useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks'
import { document as _document, window as _window } from '../utils/globals'
import { createLogger } from '../utils/logger'
import { isNull, isNumber } from '../utils/type-utils'
import { createWidgetShadow, createWidgetStyle } from './surveys-widget'
import { ConfirmationMessage } from './surveys/components/ConfirmationMessage'
import { Cancel } from './surveys/components/QuestionHeader'
import {
OpenTextQuestion,
LinkQuestion,
RatingQuestion,
MultipleChoiceQuestion,
OpenTextQuestion,
RatingQuestion,
} from './surveys/components/QuestionTypes'
import { Cancel } from './surveys/components/QuestionHeader'
import { createLogger } from '../utils/logger'
import {
createShadow,
defaultSurveyAppearance,
dismissedSurveyEvent,
getContrastingTextColor,
getDisplayOrderQuestions,
getSurveySeen,
sendSurveyEvent,
style,
SurveyContext,
} from './surveys/surveys-utils'
const logger = createLogger('[Surveys]')

// We cast the types here which is dangerous but protected by the top level generateSurveys call
Expand Down Expand Up @@ -277,11 +277,13 @@ export const renderSurveysPreview = ({
parentElement,
previewPageIndex,
forceDisableHtml,
onPreviewSubmit,
}: {
survey: Survey
parentElement: HTMLElement
previewPageIndex: number
forceDisableHtml?: boolean
onPreviewSubmit?: (res: string | string[] | number | null) => void
}) => {
const surveyStyleSheet = style(survey.appearance)
const styleElement = Object.assign(document.createElement('style'), { innerText: surveyStyleSheet })
Expand Down Expand Up @@ -310,6 +312,7 @@ export const renderSurveysPreview = ({
borderRadius: 10,
color: textColor,
}}
onPreviewSubmit={onPreviewSubmit}
previewPageIndex={previewPageIndex}
removeSurveyFromFocus={() => {}}
isPopup={true}
Expand Down Expand Up @@ -440,6 +443,17 @@ export function usePopupVisibility(
return { isPopupVisible, isSurveySent, setIsPopupVisible }
}

interface SurveyPopupProps {
survey: Survey
forceDisableHtml?: boolean
posthog?: PostHog
style?: React.CSSProperties
previewPageIndex?: number | undefined
removeSurveyFromFocus: (id: string) => void
isPopup?: boolean
onPreviewSubmit?: (res: string | string[] | number | null) => void
}

export function SurveyPopup({
survey,
forceDisableHtml,
Expand All @@ -448,15 +462,8 @@ export function SurveyPopup({
previewPageIndex,
removeSurveyFromFocus,
isPopup,
}: {
survey: Survey
forceDisableHtml?: boolean
posthog?: PostHog
style?: React.CSSProperties
previewPageIndex?: number | undefined
removeSurveyFromFocus: (id: string) => void
isPopup?: boolean
}) {
onPreviewSubmit = () => {},
}: SurveyPopupProps) {
const isPreviewMode = Number.isInteger(previewPageIndex)
// NB: The client-side code passes the millisecondDelay in seconds, but setTimeout expects milliseconds, so we multiply by 1000
const surveyPopupDelayMilliseconds = survey.appearance?.surveyPopupDelaySeconds
Expand Down Expand Up @@ -486,6 +493,7 @@ export function SurveyPopup({
previewPageIndex: previewPageIndex,
handleCloseSurveyPopup: () => dismissedSurveyEvent(survey, posthog, isPreviewMode),
isPopup: isPopup || false,
onPreviewSubmit,
}}
>
{!shouldShowConfirmation ? (
Expand Down Expand Up @@ -527,7 +535,8 @@ export function Questions({
survey.appearance?.backgroundColor || defaultSurveyAppearance.backgroundColor
)
const [questionsResponses, setQuestionsResponses] = useState({})
const { isPreviewMode, previewPageIndex, handleCloseSurveyPopup, isPopup } = useContext(SurveyContext)
const { isPreviewMode, previewPageIndex, handleCloseSurveyPopup, isPopup, onPreviewSubmit } =
useContext(SurveyContext)
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(previewPageIndex || 0)
const surveyQuestions = useMemo(() => getDisplayOrderQuestions(survey), [survey])

Expand Down Expand Up @@ -618,6 +627,7 @@ export function Questions({
originalQuestionIndex,
displayQuestionIndex,
}),
onPreviewSubmit,
})}
</div>
)
Expand Down Expand Up @@ -705,6 +715,7 @@ interface GetQuestionComponentProps {
displayQuestionIndex: number
appearance: SurveyAppearance
onSubmit: (res: string | string[] | number | null) => void
onPreviewSubmit: (res: string | string[] | number | null) => void
}

const getQuestionComponent = ({
Expand All @@ -713,6 +724,7 @@ const getQuestionComponent = ({
displayQuestionIndex,
appearance,
onSubmit,
onPreviewSubmit,
}: GetQuestionComponentProps): JSX.Element => {
const questionComponents = {
[SurveyQuestionType.Open]: OpenTextQuestion,
Expand All @@ -726,7 +738,12 @@ const getQuestionComponent = ({
question,
forceDisableHtml,
appearance,
onSubmit,
onPreviewSubmit: (res: string | string[] | number | null) => {
onPreviewSubmit(res)
},
onSubmit: (res: string | string[] | number | null) => {
onSubmit(res)
},
}

const additionalProps: Record<SurveyQuestionType, any> = {
Expand Down
11 changes: 8 additions & 3 deletions src/extensions/surveys/components/BottomSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ import { window } from '../../../utils/globals'

import { SurveyAppearance } from '../../../posthog-surveys-types'

import { PostHogLogo } from './PostHogLogo'
import { useContext } from 'preact/hooks'
import { SurveyContext, defaultSurveyAppearance, getContrastingTextColor } from '../surveys-utils'
import { PostHogLogo } from './PostHogLogo'

export function BottomSection({
text,
submitDisabled,
appearance,
onSubmit,
link,
onPreviewSubmit,
}: {
text: string
submitDisabled: boolean
appearance: SurveyAppearance
onSubmit: () => void
link?: string | null
onPreviewSubmit?: () => void
}) {
const { isPreviewMode, isPopup } = useContext(SurveyContext)
const textColor =
Expand All @@ -28,11 +30,14 @@ export function BottomSection({
<div className="buttons">
<button
className="form-submit"
disabled={submitDisabled && !isPreviewMode}
disabled={submitDisabled}
type="button"
style={isPopup ? { color: textColor } : {}}
onClick={() => {
if (isPreviewMode) return
if (isPreviewMode) {
onPreviewSubmit?.()
return
}
if (link) {
window?.open(link)
}
Expand Down
Loading

0 comments on commit 30f0d49

Please sign in to comment.