Skip to content

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

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

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions