Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b4174f9
fix(core): restore prop defaults when consumers downlevel (#31375)
ShaneK Aug 19, 2026
fe3d351
chore(deps): update actions/setup-node action to v7 (#31384)
renovate[bot] Aug 20, 2026
fea11f4
chore(deps): update actions/checkout action to v7.0.1 (#31378)
renovate[bot] Aug 20, 2026
461fa1b
chore(deps): update dependency ts-morph to v28 (#31385)
renovate[bot] Aug 20, 2026
d41f258
fix(refresher): validate slot after frameworks assign it (#31377)
ShaneK Aug 20, 2026
898001c
fix(react-router): skip page clone when nothing will animate (#31387)
ShaneK Aug 20, 2026
8ab64ad
chore(deps): update capacitor to v8.5.0 (#31383)
renovate[bot] Aug 20, 2026
7956b97
fix(item-sliding, segment-button): read props after frameworks assign…
ShaneK Aug 20, 2026
51a2694
chore(deps): update dependency vitest to v4 (#31395)
renovate[bot] Aug 21, 2026
7879ec5
fix(select): derive option text from all child nodes (#31382)
ptmkenny Aug 21, 2026
ab731a1
chore(deps): update to stencil/core 4.44.1 (#31396)
OS-jacobbell Aug 21, 2026
95e2224
fix(picker-column): commit value on outside press (#31397)
ShaneK Aug 21, 2026
bfff8e1
fix(overlays): restore presented state after a DOM move (#31400)
ShaneK Aug 25, 2026
81bae39
chore(deps): update stencil/core to 4.44.2 and vue-output-target to 0…
ShaneK Aug 26, 2026
ea91bab
chore(deps): update github/codeql-action action to v4.37.8 (#31398)
renovate[bot] Aug 26, 2026
d696a6a
v9.0.1
Ionitron Aug 26, 2026
d1f086e
chore(): update package lock files
Ionitron Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
- uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
languages: javascript
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
- uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
6 changes: 3 additions & 3 deletions .github/workflows/migrate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
outputs:
migrate: ${{ steps.filter.outputs.migrate }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Full history so the PR base commit is reachable for the diff below.
fetch-depth: 0
Expand Down Expand Up @@ -63,8 +63,8 @@ jobs:
run:
working-directory: ./packages/migrate
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.x
cache: 'npm'
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [9.0.1](https://github.com/ionic-team/ionic-framework/compare/v9.0.0...v9.0.1) (2026-08-26)

### Bug Fixes

* **core:** restore prop defaults when consumers downlevel ([#31375](https://github.com/ionic-team/ionic-framework/issues/31375)) ([b4174f9](https://github.com/ionic-team/ionic-framework/commit/b4174f96f5061ebc93119cc7e1363162ae666ae9)), closes [#31280](https://github.com/ionic-team/ionic-framework/issues/31280)
* **item-sliding, segment-button:** read props after frameworks assign them ([#31391](https://github.com/ionic-team/ionic-framework/issues/31391)) ([7956b97](https://github.com/ionic-team/ionic-framework/commit/7956b973b8bb55e5f0cae844ee992fe9921432cb)), closes [#31388](https://github.com/ionic-team/ionic-framework/issues/31388)
* **overlays:** restore presented state after a DOM move ([#31400](https://github.com/ionic-team/ionic-framework/issues/31400)) ([bfff8e1](https://github.com/ionic-team/ionic-framework/commit/bfff8e1377a8faff3581e61026733ce29b671513)), closes [#31389](https://github.com/ionic-team/ionic-framework/issues/31389) [#31390](https://github.com/ionic-team/ionic-framework/issues/31390) [#31389](https://github.com/ionic-team/ionic-framework/issues/31389)
* **picker-column:** commit value on outside press ([#31397](https://github.com/ionic-team/ionic-framework/issues/31397)) ([95e2224](https://github.com/ionic-team/ionic-framework/commit/95e222488d4e4bac9c6c8c0b42a2154082846767)), closes [#30449](https://github.com/ionic-team/ionic-framework/issues/30449) [#30674](https://github.com/ionic-team/ionic-framework/issues/30674)
* **react-router:** skip page clone when nothing will animate ([#31387](https://github.com/ionic-team/ionic-framework/issues/31387)) ([898001c](https://github.com/ionic-team/ionic-framework/commit/898001c5c6a92a3269dd8e60c4e79da1c634af9d))
* **refresher:** validate slot after frameworks assign it ([#31377](https://github.com/ionic-team/ionic-framework/issues/31377)) ([d41f258](https://github.com/ionic-team/ionic-framework/commit/d41f25860dcb9a71a40226973cca9ca223b7f7ca)), closes [#31376](https://github.com/ionic-team/ionic-framework/issues/31376)
* **select:** derive option text from all child nodes ([#31382](https://github.com/ionic-team/ionic-framework/issues/31382)) ([7879ec5](https://github.com/ionic-team/ionic-framework/commit/7879ec5763be92b4ecf03c6c58975265166e33ba)), closes [#31381](https://github.com/ionic-team/ionic-framework/issues/31381)


# [9.0.0](https://github.com/ionic-team/ionic-framework/compare/v8.8.19...v9.0.0) (2026-08-19)

**Full Changelog**: https://github.com/ionic-team/ionic-framework/compare/v8.8.19...v9.0.0
Expand Down
12 changes: 12 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [9.0.1](https://github.com/ionic-team/ionic-framework/compare/v9.0.0...v9.0.1) (2026-08-26)

### Bug Fixes

* **core:** restore prop defaults when consumers downlevel ([#31375](https://github.com/ionic-team/ionic-framework/issues/31375)) ([b4174f9](https://github.com/ionic-team/ionic-framework/commit/b4174f96f5061ebc93119cc7e1363162ae666ae9)), closes [#31280](https://github.com/ionic-team/ionic-framework/issues/31280)
* **item-sliding, segment-button:** read props after frameworks assign them ([#31391](https://github.com/ionic-team/ionic-framework/issues/31391)) ([7956b97](https://github.com/ionic-team/ionic-framework/commit/7956b973b8bb55e5f0cae844ee992fe9921432cb)), closes [#31388](https://github.com/ionic-team/ionic-framework/issues/31388)
* **overlays:** restore presented state after a DOM move ([#31400](https://github.com/ionic-team/ionic-framework/issues/31400)) ([bfff8e1](https://github.com/ionic-team/ionic-framework/commit/bfff8e1377a8faff3581e61026733ce29b671513)), closes [#31389](https://github.com/ionic-team/ionic-framework/issues/31389) [#31390](https://github.com/ionic-team/ionic-framework/issues/31390) [#31389](https://github.com/ionic-team/ionic-framework/issues/31389)
* **picker-column:** commit value on outside press ([#31397](https://github.com/ionic-team/ionic-framework/issues/31397)) ([95e2224](https://github.com/ionic-team/ionic-framework/commit/95e222488d4e4bac9c6c8c0b42a2154082846767)), closes [#30449](https://github.com/ionic-team/ionic-framework/issues/30449) [#30674](https://github.com/ionic-team/ionic-framework/issues/30674)
* **refresher:** validate slot after frameworks assign it ([#31377](https://github.com/ionic-team/ionic-framework/issues/31377)) ([d41f258](https://github.com/ionic-team/ionic-framework/commit/d41f25860dcb9a71a40226973cca9ca223b7f7ca)), closes [#31376](https://github.com/ionic-team/ionic-framework/issues/31376)
* **select:** derive option text from all child nodes ([#31382](https://github.com/ionic-team/ionic-framework/issues/31382)) ([7879ec5](https://github.com/ionic-team/ionic-framework/commit/7879ec5763be92b4ecf03c6c58975265166e33ba)), closes [#31381](https://github.com/ionic-team/ionic-framework/issues/31381)


# [9.0.0](https://github.com/ionic-team/ionic-framework/compare/v8.8.19...v9.0.0) (2026-08-19)

**Full Changelog**: https://github.com/ionic-team/ionic-framework/compare/v8.8.19...v9.0.0
Expand Down
32 changes: 16 additions & 16 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "9.0.0",
"version": "9.0.1",
"description": "Base components for Ionic",
"engines": {
"node": ">= 16"
Expand Down Expand Up @@ -66,7 +66,7 @@
"loader/"
],
"dependencies": {
"@stencil/core": "^4.43.5",
"@stencil/core": "^4.44.2",
"ionicons": "^8.1.0",
"tslib": "^2.1.0"
},
Expand All @@ -86,7 +86,7 @@
"@stencil/angular-output-target": "^1.4.1",
"@stencil/react-output-target": "^1.6.2",
"@stencil/sass": "^3.0.9",
"@stencil/vue-output-target": "0.14.1",
"@stencil/vue-output-target": "0.14.2",
"@types/jest": "^29.5.6",
"@types/node": "^24.13.3",
"@typescript-eslint/eslint-plugin": "^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5168,7 +5168,7 @@ declare global {
}
}
declare namespace LocalJSX {
type OneOf<K extends string, PropT, AttrT = PropT> = { [P in K]: PropT } & { [P in `attr:${K}` | `prop:${K}`]?: never } | { [P in `attr:${K}`]: AttrT } & { [P in K | `prop:${K}`]?: never } | { [P in `prop:${K}`]: PropT } & { [P in K | `attr:${K}`]?: never };
type OneOf<K extends string, PropT, AttrT = PropT> = { [P in K]: PropT } & { [P in `attr:${K}`]?: never } | { [P in `attr:${K}`]: AttrT } & { [P in K]?: never };

interface IonAccordion {
/**
Expand Down
56 changes: 38 additions & 18 deletions core/src/components/action-sheet/action-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isCancel,
prepareOverlay,
present,
restoreRootFocusTrapAccessibility,
safeCall,
setOverlayId,
} from '@utils/overlays';
Expand Down Expand Up @@ -455,6 +456,15 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
connectedCallback() {
prepareOverlay(this.el);
this.triggerChanged();

// `componentDidLoad` only fires once per instance, so a reconnect has to
// rebuild the gesture its disconnect destroyed.
this.setupButtonActiveGesture();

// Re-apply the root lock if moved without dismiss() being called
if (this.presented) {
restoreRootFocusTrapAccessibility(this.el);
}
}

disconnectedCallback() {
Expand All @@ -478,26 +488,36 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
this.buttonsChanged();
}

componentDidLoad() {
/**
* Only create gesture if:
* 1. A gesture does not already exist
* 2. App is running in iOS mode
* 3. A wrapper ref exists
* 4. A group ref exists
*/
/**
* Only create gesture if:
* 1. A gesture does not already exist
* 2. App is running in iOS mode
* 3. A wrapper ref exists
* 4. A group ref exists
* 5. The host is still connected, since a reconnect can schedule this and
* disconnect again before the task runs
*/
private setupButtonActiveGesture() {
const { groupEl, wrapperEl } = this;
if (!this.gesture && getIonMode(this) === 'ios' && wrapperEl && groupEl) {
readTask(() => {
const isScrollable = groupEl.scrollHeight > groupEl.clientHeight;
if (!isScrollable) {
this.gesture = createButtonActiveGesture(wrapperEl, (refEl: HTMLElement) =>
refEl.classList.contains('action-sheet-button')
);
this.gesture.enable(true);
}
});
if (getIonMode(this) !== 'ios' || !wrapperEl || !groupEl) {
return;
}
readTask(() => {
// Bail if a call queued ahead of this one already built the gesture
// (a second would orphan the first with its listeners still bound), if
// the host disconnected while this task waited, or if the group scrolls.
if (this.gesture || !this.el.isConnected || groupEl.scrollHeight > groupEl.clientHeight) {
return;
}
this.gesture = createButtonActiveGesture(wrapperEl, (refEl: HTMLElement) =>
refEl.classList.contains('action-sheet-button')
);
this.gesture.enable(true);
});
}

componentDidLoad() {
this.setupButtonActiveGesture();

/**
* If action sheet was rendered with isOpen="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
import { configs, detachAndReattach, test } from '@utils/test/playwright';

import { ActionSheetFixture } from './fixture';

Expand Down Expand Up @@ -166,3 +166,56 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
});
});
});

/**
* The button gesture only exists in iOS mode, so these run there.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => {
test.describe(title('action sheet: moved while presented'), () => {
test.beforeEach(async ({ page }) => {
await page.goto('/src/components/action-sheet/test/basic', config);
});

test('should keep the app root locked', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/31389',
});

const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');

await page.click('#basic');
await ionActionSheetDidPresent.next();

await expect(page.locator('body')).toHaveClass(/backdrop-no-scroll/);

await detachAndReattach(page.locator('ion-action-sheet'));

await expect(page.locator('body')).toHaveClass(/backdrop-no-scroll/);
});

test('should keep activating buttons on press', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/31389',
});

const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');

await page.click('#basic');
await ionActionSheetDidPresent.next();

await detachAndReattach(page.locator('ion-action-sheet'));

const button = page.locator('ion-action-sheet .action-sheet-button').first();
const box = (await button.boundingBox())!;

await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();

await expect(button).toHaveClass(/ion-activated/);

await page.mouse.up();
});
});
});
28 changes: 19 additions & 9 deletions core/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
isCancel,
prepareOverlay,
present,
restoreRootFocusTrapAccessibility,
safeCall,
setOverlayId,
} from '@utils/overlays';
Expand Down Expand Up @@ -363,10 +364,16 @@ export class Alert implements ComponentInterface, OverlayInterface {
this.triggerChanged();
/**
* If the alert was previously connected and is being reattached, the
* ResizeObserver was disconnected. componentDidLoad only fires once per
* instance, so re-establish the observer here on reconnect.
* `ResizeObserver` and the button gesture were torn down. `componentDidLoad`
* only fires once per instance, so re-establish both here on reconnect.
*/
this.setupButtonGroupResizeObserver();
this.setupButtonActiveGesture();

// Re-apply the root lock if moved without dismiss() being called
if (this.presented) {
restoreRootFocusTrapAccessibility(this.el);
}
}

componentWillLoad() {
Expand Down Expand Up @@ -394,20 +401,23 @@ export class Alert implements ComponentInterface, OverlayInterface {
this.buttonGroupResizeObserver = undefined;
}

componentDidLoad() {
/**
* Only create gesture if:
* 1. A gesture does not already exist
* 2. App is running in iOS mode
* 3. A wrapper ref exists
*/
/**
* Only create gesture if:
* 1. A gesture does not already exist
* 2. App is running in iOS mode
* 3. A wrapper ref exists
*/
private setupButtonActiveGesture() {
if (!this.gesture && getIonMode(this) === 'ios' && this.wrapperEl) {
this.gesture = createButtonActiveGesture(this.wrapperEl, (refEl: HTMLElement) =>
refEl.classList.contains('alert-button')
);
this.gesture.enable(true);
}
}

componentDidLoad() {
this.setupButtonActiveGesture();
this.setupButtonGroupResizeObserver();

/**
Expand Down
Loading
Loading