diff --git a/.changeset/float-teaser-dismissible-react.md b/.changeset/float-teaser-dismissible-react.md new file mode 100644 index 0000000..fb92032 --- /dev/null +++ b/.changeset/float-teaser-dismissible-react.md @@ -0,0 +1,8 @@ +--- +"@perspective-ai/sdk-react": patch +"@perspective-ai/sdk": patch +--- + +Forward the `teaser.dismissible` option through `FloatBubble` / `useFloatBubble` to the core SDK. The hook that stabilizes the teaser prop only carried `enabled`, `delay`, and `sound`, so it silently dropped `dismissible` — setting `teaser={{ dismissible: false }}` had no effect when using the React package (the × dismiss button still rendered). It is now forwarded alongside the other teaser fields. + +Also corrects the `EmbedConfig.teaser` JSDoc to list `dismissible` among the fields it controls. The vanilla config, CDN `data-perspective-teaser-dismissible` attribute, and core resolution already handled `dismissible` correctly; only the React wrapper was dropping it. diff --git a/packages/sdk-react/src/hooks/useFloatBubble.test.ts b/packages/sdk-react/src/hooks/useFloatBubble.test.ts index 019207a..71ead81 100644 --- a/packages/sdk-react/src/hooks/useFloatBubble.test.ts +++ b/packages/sdk-react/src/hooks/useFloatBubble.test.ts @@ -64,6 +64,34 @@ describe("useFloatBubble", () => { expect(mockUnmount).toHaveBeenCalledTimes(1); }); + it("forwards all teaser fields to createFloatBubble", async () => { + const { unmount } = renderHook(() => + useFloatBubble({ + researchId: "test-research-id", + teaser: { + enabled: true, + delay: 2000, + sound: false, + dismissible: false, + }, + }) + ); + await act(async () => {}); + + expect(mockCreateFloatBubble).toHaveBeenCalledWith( + expect.objectContaining({ + teaser: { + enabled: true, + delay: 2000, + sound: false, + dismissible: false, + }, + }) + ); + + unmount(); + }); + it("returns expected interface", async () => { const { result, unmount } = renderHook(() => useFloatBubble({ researchId: "test-research-id" }) diff --git a/packages/sdk-react/src/hooks/useFloatBubble.ts b/packages/sdk-react/src/hooks/useFloatBubble.ts index dd76bbd..4247f46 100644 --- a/packages/sdk-react/src/hooks/useFloatBubble.ts +++ b/packages/sdk-react/src/hooks/useFloatBubble.ts @@ -115,13 +115,19 @@ export function useFloatBubble( const teaserEnabled = teaser?.enabled; const teaserDelay = teaser?.delay; const teaserSound = teaser?.sound; + const teaserDismissible = teaser?.dismissible; const hasTeaser = teaser !== undefined; const stableTeaser = useMemo( (): EmbedConfig["teaser"] => hasTeaser - ? { enabled: teaserEnabled, delay: teaserDelay, sound: teaserSound } + ? { + enabled: teaserEnabled, + delay: teaserDelay, + sound: teaserSound, + dismissible: teaserDismissible, + } : undefined, - [hasTeaser, teaserEnabled, teaserDelay, teaserSound] + [hasTeaser, teaserEnabled, teaserDelay, teaserSound, teaserDismissible] ); // Resolve ReactNode icons to SVG strings for the core SDK diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 4121cf9..8884667 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -149,7 +149,7 @@ export interface EmbedConfig { channel?: AIAssistantChannel | AIAssistantChannel[] | null; /** Welcome message shown as a teaser bubble next to the float button. Only used for float-type embeds. */ welcomeMessage?: string; - /** Controls if and when the welcome teaser appears (`enabled`, `delay`, `sound`). Only used for float-type embeds. */ + /** Controls if and when the welcome teaser appears (`enabled`, `delay`, `sound`, `dismissible`). Only used for float-type embeds. */ teaser?: TeaserConfig; /** Custom button text for popup/slider triggers */ buttonText?: string;