Skip to content
This repository was archived by the owner on Oct 19, 2021. It is now read-only.

Commit 6190488

Browse files
paschalidiasudoh
authored andcommitted
fix(Tooltop): a11y improvements (#2245)
* fix(Icon): no alt in svg * fix(Tooltip): ✅ removes IconTitle * fix(Tooltip): ✅ removes aria-owns - should not used in tooltip pattern * fix(Tooltip): ✅ adds sensible default aria-label. "Help" not "tooltip" * fix(Tooltip): ✅ aria-labels are now set on the Icon only and not in the div if the user provides property `triggerText`, then the button should use aria-labelledby to point to its id, if the user doesn't provide property `triggerText`, then they need to provide an aria-label via the `iconDescription` property. * fix(Tooltip): ✅ aria-labels * fix: FinalIcon is now shown when Icon is there * fix: rearanges * fix: refs cannot be passed into func components * fix: adds proptypes validation using helpers * fix: ref a propRef
1 parent c3ae22f commit 6190488

File tree

2 files changed

+55
-83
lines changed

2 files changed

+55
-83
lines changed

src/components/Tooltip/Tooltip-story.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const props = {
7676
true
7777
),
7878
direction: select('Tooltip direction (direction)', directions, 'bottom'),
79-
triggerText: null,
79+
iconDescription: 'Helpful Information',
8080
tabIndex: number('Tab index (tabIndex in <Tooltip>)', 0),
8181
renderIcon: OverflowMenuVertical16,
8282
}),

src/components/Tooltip/Tooltip.js

Lines changed: 54 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import ClickListener from '../../internal/ClickListener';
2525
import { breakingChangesX, componentsX } from '../../internal/FeatureFlags';
2626
import mergeRefs from '../../tools/mergeRefs';
2727
import { keys, keyCodes, matches as keyDownMatch } from '../../tools/key';
28+
import isRequiredOneOf from '../../prop-types/isRequiredOneOf';
2829

2930
const { prefix } = settings;
3031

@@ -173,11 +174,6 @@ class Tooltip extends Component {
173174
PropTypes.func,
174175
]),
175176

176-
/**
177-
* The content to put into the trigger UI, except the (default) tooltip icon.
178-
*/
179-
triggerText: PropTypes.node,
180-
181177
/**
182178
* The callback function to optionally render the icon element.
183179
* It should be a component with React.forwardRef().
@@ -213,15 +209,16 @@ class Tooltip extends Component {
213209
*/
214210
iconName: PropTypes.string,
215211

216-
/**
217-
* The description of the default tooltip icon, to be put in its SVG 'aria-label' and 'alt' .
218-
*/
219-
iconDescription: PropTypes.string,
220-
221-
/**
222-
* The title of the default tooltip icon, to be put in its SVG `<title>` element.
223-
*/
224-
iconTitle: PropTypes.string,
212+
...isRequiredOneOf({
213+
/**
214+
* The content to put into the trigger UI, except the (default) tooltip icon.
215+
*/
216+
triggerText: PropTypes.node,
217+
/**
218+
* The description of the default tooltip icon, to be put in its SVG 'aria-label' and 'alt' .
219+
*/
220+
iconDescription: PropTypes.string,
221+
}),
225222

226223
/**
227224
* `true` if opening tooltip should be triggered by clicking the trigger button.
@@ -239,9 +236,7 @@ class Tooltip extends Component {
239236
direction: DIRECTION_BOTTOM,
240237
renderIcon: !componentsX ? undefined : Information,
241238
showIcon: true,
242-
iconDescription: 'tooltip',
243-
iconTitle: '',
244-
triggerText: 'Provide triggerText',
239+
triggerText: null,
245240
menuOffset: getMenuOffset,
246241
clickToOpen: breakingChangesX,
247242
};
@@ -422,7 +417,6 @@ class Tooltip extends Component {
422417
showIcon,
423418
icon,
424419
iconName,
425-
iconTitle,
426420
iconDescription,
427421
renderIcon: IconCustomElement,
428422
menuOffset,
@@ -463,83 +457,61 @@ class Tooltip extends Component {
463457
`${prefix}--tooltip__label`,
464458
triggerClassName
465459
);
466-
const ariaOwnsProps = !open
467-
? {}
468-
: {
469-
'aria-owns': tooltipId,
470-
};
471460

472-
const ariaDescribedbyProps = !open
473-
? {}
474-
: {
475-
'aria-describedby': tooltipId,
476-
};
477-
const finalIcon = IconCustomElement ? (
478-
<IconCustomElement
479-
name={iconName}
480-
aria-labelledby={triggerId}
481-
aria-label={iconDescription}
482-
ref={mergeRefs(ref, node => {
483-
this.triggerEl = node;
484-
})}
485-
/>
486-
) : (
487-
<Icon
488-
icon={!icon && !iconName ? iconInfoGlyph : icon}
489-
name={iconName}
490-
description={iconDescription}
491-
iconTitle={iconTitle}
492-
iconRef={mergeRefs(ref, node => {
493-
this.triggerEl = node;
494-
})}
495-
/>
496-
);
461+
const refProp = mergeRefs(ref, node => {
462+
this.triggerEl = node;
463+
});
464+
465+
const iconProperties = { name: iconName, role: null, description: null };
466+
467+
const properties = {
468+
role: 'button',
469+
tabIndex: tabIndex,
470+
onClick: this.handleMouse,
471+
onKeyDown: this.handleKeyPress,
472+
onMouseOver: this.handleMouse,
473+
onMouseOut: this.handleMouse,
474+
onFocus: this.handleMouse,
475+
onBlur: this.handleMouse,
476+
'aria-haspopup': 'true',
477+
'aria-expanded': open,
478+
// if the user provides property `triggerText`,
479+
// then the button should use aria-describedby to point to its id,
480+
// if the user doesn't provide property `triggerText`,
481+
// then an aria-label will be provided via the `iconDescription` property.
482+
...(triggerText
483+
? {
484+
'aria-describedby': triggerId,
485+
}
486+
: {
487+
'aria-label': iconDescription,
488+
}),
489+
};
497490

498491
return (
499492
<>
500493
<ClickListener onClickOutside={this.handleClickOutside}>
501494
{showIcon ? (
502-
<div className={triggerClasses}>
495+
<div id={triggerId} className={triggerClasses}>
503496
{triggerText}
504-
<div
505-
role="button"
506-
id={triggerId}
507-
className={`${prefix}--tooltip__trigger`}
508-
tabIndex={tabIndex}
509-
title={iconTitle}
510-
onClick={this.handleMouse}
511-
onKeyDown={this.handleKeyPress}
512-
onMouseOver={this.handleMouse}
513-
onMouseOut={this.handleMouse}
514-
onFocus={this.handleMouse}
515-
onBlur={this.handleMouse}
516-
aria-haspopup="true"
517-
aria-label={iconDescription}
518-
aria-expanded={open}
519-
{...ariaDescribedbyProps}
520-
{...ariaOwnsProps}>
521-
{finalIcon}
497+
<div className={`${prefix}--tooltip__trigger`} {...properties}>
498+
{IconCustomElement ? (
499+
<IconCustomElement ref={refProp} {...iconProperties} />
500+
) : (
501+
<Icon
502+
icon={!icon && !iconName ? iconInfoGlyph : icon}
503+
iconRef={refProp}
504+
{...iconProperties}
505+
/>
506+
)}
522507
</div>
523508
</div>
524509
) : (
525510
<div
526-
role="button"
527-
tabIndex={tabIndex}
528511
id={triggerId}
529512
className={triggerClasses}
530-
ref={mergeRefs(ref, node => {
531-
this.triggerEl = node;
532-
})}
533-
onClick={this.handleMouse}
534-
onKeyDown={this.handleKeyPress}
535-
onMouseOver={this.handleMouse}
536-
onMouseOut={this.handleMouse}
537-
onFocus={this.handleMouse}
538-
onBlur={this.handleMouse}
539-
aria-haspopup="true"
540-
aria-expanded={open}
541-
{...ariaDescribedbyProps}
542-
{...ariaOwnsProps}>
513+
ref={refProp}
514+
{...properties}>
543515
{triggerText}
544516
</div>
545517
)}

0 commit comments

Comments
 (0)