Skip to content

Tearsheet: Escape should only close the active topmost layer #989

Open

Description

@nickmazzi

Problem

When a Tearsheet opens another modal, such as a file-selection modal, pressing
Escape closes both the modal and the underlying Tearsheet.

This suggests that the Escape-key event is being handled by multiple layers rather
than only by the active topmost layer. With stacked Tearsheets, the same behavior
could dismiss multiple workflow levels with a single keypress and unexpectedly
discard in-progress work.

Steps to reproduce

  1. Open a Tearsheet.

  2. Open a modal or file-selection flow from within the Tearsheet.

  3. Press Escape.

  4. Observe that both the nested modal and the underlying Tearsheet close.

  5. Repeat with multiple stacked Tearsheets and verify whether one Escape

    dismisses more than the active layer.

Here I hit escape to cancel out of the modal but all views are closed:

image

Expected behavior

Pressing Escape should dismiss only the active topmost modal or Tearsheet.
Underlying layers should remain open, and the event should not continue to
trigger their close handlers.

Context

This was observed in
odh-dashboard#9589.
The follow-up discussion is
here.

Related stacked-modal accessibility work:


Jira Issue: PF-4706

Activity

  1. thatblindgeye commented on Oct 2, 2026

    @thatblindgeye
    Contributor

    Hi @nickmazzi . At the moment it should be possible to tweak the Escape press behavior via the onEscapePress prop on Tearsheet/Modal, similar to our Modal with Dropdown example. in that example we're checking if the Dropdowwn is open (if it is, we can assume focus is on/in the dropdown or its toggle) and closing the MenuToggle only on Escape, otherwise we close the Modal.

    Our base Modal by default uses whatever is passed to the onClose prop for its Escape key press logic, and adds an eventlistener, so that'd explain partially why Escape is closing all the modals/tearsheets.

    For the Stacked Tearsheet example that was added in #954, I added the following:

      const onEscapePress = (event: KeyboardEvent) => {
        event.stopImmediatePropagation();
        if (isStack2TearsheetOpen) {
          toggleStack2Tearsheet(event);
        } else if (isStack1TearsheetOpen) {
          toggleStack1Tearsheet(event);
        } else {
          toggleTearsheet(event);
        }
      };
    

    And passed onEscapePress={onEscapePress} to each Tearsheet in that example and Escape only closed the immediate layer (only passed onEscapePress to the "base" or lowest level Tearsheet also worked fine since it's all being fired off the onkeydown eventlistener). Not the prettiest, but something like that should work whether you're dealing with only Tearsheets or Tearsheets and Modals like you described.

    I think if we were to try and finagle the code on the PatternFly side, it'd have to be in our React repo on the Modal component, since Tearsheet is just using our Modal component.

  2. GAUNSD commented on Oct 2, 2026

    @GAUNSD
    Contributor

    Now it's just a question of whether we add this implementation to the <Modal/> or to the <Tearsheet/>. That, or leave it up to consumers to decide on the behaviour

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions