Skip to content

Commit

Permalink
fix(surveys): fix feedback widget bugs (#953)
Browse files Browse the repository at this point in the history
* fix multiple question widget surveys and misc

* fix thank you element positioning

* fix thank you box and event listeners
  • Loading branch information
liyiy authored Jan 9, 2024
1 parent 2692c59 commit 12480d7
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 28 deletions.
118 changes: 97 additions & 21 deletions src/extensions/surveys-widget.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import { PostHog } from '../posthog-core'
import { Survey } from '../posthog-surveys-types'
import { createMultipleQuestionSurvey, createSingleQuestionSurvey, setTextColors, style } from './surveys/surveys-utils'
import { document as _document } from '../utils/globals'
import {
createMultipleQuestionSurvey,
createSingleQuestionSurvey,
setTextColors,
showQuestion,
style,
} from './surveys/surveys-utils'
import { document as _document, window as _window } from '../utils/globals'
import { addCancelListeners, createThankYouMessage } from './surveys'

// We cast the types here which is dangerous but protected by the top level generateSurveys call
const document = _document as Document
const window = _window as Window & typeof globalThis

export class SurveysWidget {
instance: PostHog
survey: Survey
shadow: any
widget?: any

constructor(instance: PostHog, survey: Survey) {
constructor(instance: PostHog, survey: Survey, widget?: any) {
this.instance = instance
this.survey = survey
this.shadow = this.createWidgetShadow()
this.widget = widget
}

createWidget(): void {
const survey = this.createSurveyForWidget()
const surveyPopup = this.createSurveyForWidget()
let widget
if (this.survey.appearance?.widgetType === 'selector') {
// user supplied button
Expand All @@ -28,27 +38,39 @@ export class SurveysWidget {
} else if (this.survey.appearance?.widgetType === 'button') {
widget = this.createButtonWidget()
}
this.widget = widget
if (this.survey.appearance?.widgetType !== 'selector') {
this.shadow.appendChild(widget)
this.shadow.appendChild(this.widget)
}
setTextColors(this.shadow)
// reposition survey next to widget when opened
if (survey && this.survey.appearance?.widgetType === 'tab' && widget) {
survey.style.bottom = 'auto'
survey.style.borderBottom = `1.5px solid ${this.survey.appearance?.borderColor || '#c9c6c6'}`
survey.style.borderRadius = '10px'
const widgetPos = widget.getBoundingClientRect()
survey.style.top = '50%'
survey.style.left = `${widgetPos.right - 360}px`
if (surveyPopup && this.survey.appearance?.widgetType === 'tab' && this.widget) {
surveyPopup.style.bottom = 'auto'
surveyPopup.style.borderBottom = `1.5px solid ${this.survey.appearance?.borderColor || '#c9c6c6'}`
surveyPopup.style.borderRadius = '10px'
const widgetPos = this.widget.getBoundingClientRect()
surveyPopup.style.top = '50%'
surveyPopup.style.left = `${widgetPos.right - 360}px`
}
if (widget) {
widget.addEventListener('click', () => {
if (survey) {
survey.style.display = survey.style.display === 'none' ? 'block' : 'none'
if (this.widget) {
this.widget.addEventListener('click', () => {
if (surveyPopup) {
surveyPopup.style.display = surveyPopup.style.display === 'none' ? 'block' : 'none'
}
})
widget.setAttribute('PHWidgetSurveyClickListener', 'true')
survey?.addEventListener('PHSurveyClosed', () => (survey.style.display = 'none'))
this.widget.setAttribute('PHWidgetSurveyClickListener', 'true')
if (surveyPopup) {
window.addEventListener('PHSurveySent', () => {
if (surveyPopup) {
surveyPopup.style.display = 'none'
}
const tabs = document
?.getElementsByClassName(`PostHogWidget${this.survey.id}`)[0]
?.shadowRoot?.querySelectorAll('.tab') as NodeListOf<HTMLElement>
tabs.forEach((tab) => (tab.style.display = 'none'))
showQuestion(0, this.survey.id, this.survey.type)
})
}
}
}

Expand Down Expand Up @@ -92,10 +114,64 @@ export class SurveysWidget {
: createSingleQuestionSurvey(this.instance, this.survey, this.survey.questions[0])
if (widgetSurvey) {
widgetSurvey.style.display = 'none'
addCancelListeners(this.instance, widgetSurvey as HTMLFormElement, this.survey.id, this.survey.name)
if (this.survey.appearance?.whiteLabel) {
const allBrandingElements = widgetSurvey.getElementsByClassName('footer-branding')
for (const brandingElement of allBrandingElements) {
;(brandingElement as HTMLAnchorElement).style.display = 'none'
}
}
this.shadow.appendChild(widgetSurvey)
if (this.survey.questions.length > 1) {
const currentQuestion = 0
showQuestion(currentQuestion, this.survey.id, this.survey.type)
}
setTextColors(this.shadow)
window.dispatchEvent(new Event('PHSurveyShown'))
this.instance.capture('survey shown', {
$survey_name: this.survey.name,
$survey_id: this.survey.id,
sessionRecordingUrl: this.instance.get_session_replay_url?.(),
})
if (this.survey.appearance?.displayThankYouMessage) {
window.addEventListener('PHSurveySent', () => {
const thankYouElement = createThankYouMessage(this.survey)
if (thankYouElement && this.survey.appearance?.widgetType === 'tab') {
thankYouElement.style.bottom = 'auto'
thankYouElement.style.borderBottom = `1.5px solid ${
this.survey.appearance?.borderColor || '#c9c6c6'
}`
thankYouElement.style.borderRadius = '10px'
const widgetPos = this.widget.getBoundingClientRect()
thankYouElement.style.top = '50%'
thankYouElement.style.left = `${widgetPos.right - 400}px`
}
this.shadow.appendChild(thankYouElement)
// reposition thank you box next to widget when opened
const cancelButtons = thankYouElement.querySelectorAll('.form-cancel, .form-submit')
for (const button of cancelButtons) {
button.addEventListener('click', () => {
thankYouElement.remove()
})
}
const countdownEl = thankYouElement.querySelector('.thank-you-message-countdown')
if (this.survey.appearance?.autoDisappear && countdownEl) {
let count = 3
countdownEl.textContent = `(${count})`
const countdown = setInterval(() => {
count -= 1
if (count <= 0) {
clearInterval(countdown)
thankYouElement.remove()
return
}
countdownEl.textContent = `(${count})`
}, 1000)
}
setTextColors(this.shadow)
})
}
}
this.shadow.appendChild(widgetSurvey)
// add survey cancel listener
widgetSurvey?.addEventListener('PHSurveyClosed', () => (widgetSurvey.style.display = 'none'))
return widgetSurvey as HTMLFormElement
}

Expand Down
2 changes: 1 addition & 1 deletion src/extensions/surveys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export const callSurveys = (posthog: PostHog, forceReload: boolean = false) => {
}
if (survey.questions.length > 1) {
const currentQuestion = 0
showQuestion(currentQuestion, survey.id)
showQuestion(currentQuestion, survey.id, survey.type)
}
setTextColors(shadow)
window.dispatchEvent(new Event('PHSurveyShown'))
Expand Down
15 changes: 9 additions & 6 deletions src/extensions/surveys/surveys-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Survey,
SurveyAppearance,
SurveyQuestion,
SurveyType,
} from '../../posthog-surveys-types'
import { _isUndefined } from '../../utils/type-utils'
import { window as _window, document as _document } from '../../utils/globals'
Expand Down Expand Up @@ -406,7 +407,7 @@ export const createMultipleQuestionSurvey = (posthog: PostHog, survey: Survey) =
questionElementSubmitButton.innerText = question.buttonText || 'Next'
questionElementSubmitButton.type = 'button'
questionElementSubmitButton.addEventListener('click', () => {
nextQuestion(idx, survey.id)
nextQuestion(idx, survey.id, survey.type)
})
}
questionTab.insertAdjacentElement('beforeend', questionElement)
Expand Down Expand Up @@ -782,22 +783,24 @@ export function setTextColors(parentEl: any) {
}
}

export function showQuestion(n: number, surveyId: string) {
export function showQuestion(n: number, surveyId: string, surveyType: SurveyType) {
// This function will display the specified tab of the form...
const surveyTypeClassName = surveyType === SurveyType.Popover ? 'Survey' : 'Widget'
const tabs = document
.getElementsByClassName(`PostHogSurvey${surveyId}`)[0]
.getElementsByClassName(`PostHog${surveyTypeClassName}${surveyId}`)[0]
?.shadowRoot?.querySelectorAll('.tab') as NodeListOf<HTMLElement>
tabs[n].style.display = 'block'
}

export function nextQuestion(currentQuestionIdx: number, surveyId: string) {
export function nextQuestion(currentQuestionIdx: number, surveyId: string, surveyType: SurveyType) {
// figure out which tab to display
const surveyTypeClassName = surveyType === SurveyType.Popover ? 'Survey' : 'Widget'
const tabs = document
?.getElementsByClassName(`PostHogSurvey${surveyId}`)[0]
?.getElementsByClassName(`PostHog${surveyTypeClassName}${surveyId}`)[0]
?.shadowRoot?.querySelectorAll('.tab') as NodeListOf<HTMLElement>

tabs[currentQuestionIdx].style.display = 'none'
showQuestion(currentQuestionIdx + 1, surveyId)
showQuestion(currentQuestionIdx + 1, surveyId, surveyType)
}

export const satisfiedEmoji =
Expand Down

0 comments on commit 12480d7

Please sign in to comment.