Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tool_tip): stop propagation on escape key down #8140

Merged
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: 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