Skip to content

feat(surveys): add intro screen to the hosted survey canvas - #116329

Merged
trunk-io[bot] merged 2 commits into
lucas/survey-intro-wizardfrom
lucas/survey-intro-hosted
Oct 14, 2026
Merged

trunk-io[bot] merged 2 commits into
lucas/survey-intro-wizardfrom
lucas/survey-intro-hosted

Conversation

@lucasheriques

@lucasheriques lucasheriques commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Hosted survey authors can now add and edit an intro page in the hosted survey canvas. The canvas sits behind the surveys-hosted-editor flag. Builds on #116326.

Changes

  • The flow rail lists Intro, then the questions, then Confirmation. The intro page edits inline on the canvas, and the settings panel can remove it.
  • HTML intro descriptions render through sanitizeHTML, the same path the confirmation canvas uses.
  • Fix: editing the intro or confirmation on the canvas while a translation language was active wrote that language's strings into the base appearance. Both canvases now write against the raw survey. The confirmation canvas had this bug on master too.
  • Mechanical: HostedSurveyPageKind (intro | question | confirmation) replaces the isConfirmation boolean. Progress keys off it, and the public hosted page gets intro styles.

Hosted canvas intro

New surface, so there are no before screenshots.

How did you test this code?

  • On a devbox with the flag overridden: opened a hosted survey and selected Intro in the rail, and the canvas rendered the saved intro.
  • I didn't drive the translated-canvas edit in the browser. The unit test below covers it.
  • typescript:check and jest src/scenes/surveys: pass.

Test rationale:

  • HostedSurveyCanvas.test.tsx renders the canvas with a French preview, edits the intro and confirmation headers, and checks the other fields keep their base text. It fails without the fix.
  • hostedSurveyPageKind tests catch the canvas misclassifying the intro or confirmation page.

Release status

  • This change is behind a feature flag and is not available to users

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5 (planning, review, verification) and Claude Sonnet 5.5 (implementation)

  • Rebuilt from the closed feat(surveys): add intro screen configuration, editor, and translations #78445, which conflicted with master, as a four-layer stack with each layer under 700 changed lines.
  • Skills used: /writing-ui-components, /writing-user-facing-copy, /writing-tests, /writing-code-comments, /adding-activity-logging, /stacking-prs, /writing-pr-descriptions.
  • Pre-PR gate: adversarial review by two models. The fixes it prompted are folded into the layers: hosted canvas translation leak, SDK content-type default, empty-intro warning, wizard preview jump, and translatable default button text.
  • No duplicate: no open PR adds the intro screen editor.

@lucasheriques
lucasheriques added this pull request to stack #116330 October 13, 2026 15:11
@lucasheriques lucasheriques self-assigned this Oct 13, 2026
@lucasheriques
lucasheriques marked this pull request as ready for review October 13, 2026 15:12
@parameterai

parameterai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Risk: No findings

