Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/Accordion/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Styled from './styles';
import { MdExpandMore } from 'react-icons/md';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { TOOLTIP_PLUGINS } from '../../utils/tooltip';
import { DEFAULT_TOOLTIP_PLACEMENT, DEFAULT_BUTTON_HEADER_POSITION } from './constants';

/**
Expand Down Expand Up @@ -49,7 +50,7 @@ function Accordion({
return (
<>
{tooltipLabel ? (
<Tippy content={tooltipLabel} placement={tooltipPlacement}>
<Tippy content={tooltipLabel} placement={tooltipPlacement} plugins={TOOLTIP_PLUGINS}>
{expandButton}
</Tippy>
) : (
Expand Down
3 changes: 2 additions & 1 deletion src/components/Avatar/component.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { JSX, useEffect, useState } from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { TOOLTIP_PLUGINS } from '../../utils/tooltip';
import * as Styled from './styles';
import { AvatarProps } from './types';
import { DEFAULT_AVATAR_SIZE, AVATAR_FALLBACK_COLORS } from './constants';
Expand Down Expand Up @@ -63,7 +64,7 @@ function Avatar({
}

return (
<Tippy content={name} placement="top">
<Tippy content={name} placement="top" plugins={TOOLTIP_PLUGINS}>
{avatarElement}
</Tippy>
);
Expand Down
2 changes: 2 additions & 0 deletions src/components/Button/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import * as Styled from './styles';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { TOOLTIP_PLUGINS } from '../../utils/tooltip';

/**
* A versatile Button component.
Expand Down Expand Up @@ -218,6 +219,7 @@ function Button(props: ButtonProps): JSX.Element {
<Tippy
content={tooltipLabel}
placement={tooltipPlacement}
plugins={TOOLTIP_PLUGINS}
>
{buttonElement}
</Tippy>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Checkbox/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DEFAULT_LAYOUT, DEFAULT_TOOLTIP_PLACEMENT, LAYOUTS } from './constants'
import { RadioButtonUnchecked, RadioButtonChecked } from '@mui/icons-material';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { TOOLTIP_PLUGINS } from '../../utils/tooltip';

// Cancelling the click (mouse, label or Space) keeps the box unchanged, and MUI skips
// `onChange` for a prevented click. It sits on the root because MUI drops event handlers
Expand Down Expand Up @@ -128,6 +129,7 @@ function Checkbox({
placement={tooltipPlacement}
trigger="mouseenter focusin"
aria={{ content: null }}
plugins={TOOLTIP_PLUGINS}
/>
)}
</>
Expand Down
3 changes: 3 additions & 0 deletions src/components/Slider/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
import { SliderProps } from './types';
import * as Styled from './styles';
import { DEFAULT_ANIMATE, DEFAULT_SHOW_MARK_TICKS, DEFAULT_VALUE_LABEL_DISPLAY } from './constants';
import DismissibleValueLabel from './valueLabel';

/**
* A horizontal, single-thumb slider for picking a value inside a range.
Expand All @@ -26,6 +27,7 @@ function Slider ({
ariaLabel,
ariaLabelledBy,
ariaDescribedBy,
slots,
slotProps,
...rest
}: SliderProps): JSX.Element {
Expand Down Expand Up @@ -90,6 +92,7 @@ function Slider ({
aria-label={ariaLabel}
aria-labelledby={ariaLabel ? undefined : ariaLabelledBy || labelId}
data-test={dataTest}
slots={{ valueLabel: DismissibleValueLabel, ...slots }}
slotProps={{
...slotProps,
input: {
Expand Down
33 changes: 33 additions & 0 deletions src/components/Slider/valueLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { JSX, useEffect, useState } from 'react';
import { SliderValueLabel, SliderValueLabelProps } from '@mui/material';
import { ValueLabelDisplay } from './types';
import { VALUE_LABEL_DISPLAYS } from './constants';

// WCAG 1.4.13 requires a tooltip to be dismissible without moving the pointer or focus, and
// MUI keeps the label's open state internal with no Escape handling. A label that is always
// shown ('on') is not hover content, so it stays.
function DismissibleValueLabel({
open,
...props
}: SliderValueLabelProps & { valueLabelDisplay?: ValueLabelDisplay }): JSX.Element {
const [dismissed, setDismissed] = useState(false);
const dismissible = open && props.valueLabelDisplay !== VALUE_LABEL_DISPLAYS.ON;

useEffect(() => {
if (!dismissible) {
setDismissed(false);
return undefined;
}

const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') setDismissed(true);
};
document.addEventListener('keydown', onKeyDown, true);

return () => document.removeEventListener('keydown', onKeyDown, true);
}, [dismissible]);

return <SliderValueLabel {...props} open={open && !dismissed} />;
}

export default DismissibleValueLabel;
2 changes: 2 additions & 0 deletions src/components/Toggle/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as Styled from './styles';
import { DEFAULT_TEXT_POSITION, DEFAULT_TOOLTIP_PLACEMENT, TEXT_POSITIONS } from './constants';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';
import { TOOLTIP_PLUGINS } from '../../utils/tooltip';

// Cancelling the click (mouse, label or Space) keeps the switch unchanged, and MUI skips
// `onChange` for a prevented click. It sits on the switch because MUI drops event handlers
Expand Down Expand Up @@ -168,6 +169,7 @@ function Toggle ({
getReferenceClientRect={getTooltipAnchorRect}
delay={TOOLTIP_DELAY}
aria={{ content: null }}
plugins={TOOLTIP_PLUGINS}
/>
)}
</>
Expand Down
23 changes: 23 additions & 0 deletions src/utils/tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Plugin } from 'tippy.js';

// WCAG 1.4.13 requires a tooltip to be dismissible without moving the pointer or focus.
export const hideOnEsc: Plugin = {
name: 'hideOnEsc',
defaultValue: true,
fn({ hide }) {
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') hide();
};
const stopListening = () => document.removeEventListener('keydown', onKeyDown, true);

return {
onShow() {
document.addEventListener('keydown', onKeyDown, true);
},
onHide: stopListening,
onDestroy: stopListening,
};
},
};

export const TOOLTIP_PLUGINS = [hideOnEsc];
Loading