From f7e277540ec22a05cf93ce3b9f55c8cc5818699b Mon Sep 17 00:00:00 2001 From: John Coburn Date: Tue, 7 Jan 2025 14:40:07 -0600 Subject: [PATCH] STCOM-1391 Tooltip - check for hover on tooltip content before closing the tooltip. (#2411) According to [WCAG](https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus), content that's available/visible on hover should remain visible when the content itself is hovered... mouse-readers everywhere unite! ### Approach Tooltip already uses timeouts to provide a brief gap between event listeners and state updates. This implementation added a timeout to hiding the tooltip where we only hide the tooltip if neither the trigger nor the tooltip element itself are hovered by the mouse (using `matches(':hover')`). The CSS of the tooltip class had to be affected to observe mouse events since they are all placed in the `#OverlayContainer` by default. --- CHANGELOG.md | 1 + lib/Tooltip/Tooltip.css | 1 + lib/Tooltip/Tooltip.js | 25 +++++++++++++++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc195f77..3caacedad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Clear filter value after an action chosen from `MultiSelection` menu. Refs STCOM-1385. * ExportCSV - fix usage within ``s by rendering the download link to the `div#OverlayContainer`. Refs STCOM-1387. * `` should default its heading/label tag to `H3` instead of `H1`. Refs STCOM-1392. +* `` should allow for tooltip content to be hovered without closing the tooltip. Refs STCOM-1391. * `` - change `aria-label` for the input box to enter a search query and the Boolean operator dropdown. Refs STCOM-1195. ## [12.2.0](https://github.com/folio-org/stripes-components/tree/v12.2.0) (2024-10-11) diff --git a/lib/Tooltip/Tooltip.css b/lib/Tooltip/Tooltip.css index 4a8746098..6454637a8 100644 --- a/lib/Tooltip/Tooltip.css +++ b/lib/Tooltip/Tooltip.css @@ -21,6 +21,7 @@ animation-timing-function: ease-in; animation-duration: 0.1s; font-size: var(--font-size-small); + pointer-events: all; } .text { diff --git a/lib/Tooltip/Tooltip.js b/lib/Tooltip/Tooltip.js index 67cf1e1ab..6a3583d72 100644 --- a/lib/Tooltip/Tooltip.js +++ b/lib/Tooltip/Tooltip.js @@ -48,6 +48,7 @@ export default class Tooltip extends Component { constructor(props) { super(props); this.triggerRef = props.triggerRef || createRef(null); + this.overlayRef = createRef(null); } state = { @@ -99,10 +100,22 @@ export default class Tooltip extends Component { const disable = hideOnTouch && isTouch; clearTimeout(this.timeout); - if (bool !== open && !disable) { - this.setState({ - open: bool, - }); + // Accessibility - When hiding the tooltip, ensure that the mouse is not hovered over the tooltip + // - for mouse readers (WCAG 2.1 - 1.4.13 ) + if (!bool) { + if (!this.overlayRef?.current?.matches(':hover') && + !this.triggerRef?.current?.matches(':hover') + ) { + this.setState({ + open: bool, + }); + } + } else { + if (bool !== open && !disable) { + this.setState({ + open: bool, + }); + } } } @@ -114,7 +127,7 @@ export default class Tooltip extends Component { hide = () => { clearTimeout(this.timeout); - this.toggle(false); + this.timeout = setTimeout(() => this.toggle(false), 150); } /** @@ -213,7 +226,7 @@ export default class Tooltip extends Component { placement={placement} modifiers={modifiers} > -
+
this.hide()} aria-hidden data-test-tooltip> {text && (
{text}