Skip to content

Commit

Permalink
feat(surveys): add configurable delay to popup surveys (#22780)
Browse files Browse the repository at this point in the history
* initial commit

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* fixing the ui a bit

* write tests for the survey_popup_delay api

* remove todos that were todone

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update frontend/src/scenes/surveys/SurveyCustomization.tsx

Co-authored-by: Phani Raj <[email protected]>

* Update frontend/src/types.ts

Co-authored-by: Phani Raj <[email protected]>

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* code review feedback

* add hour max to survey delay

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* not sure how this was reverted, but it's back now

* was this it?

* was this it?

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update query snapshots

* Update query snapshots

* Update query snapshots

* Update query snapshots

* use latest version of posthog-js to activate the survey popup delay features

* forgot to commit the lockfile

* Update query snapshots

* Update UI snapshots for `chromium` (2)

* Update query snapshots

* Update UI snapshots for `chromium` (2)

* Update query snapshots

* Update query snapshots

* Update query snapshots

* Update query snapshots

* Update query snapshots

* Update query snapshots

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Update query snapshots

* Update query snapshots

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Phani Raj <[email protected]>
  • Loading branch information
3 people authored Jun 24, 2024
1 parent 6c23c9b commit f49e0af
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 17 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions frontend/src/scenes/surveys/SurveyCustomization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LemonButton, LemonCheckbox, LemonDialog, LemonInput, LemonSelect } from
import { useActions, useValues } from 'kea'
import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini'
import { upgradeModalLogic } from 'lib/components/UpgradeModal/upgradeModalLogic'
import { LemonField } from 'lib/lemon-ui/LemonField'
import { surveyLogic } from 'scenes/surveys/surveyLogic'

