Skip to content

feat: allow rendering survey popups on demand #2032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

lucasheriques
Copy link
Contributor

@lucasheriques lucasheriques commented Jun 20, 2025

Changes

make it easy to render popup surveys on demand! this way, there's a simpler way for our customers to render surveys whenever they want.

Closes #1981

Checklist

  • Tests for new code (see advice on the tests we use)
  • Accounted for the impact of any changes across different browsers
  • Accounted for backwards compatibility of any changes (no breaking changes in posthog-js!)
  • Took care not to unnecessarily increase the bundle size

@lucasheriques lucasheriques self-assigned this Jun 20, 2025
@lucasheriques lucasheriques requested a review from a team as a code owner June 20, 2025 20:35
@lucasheriques lucasheriques added bump minor Bump minor version when this PR gets merged feature/surveys labels Jun 20, 2025
Copy link

vercel bot commented Jun 20, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
posthog-js ✅ Ready (Inspect) Visit Preview Jun 20, 2025 8:43pm

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

Adds the ability to programmatically render survey popups on demand through the PostHog JS client, providing more control over survey display timing.

  • Added new public method renderSurveyPopup(surveyId) to PostHog class for immediate popup rendering without delay/conditions
  • Made _handlePopoverSurvey public and added ignoreDelay parameter in SurveyManager for better control
  • Comprehensive test suite in render-on-demand.spec.ts covering edge cases like invalid IDs and multiple renders
  • Clear separation between styled popups (renderSurveyPopup) and unstyled surveys (renderSurvey) in the API

4 files reviewed, 4 comments
Edit PR Review Bot Settings | Greptile

Comment on lines +36 to +37
surveyPopupDelaySeconds: 150,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: surveyPopupDelaySeconds of 150 (2.5 minutes) seems excessive for a test. Consider using a smaller value like 3-5 seconds

Comment on lines +162 to +163
// Wait for 1 second so survey is dismissed internally
await page.waitForTimeout(1000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using arbitrary timeout of 1000ms. Consider extracting this to a named constant to make the wait time's purpose more clear

})
})

// Survey should be visible immediately (ignoring the 3-second delay)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Comment incorrectly states '3-second delay' when the actual delay is 150 seconds

Suggested change
// Survey should be visible immediately (ignoring the 3-second delay)
// Survey should be visible immediately (ignoring the 150-second delay)

