diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey.png b/frontend/__snapshots__/scenes-app-surveys--new-survey.png index 5671de85e8c52..9033d58fe25e7 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey.png differ diff --git a/frontend/src/scenes/surveys/Survey.tsx b/frontend/src/scenes/surveys/Survey.tsx index 100177211fb7b..10a67d4e96aea 100644 --- a/frontend/src/scenes/surveys/Survey.tsx +++ b/frontend/src/scenes/surveys/Survey.tsx @@ -5,12 +5,14 @@ import { Form, Group } from 'kea-forms' import { PageHeader } from 'lib/components/PageHeader' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { + LemonBanner, LemonButton, LemonCheckbox, LemonCollapse, LemonDivider, LemonInput, LemonSelect, + LemonTabs, LemonTextArea, Link, } from '@posthog/lemon-ui' @@ -36,6 +38,7 @@ import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' import { defaultSurveyFieldValues, defaultSurveyAppearance, NewSurvey, SurveyUrlMatchTypeLabels } from './constants' import { FEATURE_FLAGS } from 'lib/constants' import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' +import { CodeEditor } from 'lib/components/CodeEditors' import { NotFound } from 'lib/components/NotFound' export const scene: SceneExport = { @@ -68,9 +71,16 @@ export function SurveyComponent({ id }: { id?: string } = {}): JSX.Element { } export function SurveyForm({ id }: { id: string }): JSX.Element { - const { survey, surveyLoading, isEditingSurvey, hasTargetingFlag, urlMatchTypeValidationError } = - useValues(surveyLogic) - const { loadSurvey, editingSurvey, setSurveyValue, setDefaultForQuestionType } = useActions(surveyLogic) + const { + survey, + surveyLoading, + isEditingSurvey, + hasTargetingFlag, + urlMatchTypeValidationError, + writingHTMLDescription, + } = useValues(surveyLogic) + const { loadSurvey, editingSurvey, setSurveyValue, setDefaultForQuestionType, setWritingHTMLDescription } = + useActions(surveyLogic) const { featureFlags } = useValues(enabledFeaturesLogic) return ( @@ -153,7 +163,7 @@ export function SurveyForm({ id }: { id: string }): JSX.Element { ), content: ( - <> +
{ @@ -216,7 +226,72 @@ export function SurveyForm({ id }: { id: string }): JSX.Element { )} - + {({ value, onChange }) => ( + <> + + setWritingHTMLDescription(key === 'html') + } + tabs={[ + { + key: 'text', + label: ( + Text + ), + content: ( + onChange(v)} + /> + ), + }, + { + key: 'html', + label: ( + HTML + ), + content: ( +
+ + onChange(v ?? '') + } + height={150} + options={{ + minimap: { enabled: false }, + wordWrap: 'on', + scrollBeyondLastLine: false, + automaticLayout: true, + fixedOverflowWidgets: true, + lineNumbers: 'off', + glyphMargin: false, + folding: false, + }} + /> +
+ ), + }, + ]} + /> + {question.description && + question.description + ?.toLowerCase() + .includes(' + Scripts won't run in the survey popup and + we'll remove these on save. Use the API + question mode to run your own scripts in + surveys. + + )} + + )}
{question.type === SurveyQuestionType.Rating && (
@@ -331,7 +406,7 @@ export function SurveyForm({ id }: { id: string }): JSX.Element {
)} - +
), }, ]} diff --git a/frontend/src/scenes/surveys/SurveyAppearance.tsx b/frontend/src/scenes/surveys/SurveyAppearance.tsx index b9199f0906eb5..4ab57e95ed773 100644 --- a/frontend/src/scenes/surveys/SurveyAppearance.tsx +++ b/frontend/src/scenes/surveys/SurveyAppearance.tsx @@ -24,6 +24,7 @@ import { useValues } from 'kea' import { useEffect, useRef, useState } from 'react' import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { sanitize } from 'dompurify' interface SurveyAppearanceProps { type: SurveyQuestionType @@ -270,7 +271,12 @@ function BaseAppearance({
{question}
- {description &&
{description}
} + {/* Using dangerouslySetInnerHTML is safe here, because it's taking the user's input and showing it to the same user. + They can try passing in arbitrary scripts, but it would show up only for them, so it's like trying to XSS yourself, where + you already have all the data. Furthermore, sanitization should catch all obvious attempts */} + {description && ( +
+ )} {type === SurveyQuestionType.Open && (