diff --git a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png
new file mode 100644
index 0000000000000..b2c6486fc3359
Binary files /dev/null and b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png
new file mode 100644
index 0000000000000..a4afed13ffb6d
Binary files /dev/null and b/frontend/__snapshots__/scenes-app-surveys--new-multi-question-survey-section--light.png differ
diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--dark.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--dark.png
index f787d91fbb1cb..c1cc6a66a81c7 100644
Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--dark.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--light.png b/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--light.png
index f77a2253ff51c..4d97555591e6e 100644
Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--light.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey-customisation-section--light.png differ
diff --git a/frontend/__snapshots__/scenes-app-surveys--surveys-list--dark.png b/frontend/__snapshots__/scenes-app-surveys--surveys-list--dark.png
index aedfb4ba080d9..a4ceb3edb03b9 100644
Binary files a/frontend/__snapshots__/scenes-app-surveys--surveys-list--dark.png and b/frontend/__snapshots__/scenes-app-surveys--surveys-list--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-surveys--surveys-list--light.png b/frontend/__snapshots__/scenes-app-surveys--surveys-list--light.png
index e3c2c0b682d9d..4315a51e7e8e1 100644
Binary files a/frontend/__snapshots__/scenes-app-surveys--surveys-list--light.png and b/frontend/__snapshots__/scenes-app-surveys--surveys-list--light.png differ
diff --git a/frontend/src/scenes/surveys/SurveyCustomization.tsx b/frontend/src/scenes/surveys/SurveyCustomization.tsx
index eb45becf9fce9..5e5e224819788 100644
--- a/frontend/src/scenes/surveys/SurveyCustomization.tsx
+++ b/frontend/src/scenes/surveys/SurveyCustomization.tsx
@@ -117,6 +117,18 @@ export function Customization({ appearance, surveyQuestionItem, onAppearanceChan
checked={appearance?.whiteLabel}
/>
+
+
+
+ Shuffle questions
+
+ }
+ onChange={(checked) => onAppearanceChange({ ...appearance, shuffleQuestions: checked })}
+ checked={appearance?.shuffleQuestions}
+ />
+
>
)
diff --git a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
index 6733171206879..ac903deb53ea2 100644
--- a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
+++ b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
@@ -278,6 +278,20 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu
Add open-ended choice
)}
+
+ {({
+ value: shuffleOptions,
+ onChange: toggleShuffleOptions,
+ }) => (
+
+ toggleShuffleOptions(checked)
+ }
+ />
+ )}
+
>
)}
diff --git a/frontend/src/scenes/surveys/Surveys.stories.tsx b/frontend/src/scenes/surveys/Surveys.stories.tsx
index 24ee09f509e40..f9b601b6ecfce 100644
--- a/frontend/src/scenes/surveys/Surveys.stories.tsx
+++ b/frontend/src/scenes/surveys/Surveys.stories.tsx
@@ -8,6 +8,7 @@ import { mswDecorator } from '~/mocks/browser'
import { toPaginatedResponse } from '~/mocks/handlers'
import {
FeatureFlagBasicType,
+ MultipleSurveyQuestion,
PropertyFilterType,
PropertyOperator,
Survey,
@@ -43,6 +44,44 @@ const MOCK_BASIC_SURVEY: Survey = {
responses_limit: null,
}
+const MOCK_SURVEY_WITH_MULTIPLE_OPTIONS: Survey = {
+ id: '998FE805-F9EF-4F25-A5D1-B9549C4E2143',
+ name: 'survey with multiple options',
+ description: 'survey with multiple options description',
+ type: SurveyType.Popover,
+ created_at: '2023-04-27T10:04:37.977401Z',
+ created_by: {
+ id: 1,
+ uuid: '01863799-062b-0000-8a61-b2842d5f8642',
+ distinct_id: 'Sopz9Z4NMIfXGlJe6W1XF98GOqhHNui5J5eRe0tBGTE',
+ first_name: 'Employee 427',
+ email: 'test2@posthog.com',
+ },
+ questions: [
+ {
+ type: SurveyQuestionType.MultipleChoice,
+ question: "We're sorry to see you go. What's your reason for unsubscribing?",
+ choices: [
+ 'I no longer need the product',
+ 'I found a better product',
+ 'I found the product too difficult to use',
+ 'Other',
+ ],
+ shuffleOptions: true,
+ },
+ ],
+ conditions: null,
+ linked_flag: null,
+ linked_flag_id: null,
+ targeting_flag: null,
+ targeting_flag_filters: undefined,
+ appearance: { backgroundColor: 'white', submitButtonColor: '#2C2C2C' },
+ start_date: null,
+ end_date: null,
+ archived: false,
+ responses_limit: null,
+}
+
const MOCK_SURVEY_WITH_RELEASE_CONS: Survey = {
id: '0187c279-bcae-0000-34f5-4f121921f006',
name: 'survey with release conditions',
@@ -154,9 +193,12 @@ const meta: Meta = {
'/api/projects/:team_id/surveys/': toPaginatedResponse([
MOCK_BASIC_SURVEY,
MOCK_SURVEY_WITH_RELEASE_CONS,
+ MOCK_SURVEY_WITH_MULTIPLE_OPTIONS,
]),
'/api/projects/:team_id/surveys/0187c279-bcae-0000-34f5-4f121921f005/': MOCK_BASIC_SURVEY,
'/api/projects/:team_id/surveys/0187c279-bcae-0000-34f5-4f121921f006/': MOCK_SURVEY_WITH_RELEASE_CONS,
+ '/api/projects/:team_id/surveys/998FE805-F9EF-4F25-A5D1-B9549C4E2143/':
+ MOCK_SURVEY_WITH_MULTIPLE_OPTIONS,
'/api/projects/:team_id/surveys/responses_count/': MOCK_RESPONSES_COUNT,
[`/api/projects/:team_id/feature_flags/${
(MOCK_SURVEY_WITH_RELEASE_CONS.linked_flag as FeatureFlagBasicType).id
@@ -206,6 +248,27 @@ export const NewSurveyCustomisationSection: StoryFn = () => {
return
}
+export const NewMultiQuestionSurveySection: StoryFn = () => {
+ useEffect(() => {
+ router.actions.push(urls.survey('new'))
+ surveyLogic({ id: 'new' }).mount()
+ surveyLogic({ id: 'new' }).actions.setSelectedSection(SurveyEditSection.Steps)
+ surveyLogic({ id: 'new' }).actions.setSurveyValue('questions', [
+ {
+ type: SurveyQuestionType.MultipleChoice,
+ question: "We're sorry to see you go. What's your reason for unsubscribing?",
+ choices: [
+ 'I no longer need the product',
+ 'I found a better product',
+ 'I found the product too difficult to use',
+ 'Other',
+ ],
+ } as MultipleSurveyQuestion,
+ ])
+ }, [])
+ return
+}
+
export const NewSurveyPresentationSection: StoryFn = () => {
useEffect(() => {
router.actions.push(urls.survey('new'))
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 57eb709732208..b864eec21d68f 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -2497,6 +2497,7 @@ export interface SurveyAppearance {
widgetSelector?: string
widgetLabel?: string
widgetColor?: string
+ shuffleQuestions?: boolean
}
export interface SurveyQuestionBase {
@@ -2526,6 +2527,7 @@ export interface RatingSurveyQuestion extends SurveyQuestionBase {
export interface MultipleSurveyQuestion extends SurveyQuestionBase {
type: SurveyQuestionType.SingleChoice | SurveyQuestionType.MultipleChoice
choices: string[]
+ shuffleOptions?: boolean
hasOpenChoice?: boolean
}
diff --git a/posthog/api/test/test_survey.py b/posthog/api/test/test_survey.py
index a22a116958d82..31d846e8a36a2 100644
--- a/posthog/api/test/test_survey.py
+++ b/posthog/api/test/test_survey.py
@@ -1009,6 +1009,70 @@ def test_surveys_opt_in_post_delete(self):
assert self.team.surveys_opt_in is False
+class TestMultipleChoiceQuestions(APIBaseTest):
+ def test_create_survey_has_open_choice(self):
+ response = self.client.post(
+ f"/api/projects/{self.team.id}/surveys/",
+ data={
+ "name": "Notebooks beta release survey",
+ "description": "Get feedback on the new notebooks feature",
+ "type": "popover",
+ "questions": [
+ {
+ "type": "multiple_choice",
+ "choices": ["Tutorials", "Customer case studies", "Product announcements", "Other"],
+ "question": "What can we do to improve our product?",
+ "buttonText": "Submit",
+ "description": "",
+ "hasOpenChoice": True,
+ }
+ ],
+ "appearance": {
+ "thankYouMessageHeader": "Thanks for your feedback!",
+ "thankYouMessageDescription": "We'll use it to make notebooks better.",
+ },
+ },
+ format="json",
+ )
+ response_data = response.json()
+ assert response.status_code == status.HTTP_201_CREATED, response_data
+ assert Survey.objects.filter(id=response_data["id"]).exists()
+ assert response_data["name"] == "Notebooks beta release survey"
+ assert response_data["questions"][0]["hasOpenChoice"] is True
+
+ def test_create_survey_with_shuffle_options(self):
+ response = self.client.post(
+ f"/api/projects/{self.team.id}/surveys/",
+ data={
+ "name": "Notebooks beta release survey",
+ "description": "Get feedback on the new notebooks feature",
+ "type": "popover",
+ "questions": [
+ {
+ "type": "multiple_choice",
+ "choices": ["Tutorials", "Customer case studies", "Product announcements", "Other"],
+ "question": "What can we do to improve our product?",
+ "buttonText": "Submit",
+ "description": "",
+ "hasOpenChoice": True,
+ "shuffleOptions": True,
+ }
+ ],
+ "appearance": {
+ "thankYouMessageHeader": "Thanks for your feedback!",
+ "thankYouMessageDescription": "We'll use it to make notebooks better.",
+ },
+ },
+ format="json",
+ )
+ response_data = response.json()
+ assert response.status_code == status.HTTP_201_CREATED, response_data
+ assert Survey.objects.filter(id=response_data["id"]).exists()
+ assert response_data["name"] == "Notebooks beta release survey"
+ assert response_data["questions"][0]["hasOpenChoice"] is True
+ assert response_data["questions"][0]["shuffleOptions"] is True
+
+
class TestSurveyQuestionValidation(APIBaseTest):
def test_create_basic_survey_question_validation(self):
response = self.client.post(
@@ -1032,6 +1096,7 @@ def test_create_basic_survey_question_validation(self):
"appearance": {
"thankYouMessageHeader": "Thanks for your feedback!",
"thankYouMessageDescription": "We'll use it to make notebooks better.",
+ "shuffleQuestions": True,
},
},
format="json",
@@ -1053,6 +1118,7 @@ def test_create_basic_survey_question_validation(self):
assert response_data["appearance"] == {
"thankYouMessageHeader": "Thanks for your feedback!",
"thankYouMessageDescription": "We'll use it to make notebooks better.",
+ "shuffleQuestions": True,
}
assert response_data["created_by"]["id"] == self.user.id