{
- setMonaco(monaco)
- }}
- />
+ >
+
+
+ {/* Using a standard resize css means we need overflow-hidden which hides parts of the editor unnecessarily */}
+
{
+ const startY = e.clientY
+ const startHeight = height
+ const onMouseMove = (event: MouseEvent): void => {
+ setManualHeight(startHeight + event.clientY - startY)
+ }
+ const onMouseUp = (): void => {
+ window.removeEventListener('mousemove', onMouseMove)
+ window.removeEventListener('mouseup', onMouseUp)
+ }
+ window.addEventListener('mousemove', onMouseMove)
+ window.addEventListener('mouseup', onMouseUp)
+ }}
+ />
+
)
}
diff --git a/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx b/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx
index 738d80016dcf2..e195319f037f9 100644
--- a/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx
+++ b/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx
@@ -189,6 +189,7 @@ export function ItemPerformanceEvent({
+
{/* We only show the status if it exists and is an error status */}
{otherProps.response_status && otherProps.response_status >= 400 ? (
diff --git a/frontend/src/scenes/surveys/QuestionBranchingInput.tsx b/frontend/src/scenes/surveys/QuestionBranchingInput.tsx
index 96c6ea55912d6..be078d80f050f 100644
--- a/frontend/src/scenes/surveys/QuestionBranchingInput.tsx
+++ b/frontend/src/scenes/surveys/QuestionBranchingInput.tsx
@@ -3,8 +3,9 @@ import './EditSurvey.scss'
import { LemonSelect } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { LemonField } from 'lib/lemon-ui/LemonField'
+import { truncate } from 'lib/utils'
-import { MultipleSurveyQuestion, RatingSurveyQuestion, SurveyQuestionBranchingType } from '~/types'
+import { MultipleSurveyQuestion, RatingSurveyQuestion, SurveyQuestionBranchingType, SurveyQuestionType } from '~/types'
import { surveyLogic } from './surveyLogic'
@@ -16,7 +17,7 @@ export function QuestionBranchingInput({
question: RatingSurveyQuestion | MultipleSurveyQuestion
}): JSX.Element {
const { survey, getBranchingDropdownValue } = useValues(surveyLogic)
- const { setQuestionBranching } = useActions(surveyLogic)
+ const { setQuestionBranchingType } = useActions(surveyLogic)
const availableNextQuestions = survey.questions
.map((question, questionIndex) => ({
@@ -25,6 +26,8 @@ export function QuestionBranchingInput({
}))
.filter((_, idx) => questionIndex !== idx)
const branchingDropdownValue = getBranchingDropdownValue(questionIndex, question)
+ const hasResponseBasedBranching =
+ question.type === SurveyQuestionType.Rating || question.type === SurveyQuestionType.SingleChoice
return (
<>
@@ -33,7 +36,14 @@ export function QuestionBranchingInput({
className="max-w-80 whitespace-nowrap"
value={branchingDropdownValue}
data-attr={`branching-question-${questionIndex}`}
- onSelect={(value) => setQuestionBranching(questionIndex, value)}
+ onSelect={(type) => {
+ let specificQuestionIndex
+ if (type.startsWith(SurveyQuestionBranchingType.SpecificQuestion)) {
+ specificQuestionIndex = parseInt(type.split(':')[1])
+ type = SurveyQuestionBranchingType.SpecificQuestion
+ }
+ setQuestionBranchingType(questionIndex, type, specificQuestionIndex)
+ }}
options={[
...(questionIndex < survey.questions.length - 1
? [
@@ -47,22 +57,122 @@ export function QuestionBranchingInput({
label: 'Confirmation message',
value: SurveyQuestionBranchingType.ConfirmationMessage,
},
- {
- label: 'Specific question based on answer',
- value: SurveyQuestionBranchingType.ResponseBased,
- },
+ ...(hasResponseBasedBranching
+ ? [
+ {
+ label: 'Specific question based on answer',
+ value: SurveyQuestionBranchingType.ResponseBased,
+ },
+ ]
+ : []),
...availableNextQuestions.map((question) => ({
- label: `${question.questionIndex + 1}. ${question.question}`,
+ label: truncate(`${question.questionIndex + 1}. ${question.question}`, 40),
value: `${SurveyQuestionBranchingType.SpecificQuestion}:${question.questionIndex}`,
})),
]}
/>
{branchingDropdownValue === SurveyQuestionBranchingType.ResponseBased && (
-
- TODO: dropdowns for the response-based branching
-
+
)}
>
)
}
+
+function QuestionResponseBasedBranchingInput({
+ questionIndex,
+ question,
+}: {
+ questionIndex: number
+ question: RatingSurveyQuestion | MultipleSurveyQuestion
+}): JSX.Element {
+ const { survey, getResponseBasedBranchingDropdownValue } = useValues(surveyLogic)
+ const { setResponseBasedBranchingForQuestion } = useActions(surveyLogic)
+
+ const availableNextQuestions = survey.questions
+ .map((question, questionIndex) => ({
+ ...question,
+ questionIndex,
+ }))
+ .filter((_, idx) => questionIndex !== idx)
+
+ let config: { value: string | number; label: string }[] = []
+
+ if (question.type === SurveyQuestionType.Rating && question.scale === 3) {
+ config = [
+ { value: 'negative', label: '1 (Negative)' },
+ { value: 'neutral', label: '2 (Neutral)' },
+ { value: 'positive', label: '3 (Positive)' },
+ ]
+ } else if (question.type === SurveyQuestionType.Rating && question.scale === 5) {
+ config = [
+ { value: 'negative', label: '1 to 2 (Negative)' },
+ { value: 'neutral', label: '3 (Neutral)' },
+ { value: 'positive', label: '4 to 5 (Positive)' },
+ ]
+ } else if (question.type === SurveyQuestionType.Rating && question.scale === 10) {
+ config = [
+ // NPS categories
+ { value: 'detractors', label: '0 to 6 (Detractors)' },
+ { value: 'passives', label: '7 to 8 (Passives)' },
+ { value: 'promoters', label: '9 to 10 (Promoters)' },
+ ]
+ } else if (question.type === SurveyQuestionType.SingleChoice) {
+ config = question.choices.map((choice, choiceIndex) => ({
+ value: choiceIndex,
+ label: `Option ${choiceIndex + 1} ("${truncate(choice, 15)}")`,
+ }))
+ }
+
+ return (
+
+ {config.map(({ value, label }, i) => (
+
+
+
+ If the answer is {label}, go to:
+
+
+
+ {
+ let specificQuestionIndex
+ if (nextStep.startsWith(SurveyQuestionBranchingType.SpecificQuestion)) {
+ specificQuestionIndex = parseInt(nextStep.split(':')[1])
+ nextStep = SurveyQuestionBranchingType.SpecificQuestion
+ }
+ setResponseBasedBranchingForQuestion(
+ questionIndex,
+ value,
+ nextStep,
+ specificQuestionIndex
+ )
+ }}
+ options={[
+ ...(questionIndex < survey.questions.length - 1
+ ? [
+ {
+ label: 'Next question',
+ value: SurveyQuestionBranchingType.NextQuestion,
+ },
+ ]
+ : []),
+ {
+ label: 'Confirmation message',
+ value: SurveyQuestionBranchingType.ConfirmationMessage,
+ },
+ ...availableNextQuestions.map((question) => ({
+ label: truncate(`${question.questionIndex + 1}. ${question.question}`, 20),
+ value: `${SurveyQuestionBranchingType.SpecificQuestion}:${question.questionIndex}`,
+ })),
+ ]}
+ />
+
+
+ ))}
+
+ )
+}
diff --git a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
index ec3b03d54b41d..e1b24b1bea182 100644
--- a/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
+++ b/frontend/src/scenes/surveys/SurveyEditQuestionRow.tsx
@@ -87,11 +87,9 @@ export function SurveyEditQuestionHeader({
export function SurveyEditQuestionGroup({ index, question }: { index: number; question: any }): JSX.Element {
const { survey, descriptionContentType } = useValues(surveyLogic)
- const { setDefaultForQuestionType, setSurveyValue } = useActions(surveyLogic)
+ const { setDefaultForQuestionType, setSurveyValue, resetBranchingForQuestion } = useActions(surveyLogic)
const { featureFlags } = useValues(enabledFeaturesLogic)
- const hasBranching =
- featureFlags[FEATURE_FLAGS.SURVEYS_BRANCHING_LOGIC] &&
- (question.type === SurveyQuestionType.Rating || question.type === SurveyQuestionType.SingleChoice)
+ const hasBranching = featureFlags[FEATURE_FLAGS.SURVEYS_BRANCHING_LOGIC]
const initialDescriptionContentType = descriptionContentType(index) ?? 'text'
@@ -134,6 +132,7 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu
editingDescription,
editingThankYouMessage
)
+ resetBranchingForQuestion(index)
}}
options={[
{
@@ -201,6 +200,7 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu
const newQuestions = [...survey.questions]
newQuestions[index] = newQuestion
setSurveyValue('questions', newQuestions)
+ resetBranchingForQuestion(index)
}}
/>
@@ -212,8 +212,17 @@ export function SurveyEditQuestionGroup({ index, question }: { index: number; qu
label: '1 - 5',
value: 5,
},
- ...(question.display === 'number' ? [{ label: '0 - 10', value: 10 }] : []),
+ ...(question.display === 'number'
+ ? [{ label: '0 - 10 (Net Promoter Score)', value: 10 }]
+ : []),
]}
+ onChange={(val) => {
+ const newQuestion = { ...survey.questions[index], scale: val }
+ const newQuestions = [...survey.questions]
+ newQuestions[index] = newQuestion
+ setSurveyValue('questions', newQuestions)
+ resetBranchingForQuestion(index)
+ }}
/>
diff --git a/frontend/src/scenes/surveys/constants.tsx b/frontend/src/scenes/surveys/constants.tsx
index 738e51b2a209a..6411868691bbd 100644
--- a/frontend/src/scenes/surveys/constants.tsx
+++ b/frontend/src/scenes/surveys/constants.tsx
@@ -1,3 +1,5 @@
+import { allOperatorsMapping } from 'lib/utils'
+
import {
Survey,
SurveyQuestionDescriptionContentType,
@@ -17,10 +19,14 @@ export const SurveyQuestionLabel = {
[SurveyQuestionType.MultipleChoice]: 'Multiple choice select',
}
+// Create SurveyUrlMatchTypeLabels using allOperatorsMapping
export const SurveyUrlMatchTypeLabels = {
- [SurveyUrlMatchType.Contains]: '∋ contains',
- [SurveyUrlMatchType.Regex]: '∼ matches regex',
- [SurveyUrlMatchType.Exact]: '= equals',
+ [SurveyUrlMatchType.Exact]: allOperatorsMapping[SurveyUrlMatchType.Exact],
+ [SurveyUrlMatchType.IsNot]: allOperatorsMapping[SurveyUrlMatchType.IsNot],
+ [SurveyUrlMatchType.Contains]: allOperatorsMapping[SurveyUrlMatchType.Contains],
+ [SurveyUrlMatchType.NotIContains]: allOperatorsMapping[SurveyUrlMatchType.NotIContains],
+ [SurveyUrlMatchType.Regex]: allOperatorsMapping[SurveyUrlMatchType.Regex],
+ [SurveyUrlMatchType.NotRegex]: allOperatorsMapping[SurveyUrlMatchType.NotRegex],
}
export const defaultSurveyAppearance = {
diff --git a/frontend/src/scenes/surveys/surveyLogic.test.ts b/frontend/src/scenes/surveys/surveyLogic.test.ts
index 56e6615beb36f..4b12c4b1459f1 100644
--- a/frontend/src/scenes/surveys/surveyLogic.test.ts
+++ b/frontend/src/scenes/surveys/surveyLogic.test.ts
@@ -1,9 +1,9 @@
-import { expectLogic } from 'kea-test-utils'
+import { expectLogic, partial } from 'kea-test-utils'
import { surveyLogic } from 'scenes/surveys/surveyLogic'
import { useMocks } from '~/mocks/jest'
import { initKeaTests } from '~/test/init'
-import { Survey, SurveyQuestionType, SurveyType } from '~/types'
+import { Survey, SurveyQuestionBranchingType, SurveyQuestionType, SurveyType } from '~/types'
const MULTIPLE_CHOICE_SURVEY: Survey = {
id: '018b22a3-09b1-0000-2f5b-1bd8352ceec9',
@@ -398,3 +398,439 @@ describe('single choice survey with open choice logic', () => {
})
})
})
+
+describe('set response-based survey branching', () => {
+ let logic: ReturnType