Skip to main content
userTourKit
@tour-kit/surveysComponents

SurveyPopover

Floating panel anchored to a DOM element via Floating UI, with automatic flip and shift collision avoidance

domidex01Published Updated

SurveyPopover positions a survey panel adjacent to a target element using @floating-ui/react. It renders into a portal and returns null when neither visible nor anchored.

Import

import { SurveyPopover } from '@tour-kit/surveys';

Usage

With an element ref

import { useRef } from 'react';
import { SurveyPopover, QuestionRating, useSurvey } from '@tour-kit/surveys';

function FeatureRating() {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const survey = useSurvey('feature-rating');

  return (
    <>
      <button
        ref={buttonRef}
        onClick={survey.show}
        data-survey-popover-anchor="feature-rating"
      >
        Rate this feature
      </button>

      <SurveyPopover
        surveyId="feature-rating"
        anchor={buttonRef.current}
        options={{ position: 'bottom-right', offset: 8 }}
      >
        <QuestionRating
          id="rating"
          label="How useful is this feature?"
          min={1}
          max={5}
          style="stars"
          onChange={(value) => {
            survey.answer('rating', value);
            survey.complete();
          }}
        />
      </SurveyPopover>
    </>
  );
}

With a CSS selector

<SurveyPopover
  surveyId="feature-rating"
  anchorSelector="#rate-button"
  options={{ position: 'top-right' }}
>
  {/* content */}
</SurveyPopover>

Auto anchor via data attribute

If neither anchor nor anchorSelector is provided, the popover looks for [data-survey-popover-anchor="<surveyId>"] in the document.

Props

Prop

Type

PopoverOptions

Prop

Type

The popover renders null if the survey is visible but no anchor can be resolved (no anchor prop, no anchorSelector match, and no [data-survey-popover-anchor] element). Ensure the anchor element is in the DOM before calling survey.show().

Collision avoidance

The popover uses Floating UI middleware in order: offsetflipshift({ padding: 8 }). The panel will flip to the opposite side if the preferred placement overflows the viewport, and will shift along the axis to keep within the viewport with 8px of padding.