Skip to content

Commit

Permalink
[EuiFlyout] Remain open when mousedown happens inside the flyout (#…
Browse files Browse the repository at this point in the history
…5810)

* refactor flyout wrappers

* CL

* minimal cypress setup

* add realMouseMove

* Update upcoming_changelogs/5810.md

Co-authored-by: Constance <constancecchen@users.noreply.github.com>
  • Loading branch information
thompsongl and Constance committed Apr 21, 2022
1 parent a242b7f commit d4106ef
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 39 deletions.
99 changes: 99 additions & 0 deletions src/components/flyout/flyout.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/// <reference types="../../../cypress/support"/>

import React, { useState } from 'react';

import { EuiFlyout } from './flyout';

const childrenDefault = (
<>
<button data-test-subj="itemA">Item A</button>
<button data-test-subj="itemB">Item B</button>
<button data-test-subj="itemC">Item C</button>
<input data-test-subj="itemD" />
</>
);

const Flyout = ({ children = childrenDefault }) => {
const [isOpen, setIsOpen] = useState(true);

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

describe('EuiFlyout', () => {
describe('Focus behavior', () => {
it('focuses the close button by default', () => {
cy.mount(<Flyout />);
cy.focused().should(
'have.attr',
'data-test-subj',
'euiFlyoutCloseButton'
);
});

it('traps focus and cycles tabbable items', () => {
cy.mount(<Flyout />);
cy.realPress('Tab');
cy.realPress('Tab');
cy.realPress('Tab');
cy.focused().should('have.attr', 'data-test-subj', 'itemC');
cy.realPress('Tab');
cy.realPress('Tab');
cy.focused().should(
'have.attr',
'data-test-subj',
'euiFlyoutCloseButton'
);
});
});

describe('Close behavior', () => {
it('closes the flyout when the close button is clicked', () => {
cy.mount(<Flyout />);
cy.realPress('Enter').then(() => {
expect(cy.get('[data-test-subj="flyoutSpec"]').should('not.exist'));
});
});

it('closes the flyout with `escape` key', () => {
cy.mount(<Flyout />);
cy.realPress('Escape').then(() => {
expect(cy.get('[data-test-subj="flyoutSpec"]').should('not.exist'));
});
});

it('closes the flyout when the overlay mask is clicked', () => {
cy.mount(<Flyout />);
cy.get('.euiOverlayMask')
.realClick()
.then(() => {
expect(cy.get('[data-test-subj="flyoutSpec"]').should('not.exist'));
});
});

it('does not close the flyout when the overlay mask is only the target of mouseup', () => {
cy.mount(<Flyout />);
cy.get('[data-test-subj="itemD"]').realMouseDown().realMouseMove(-100, 0);
cy.get('.euiOverlayMask')
.realMouseUp()
.then(() => {
expect(cy.get('[data-test-subj="flyoutSpec"]').should('exist'));
});
});
});
});
68 changes: 29 additions & 39 deletions src/components/flyout/flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import { EuiOverlayMask, EuiOverlayMaskProps } from '../overlay_mask';
import { EuiButtonIcon, EuiButtonIconPropsForButton } from '../button';
import { EuiI18n } from '../i18n';
import { useResizeObserver } from '../observer/resize_observer';
import { EuiOutsideClickDetector } from '../outside_click_detector';
import { EuiPortal } from '../portal';

const typeToClassNameMap = {
Expand Down Expand Up @@ -340,57 +339,48 @@ export const EuiFlyout = forwardRef(
);
}

const flyoutContent = (
<Element
{...(rest as ComponentPropsWithRef<T>)}
role={role}
className={classes}
tabIndex={-1}
style={newStyle || style}
ref={setRef}
>
{closeButton}
{children}
</Element>
);

const isDefaultConfiguration = ownFocus && !isPushed;
const onClickOutside =
(isDefaultConfiguration && outsideClickCloses !== false) ||
outsideClickCloses === true
? onClose
: undefined;
/*
* Trap focus even when `ownFocus={false}`, otherwise closing
* the flyout won't return focus to the originating button.
*
* Set `clickOutsideDisables={true}` when `ownFocus={false}`
* to allow non-keyboard users the ability to interact with
* elements outside the flyout.
*
* Set `onClickOutside={onClose}` when `ownFocus` and `type` are the defaults,
* or if `outsideClickCloses={true}` to close on clicks that target
* (both mousedown and mouseup) the overlay mask.
*/
let flyout = (
<EuiFocusTrap disabled={isPushed} clickOutsideDisables={!ownFocus}>
{flyoutContent}
<EuiFocusTrap
disabled={isPushed}
clickOutsideDisables={!ownFocus}
onClickOutside={onClickOutside}
>
<Element
{...(rest as ComponentPropsWithRef<T>)}
role={role}
className={classes}
tabIndex={-1}
style={newStyle || style}
ref={setRef}
>
{closeButton}
{children}
</Element>
</EuiFocusTrap>
);
/**
* Unless outsideClickCloses = true, then add the outside click detector
*/
if (ownFocus === false && outsideClickCloses === true) {
flyout = (
<EuiFocusTrap disabled={isPushed} clickOutsideDisables={!ownFocus}>
{/* Outside click detector is needed if theres no overlay mask to auto-close when clicking on elements outside */}
<EuiOutsideClickDetector
isDisabled={isPushed}
onOutsideClick={() => onClose()}
>
{flyoutContent}
</EuiOutsideClickDetector>
</EuiFocusTrap>
);
}

// If ownFocus is set, wrap with an overlay and allow the user to click it to close it.
if (ownFocus && !isPushed) {
if (isDefaultConfiguration) {
flyout = (
<EuiOverlayMask
onClick={outsideClickCloses === false ? undefined : onClose}
headerZindexLocation="below"
{...maskProps}
>
<EuiOverlayMask headerZindexLocation="below" {...maskProps}>
{flyout}
</EuiOverlayMask>
);
Expand Down
4 changes: 4 additions & 0 deletions upcoming_changelogs/5810.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**Bug fixes**

- Fixed `EuiFlyout` so that it no longer closes when a click starts inside the flyout but completes outside

0 comments on commit d4106ef

Please sign in to comment.