From 73a06ae8ce1bee644e10f245edcf2f9f2b773964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Lajto=C5=A1?= Date: Tue, 15 Mar 2022 19:06:17 +0100 Subject: [PATCH] feat: control whether Tooltip can be closed with Esc key (#5199) * Control whether Tooltip can be closed with Esc key * Move condition inside the effect * Add `handler` to deps * chore: update changeset * chore: remove noop Co-authored-by: Tim Kolberger * test(tooltip): add tests for `closeOnEsc` * test(tooltip): dispatch keypress event on the tooltip, not document Co-authored-by: Tim Kolberger --- .changeset/real-cooks-run.md | 6 ++++ packages/hooks/src/use-event-listener.ts | 8 +++-- packages/tooltip/src/use-tooltip.ts | 7 ++++- packages/tooltip/tests/tooltip.test.tsx | 39 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 .changeset/real-cooks-run.md diff --git a/.changeset/real-cooks-run.md b/.changeset/real-cooks-run.md new file mode 100644 index 00000000000..f4634654ef8 --- /dev/null +++ b/.changeset/real-cooks-run.md @@ -0,0 +1,6 @@ +--- +"@chakra-ui/hooks": minor +"@chakra-ui/tooltip": minor +--- + +Control whether Tooltip can be closed with Esc key diff --git a/packages/hooks/src/use-event-listener.ts b/packages/hooks/src/use-event-listener.ts index a1d5b664b00..55afbe5ed17 100644 --- a/packages/hooks/src/use-event-listener.ts +++ b/packages/hooks/src/use-event-listener.ts @@ -18,7 +18,7 @@ export type EventListenerEnv = (() => DocumentOrElement) | DocumentOrElement */ export function useEventListener( event: K | (string & {}), - handler: (event: DocumentEventMap[K]) => void, + handler?: (event: DocumentEventMap[K]) => void, env?: EventListenerEnv, options?: boolean | AddEventListenerOptions, ) { @@ -27,11 +27,15 @@ export function useEventListener( React.useEffect(() => { const node = runIfFn(env) ?? document + if (!handler) { + return + } + node.addEventListener(event, listener, options) return () => { node.removeEventListener(event, listener, options) } - }, [event, env, options, listener]) + }, [event, env, options, listener, handler]) return () => { const node = runIfFn(env) ?? document diff --git a/packages/tooltip/src/use-tooltip.ts b/packages/tooltip/src/use-tooltip.ts index c583f9312b0..121494c846a 100644 --- a/packages/tooltip/src/use-tooltip.ts +++ b/packages/tooltip/src/use-tooltip.ts @@ -33,6 +33,10 @@ export interface UseTooltipProps * is down */ closeOnMouseDown?: boolean + /** + * If `true`, the tooltip will hide on pressing Esc key + */ + closeOnEsc?: boolean /** * Callback to run when the tooltip shows */ @@ -64,6 +68,7 @@ export function useTooltip(props: UseTooltipProps = {}) { closeDelay = 0, closeOnClick = true, closeOnMouseDown, + closeOnEsc = true, onOpen: onOpenProp, onClose: onCloseProp, placement, @@ -140,7 +145,7 @@ export function useTooltip(props: UseTooltipProps = {}) { [isOpen, closeWithDelay], ) - useEventListener("keydown", onKeyDown) + useEventListener("keydown", closeOnEsc ? onKeyDown : undefined) React.useEffect( () => () => { diff --git a/packages/tooltip/tests/tooltip.test.tsx b/packages/tooltip/tests/tooltip.test.tsx index 9ca41868b7c..2a415f4cb45 100644 --- a/packages/tooltip/tests/tooltip.test.tsx +++ b/packages/tooltip/tests/tooltip.test.tsx @@ -5,6 +5,7 @@ import { screen, testA11y, waitForElementToBeRemoved, + press, } from "@chakra-ui/test-utils" import * as React from "react" import { Tooltip, TooltipProps } from "../src" @@ -125,3 +126,41 @@ test("should close on mouseleave if shouldWrapChildren is true and child is a di await waitForElementToBeRemoved(() => screen.getByText(tooltipLabel)) }) + +test("shows on mouseover and closes on pressing 'esc'", async () => { + render() + + act(() => { + fireEvent.mouseOver(screen.getByText(buttonLabel)) + }) + + await screen.findByRole("tooltip") + + expect(screen.getByText(buttonLabel)).toBeInTheDocument() + expect(screen.getByRole("tooltip")).toBeInTheDocument() + + act(() => { + press.Escape(screen.getByRole("tooltip")) + }) + + await waitForElementToBeRemoved(() => screen.getByText(tooltipLabel)) +}) + +test("shows on mouseover and stays on pressing 'esc' if 'closeOnEsc' is false", async () => { + render() + + act(() => { + fireEvent.mouseOver(screen.getByText(buttonLabel)) + }) + + await screen.findByRole("tooltip") + + expect(screen.getByText(buttonLabel)).toBeInTheDocument() + expect(screen.getByRole("tooltip")).toBeInTheDocument() + + act(() => { + press.Escape(screen.getByRole("tooltip")) + }) + + expect(screen.getByRole("tooltip")).toBeInTheDocument() +})