import {
Expand Down Expand Up @@ -162,6 +163,40 @@ export function Customization({ appearance, surveyQuestionItem, onAppearanceChan
checked={appearance?.shuffleQuestions}
/>
</div>
<div className="mt-1">
<LemonField.Pure>
<div className="flex flex-row gap-2 items-center font-medium">
<LemonCheckbox
checked={!!appearance?.surveyPopupDelaySeconds}
onChange={(checked) => {
const surveyPopupDelaySeconds = checked ? 5 : undefined
onAppearanceChange({ ...appearance, surveyPopupDelaySeconds })
}}
/>
Delay survey popup after page load by at least{' '}
<LemonInput
type="number"
data-attr="survey-popup-delay-input"
size="small"
min={1}
max={3600}
value={appearance?.surveyPopupDelaySeconds || NaN}
onChange={(newValue) => {
if (newValue && newValue > 0) {
onAppearanceChange({ ...appearance, surveyPopupDelaySeconds: newValue })
} else {
onAppearanceChange({
...appearance,
surveyPopupDelaySeconds: undefined,
})
}
}}
className="w-12"
/>{' '}
seconds.
</div>
</LemonField.Pure>
</div>
</div>
</>
)
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2653,12 +2653,13 @@ export interface SurveyAppearance {
thankYouMessageDescriptionContentType?: SurveyQuestionDescriptionContentType
autoDisappear?: boolean
position?: string
shuffleQuestions?: boolean
surveyPopupDelaySeconds?: number
// widget only
widgetType?: 'button' | 'tab' | 'selector'
widgetSelector?: string
widgetLabel?: string
widgetColor?: string
shuffleQuestions?: boolean
}

export interface SurveyQuestionBase {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
"pmtiles": "^2.11.0",
"postcss": "^8.4.31",
"postcss-preset-env": "^9.3.0",
"posthog-js": "1.140.1",
"posthog-js": "1.141.0",
"posthog-js-lite": "3.0.0",
"prettier": "^2.8.8",
"prop-types": "^15.7.2",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions posthog/api/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import nh3
from django.db.models import Min
from django.http import JsonResponse
from django.utils.text import slugify
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from nanoid import generate
from rest_framework import request, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
Expand All @@ -20,13 +22,10 @@
from posthog.api.shared import UserBasicSerializer
from posthog.api.utils import get_token
from posthog.client import sync_execute
from posthog.exceptions import generate_exception_response
from posthog.models.feedback.survey import Survey
from django.utils.text import slugify
from nanoid import generate
from rest_framework import request, serializers, viewsets
from posthog.constants import AvailableFeature
from posthog.exceptions import generate_exception_response
from posthog.models.feature_flag.feature_flag import FeatureFlag
from posthog.models.feedback.survey import Survey
from posthog.models.team.team import Team
from posthog.utils_cors import cors_response

Expand Down Expand Up @@ -134,6 +133,10 @@ def validate_appearance(self, value):
"You need to upgrade to PostHog Enterprise to use HTML in survey thank you message"
)

survey_popup_delay_seconds = value.get("surveyPopupDelaySeconds")
if survey_popup_delay_seconds and survey_popup_delay_seconds < 0:
raise serializers.ValidationError("Survey popup delay seconds must be a positive integer")

return value

def validate_questions(self, value):
Expand Down
8 changes: 4 additions & 4 deletions posthog/api/test/__snapshots__/test_properties_timeline.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@
ORDER BY timestamp ASC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS end_event_number
FROM
(SELECT timestamp, person_properties AS properties,
array(replaceRegexpAll(JSONExtractRaw(person_properties, 'bar'), '^"|"$', ''), replaceRegexpAll(JSONExtractRaw(person_properties, 'foo'), '^"|"$', '')) AS relevant_property_values,
array(replaceRegexpAll(JSONExtractRaw(person_properties, 'foo'), '^"|"$', ''), replaceRegexpAll(JSONExtractRaw(person_properties, 'bar'), '^"|"$', '')) AS relevant_property_values,
lagInFrame(relevant_property_values) OVER (
ORDER BY timestamp ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_relevant_property_values,
row_number() OVER (
Expand Down Expand Up @@ -482,7 +482,7 @@
ORDER BY timestamp ASC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS end_event_number
FROM
(SELECT timestamp, person_properties AS properties,
array("mat_pp_bar", "mat_pp_foo") AS relevant_property_values,
array("mat_pp_foo", "mat_pp_bar") AS relevant_property_values,
lagInFrame(relevant_property_values) OVER (
ORDER BY timestamp ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_relevant_property_values,
row_number() OVER (
Expand Down Expand Up @@ -522,7 +522,7 @@
ORDER BY timestamp ASC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS end_event_number
FROM
(SELECT timestamp, person_properties AS properties,
array(replaceRegexpAll(JSONExtractRaw(person_properties, 'bar'), '^"|"$', ''), replaceRegexpAll(JSONExtractRaw(person_properties, 'foo'), '^"|"$', '')) AS relevant_property_values,
array(replaceRegexpAll(JSONExtractRaw(person_properties, 'foo'), '^"|"$', ''), replaceRegexpAll(JSONExtractRaw(person_properties, 'bar'), '^"|"$', '')) AS relevant_property_values,
lagInFrame(relevant_property_values) OVER (
ORDER BY timestamp ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_relevant_property_values,
row_number() OVER (
Expand Down Expand Up @@ -558,7 +558,7 @@
ORDER BY timestamp ASC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS end_event_number
FROM
(SELECT timestamp, person_properties AS properties,
array("mat_pp_bar", "mat_pp_foo") AS relevant_property_values,
array("mat_pp_foo", "mat_pp_bar") AS relevant_property_values,
lagInFrame(relevant_property_values) OVER (
ORDER BY timestamp ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS previous_relevant_property_values,
row_number() OVER (
Expand Down
35 changes: 34 additions & 1 deletion posthog/api/test/test_survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from posthog.api.survey import nh3_clean_with_allow_list
from posthog.models.cohort.cohort import Cohort
from nanoid import generate

from rest_framework import status

from posthog.constants import AvailableFeature
Expand Down Expand Up @@ -1249,6 +1248,7 @@ def test_create_basic_survey_question_validation(self):
"thankYouMessageHeader": "Thanks for your feedback!",
"thankYouMessageDescription": "<b>We'll use it to make notebooks better.<script>alert(0)</script>",
"shuffleQuestions": True,
"surveyPopupDelaySeconds": 60,
},
},
format="json",
Expand All @@ -1271,6 +1271,7 @@ def test_create_basic_survey_question_validation(self):
"thankYouMessageHeader": "Thanks for your feedback!",
"thankYouMessageDescription": "<b>We'll use it to make notebooks better.</b>",
"shuffleQuestions": True,
"surveyPopupDelaySeconds": 60,
}
assert response_data["created_by"]["id"] == self.user.id

Expand Down Expand Up @@ -1676,6 +1677,38 @@ def test_validate_thank_you_description_content_type(self):
assert response.status_code == status.HTTP_400_BAD_REQUEST, response_data
assert response_data["detail"] == "thankYouMessageDescriptionContentType must be one of ['text', 'html']"

def test_create_survey_with_survey_popup_delay(self):
response = self.client.post(
f"/api/projects/{self.team.id}/surveys/",
data={
"name": "Notebooks beta release survey",
"type": "popover",
"appearance": {
"surveyPopupDelaySeconds": 6000,
},
},
format="json",
)
response_data = response.json()
assert response.status_code == status.HTTP_201_CREATED, response_data
assert response_data["appearance"]["surveyPopupDelaySeconds"] == 6000

def test_validate_survey_popup_delay(self):
response = self.client.post(
f"/api/projects/{self.team.id}/surveys/",
data={
"name": "Notebooks beta release survey",
"type": "popover",
"appearance": {
"surveyPopupDelaySeconds": -100,
},
},
format="json",
)
response_data = response.json()
assert response.status_code == status.HTTP_400_BAD_REQUEST, response_data
assert response_data["detail"] == "Survey popup delay seconds must be a positive integer"

def test_create_survey_with_valid_question_description_content_type_html(self):
response = self.client.post(
f"/api/projects/{self.team.id}/surveys/",
Expand Down

0 comments on commit f49e0af

Please sign in to comment.