Skip to content

Commit 5850a35

Browse files
authored
Merge branch 'ionic-team:main' into main
2 parents fefb1ec + 3b80473 commit 5850a35

File tree

66 files changed

+1489
-139
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1489
-139
lines changed

.github/workflows/assign-issues.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ jobs:
1313
- name: 'Auto-assign issue'
1414
uses: pozil/auto-assign-issue@39c06395cbac76e79afc4ad4e5c5c6db6ecfdd2e # v2.2.0
1515
with:
16-
assignees: brandyscarney, ShaneK
16+
assignees: brandyscarney, thetaPC, ShaneK
1717
numOfAssignee: 1
1818
allowSelfAssign: false

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
7+
8+
9+
### Bug Fixes
10+
11+
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
12+
* **vue:** emit component-specific overlay events ([#30688](https://github.com/ionic-team/ionic-framework/issues/30688)) ([024d090](https://github.com/ionic-team/ionic-framework/commit/024d090122548e26ec2cdcfae4637dde8f288278)), closes [#30641](https://github.com/ionic-team/ionic-framework/issues/30641)
13+
14+
15+
16+
17+
618
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
719

820

core/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
7+
8+
9+
### Bug Fixes
10+
11+
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
12+
13+
14+
15+
16+
617
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
718

819

core/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Get Playwright
2-
FROM mcr.microsoft.com/playwright:v1.55.0
2+
FROM mcr.microsoft.com/playwright:v1.55.1
33

44
# Set the working directory
55
WORKDIR /ionic

core/package-lock.json

Lines changed: 25 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ionic/core",
3-
"version": "8.7.4",
3+
"version": "8.7.5",
44
"description": "Base components for Ionic",
55
"keywords": [
66
"ionic",
@@ -44,7 +44,7 @@
4444
"@clack/prompts": "^0.11.0",
4545
"@ionic/eslint-config": "^0.3.0",
4646
"@ionic/prettier-config": "^2.0.0",
47-
"@playwright/test": "^1.55.0",
47+
"@playwright/test": "^1.55.1",
4848
"@rollup/plugin-node-resolve": "^8.4.0",
4949
"@rollup/plugin-virtual": "^2.0.3",
5050
"@stencil/angular-output-target": "^0.10.0",

core/src/components/modal/gestures/sheet.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ export const createSheetGesture = (
9595
const contentAnimation = animation.childAnimations.find((ani) => ani.id === 'contentAnimation');
9696

9797
const enableBackdrop = () => {
98+
// Respect explicit opt-out of focus trapping/backdrop interactions
99+
// If focusTrap is false or showBackdrop is false, do not enable the backdrop or re-enable focus trap
100+
const el = baseEl as HTMLIonModalElement & { focusTrap?: boolean; showBackdrop?: boolean };
101+
if (el.focusTrap === false || el.showBackdrop === false) {
102+
return;
103+
}
98104
baseEl.style.setProperty('pointer-events', 'auto');
99105
backdropEl.style.setProperty('pointer-events', 'auto');
100106

@@ -235,7 +241,10 @@ export const createSheetGesture = (
235241
* ion-backdrop and .modal-wrapper always have pointer-events: auto
236242
* applied, so the modal content can still be interacted with.
237243
*/
238-
const shouldEnableBackdrop = currentBreakpoint > backdropBreakpoint;
244+
const shouldEnableBackdrop =
245+
currentBreakpoint > backdropBreakpoint &&
246+
(baseEl as HTMLIonModalElement & { focusTrap?: boolean }).focusTrap !== false &&
247+
(baseEl as HTMLIonModalElement & { showBackdrop?: boolean }).showBackdrop !== false;
239248
if (shouldEnableBackdrop) {
240249
enableBackdrop();
241250
} else {
@@ -582,7 +591,10 @@ export const createSheetGesture = (
582591
* Backdrop should become enabled
583592
* after the backdropBreakpoint value
584593
*/
585-
const shouldEnableBackdrop = currentBreakpoint > backdropBreakpoint;
594+
const shouldEnableBackdrop =
595+
currentBreakpoint > backdropBreakpoint &&
596+
(baseEl as HTMLIonModalElement & { focusTrap?: boolean }).focusTrap !== false &&
597+
(baseEl as HTMLIonModalElement & { showBackdrop?: boolean }).showBackdrop !== false;
586598
if (shouldEnableBackdrop) {
587599
enableBackdrop();
588600
} else {

core/src/utils/overlays.ts

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,8 @@ export const setRootAriaHidden = (hidden = false) => {
494494

495495
if (hidden) {
496496
viewContainer.setAttribute('aria-hidden', 'true');
497-
viewContainer.setAttribute('inert', '');
498497
} else {
499498
viewContainer.removeAttribute('aria-hidden');
500-
viewContainer.removeAttribute('inert');
501499
}
502500
};
503501

@@ -529,15 +527,37 @@ export const present = async <OverlayPresentOptions>(
529527
* focus traps.
530528
*
531529
* All other overlays should have focus traps to prevent
532-
* the keyboard focus from leaving the overlay.
530+
* the keyboard focus from leaving the overlay unless
531+
* developers explicitly opt out (for example, sheet
532+
* modals that should permit background interaction).
533+
*
534+
* Note: Some apps move inline overlays to a specific container
535+
* during the willPresent lifecycle (e.g., React portals via
536+
* onWillPresent). Defer applying aria-hidden/inert to the app
537+
* root until after willPresent so we can detect where the
538+
* overlay is finally inserted. If the overlay is inside the
539+
* view container subtree, skip adding aria-hidden/inert there
540+
* to avoid disabling the overlay.
533541
*/
534-
if (overlay.el.tagName !== 'ION-TOAST') {
535-
setRootAriaHidden(true);
536-
document.body.classList.add(BACKDROP_NO_SCROLL);
537-
}
542+
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
543+
const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
544+
// Only lock out root content when backdrop is active. Developers relying on showBackdrop=false
545+
// expect background interaction to remain enabled.
546+
const shouldLockRoot = shouldTrapFocus && overlayEl.showBackdrop !== false;
538547

539548
overlay.presented = true;
540549
overlay.willPresent.emit();
550+
551+
if (shouldLockRoot) {
552+
const root = getAppRoot(document);
553+
const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root');
554+
const overlayInsideViewContainer = viewContainer ? viewContainer.contains(overlayEl) : false;
555+
556+
if (!overlayInsideViewContainer) {
557+
setRootAriaHidden(true);
558+
}
559+
document.body.classList.add(BACKDROP_NO_SCROLL);
560+
}
541561
overlay.willPresentShorthand?.emit();
542562

543563
const mode = getIonMode(overlay);
@@ -653,22 +673,28 @@ export const dismiss = async <OverlayDismissOptions>(
653673
* For accessibility, toasts lack focus traps and don't receive
654674
* `aria-hidden` on the root element when presented.
655675
*
656-
* All other overlays use focus traps to keep keyboard focus
657-
* within the overlay, setting `aria-hidden` on the root element
658-
* to enhance accessibility.
659-
*
660-
* Therefore, we must remove `aria-hidden` from the root element
661-
* when the last non-toast overlay is dismissed.
676+
* Overlays that opt into focus trapping set `aria-hidden`
677+
* on the root element to keep keyboard focus and pointer
678+
* events inside the overlay. We must remove `aria-hidden`
679+
* from the root element when the last focus-trapping overlay
680+
* is dismissed.
662681
*/
663-
const overlaysNotToast = presentedOverlays.filter((o) => o.tagName !== 'ION-TOAST');
664-
665-
const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
682+
const overlaysLockingRoot = presentedOverlays.filter((o) => {
683+
const el = o as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
684+
return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && el.showBackdrop !== false;
685+
});
686+
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
687+
const locksRoot =
688+
overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false && overlayEl.showBackdrop !== false;
666689

667690
/**
668-
* If this is the last visible overlay that is not a toast
691+
* If this is the last visible overlay that is trapping focus
669692
* then we want to re-add the root to the accessibility tree.
670693
*/
671-
if (lastOverlayNotToast) {
694+
const lastOverlayTrappingFocus =
695+
locksRoot && overlaysLockingRoot.length === 1 && overlaysLockingRoot[0].id === overlayEl.id;
696+
697+
if (lastOverlayTrappingFocus) {
672698
setRootAriaHidden(false);
673699
document.body.classList.remove(BACKDROP_NO_SCROLL);
674700
}

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"core",
44
"packages/*"
55
],
6-
"version": "8.7.4"
6+
"version": "8.7.5"
77
}

packages/angular-server/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
7+
8+
**Note:** Version bump only for package @ionic/angular-server
9+
10+
11+
12+
13+
614
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
715

816
**Note:** Version bump only for package @ionic/angular-server

0 commit comments

Comments
 (0)