Comment on lines 329 to 340
renderSurveyPopup(surveyId: string) {
if (isNullish(this._surveyManager)) {
logger.warn('init was not called')
return
}
const survey = this._getSurveyById(surveyId)
if (!survey) {
logger.warn('Survey not found')
return
}
this._surveyManager.handlePopoverSurvey(survey, true)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: should check eligibility with _checkSurveyEligibility before rendering, like other render methods do

Copy link

github-actions bot commented Jun 20, 2025

Copy link

github-actions bot commented Jun 20, 2025

Size Change: +4.03 kB (+0.11%)

Total Size: 3.57 MB

Filename Size Change
packages/browser/dist/all-external-dependencies.js 220 kB +130 B (+0.06%)
packages/browser/dist/array.full.es5.js 306 kB +445 B (+0.15%)
packages/browser/dist/array.full.js 371 kB +417 B (+0.11%)
packages/browser/dist/array.full.no-external.js 388 kB +400 B (+0.1%)
packages/browser/dist/array.js 171 kB +370 B (+0.22%)
packages/browser/dist/array.no-external.js 186 kB +406 B (+0.22%)
packages/browser/dist/main.js 172 kB +291 B (+0.17%)
packages/browser/dist/module.full.js 371 kB +417 B (+0.11%)
packages/browser/dist/module.full.no-external.js 388 kB +400 B (+0.1%)
packages/browser/dist/module.js 172 kB +291 B (+0.17%)
packages/browser/dist/module.no-external.js 187 kB +327 B (+0.18%)
packages/browser/dist/surveys.js 78.7 kB +133 B (+0.17%)
ℹ️ View Unchanged
Filename Size
packages/browser/dist/customizations.full.js 15.9 kB
packages/browser/dist/dead-clicks-autocapture.js 12.6 kB
packages/browser/dist/exception-autocapture.js 9.55 kB
packages/browser/dist/external-scripts-loader.js 2.57 kB
packages/browser/dist/posthog-recorder.js 207 kB
packages/browser/dist/recorder-v2.js 113 kB
packages/browser/dist/recorder.js 113 kB
packages/browser/dist/surveys-preview.js 70.6 kB
packages/browser/dist/tracing-headers.js 1.63 kB
packages/browser/dist/web-vitals.js 10.4 kB

compressed-size-action

Comment on lines +1433 to +1436
/** Render a styled survey popup. Notice that this method ignores any delay or conditions set on the survey. */
renderSurveyPopup(surveyId: string): void {
this.surveys.renderSurveyPopup(surveyId)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be just an extra param to renderSurvey method?
its a bit confusing right now, 2 methods with similar names, and the difference is ignoring delay or conditions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can be, but the outcome is very different for both of them, which is why I made them different.

renderSurvey render the survey form fields, unstyled, in a parent container. Nothing else.

now, renderSurveyPopup, or new method, does:

  • hooks the survey in the SurveyManager for tracking focus states
  • render the survey exactly how it's rendered in the Survey Preview. i.e. the floating popover, with all the styles and shadow DOM
  • it also ignores both delay and display conditions (answering your question - it ignores all conditions and just displays it)

which is why I decided to make it a separate method. The renderSurvey also needs a selector parameter, which this one doesn't. JS doesn't have overloaded parameters, so we'd also have to change the signature to accept either a selector or styled parameter, and potentially change it to an object.

does this make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does, but that required quite a big explanation, i dont think the name renderSurvey vs renderSurveyPopup will be clear for users tbh, UX here is key so people find what they need and dont run into bugs/different behaviours just because they used the wrong method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have a suggestion? i don't think using the same method is ideal because of the signature change it requires.

maybe we use a name similar to the event we use? the event is survey shown, so we could rename it to showSurvey, wdyt?

Copy link
Member

@marandaneto marandaneto Jun 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the difference is ignoring delay or conditions

i thought that was the only difference, so using the same method name does not make sense

renderSurveyPopup would ignore conditions which is not what i would expect and it'd be different than renderSurvey

maybe renderSurveyPopup takes a param to either ignroe or now the conditions, so at least the behaviour is aligned to renderSurvey, default false, so its aligned with renderSurvey

same for renderSurveyPopup

one can:
renderSurvey(..., ignoreConditions: true) // (defualt false)
renderSurveyPopup(..., ignoreConditions: true) // (defualt false)

write better comments on both methods explaining the difference and why we have the new ignoreConditions

thats what i'd think it'd help a bit with a less confusing API/UX. wdyt?

Copy link
Member

@marandaneto marandaneto Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

displaySurvey makes sense to me; there's still a difference between rendering a popup or in a selector.
That can be another optional parameter.

interface DisplaySurveyOptions {
  styled: bool // default: true
  ignoreDelay: bool // default: false
  ignoreConditions: book // default: false
  displayType: enum // (popup or inlined) default: popup
  selector: string? // default null, requires displayType == inlined
}

something like that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @ioannisj for opinions since we will need this on mobile most likely in the future

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the final form of this looks reasonable enough to me. Not sure we want displayType if are passing a selector though?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean what will a displayType: popup with a selector do?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case, ignore the selector
also fine with just the selector, not sure if it will be obvious to users tho, they will have to figure out the difference between having and not having the selector, whereas with a displayType, you can write comments etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bump minor Bump minor version when this PR gets merged feature/surveys
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ability to Hide/Show Survey Programmatically
4 participants