diff --git a/packages/react-core/src/components/Tabs/examples/Tabs.md b/packages/react-core/src/components/Tabs/examples/Tabs.md index ac8d105e1fa..6206e5ce5a4 100644 --- a/packages/react-core/src/components/Tabs/examples/Tabs.md +++ b/packages/react-core/src/components/Tabs/examples/Tabs.md @@ -56,7 +56,12 @@ class SimpleTabs extends React.Component { return (
- + Users}> Users @@ -99,7 +104,9 @@ class SimpleTabs extends React.Component { ### With tooltip react ref -When using a React ref to link a Tooltip to a Tab component, an `id` must be manually set on the Tooltip component, and the Tab component must have a matching `aria-describedby` attribute so that screen readers are able to announce the Tooltip contents. +When using a React ref to link a Tooltip to a Tab component via the `reference` prop, you should avoid manually passing in a value of "off" to the `aria-live` prop. Doing so may lead to the tooltip becoming less accessible to assistive technologies. + +The tooltip should also have the `id` prop passed in. The value given to this prop should then be passed into the tab's `aria-describedby` prop. This ensures a tooltip used with a React ref will be announced by the JAWS and NVDA screen readers. ```js import React from 'react'; @@ -132,7 +139,12 @@ class SimpleTabs extends React.Component { return (
- + Users}> Users @@ -153,15 +165,15 @@ class SimpleTabs extends React.Component { title={ARIA Disabled (Tooltip)} isAriaDisabled ref={tooltipRef} - aria-describedby="tooltip-tab-5" + aria-describedby="tooltip-ref1" > ARIA Disabled (Tooltip)
- + Users}> Users @@ -424,7 +441,13 @@ class VerticalTabs extends React.Component { return (
- + Users}> Users @@ -689,7 +712,13 @@ class PageInsetsTabs extends React.Component { const { activeTabKey, isBox } = this.state; return (
- + Users}> Users @@ -753,7 +782,11 @@ class IconAndTextTabs extends React.Component { render() { return ( - + - + Users}> - + Secondary tab item 1}> Secondary tab item 1 item section @@ -983,7 +1026,13 @@ class FilledTabs extends React.Component { const { activeTabKey, isBox } = this.state; return (
- + Users}> Users @@ -1044,7 +1093,13 @@ class FilledTabsWithIcons extends React.Component { const { activeTabKey, isBox } = this.state; return (
- + - + Tab item 1} @@ -1291,13 +1350,30 @@ class SeparateTabContent extends React.Component { />
- + Tab 1 section -
@@ -1351,10 +1427,22 @@ const TabContentWithBody = () => { Tab 1 section -
@@ -1385,7 +1473,12 @@ class MountingSimpleTabs extends React.Component { render() { return ( - + Tab item 1}> Tab 1 section @@ -1423,7 +1516,12 @@ class UnmountingSimpleTabs extends React.Component { render() { return ( - + Tab item 1}> Tab 1 section @@ -1473,7 +1571,11 @@ class ToggledSeparateContent extends React.Component { {isTab2Hidden ? 'Show' : 'Hide'} tab 2 - + {!isTab2Hidden && ( @@ -1481,15 +1583,32 @@ class ToggledSeparateContent extends React.Component {
- + Tab 1 section {!isTab2Hidden && ( -
diff --git a/packages/react-core/src/components/Tooltip/Tooltip.tsx b/packages/react-core/src/components/Tooltip/Tooltip.tsx index 3ff65e1772e..dbe0927c6e0 100644 --- a/packages/react-core/src/components/Tooltip/Tooltip.tsx +++ b/packages/react-core/src/components/Tooltip/Tooltip.tsx @@ -35,6 +35,12 @@ export interface TooltipProps extends Omit, 'con * If you don't want that or prefer to add the aria attribute yourself on the trigger, set aria to 'none'. */ aria?: 'describedby' | 'labelledby' | 'none'; + /** + * Determines whether the tooltip is an aria-live region. If the reference prop is passed in the + * default behavior is 'polite' in order to ensure the tooltip contents is announced to + * assistive technologies. Otherwise the default behavior is 'off'. + */ + 'aria-live'?: 'off' | 'polite'; /** * The reference element to which the Tooltip is relatively placed to. * If you cannot wrap the reference with the Tooltip, you can use the reference prop instead. @@ -155,6 +161,7 @@ export const Tooltip: React.FunctionComponent = ({ children, animationDuration = 300, reference, + 'aria-live': ariaLive = reference ? 'polite' : 'off', boundary, isAppLauncher, tippyProps, @@ -251,6 +258,7 @@ export const Tooltip: React.FunctionComponent = ({ const hasCustomMaxWidth = maxWidth !== tooltipMaxWidth.value; const content = ( } > - I have a tooltip! + + I have a tooltip! + -
+
; ``` ### Tooltip react ref + ```js import React from 'react'; import { Tooltip } from '@patternfly/react-core'; @@ -36,35 +40,49 @@ TooltipReactRef = () => { const tooltipRef = React.useRef(); return (
- + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis. +
} reference={tooltipRef} />
); -} +}; ``` ### Tooltip selector ref + ```js import React from 'react'; import { Tooltip } from '@patternfly/react-core';
- + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
} reference={() => document.getElementById('tooltip-selector')} /> -
+
; ``` -### On icon +### On icon with dynamic content + +When the tooltip is used as a wrapper and its content will dynamically update, the `aria` prop should have a value of "none" passed in. This prevents assistive technologies from announcing the tooltip contents more than once. Additionally, the `aria-live` prop should have a value of "polite" passed in, in order for assistive technologies to announce when the tooltip contents gets updated. + +When using a React or selector ref with a tooltip that has dynamic content, the `aria` and `aria-live` props do not need to be manually passed in. + ```js import React from 'react'; import { Tooltip, Button } from '@patternfly/react-core'; @@ -77,8 +95,13 @@ IconExample = () => { const [content, setContent] = React.useState(copyText); return (
- - @@ -88,6 +111,7 @@ IconExample = () => { ``` ### Options + ```js import React from 'react'; import { Button, Tooltip, Checkbox, Select, SelectOption, TextInput } from '@patternfly/react-core'; @@ -105,26 +129,26 @@ OptionsTooltip = () => { const [exitDelayInput, setExitDelayInput] = React.useState(0); const [animationDuration, setAnimationDuration] = React.useState(300); const tipBoxRef = React.useRef(null); - + const scrollToRef = ref => { if (ref && ref.current) { ref.current.scrollTop = 400; ref.current.scrollLeft = 300; } - } - + }; + React.useEffect(() => { scrollToRef(tipBoxRef); }, []); - + return ( <>
-
+
{ + onChange={checked => { let updatedTrigger; checked && (updatedTrigger = trigger.concat('mouseenter')); !checked && (updatedTrigger = trigger.filter(t => t !== 'mouseenter')); @@ -137,7 +161,7 @@ OptionsTooltip = () => { { + onChange={checked => { let updatedTrigger; checked && (updatedTrigger = trigger.concat('focus')); !checked && (updatedTrigger = trigger.filter(t => t !== 'focus')); @@ -150,7 +174,7 @@ OptionsTooltip = () => { { + onChange={checked => { let updatedTrigger; checked && (updatedTrigger = trigger.concat('click')); !checked && (updatedTrigger = trigger.filter(t => t !== 'click')); @@ -163,36 +187,36 @@ OptionsTooltip = () => { { + onChange={checked => { let updatedTrigger; checked && (updatedTrigger = trigger.concat('manual')); !checked && (updatedTrigger = trigger.filter(t => t !== 'manual')); setIsVisible(false); setTrigger(updatedTrigger); }} - aria-label="trigger: manual" - id="trigger_manual" + aria-label="trigger: manual" + id="trigger_manual" />
-
+
setContentLeftAligned(checked)} + onChange={checked => setContentLeftAligned(checked)} aria-label="content left-aligned" - id="content_left_aligned" + id="content_left_aligned" />
-
+
setEnableFlip(checked)} - aria-label="enableFlip" - id="enable_flip" + onChange={checked => setEnableFlip(checked)} + aria-label="enableFlip" + id="enable_flip" />
-
+
position (will flip if enableFlip is true). The 'auto' position requires enableFlip to be set to true.
-
+
setIsVisible(checked)} - aria-label="isVisible" - id="is_visible" + onChange={checked => setIsVisible(checked)} + aria-label="isVisible" + id="is_visible" />
-
- Entry delay (ms) setEntryDelayInput(val)} aria-label="entry delay" /> - Exit delay (ms) setExitDelayInput(val)} aria-label="exit delay" /> - Animation duration (ms) setAnimationDuration(val)} aria-label="animation duration" /> +
+ Entry delay (ms){' '} + setEntryDelayInput(val)} + aria-label="entry delay" + /> + Exit delay (ms) setExitDelayInput(val)} + aria-label="exit delay" + /> + Animation duration (ms){' '} + setAnimationDuration(val)} + aria-label="animation duration" + />
-
- flip behavior examples (enableFlip has to be true). "flip" will try to flip the tooltip to the opposite of the starting position. The second option ensures that there are 3 escape positions for every possible starting position (default). This setting is ignored if position prop is set to 'auto'. +
+ flip behavior examples (enableFlip has to be true). "flip" will try to flip the tooltip to the opposite of the + starting position. The second option ensures that there are 3 escape positions for every possible starting + position (default). This setting is ignored if position prop is set to 'auto'.