diff --git a/packages/browser-sdk/package.json b/packages/browser-sdk/package.json index d7d90b83..77886862 100644 --- a/packages/browser-sdk/package.json +++ b/packages/browser-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@reflag/browser-sdk", - "version": "1.4.0", + "version": "1.4.1", "packageManager": "yarn@4.1.1", "license": "MIT", "repository": { diff --git a/packages/browser-sdk/src/feedback/ui/FeedbackDialog.css b/packages/browser-sdk/src/feedback/ui/FeedbackDialog.css index 0de0c6a4..117aa074 100644 --- a/packages/browser-sdk/src/feedback/ui/FeedbackDialog.css +++ b/packages/browser-sdk/src/feedback/ui/FeedbackDialog.css @@ -1,6 +1,7 @@ .dialog { position: fixed; width: 210px; + overflow: visible; padding: 16px 22px 10px; font-size: var(--reflag-feedback-dialog-font-size, 1rem); font-family: var( diff --git a/packages/browser-sdk/src/feedback/ui/index.ts b/packages/browser-sdk/src/feedback/ui/index.ts index 62275b4a..600d8d26 100644 --- a/packages/browser-sdk/src/feedback/ui/index.ts +++ b/packages/browser-sdk/src/feedback/ui/index.ts @@ -11,6 +11,14 @@ export const DEFAULT_POSITION: Position = { placement: "bottom-right", }; +function supportsPopoverApi() { + return ( + typeof HTMLElement !== "undefined" && + "showPopover" in HTMLElement.prototype && + "hidePopover" in HTMLElement.prototype + ); +} + function stopPropagation(e: Event) { e.stopPropagation(); } @@ -39,9 +47,18 @@ function attachDialogContainer() { let openInstances = 0; export function openFeedbackForm(options: OpenFeedbackFormOptions): void { - const shadowRoot = attachDialogContainer(); const position = options.position || DEFAULT_POSITION; + if (position.type !== "MODAL" && !supportsPopoverApi()) { + console.warn( + "[Reflag]", + "Unable to open feedback popup. Popover API is not supported in this browser", + ); + return; + } + + const shadowRoot = attachDialogContainer(); + if (position.type === "POPOVER") { if (!position.anchor) { console.warn( diff --git a/packages/browser-sdk/src/ui/Dialog.css b/packages/browser-sdk/src/ui/Dialog.css index e88767f8..c979e1d1 100644 --- a/packages/browser-sdk/src/ui/Dialog.css +++ b/packages/browser-sdk/src/ui/Dialog.css @@ -42,7 +42,7 @@ margin: auto; margin-top: 4rem; - &[open] { + &.open { animation: /* easeOutQuint */ scale 100ms cubic-bezier(0.22, 1, 0.36, 1), fade 100ms cubic-bezier(0.22, 1, 0.36, 1); @@ -59,7 +59,7 @@ position: absolute; margin: 0; - &[open] { + &.open { animation: /* easeOutQuint */ scale 100ms cubic-bezier(0.22, 1, 0.36, 1), fade 100ms cubic-bezier(0.22, 1, 0.36, 1); @@ -92,7 +92,7 @@ /* Unanchored */ -.dialog[open].unanchored { +.dialog.open.unanchored { &.unanchored-bottom-left, &.unanchored-bottom-right { animation: /* easeOutQuint */ diff --git a/packages/browser-sdk/src/ui/Dialog.tsx b/packages/browser-sdk/src/ui/Dialog.tsx index 83bd1ee9..64b0b85b 100644 --- a/packages/browser-sdk/src/ui/Dialog.tsx +++ b/packages/browser-sdk/src/ui/Dialog.tsx @@ -81,7 +81,7 @@ export const Dialog: FunctionComponent = ({ showArrow = true, }) => { const arrowRef = useRef(null); - const dialogRef = useRef(null); + const dialogRef = useRef(null); const anchor = position.type === "POPOVER" ? position.anchor : null; const placement = @@ -173,23 +173,56 @@ export const Dialog: FunctionComponent = ({ // eslint-disable-next-line react-hooks/exhaustive-deps -- anchor only exists in popover }, [position.type, close, (position as any).anchor, dismiss, containerId]); - function setDiagRef(node: HTMLDialogElement | null) { + function setDiagRef(node: HTMLElement | null) { refs.setFloating(node); dialogRef.current = node; } useEffect(() => { if (!dialogRef.current) return; - if (isOpen && !dialogRef.current.hasAttribute("open")) { - dialogRef.current[position.type === "MODAL" ? "showModal" : "show"](); + + const isPopoverOpen = () => { + try { + return dialogRef.current?.matches(":popover-open") ?? false; + } catch { + return false; + } + }; + + const isModalOpen = + dialogRef.current instanceof HTMLDialogElement && + dialogRef.current.hasAttribute("open"); + + if ( + isOpen && + ((position.type === "MODAL" && !isModalOpen) || + (position.type !== "MODAL" && !isPopoverOpen())) + ) { + if ( + position.type === "MODAL" && + dialogRef.current instanceof HTMLDialogElement + ) { + dialogRef.current.showModal(); + } else { + dialogRef.current.showPopover(); + } } - if (!isOpen && dialogRef.current.hasAttribute("open")) { - dialogRef.current.close(); + if (!isOpen) { + if ( + position.type === "MODAL" && + dialogRef.current instanceof HTMLDialogElement && + dialogRef.current.hasAttribute("open") + ) { + dialogRef.current.close(); + } else if (position.type !== "MODAL" && isPopoverOpen()) { + dialogRef.current.hidePopover(); + } } }, [dialogRef, isOpen, position.type]); const classes = [ "dialog", + isOpen ? "open" : "", position.type === "MODAL" ? "modal" : position.type === "POPOVER" @@ -201,21 +234,40 @@ export const Dialog: FunctionComponent = ({ return ( <>