Просмотр исходного кода

Hide tooltips on the Esc key (#26305)

* Hide the tooltip when Esc key is pressed

* Simplify ActionsDropdown

* Rename to tooltipDescription

* Use OLTooltip instead of Tooltip

GitOrigin-RevId: ee27cde2735ae3a0de5e37bfb8ab1dd99069742c
Rebeka Dekany 1 год назад
Родитель
Сommit
9aa261eaf6

+ 19 - 11
services/web/frontend/js/features/history/components/change-list/dropdown/actions-dropdown.tsx

@@ -2,20 +2,21 @@ import React, { ReactNode } from 'react'
 import {
   Dropdown,
   DropdownMenu,
+  DropdownToggle,
 } from '@/features/ui/components/bootstrap-5/dropdown-menu'
-import DropdownToggleWithTooltip from '@/features/ui/components/bootstrap-5/dropdown-toggle-with-tooltip'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
 
 type ActionDropdownProps = {
   id: string
   children: React.ReactNode
   isOpened: boolean
   iconTag: ReactNode
-  toolTipDescription: string
+  tooltipDescription: string
   setIsOpened: (isOpened: boolean) => void
 }
 
 function ActionsDropdown(props: ActionDropdownProps) {
-  const { id, children, isOpened, iconTag, setIsOpened, toolTipDescription } =
+  const { id, children, isOpened, iconTag, setIsOpened, tooltipDescription } =
     props
   return (
     <Dropdown
@@ -24,16 +25,23 @@ function ActionsDropdown(props: ActionDropdownProps) {
       show={isOpened}
       onToggle={open => setIsOpened(open)}
     >
-      <DropdownToggleWithTooltip
+      <OLTooltip
         id={`history-version-dropdown-${id}`}
-        className="history-version-dropdown-menu-btn"
-        aria-label={toolTipDescription}
-        toolTipDescription={toolTipDescription}
-        overlayTriggerProps={{ placement: 'bottom' }}
-        tooltipProps={{ hidden: isOpened }}
+        description={tooltipDescription}
+        overlayProps={{ placement: 'bottom' }}
+        hidden={isOpened}
       >
-        {iconTag}
-      </DropdownToggleWithTooltip>
+        {/* OverlayTrigger won't fire unless the child is a non-react html element (e.g div, span) */}
+        <span>
+          <DropdownToggle
+            id={`history-version-dropdown-toggle-${id}`}
+            className="history-version-dropdown-menu-btn"
+            as="button"
+          >
+            {iconTag}
+          </DropdownToggle>
+        </span>
+      </OLTooltip>
       <DropdownMenu className="history-version-dropdown-menu">
         {children}
       </DropdownMenu>

+ 1 - 1
services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown.tsx

@@ -21,7 +21,7 @@ function CompareVersionDropdown({
       id={id}
       isOpened={isOpened}
       setIsOpened={setIsOpened}
-      toolTipDescription={t('compare')}
+      tooltipDescription={t('compare')}
       iconTag={
         <MaterialIcon
           type="align_space_even"

+ 1 - 1
services/web/frontend/js/features/history/components/change-list/dropdown/history-dropdown.tsx

@@ -20,7 +20,7 @@ function HistoryDropdown({
     <ActionsDropdown
       id={id}
       isOpened={isOpened}
-      toolTipDescription={t('more_actions')}
+      tooltipDescription={t('more_actions')}
       setIsOpened={setIsOpened}
       iconTag={
         <MaterialIcon type="more_vert" accessibilityLabel={t('more_actions')} />

+ 2 - 2
services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx

@@ -34,7 +34,7 @@ function CompareItems({
             toVTimestamp: selRange.toVTimestamp,
           }}
           closeDropdown={closeDropdown}
-          toolTipDescription={t('history_compare_from_this_version')}
+          tooltipDescription={t('history_compare_from_this_version')}
           icon={
             <MaterialIcon
               type="align_end"
@@ -52,7 +52,7 @@ function CompareItems({
             toVTimestamp: updateRange.toVTimestamp,
           }}
           closeDropdown={closeDropdown}
-          toolTipDescription={t('history_compare_up_to_this_version')}
+          tooltipDescription={t('history_compare_up_to_this_version')}
           icon={
             <MaterialIcon
               type="align_start"

+ 4 - 4
services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare.tsx

@@ -6,14 +6,14 @@ import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
 type CompareProps = {
   comparisonRange: UpdateRange
   icon: ReactNode
-  toolTipDescription?: string
+  tooltipDescription?: string
   closeDropdown: () => void
 }
 
 function Compare({
   comparisonRange,
   closeDropdown,
-  toolTipDescription,
+  tooltipDescription,
   icon,
 }: CompareProps) {
   const { setSelection } = useHistoryContext()
@@ -32,12 +32,12 @@ function Compare({
 
   return (
     <OLTooltip
-      description={toolTipDescription}
+      description={tooltipDescription}
       id="compare-btn"
       overlayProps={{ placement: 'left' }}
     >
       <button className="history-compare-btn" onClick={handleCompareVersion}>
-        <span className="visually-hidden">{toolTipDescription}</span>
+        <span className="visually-hidden">{tooltipDescription}</span>
         {icon}
       </button>
     </OLTooltip>

+ 0 - 52
services/web/frontend/js/features/ui/components/bootstrap-5/dropdown-toggle-with-tooltip.tsx

@@ -1,52 +0,0 @@
-import { ReactNode, forwardRef } from 'react'
-import { BsPrefixRefForwardingComponent } from 'react-bootstrap/helpers'
-import type { DropdownToggleProps } from '@/features/ui/components/types/dropdown-menu-props'
-import {
-  DropdownToggle as BS5DropdownToggle,
-  OverlayTrigger,
-  OverlayTriggerProps,
-  Tooltip,
-} from 'react-bootstrap'
-import type { MergeAndOverride } from '../../../../../../types/utils'
-
-type DropdownToggleWithTooltipProps = MergeAndOverride<
-  DropdownToggleProps,
-  {
-    children: ReactNode
-    overlayTriggerProps?: Omit<OverlayTriggerProps, 'overlay' | 'children'>
-    toolTipDescription: string
-    tooltipProps?: Omit<React.ComponentProps<typeof Tooltip>, 'children'>
-    'aria-label'?: string
-  }
->
-const DropdownToggleWithTooltip = forwardRef<
-  BsPrefixRefForwardingComponent<'button', DropdownToggleProps>,
-  DropdownToggleWithTooltipProps
->(
-  (
-    {
-      children,
-      toolTipDescription,
-      overlayTriggerProps,
-      tooltipProps,
-      id: _id,
-      ...toggleProps
-    },
-    ref
-  ) => {
-    return (
-      <OverlayTrigger
-        overlay={<Tooltip {...tooltipProps}>{toolTipDescription}</Tooltip>}
-        {...overlayTriggerProps}
-      >
-        <BS5DropdownToggle {...toggleProps} ref={ref}>
-          {children}
-        </BS5DropdownToggle>
-      </OverlayTrigger>
-    )
-  }
-)
-
-DropdownToggleWithTooltip.displayName = 'DropdownToggleWithTooltip'
-
-export default DropdownToggleWithTooltip

+ 33 - 7
services/web/frontend/js/features/ui/components/bootstrap-5/tooltip.tsx

@@ -1,4 +1,10 @@
-import { cloneElement, useEffect, forwardRef } from 'react'
+import {
+  cloneElement,
+  useEffect,
+  forwardRef,
+  useState,
+  useCallback,
+} from 'react'
 import {
   OverlayTrigger,
   OverlayTriggerProps,
@@ -41,6 +47,30 @@ function Tooltip({
   overlayProps,
   hidden,
 }: TooltipProps) {
+  const [show, setShow] = useState(false)
+
+  const handleKeyDown = useCallback(
+    (e: KeyboardEvent) => {
+      if (show && e.key === 'Escape') {
+        setShow(false)
+        e.stopPropagation()
+      }
+    },
+    [show, setShow]
+  )
+
+  useEffect(() => {
+    document.addEventListener('keydown', handleKeyDown, true)
+    return () => document.removeEventListener('keydown', handleKeyDown, true)
+  }, [handleKeyDown])
+
+  const hideTooltip = (e: React.MouseEvent) => {
+    if (e.currentTarget instanceof HTMLElement) {
+      e.currentTarget.blur()
+    }
+    setShow(false)
+  }
+
   const delay = overlayProps?.delay
   let delayShow = 300
   let delayHide = 300
@@ -49,12 +79,6 @@ function Tooltip({
     delayHide = typeof delay === 'number' ? delay : delay.hide
   }
 
-  const hideTooltip = (e: React.MouseEvent) => {
-    if (e.currentTarget instanceof HTMLElement) {
-      e.currentTarget.blur()
-    }
-  }
-
   return (
     <OverlayTrigger
       overlay={
@@ -69,6 +93,8 @@ function Tooltip({
       {...overlayProps}
       delay={{ show: delayShow, hide: delayHide }}
       placement={overlayProps?.placement || 'top'}
+      show={show}
+      onToggle={setShow}
     >
       {cloneElement(children, {
         onClick: callFnsInSequence(children.props.onClick, hideTooltip),

+ 29 - 0
services/web/test/frontend/components/shared/tooltip.spec.tsx

@@ -32,4 +32,33 @@ describe('<OLTooltip />', function () {
     cy.get('@blurHandler').should('have.been.calledOnce')
     cy.findByText(description).should('not.exist')
   })
+
+  it('hides the tooltip when Escape key is pressed', function () {
+    const description = 'Press Escape to close'
+    const btnText = 'Hover me!'
+
+    cy.mount(
+      <div
+        style={{
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+          height: '100vh',
+        }}
+      >
+        <OLTooltip id="esc" description={description}>
+          <button>{btnText}</button>
+        </OLTooltip>
+      </div>
+    )
+
+    cy.findByRole('button', { name: btnText }).as('button')
+    cy.get('@button').trigger('mouseover')
+    cy.findByText(description)
+    cy.get('@button').trigger('mouseout')
+    cy.get('@button').focus()
+    cy.findByText(description)
+    cy.get('body').type('{esc}')
+    cy.findByText(description).should('not.exist')
+  })
 })

+ 1 - 1
services/web/test/frontend/features/history/components/change-list.spec.tsx

@@ -372,7 +372,7 @@ describe('change list (Bootstrap 5)', function () {
       cy.findAllByTestId('history-version-details')
         .eq(1)
         .within(() => {
-          cy.get('[aria-label="Compare"]').click()
+          cy.findByRole('button', { name: /compare/i }).click()
           cy.findByRole('menu').within(() => {
             cy.findByRole('menuitem', {
               name: /compare up to this version/i,