From 33311f937e534d0bc75c8286c769cbd39ea61f2c Mon Sep 17 00:00:00 2001 From: albertfolch-redeemeum <102516373+albertfolch-redeemeum@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:13:56 +0200 Subject: [PATCH] feat: close tracing dropdown on clicking away (#42) --- e2e-tests/Landing.spec.ts | 22 +++++++++++++ src/components/header/Settings.tsx | 50 +++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/e2e-tests/Landing.spec.ts b/e2e-tests/Landing.spec.ts index c3d5dc03d..bd1b9d036 100644 --- a/e2e-tests/Landing.spec.ts +++ b/e2e-tests/Landing.spec.ts @@ -73,6 +73,28 @@ test.describe("Root page (Landing page)", () => { await expect(headerDropdown).not.toBeVisible(); }); + test("should close opened tracing dropdown when clicking away", async ({ + page + }) => { + await mockSubgraph({ + page + }); + await page.goto("/"); + const settings = page.locator("[data-testid=settings]"); + + await expect(settings).toBeVisible(); + + await settings.click(); + + const headerDropdown = page.locator("[data-testid=header-dropdown]"); + + await expect(headerDropdown).toBeVisible(); + await page.pause(); + const h2 = page.locator("h2"); + await h2.click(); + + await expect(headerDropdown).not.toBeVisible(); + }); test("should display an error when typing a wrong url into the tracing url dropdown", async ({ page }) => { diff --git a/src/components/header/Settings.tsx b/src/components/header/Settings.tsx index 021aed032..13926d5e2 100644 --- a/src/components/header/Settings.tsx +++ b/src/components/header/Settings.tsx @@ -1,6 +1,6 @@ import * as Sentry from "@sentry/react"; import { BrowserTracing } from "@sentry/tracing"; -import React, { useEffect, useReducer, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { IoIosSave, IoMdSettings } from "react-icons/io"; import { usePopper } from "react-popper"; import { @@ -107,7 +107,8 @@ export default function Settings() { ); const [tracingUrl, setTracingUrl] = useState(sentryTracingUrl); const [sentryError, setSentryError] = useState(""); - + const dropRef = useRef(null); + const settingsRef = useRef(null); const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState( @@ -131,10 +132,12 @@ export default function Settings() { placement: "bottom", modifiers: [{ name: "arrow", options: { element: saveButtonArrow } }] }); - const [isDropdownVisible, toggleDropdownVisibility] = useReducer( - (state) => !state, - false - ); + const [isDropdownVisible, setDropdownVisibility] = useState(false); + const toggleDropdown = () => { + setDropdownVisibility(!isDropdownVisible); + // tell popper to recalculate the position as the dropdown wasn't rendered + saveButtonUpdate?.(); + }; useEffect(() => { try { Sentry.init({ @@ -161,21 +164,44 @@ export default function Settings() { } }, [sentryTracingUrl]); + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + const isClickingOnDropdown = + dropRef.current && dropRef?.current.contains(event.target as Node); + const isClickingOnSettingsButton = + settingsRef.current && + settingsRef?.current.contains(event.target as Node); + if ( + isDropdownVisible && + !isClickingOnDropdown && + !isClickingOnSettingsButton + ) { + setDropdownVisibility(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [dropRef, isDropdownVisible]); + return ( <> { - toggleDropdownVisibility(); - // tell popper to recalculate the position as the dropdown wasn't rendered - saveButtonUpdate?.(); + ref={(ref) => { + settingsRef.current = ref; + setReferenceElement(ref); }} + onClick={toggleDropdown} > { + dropRef.current = ref; + setPopperElement(ref); + }} style={styles.popper} {...attributes.popper} hidden={!isDropdownVisible}