Skip to content

fix(Modal): allow Escape to close ContextualMenu before closing Modal#1339

Open
koushik717 wants to merge 1 commit intocanonical:mainfrom
koushik717:fix/contextual-menu-escape-in-modal
Open

fix(Modal): allow Escape to close ContextualMenu before closing Modal#1339
koushik717 wants to merge 1 commit intocanonical:mainfrom
koushik717:fix/contextual-menu-escape-in-modal

Conversation

@koushik717
Copy link
Copy Markdown

Problem

Fixes #1305

When a ContextualMenu is open inside a Modal, pressing Escape closes
the Modal immediately rather than closing the menu first. This breaks the
expected layered-dismiss UX and is an accessibility issue — screen reader
users expect Escape to close the topmost layer only.

Root cause

Modal.tsx calls stopImmediatePropagation() unconditionally on every
Escape keydown event. Because ContextualMenu renders its dropdown via a
Portal (a DOM sibling of Modal, outside the React tree), its own keydown
handler never receives the event — Modal has already stopped propagation.

Fix

Added a guard in Modal.tsx: before stopping propagation, check whether
an open contextual menu dropdown exists in the DOM
(.p-contextual-menu__dropdown[aria-hidden="false"]). If one is found,
allow the event to propagate so the ContextualMenu can handle it and close.
A subsequent Escape keydown (with no open dropdown) then closes the Modal
as normal.

This produces the correct two-step dismiss behaviour:

  1. First Escape → closes the ContextualMenu
  2. Second Escape → closes the Modal

Changes

  • src/components/Modal/Modal.tsx — guard logic before stopImmediatePropagation
  • src/components/Modal/Modal.test.tsx — regression test for the two-step dismiss

Testing

All 832 existing tests pass. New regression test added specifically for
Escape behaviour when a ContextualMenu is open inside a Modal.

yarn lint  ✓
yarn test  ✓  (832 tests)

When a ContextualMenu is rendered inside a Modal, pressing Escape failed
to close the menu because Modal's handleEscKey called
stopImmediatePropagation() unconditionally. Since ContextualMenu renders
via a Portal (a DOM sibling), its document-level keydown listener was
silenced before it could run.

Fix by checking for an open contextual menu dropdown
(.p-contextual-menu__dropdown[aria-hidden="false"]) before stopping
propagation. If one exists, the Escape event is allowed to continue so
the menu's own handler can close it first. A subsequent Escape press
will then close the Modal as expected.

Fixes canonical#1305
@webteam-app
Copy link
Copy Markdown

koushik717 is not a collaborator of the repo

koushik717 added a commit to koushik717/cloud-init-builder that referenced this pull request Apr 3, 2026
- Schema-driven forms for 8 cloud-init modules (users, packages, runcmd,
  write_files, ssh, hostname, timezone, ntp)
- Real-time YAML output with Monaco Editor starting with #cloud-config
- Client-side validation with Ajv against official cloud-init JSON schema
- Server-side validation via FastAPI backend
- 5 built-in templates: Ubuntu Server, Docker Host, Kubernetes Node,
  Web Server, Developer Workstation
- Shareable config links via lz-string URL encoding
- Full keyboard navigation and ARIA accessibility
- axe-core accessibility tests in CI
- Built with @canonical/react-components (Vanilla Framework)
- Motivated by canonical/cloud-init#6796 and canonical/react-components#1339
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ContextualMenu cannot be closed with ESCAPE key when inside a Modal

2 participants