Skip to content

feat(js,js-components): survey support, Survey Popover component#629

Open
VojtechVidra wants to merge 13 commits intomainfrom
feat/js-survey
Open

feat(js,js-components): survey support, Survey Popover component#629
VojtechVidra wants to merge 13 commits intomainfrom
feat/js-survey

Conversation

@VojtechVidra
Copy link
Copy Markdown
Member

@VojtechVidra VojtechVidra commented Apr 10, 2026

Pull request checklist

  • Create and update TSDoc and export relevant TypeScript types
  • Write test for the new/updated features if possible
  • Test manually each SDK for larger UI changes
  • Link issue to this PR
  • Review docs and add or update as needed

@VojtechVidra VojtechVidra linked an issue Apr 10, 2026 that may be closed by this pull request
@VojtechVidra VojtechVidra requested a review from OPesicka April 16, 2026 11:49
@VojtechVidra VojtechVidra marked this pull request as ready for review April 16, 2026 11:49
@VojtechVidra VojtechVidra requested a review from Copilot April 16, 2026 11:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds cross-SDK survey support (JS + JS Components) and introduces a Survey Popover UI component, while centralizing shared survey constants/helpers and expanding E2E coverage beyond React.

Changes:

  • Introduce shared survey utilities/constants (sessionStorage helpers, defaults, animations, answered-check helper) and export them from @flows/shared.
  • Add JS SDK survey runtime (trigger detection, running survey state, API submit plumbing) and wire it into block rendering/click handling.
  • Add @flows/js-components Survey Popover (Lit) and update setup/exports so survey components can be registered and rendered; expand E2E survey/page-targeting coverage to JS.

Reviewed changes

