Skip to content
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

fix(surveys): Add html descriptions for thank you #18036

Merged
merged 14 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions frontend/src/scenes/surveys/SurveyAppearance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ export function BaseAppearance({
</div>
)}
<div className="question-textarea-wrapper">
<div className="survey-question">{question}</div>
<div className="survey-question" dangerouslySetInnerHTML={{ __html: sanitize(question) }} />
{/* 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 */}
Expand Down Expand Up @@ -638,8 +638,14 @@ export function SurveyThankYou({ appearance }: { appearance: SurveyAppearanceTyp
{cancel}
</button>
</div>
<h3 className="thank-you-message-header">{appearance?.thankYouMessageHeader || 'Thank you!'}</h3>
<div className="thank-you-message-body">{appearance?.thankYouMessageDescription || ''}</div>
<h3
className="thank-you-message-header"
dangerouslySetInnerHTML={{ __html: sanitize(appearance?.thankYouMessageHeader || 'Thank you!') }}
/>
<div
className="thank-you-message-body"
dangerouslySetInnerHTML={{ __html: sanitize(appearance?.thankYouMessageDescription || '') }}
/>
<Button appearance={appearance} onSubmit={() => undefined}>
Close
</Button>
Expand Down
194 changes: 104 additions & 90 deletions frontend/src/scenes/surveys/SurveyEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,93 +158,24 @@ export default function SurveyEdit(): JSX.Element {
label="Description (optional)"
>
{({ value, onChange }) => (
<>
<LemonTabs
activeKey={
writingHTMLDescription
? 'html'
: 'text'
}
onChange={(key) =>
setWritingHTMLDescription(
key === 'html'
)
}
tabs={[
{
key: 'text',
label: (
<span className="text-sm">
Text
</span>
),
content: (
<LemonTextArea
data-attr="survey-description"
minRows={2}
value={value}
onChange={(v) =>
onChange(v)
}
/>
),
},
{
key: 'html',
label: (
<span className="text-sm">
HTML
</span>
),
content: (
<div>
<CodeEditor
className="border"
language="html"
value={value}
onChange={(v) =>
onChange(
v ?? ''
)
}
height={150}
options={{
minimap: {
enabled:
false,
},
wordWrap: 'on',
scrollBeyondLastLine:
false,
automaticLayout:
true,
fixedOverflowWidgets:
true,
lineNumbers:
'off',
glyphMargin:
false,
folding: false,
}}
/>
</div>
),
},
]}
/>
{question.description &&
question.description
?.toLowerCase()
.includes('<script') && (
<LemonBanner type="warning">
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.
</LemonBanner>
)}
</>
<HTMLEditor
value={value}
onChange={onChange}
showScriptWarning={
!!(
neilkakkar marked this conversation as resolved.
Show resolved Hide resolved
question.description &&
question.description
?.toLowerCase()
.includes('<script')
)
}
writingHTMLDescription={
writingHTMLDescription
}
setWritingHTMLDescription={
setWritingHTMLDescription
}
/>
)}
</Field>
<Field
Expand Down Expand Up @@ -616,7 +547,7 @@ export default function SurveyEdit(): JSX.Element {
/>
</PureField>
<PureField label="Thank you description">
<LemonTextArea
<HTMLEditor
value={
survey.appearance
.thankYouMessageDescription
Expand All @@ -627,8 +558,21 @@ export default function SurveyEdit(): JSX.Element {
thankYouMessageDescription: val,
})
}
minRows={2}
placeholder="ex: We really appreciate it."
showScriptWarning={
!!(
neilkakkar marked this conversation as resolved.
Show resolved Hide resolved
survey.appearance
.thankYouMessageDescription &&
survey.appearance.thankYouMessageDescription
?.toLowerCase()
.includes('<script')
)
}
writingHTMLDescription={
writingHTMLDescription
}
setWritingHTMLDescription={
setWritingHTMLDescription
}
/>
</PureField>
</>
Expand Down Expand Up @@ -967,3 +911,73 @@ export default function SurveyEdit(): JSX.Element {
</div>
)
}

export function HTMLEditor({
value,
onChange,
showScriptWarning,
writingHTMLDescription,
setWritingHTMLDescription,
}: {
value?: string
onChange: (value: any) => void
writingHTMLDescription: boolean
setWritingHTMLDescription: (writingHTML: boolean) => void
showScriptWarning: boolean
}): JSX.Element {
return (
<>
<LemonTabs
activeKey={writingHTMLDescription ? 'html' : 'text'}
onChange={(key) => setWritingHTMLDescription(key === 'html')}
tabs={[
{
key: 'text',
label: <span className="text-sm">Text</span>,
content: (
<LemonTextArea
data-attr="survey-description"
neilkakkar marked this conversation as resolved.
Show resolved Hide resolved
minRows={2}
value={value}
onChange={(v) => onChange(v)}
/>
),
},
{
key: 'html',
label: <span className="text-sm">HTML</span>,
content: (
<div>
<CodeEditor
className="border"
language="html"
value={value}
onChange={(v) => onChange(v ?? '')}
height={150}
options={{
minimap: {
enabled: false,
},
wordWrap: 'on',
scrollBeyondLastLine: false,
automaticLayout: true,
fixedOverflowWidgets: true,
lineNumbers: 'off',
glyphMargin: false,
folding: false,
}}
/>
</div>
),
},
]}
/>
{showScriptWarning && (
<LemonBanner type="warning">
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.
</LemonBanner>
)}
</>
)
}
9 changes: 9 additions & 0 deletions frontend/src/scenes/surveys/surveyLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,10 @@ function sanitizeQuestions(surveyPayload: Partial<Survey>): Partial<Survey> {
if (!surveyPayload.questions) {
return surveyPayload
}

const sanitizedThankYouHeader = sanitize(surveyPayload.appearance?.thankYouMessageHeader || '')
const sanitizedThankYouDescription = sanitize(surveyPayload.appearance?.thankYouMessageDescription || '')

return {
...surveyPayload,
questions: surveyPayload.questions?.map((rawQuestion) => {
Expand All @@ -796,5 +800,10 @@ function sanitizeQuestions(surveyPayload: Partial<Survey>): Partial<Survey> {
question: sanitize(rawQuestion.question || ''),
}
}),
appearance: {
...surveyPayload.appearance,
...(sanitizedThankYouHeader && { thankYouMessageHeader: sanitizedThankYouHeader }),
...(sanitizedThankYouDescription && { thankYouMessageDescription: sanitizedThankYouDescription }),
},
}
}
Loading
Loading