Skip to content

Commit

Permalink
fix(tool_tip): stop propagation on escape key down (#8140)
Browse files Browse the repository at this point in the history
  • Loading branch information
weronikaolejniczak authored Nov 29, 2024
1 parent ab3c0d5 commit b0425f4
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 15 deletions.
3 changes: 3 additions & 0 deletions packages/eui/changelogs/upcoming/8140.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Accessibility**

- When the tooltips components (`EuiTooltip`, `EuiIconTip`) are used inside components that handle the Escape key (like `EuiFlyout` or `EuiModal`), pressing the Escape key will now only close the tooltip and not the entire wrapping component.
166 changes: 151 additions & 15 deletions packages/eui/src/components/tool_tip/tool_tip.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
/// <reference types="cypress-real-events" />
/// <reference types="../../../cypress/support" />

import React from 'react';
import React, { ComponentProps, useState } from 'react';

import { EuiButton } from '../../components';
import { EuiButton } from '../button';
import { EuiFlyout } from '../flyout';
import { EuiModal } from '../modal';
import { EuiPopover } from '../popover';
import { EuiToolTip } from './tool_tip';

describe('EuiToolTip', () => {
Expand Down Expand Up @@ -46,19 +49,6 @@ describe('EuiToolTip', () => {
cy.get('[data-test-subj="tooltip"]').should('not.exist');
});

it('hides the tooltip on Escape key down', () => {
cy.realMount(
<EuiToolTip content="Tooltip text here" data-test-subj="tooltip">
<EuiButton data-test-subj="toggleToolTip">Show tooltip</EuiButton>
</EuiToolTip>
);
cy.realPress('Tab');
cy.get('[data-test-subj="tooltip"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="tooltip"]').should('not.exist');
});

it('does not show multiple tooltips if one tooltip toggle is focused and another tooltip toggle is hovered', () => {
cy.mount(
<>
Expand All @@ -80,4 +70,150 @@ describe('EuiToolTip', () => {
cy.contains('Tooltip B').should('exist');
cy.contains('Tooltip A').should('not.exist');
});

describe('Escape key', () => {
it('hides the tooltip when rendered by itself', () => {
cy.mount(
<EuiToolTip content="Tooltip text here" data-test-subj="tooltip">
<EuiButton data-test-subj="toggleToolTip">Show tooltip</EuiButton>
</EuiToolTip>
);
cy.realPress('Tab');
cy.get('[data-test-subj="tooltip"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="tooltip"]').should('not.exist');
});

it('hides the tooltip when rendered by EuiFlyout', () => {
const Flyout = (
props: Omit<ComponentProps<typeof EuiFlyout>, 'onClose'>
) => {
const [isOpen, setIsOpen] = useState(true);

return isOpen ? (
<EuiFlyout
{...props}
data-test-subj="flyout"
onClose={() => setIsOpen(false)}
/>
) : null;
};

cy.mount(
<Flyout>
<EuiToolTip content="Tooltip text here" data-test-subj="tool_tip">
<EuiButton data-test-subj="tool_tip_trigger">
Show tooltip
</EuiButton>
</EuiToolTip>
</Flyout>
);
cy.get('[data-test-subj="tool_tip"]').should('not.exist');

cy.get('[data-test-subj="flyout"]').focus();

cy.repeatRealPress('Tab', 2);
cy.get('[data-test-subj="tool_tip"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="tool_tip"]').should('not.exist');
cy.get('[data-test-subj="flyout"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="flyout"]').should('not.exist');
});

it('hides the tooltip when rendered by EuiModal', () => {
const Modal = (
props: Omit<ComponentProps<typeof EuiModal>, 'onClose'>
) => {
const [isOpen, setIsOpen] = useState(true);

return isOpen ? (
<EuiModal
{...props}
data-test-subj="modal"
onClose={() => setIsOpen(false)}
/>
) : null;
};

cy.mount(
<Modal>
<EuiToolTip content="Tooltip text here" data-test-subj="tool_tip">
<EuiButton data-test-subj="tool_tip_trigger">
Show tooltip
</EuiButton>
</EuiToolTip>
</Modal>
);
cy.get('[data-test-subj="tool_tip"]').should('not.exist');

cy.get('[data-test-subj="modal"]').focus();

cy.repeatRealPress('Tab', 2);
cy.get('[data-test-subj="tool_tip"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="tool_tip"]').should('not.exist');
cy.get('[data-test-subj="modal"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="modal"]').should('not.exist');
});

it('hides the tooltip when rendered by EuiPopover', () => {
const Popover = (
props: Omit<
ComponentProps<typeof EuiPopover>,
'closePopover' | 'button'
>
) => {
const [isOpen, setIsOpen] = useState(true);

return (
<EuiPopover
{...props}
button={
<EuiButton
onClick={() => setIsOpen(true)}
data-test-subj="popover_toggle"
>
Show popover
</EuiButton>
}
panelProps={{ 'data-test-subj': 'popover' }}
isOpen={isOpen}
closePopover={() => setIsOpen(false)}
>
{props.children}
</EuiPopover>
);
};

cy.mount(
<Popover>
<EuiToolTip content="Tooltip text here" data-test-subj="tool_tip">
<EuiButton data-test-subj="tool_tip_trigger">
Show tooltip
</EuiButton>
</EuiToolTip>
</Popover>
);
cy.get('[data-test-subj="tool_tip"]').should('not.exist');

cy.get('[data-test-subj="popover"]').focus();

cy.realPress('Tab');
cy.get('[data-test-subj="tool_tip"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="tool_tip"]').should('not.exist');
cy.get('[data-test-subj="popover"]').should('exist');

cy.realPress('Escape');
cy.get('[data-test-subj="popover"]').should('not.exist');
});
});
});
1 change: 1 addition & 0 deletions packages/eui/src/components/tool_tip/tool_tip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ export class EuiToolTip extends Component<EuiToolTipProps, State> {

onEscapeKey = (event: React.KeyboardEvent<HTMLSpanElement>) => {
if (event.key === keys.ESCAPE) {
if (this.state.visible) event.stopPropagation();
this.setState({ hasFocus: false }); // Allows mousing over back into the tooltip to work correctly
this.hideToolTip();
}
Expand Down

0 comments on commit b0425f4

Please sign in to comment.