Copilot reviewed 56 out of 57 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
workspaces/shared/src/survey.ts Adds shared survey constants + storage helpers + answered-check helper.
workspaces/shared/src/index.ts Re-exports shared survey helpers/constants.
workspaces/react/src/lib/active-block.ts Adds shared item→active-block mapping supporting survey blocks.
workspaces/react/src/index.ts Reorganizes exported types (grouping/comments).
workspaces/react/src/hooks/use-running-surveys.ts Switches to shared sessionStorage helpers for running surveys.
workspaces/react/src/hooks/use-current-blocks.ts Refactors to use itemToActiveBlock mapping.
workspaces/react-components/src/survey-components/survey-popover/use-survey-popover.ts Replaces local timing constants with shared constants.
workspaces/react-components/src/survey-components/survey-popover/survey-popover.tsx Uses shared default labels/position; adjusts question context reset.
workspaces/react-components/src/survey-components/survey-popover/survey-next-button.tsx Uses shared isSurveyQuestionAnswered.
workspaces/react-components/src/survey-components/survey-popover/single-choice-input.tsx Extracts click handler; updates Other option usage.
workspaces/react-components/src/survey-components/survey-popover/rating-input.tsx Uses shared emojis; switches hover handlers to pointer events.
workspaces/react-components/src/survey-components/survey-popover/other-option.tsx Improves focus behavior; uses shared “Other” label; pointer events.
workspaces/react-components/src/survey-components/survey-popover/multiple-choice-input.tsx Extracts click handler; updates Other option usage.
workspaces/react-components/src/survey-components/survey-popover/freeform-input.tsx Uses shared default placeholder constant.
workspaces/react-components/src/survey-components/survey-popover/end-screen.tsx Uses shared auto-close timeout constant; minor href cleanup.
workspaces/react-components/src/internal-components/text.tsx Narrows as to `"legend"
workspaces/react-components/src/internal-components/input.tsx Attempts to add ref support to Input component.
workspaces/js/src/survey.ts Adds survey trigger/run logic using signals + MutationObserver + click handler.
workspaces/js/src/store.ts Adds runningSurveyIds signal initialized from session storage.
workspaces/js/src/methods.ts Refactors slot block mapping via itemToActiveBlock.
workspaces/js/src/lib/click.ts Wires survey click handler into document click handling.
workspaces/js/src/lib/api.ts Adds postSurvey API method for submitting survey answers.
workspaces/js/src/lib/active-block.ts Adds survey active-block conversion + refactors setStateMemory.
workspaces/js/src/index.ts Exports survey-related types from shared for JS SDK consumers.
workspaces/js/src/debug/panels/settings-panel.ts Adds CSS class to version label.
workspaces/js/src/computed.ts Filters visible survey blocks by runningSurveyIds; refactors floating items mapping.
workspaces/js-components/tsup.config.ts Adds survey-components entry point to build.
workspaces/js-components/src/types/components.ts Adds SurveyComponent/SurveyComponents types.
workspaces/js-components/src/survey-components/survey-popover/survey-popover.ts New Lit Survey Popover implementation.
workspaces/js-components/src/survey-components/survey-popover/survey-next-button.ts New Survey Next button (Lit) using shared answered-check helper.
workspaces/js-components/src/survey-components/survey-popover/single-choice-input.ts New Lit single-choice input.
workspaces/js-components/src/survey-components/survey-popover/rating-input.ts New Lit rating input (stars/numbers/emojis).
workspaces/js-components/src/survey-components/survey-popover/question-context.ts New global signals-based question “context”.
workspaces/js-components/src/survey-components/survey-popover/other-option.ts New Lit “Other” option rendering + focus management.
workspaces/js-components/src/survey-components/survey-popover/multiple-choice-input.ts New Lit multiple-choice input.
workspaces/js-components/src/survey-components/survey-popover/index.ts Exports the survey-popover component.
workspaces/js-components/src/survey-components/survey-popover/freeform-input.ts New Lit freeform input (textarea).
workspaces/js-components/src/survey-components/survey-popover/end-screen.ts New Lit end screen rendering.
workspaces/js-components/src/survey-components/index.ts Re-exports survey components.
workspaces/js-components/src/survey-components.ts Adds top-level survey-components barrel export.
workspaces/js-components/src/render.ts Extends setup options + registration to include survey components.
workspaces/js-components/src/internal-components/text.ts Adds as/id support using lit/static-html.
workspaces/js-components/src/internal-components/input.ts Adds internal Input helper (Lit) used by survey components.
workspaces/js-components/src/internal-components/button.ts Adds disabled support + refactors class building/closing tag.
workspaces/js-components/src/icons/star-filled-16.ts Adds star-filled icon (Lit) for rating display.
workspaces/js-components/src/icons/star-empty-16.ts Adds star-empty icon (Lit) for rating display.
workspaces/js-components/src/components-store.ts Adds surveyComponents registry.
workspaces/js-components/src/block.ts Adds survey component resolution for rendering active blocks.
workspaces/js-components/package.json Adds survey-components export + new dependencies.
workspaces/e2e/tests/tour-trigger.spec.ts Removes React-only survey skip to enable multi-SDK coverage.
workspaces/e2e/tests/survey.spec.ts Enables running survey tests for JS; adjusts URL expectations.
workspaces/e2e/tests/page-targeting.spec.ts Removes React-only survey skip; minor formatting.
workspaces/e2e/pages/js.ts Registers surveyComponents in JS E2E page setup.
pnpm-lock.yaml Updates lockfile for new deps + tool version bumps.
package.json Bumps oxlint/oxfmt tool versions.
cspell.json Adds “describedby”/“labelledby” words.
.oxlintrc.json Enables type-aware linting.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread workspaces/shared/src/survey.ts
Comment thread workspaces/js-components/src/survey-components/survey-popover/survey-popover.ts Outdated
Comment thread workspaces/react-components/src/internal-components/input.tsx
Comment on lines +13 to +35
export const currentQuestionValue = signal<SurveyQuestion>();

export const questionToContextValue = (question: SurveyQuestion): ContextData => ({
value: "getValue" in question ? question.getValue() : undefined,
optionIds: "getSelectedOptionIds" in question ? question.getSelectedOptionIds() : [],
otherSelected: "getOtherSelected" in question ? question.getOtherSelected() : false,
});

export const getDefaultQuestionState = (): ContextData => ({
value: "",
optionIds: [],
otherSelected: false,
});

export const questionState = signal<ContextData>(getDefaultQuestionState());

const refresh = (): void => {
const currentQuestion = currentQuestionValue.value;
if (currentQuestion) {
questionState.value = questionToContextValue(currentQuestion);
} else {
questionState.value = getDefaultQuestionState();
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The question context signals (currentQuestionValue, questionState) are module-level singletons, so multiple SurveyPopover instances rendered at the same time will overwrite each other’s state (answers/selection can bleed between surveys). Make the context state instance-scoped (e.g., stored on the SurveyPopover element and passed down) rather than shared globals.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +92
connectedCallback(): void {
super.connectedCallback();

this._questionIndex = this.survey.getCurrentQuestionIndex();
this.popoverElement?.addEventListener("transitionend", this.handleTransitionEnd.bind(this));
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

popoverElement is queried from the rendered DOM, so it won’t be available in connectedCallback; the transitionend listener likely never attaches. Attach the listener in firstUpdated/updated once popoverElement exists, and remove the same bound handler in disconnectedCallback to avoid leaks.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +19
const inputRef = createRef();

export const OtherOption = ({ question }: Props) => {
const { value, otherSelected, refresh } = useQuestionContext();
const type = question.type === "single-choice" ? "radio" : "checkbox";
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

inputRef is declared at module scope, so all OtherOption instances share the same ref and can focus the wrong input when multiple surveys/questions render. Create the ref inside OtherOption so each instance has its own reference.

Suggested change
const inputRef = createRef();
export const OtherOption = ({ question }: Props) => {
const { value, otherSelected, refresh } = useQuestionContext();
const type = question.type === "single-choice" ? "radio" : "checkbox";
export const OtherOption = ({ question }: Props) => {
const { value, otherSelected, refresh } = useQuestionContext();
const type = question.type === "single-choice" ? "radio" : "checkbox";
const inputRef = createRef<HTMLInputElement>();

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +29
const hoverIndexSignal = signal<number | null>(null);

export const RatingInput = ({
descriptionId,
legendId,
onAnswer,
question,
}: Props): TemplateResult => {
const { value, refresh } = useQuestionContext();
const hoverIndex = hoverIndexSignal.value;

Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

hoverIndexSignal is module-scoped, so hover state will be shared across all RatingInput instances (and across multiple concurrent surveys). Make this hover state instance-scoped (e.g., per SurveyPopover / per RatingInput invocation) to prevent cross-popover interference.

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 48
export const Button = ({
className,
className: classNameProp,
variant,
size = "default",
children,
onClick,
href,
target,
disabled,
}: Props): TemplateResult => {
const tag = href ? aLiteral : buttonLiteral;

const className = clsx(
"flows_basicsV2_button",
`flows_basicsV2_button_${variant}`,
`flows_basicsV2_button_size_${size}`,
disabled && "flows_basicsV2_button_disabled",
classNameProp,
);

return html`
<${tag}
type=${tag === buttonLiteral ? "button" : undefined}
class=${clsx(
"flows_basicsV2_button",
`flows_basicsV2_button_${variant}`,
`flows_basicsV2_button_size_${size}`,
className,
)}
class=${className}
@click=${onClick}
target=${target}
href=${href}
?disabled=${disabled}
>
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

disabled doesn’t behave on <a> elements, so when href is set this won’t prevent navigation or clicks (it only adds a class/attribute). If disabled is true, consider rendering a <button> (no href) or suppressing the click/default action and removing href/target.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Survey Popover component to JS

3 participants