This delta pins introScreenDescriptionContentType: 'text' when an author types an intro description on the hosted survey canvas, so plain-text edits no longer fall back to HTML rendering, plus a regression test. I traced every rendering path the pinning touches (the DOMPurify-sanitized HTML branch, the React-escaped InlineEditable text branch, sanitizeSurveyAppearance's text passthrough, and the hosted public page) and found no new vulnerability — the change strictly narrows how typed content can be interpreted.

Sentinel reviewed 9ae4956 · Review settings

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

✅ Trunk lane — non-backend lane

This PR is assigned to the non-backend lane. It does not run backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Complexity (TypeScript) — 4 functions above the limit (max 18)

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

Function Location Complexity Limit
buildCanvasStyles frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx:115 18 10
QuestionCanvas frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx:219 15 10
HostedSurveySettingsPanel frontend/src/scenes/surveys/hosted-canvas/HostedSurveySettingsPanel.tsx:66 15 10
HostedSurveyEdit frontend/src/scenes/surveys/HostedSurveyEdit.tsx:437 11 10
✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

⚠️ Duplication (TypeScript) — 1 new duplicated block (worst 93 tokens)

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx:657 frontend/src/scenes/surveys/hosted-canvas/IntroCanvas.tsx:12 11 93
⚠️ Bundle size — 🔺 +51 B (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 75.87 MiB · 🔺 +51 B (+0.0%)

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

✅ Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1005.8 KiB · 23 files no change █████████░ 85.8% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.87 MiB · 606 files no change █████████░ 94.5% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.33 MiB · 2,533 files no change ██████████ 95.9% of 7.64 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.57 MiB · 3,618 files no change █████████░ 93.0% of 10.30 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.35 MiB · 2,543 files no change █████████░ 93.2% of 7.89 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.15 MiB · 3,446 files no change █████████░ 92.7% of 9.88 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
11.93 MiB · 4,287 files no change ████████░░ 79.3% of 15.05 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
1.3 KiB src/index.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
839 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
28.7 KiB src/scenes/scenes.ts
27.6 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.2 KiB ../packages/quill/packages/quill/dist/index.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.2 KiB ../packages/quill/packages/quill/dist/index.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.2 KiB ../packages/quill/packages/quill/dist/index.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.2 KiB ../packages/quill/packages/quill/dist/index.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.0 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.2 KiB ../packages/quill/packages/quill/dist/index.js
103.2 KiB src/products.tsx
99.6 KiB src/lib/api.ts

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

✅ Toolbar bundle — eager 2.23 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.23 MiB · 19 files no change ████░░░░░░ 39.0% of 5.72 MiB
Deferred (lazy) 2.10 MiB · 40 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
874.8 KiB dist/toolbar/toolbar-app-3ZTHQS4E.css
664.0 KiB dist/toolbar/chunk-chunk-BGAZTCLU.js
257.8 KiB dist/toolbar/chunk-chunk-IVAG7WAO.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
129.6 KiB dist/toolbar/chunk-chunk-6OD756QW.js
74.7 KiB dist/toolbar/toolbar-app-VYQLG45N.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-3II7CUPB.js
21.7 KiB dist/toolbar/chunk-chunk-II4PNXNA.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — 🔺 +32.3 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1009.90 MiB · 🔺 +32.3 KiB (+0.0%)

⚠️ Playwright — 2 flaky

🎭 Playwright report · View test results →

⚠️ 2 flaky tests:

  • create experiment via wizard, add metrics, and launch (chromium)
  • Materialize view pane (chromium)

These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!

✅ Hogbox preview — ready, open the preview

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds: a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit cbf14bf · box box-7164b0d9f5a6 · ready in 1215s (push → usable) · build log · rebuilds on every push, torn down on close

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 2da5c0a: 2 Should fix.

Comment thread frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx Outdated
Comment thread frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx Outdated
@greptile-apps

greptile-apps Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[Medium impact] Adds intro screen feature to hosted survey canvas.

Split the new components into separate files before merging to meet the frontend guide.

Reviews (1) · Last reviewed commit: "feat(surveys): add intro screen to the h..." · Reviewed by Greptile

Comment thread frontend/src/scenes/surveys/hosted-canvas/HostedSurveyCanvas.tsx Outdated
@trunk-io

trunk-io Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
Scenes-App/Experiments ExperimentWithHealthFindings play-test The Storybook test could not find the expected text "Why?" because the scene remained stuck on its loading spinner. Logs ↗︎
Experiment lifecycle › creation and launch › create experiment via wizard, add metrics, and launch The test failed because waiting for the URL to change timed out after 15 seconds. Logs ↗︎
SQL Editor › Basic flow › Materialize view pane The test failed because the expected element was not visible. Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@lucasheriques
lucasheriques force-pushed the lucas/survey-intro-hosted branch from 2da5c0a to a82cf8c Compare October 13, 2026 16:19
@lucasheriques lucasheriques added the stamphog Request AI approval (no full review) label Oct 13, 2026
stamphog[bot]

This comment was marked as outdated.

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed e63e448: 1 Should fix.

Comment thread frontend/src/scenes/surveys/hosted-canvas/IntroCanvas.tsx Outdated
@posthog

posthog Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

✅ Visual changes approved by @lucasheriques — baseline updated in c582edd.

View this run in PostHog

1 changed.

Install the Visual Review Chrome extension to see visual review results at the top of your pull requests.

@stamphog
stamphog Bot dismissed their stale review October 13, 2026 17:02

A new stamphog review started for this PR — the fresh verdict replaces this approval.

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

Strong author familiarity provides assurance for the security-sensitive rendering change, but @PostHog's open editing concern remains in the current code. Set the description content type to text when committing plain-text edits in IntroCanvas.tsx, add a regression test for editing twice, and re-request review.

  • Author wrote 92% of the modified lines and has 92 merged PRs in these paths (familiarity STRONG).
  • PostHog Review (Standard) reviewed the current head: no findings.
  • 👍 on the PR from greptile-apps[bot].
  • @PostHog — frontend/src/scenes/surveys/hosted-canvas/IntroCanvas.tsx: the open concern remains unfixed. Committing an initially empty description updates only its text, preserving an unset or HTML content type; the helper defaults an unset type to HTML, so the newly nonempty description switches to the non-editable rendered branch.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 271L, 8F substantive, 484L/11F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (484L, 11F, single-area, feat)
stamphog 2.5.0 .stamphog/policy.yml @ c582edd · reviewed head c582edd

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 13, 2026
@lucasheriques
lucasheriques force-pushed the lucas/survey-intro-hosted branch from c582edd to 9ae4956 Compare October 13, 2026 19:15
stamphog[bot]

This comment was marked as outdated.

@trunk-io
trunk-io Bot force-pushed the lucas/survey-intro-hosted branch from 9ae4956 to 3e94472 Compare October 13, 2026 20:01
@lucasheriques
lucasheriques force-pushed the lucas/survey-intro-hosted branch from 3e94472 to 8d61813 Compare October 13, 2026 20:27
@lucasheriques
lucasheriques force-pushed the lucas/survey-intro-hosted branch from 8d61813 to d38b3e7 Compare October 13, 2026 20:36
@stamphog
stamphog Bot dismissed their stale review October 13, 2026 20:36

A new stamphog review started for this PR — the fresh verdict replaces this approval.

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

The HTML-rendering surface has independent current-head assurance and a STRONG-familiarity author; sanitization is verified in the source. No substantive unresolved concerns were provided; Greptile's component-organization request is stylistic rather than blocking.

  • Author wrote 94% of the modified lines and has 55 merged PRs in these paths (familiarity STRONG).
  • PostHog Review (Standard) reviewed the current head: no findings.
  • 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 274L, 7F substantive, 522L/10F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (522L, 10F, single-area, feat)
stamphog 2.5.0 .stamphog/policy.yml @ d38b3e7 · reviewed head d38b3e7

@trunk-io

trunk-io Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

😎 Stack merged successfully - details.

@lucasheriques

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io

trunk-io Bot commented Oct 13, 2026

Copy link
Copy Markdown

This PR is already queued as a stacked merge. Cancel it first to re-submit.

What: the hosted survey editor gets an "Intro" entry at the top of the flow rail (or an "Add intro screen" button), an IntroCanvas with inline-editable header, description and button label, and intro settings in the right panel with a remove action. hosted-survey.css and the canvas SCSS style the .intro-screen, .intro-screen-header and .intro-screen-body classes that the SDK renders. An HTML intro description is shown through the same sanitizeHTML path as the confirmation canvas, never raw.

Why: hosted surveys render the same SDK intro screen, and the canvas editor had no way to author or preview it. The canvas previously told its two screen kinds apart with an isConfirmation boolean. A third kind made two exclusive booleans, so the canvas and settings panel now take one HostedSurveyPageKind ('intro' | 'question' | 'confirmation') from getHostedSurveyPageKind. The rail's intro and confirmation entries share a small HostedSurveyRailScreen button.

Tested:
- pnpm --filter=@posthog/frontend typescript:check: no errors
- jest src/scenes/surveys: 26 suites / 461 tests pass, including the new parameterized getHostedSurveyPageKind cases
- oxlint and oxfmt on changed files: clean

Notes for the reviewer: not driven in a browser (no dev stack here), so the hosted canvas styling is unchecked visually.
Typing a description into an intro whose content type was unset left the type
unset, which renders as HTML, so the canvas swapped the field for the read-only
HTML view. Canvas edits are plain text, so they now pin the type to text.

Tested: jest src/scenes/surveys/hosted-canvas passes; the new two-edit test fails
without the fix.
@lucasheriques
lucasheriques force-pushed the lucas/survey-intro-hosted branch from c3a4d12 to cbf14bf Compare October 14, 2026 03:52
@trunk-io
trunk-io Bot merged commit 8d82166 into master Oct 14, 2026
227 checks passed
@trunk-io
trunk-io Bot deleted the lucas/survey-intro-hosted branch October 14, 2026 04:40
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-14 04:58 UTC Run
prod-us ✅ Deployed 2026-10-14 05:10 UTC Run
prod-eu ✅ Deployed 2026-10-14 05:09 UTC Run

This branch was successfully deployed

1 active deployment
hogbox-preview — cbf14bf3 Deployed Oct 14, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant