diff --git a/package.json b/package.json index 79feb422..2f6d0d33 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/pybricks/pybricks-code" }, "dependencies": { - "@blueprintjs/core": "^3.52.0", + "@blueprintjs/core": "^3.53.0", "@blueprintjs/popover2": "^0.13.0", "@craco/craco": "^6.4.3", "@pybricks/firmware": "4.14.0", diff --git a/src/toolbar/ActionButton.tsx b/src/toolbar/ActionButton.tsx index 8de557c3..b415a0c5 100644 --- a/src/toolbar/ActionButton.tsx +++ b/src/toolbar/ActionButton.tsx @@ -13,7 +13,6 @@ import { Tooltip2 } from '@blueprintjs/popover2'; import { useI18n } from '@shopify/react-i18n'; import React, { useEffect, useMemo, useState } from 'react'; import { tooltipDelay } from '../app/constants'; -import { closeTooltip2, useTooltip2MonkeyPatch } from '../utils/monkey-patch'; import { TooltipId } from './i18n'; import en from './i18n.en.json'; @@ -91,11 +90,8 @@ const ActionButton: React.FC = (props) => { useHotkeys(hotkeys); - const tooltipRef = useTooltip2MonkeyPatch(); - return ( = (props) => { onMouseDown={(e) => { // prevent focus from mouse click e.preventDefault(); - // close/prevent tooltip - closeTooltip2(tooltipRef); }} onClick={() => props.onAction()} disabled={props.enabled === false} diff --git a/src/toolbar/OpenFileButton.tsx b/src/toolbar/OpenFileButton.tsx index 6d7bf042..c3a7db3f 100644 --- a/src/toolbar/OpenFileButton.tsx +++ b/src/toolbar/OpenFileButton.tsx @@ -7,7 +7,6 @@ import { useI18n } from '@shopify/react-i18n'; import React, { useEffect, useState } from 'react'; import { useDropzone } from 'react-dropzone'; import { tooltipDelay } from '../app/constants'; -import { closeTooltip2, useTooltip2MonkeyPatch } from '../utils/monkey-patch'; import { TooltipId } from './i18n'; import en from './i18n.en.json'; @@ -91,11 +90,8 @@ const OpenFileButton: React.FC = (props) => { }, }); - const tooltipRef = useTooltip2MonkeyPatch(); - return ( = (props) => { onMouseDown: (e) => { // prevent focus from mouse click e.preventDefault(); - // close/prevent tooltip - closeTooltip2(tooltipRef); }, onClick: props.onClick, })} diff --git a/src/utils/monkey-patch.test.tsx b/src/utils/monkey-patch.test.tsx deleted file mode 100644 index 3df61f27..00000000 --- a/src/utils/monkey-patch.test.tsx +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2021 The Pybricks Authors - -import { Button, IRef } from '@blueprintjs/core'; -import { Tooltip2 } from '@blueprintjs/popover2'; -import { - fireEvent, - render, - screen, - waitFor, - waitForElementToBeRemoved, -} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { useTooltip2MonkeyPatch } from './monkey-patch'; - -const testTooltipText = 'Test tooltip.'; -const testButtonId = 'test-button'; - -type TestComponentProps = { monkeyPatch: boolean }; - -const TestComponent: React.FC = (props) => { - const tooltipRef = props.monkeyPatch ? useTooltip2MonkeyPatch() : undefined; - - return ( - ( - - )} - /> - ); -}; - -it.each([false, true])( - 'should work around https://github.com/palantir/blueprint/issues/4503', - async (monkeyPatch) => { - render(); - - expect(screen.queryByText(testTooltipText)).not.toBeInTheDocument(); - - // pressing the tab key should focus the button and open the tooltip - userEvent.tab(); - - await waitFor(() => { - expect(screen.getByText(testTooltipText)).toBeInTheDocument(); - }); - - // a blur event with renderTarget=null should trigger the bug and workaround - fireEvent.blur(screen.getByTestId(testButtonId)); - - if (monkeyPatch) { - // if the patch was applied, there should not be any error - await waitForElementToBeRemoved(screen.getByText(testTooltipText)); - } else { - // if the patch was not applied, the bug should be triggered - await expect( - waitForElementToBeRemoved(screen.getByText(testTooltipText)), - ).rejects.toBeInstanceOf(Error); - } - }, -); diff --git a/src/utils/monkey-patch.ts b/src/utils/monkey-patch.ts deleted file mode 100644 index 7f7600b1..00000000 --- a/src/utils/monkey-patch.ts +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2021 The Pybricks Authors - -import { Tooltip2 } from '@blueprintjs/popover2'; -import React, { useEffect, useRef } from 'react'; - -/** Hack to access private members of Popover2. */ -interface Popover2Private { - handleTargetBlur: (e: React.FocusEvent) => void; - handleMouseLeave: (e: React.MouseEvent) => void; -} -/** Hack to access private members of Tooltip2. */ -interface Tooltip2Private { - popover: Popover2Private; -} - -/** - * Monkey patch Popover2 blur event handler to make tooltips close when - * focus is lost. - * - * https://github.com/palantir/blueprint/issues/4503 - * - * @returns A React ref to be attached to the Tooltip2 component. - */ -export function useTooltip2MonkeyPatch(): React.RefObject> { - const tooltipRef = useRef>(null); - - useEffect(() => { - // istanbul ignore if: should not happen ever - if (!tooltipRef.current) { - return; - } - - const tooltip = tooltipRef.current as unknown as Tooltip2Private; - - const oldHandleTargetBlur = tooltip.popover.handleTargetBlur; - - tooltip.popover.handleTargetBlur = (e) => { - if (e.relatedTarget) { - oldHandleTargetBlur(e); - } else { - closeTooltip2(tooltipRef); - } - }; - - return () => { - tooltip.popover.handleTargetBlur = oldHandleTargetBlur; - }; - }, [tooltipRef]); - - return tooltipRef; -} - -/** - * Hack to access private members of Tooltip2 to programmatically close the tooltip. - * @param tooltipRef The reference returned from useTooltip2MonkeyPatch() - */ -export function closeTooltip2(tooltipRef: React.RefObject>): void { - // istanbul ignore if: should not happen ever - if (!tooltipRef.current) { - return; - } - - const tooltip = tooltipRef.current as unknown as Tooltip2Private; - - // Currently, the event arg is not used so it should be safe to pass undefined. - tooltip.popover.handleMouseLeave( - undefined as unknown as React.MouseEvent, - ); -} diff --git a/yarn.lock b/yarn.lock index 49767063..11066f7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1206,7 +1206,7 @@ resolved "https://registry.yarnpkg.com/@blueprintjs/colors/-/colors-4.0.0-alpha.2.tgz#ce8e7450ae542fcddb72b372b49669adf13c0173" integrity sha512-B8NX2oKVTPGVJd7ZCbm9FMPoEdQiLKceo0/UxsJ+gwqNiuo45RzdmrXACI6oFO4hGHL2WiLU0oysLNj2zpHDkA== -"@blueprintjs/core@^3.52.0", "@blueprintjs/core@^3.53.0": +"@blueprintjs/core@^3.53.0": version "3.53.0" resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.53.0.tgz#ad9ac906107baf07a3a84f2c705e70032a053fb5" integrity sha512-cvoJJtnPrBgaVvWhfHoFi/zH+Bk4VPDqbG4DdBl+BPK5MyvvkcBP/oX/iVG1qgwdvQ9FqUsHpuCLLgF6j8bItA==