From 28b1b1808b61a287557b7624d6f4cdea5c3a498a Mon Sep 17 00:00:00 2001 From: jshaw22 Date: Tue, 20 Apr 2021 18:45:18 -0700 Subject: [PATCH 1/2] fix(src/index.js): add accessibility support for tabbing --- src/index.js | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index b951563ad..a3c8a6d44 100755 --- a/src/index.js +++ b/src/index.js @@ -136,7 +136,7 @@ class ReactTooltip extends React.Component { * For unify the bind and unbind listener */ bind(methodArray) { - methodArray.forEach(method => { + methodArray.forEach((method) => { this[method] = this[method].bind(this); }); } @@ -152,7 +152,7 @@ class ReactTooltip extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { const { ariaProps } = prevState; const newAriaProps = parseAria(nextProps); - const isChanged = Object.keys(newAriaProps).some(props => { + const isChanged = Object.keys(newAriaProps).some((props) => { return newAriaProps[props] !== ariaProps[props]; }); if (!isChanged) { @@ -247,8 +247,8 @@ class ReactTooltip extends React.Component { // Scan document for shadow DOM elements nodeListToArray(document.getElementsByTagName('*')) - .filter(element => element.shadowRoot) - .forEach(element => { + .filter((element) => element.shadowRoot) + .forEach((element) => { targetArray = targetArray.concat( nodeListToArray(element.shadowRoot.querySelectorAll(selector)) ); @@ -266,7 +266,7 @@ class ReactTooltip extends React.Component { const { id, globalEventOff, isCapture } = this.props; const targetArray = this.getTargetArray(id); - targetArray.forEach(target => { + targetArray.forEach((target) => { if (target.getAttribute('currentItem') === null) { target.setAttribute('currentItem', 'false'); } @@ -279,7 +279,7 @@ class ReactTooltip extends React.Component { if (this.isBodyMode()) { this.bindBodyListener(targetArray); } else { - targetArray.forEach(target => { + targetArray.forEach((target) => { const isCaptureMode = this.isCapture(target); const effect = this.getEffect(target); if (this.isCustomEvent(target)) { @@ -288,6 +288,7 @@ class ReactTooltip extends React.Component { } target.addEventListener('mouseenter', this.showTooltip, isCaptureMode); + target.addEventListener('focus', this.showTooltip, isCaptureMode); if (effect === 'float') { target.addEventListener( 'mousemove', @@ -296,6 +297,7 @@ class ReactTooltip extends React.Component { ); } target.addEventListener('mouseleave', this.hideTooltip, isCaptureMode); + target.addEventListener('blur', this.showTooltip, isCaptureMode); }); } @@ -318,7 +320,7 @@ class ReactTooltip extends React.Component { this.unbindBodyListener(); } else { const targetArray = this.getTargetArray(id); - targetArray.forEach(target => { + targetArray.forEach((target) => { this.unbindBasicListener(target); if (this.isCustomEvent(target)) this.customUnbindListener(target); }); @@ -380,7 +382,7 @@ class ReactTooltip extends React.Component { if (isGlobalCall) { // Don't trigger other elements belongs to other ReactTooltip const targetArray = this.getTargetArray(this.props.id); - const isMyElement = targetArray.some(ele => ele === e.currentTarget); + const isMyElement = targetArray.some((ele) => ele === e.currentTarget); if (!isMyElement) return; } // Get the tooltip content @@ -401,6 +403,11 @@ class ReactTooltip extends React.Component { scrollHide = this.props.scrollHide; } + // adding aria-describedby to target to make tooltips read by screen readers + if (e && e.currentTarget && e.currentTarget.setAttribute) { + e.currentTarget.setAttribute('aria-describedby', this.state.uuid); + } + // Make sure the correct place is set const desiredPlace = e.currentTarget.getAttribute('data-place') || this.props.place || 'top'; @@ -617,10 +624,15 @@ class ReactTooltip extends React.Component { if (hasTarget) { // Don't trigger other elements belongs to other ReactTooltip const targetArray = this.getTargetArray(this.props.id); - const isMyElement = targetArray.some(ele => ele === e.currentTarget); + const isMyElement = targetArray.some((ele) => ele === e.currentTarget); if (!isMyElement || !this.state.show) return; } + // clean up aria-describedby when hiding tooltip + if (e && e.currentTarget && e.currentTarget.removeAttribute) { + e.currentTarget.removeAttribute('aria-describedby'); + } + const resetState = () => { const isVisible = this.state.show; // Check if the mouse is actually over the tooltip, if so don't hide the tooltip @@ -730,14 +742,14 @@ class ReactTooltip extends React.Component { hasCustomColors() { return Boolean( Object.keys(this.state.customColors).find( - color => color !== 'border' && this.state.customColors[color] + (color) => color !== 'border' && this.state.customColors[color] ) || (this.state.border && this.state.customColors['border']) ); } render() { - const { extraClass, html, ariaProps, disable } = this.state; + const { extraClass, html, ariaProps, disable, uuid } = this.state; const content = this.getTooltipContent(); const isEmptyTip = this.isEmptyTip(content); const style = generateTooltipStyle( @@ -773,8 +785,8 @@ class ReactTooltip extends React.Component { return ( (this.tooltipRef = ref)} + id={this.props.id || uuid} + ref={(ref) => (this.tooltipRef = ref)} {...ariaProps} data-id="tooltip" dangerouslySetInnerHTML={{ __html: htmlContent }} @@ -784,9 +796,9 @@ class ReactTooltip extends React.Component { return ( (this.tooltipRef = ref)} + ref={(ref) => (this.tooltipRef = ref)} data-id="tooltip" >