chore(git): sync with main - #31422
Draft
ShaneK wants to merge 132 commits into
Draft
Conversation
Syncing Major 9.0 with main
This PR just serves to update major-9.0 to reflect the current state of main
Sync major-9.0 with main after v8.8.3 release.
…k per outlet (#31055) Issue number: resolves internal --------- ## What is the current behavior? The swipe-to-go-back gesture on ion-router-outlet is controlled differently across frameworks: - React and Vue check the `swipeBackEnabled` config option on every swipe attempt in their framework wrappers (`StackManager.canStart()` and `IonRouterOutlet.canStart()`) - Angular reads the config once at mount and controls the gesture through its directive's `swipeGesture` setter - There is no public, per-outlet property to enable or disable the gesture. The only mechanism is the global `swipeBackEnabled` config option ## What is the new behavior? `ion-router-outlet` now exposes a public `swipeGesture` property in core that controls the swipe-to-go-back gesture per outlet instance. It defaults to true in "ios" mode and false in "md" mode, matching existing behavior. - The `swipeBackEnabled` config is read once when the outlet mounts (as the default for `swipeGesture`) rather than checked on every swipe attempt - The config check has been removed from React's StackManager and Vue's IonRouterOutlet -- core now owns this state - Angular's directive forwards the value to the core component's `swipeGesture` property for consistency - Apps can disable the gesture on a specific outlet: `<IonRouterOutlet swipeGesture={false} />` - Apps that set `swipeBackEnabled` once at startup require no changes ## Does this introduce a breaking change? - [X] Yes - [ ] No ## Other information I tried to align the updates to the BREAKING.md documents with the RR6 PR's changes to it to prevent issues from merging as much as possible
## What is the current behavior? Ionic Framework uses React 17 and Stencil react-output-target 0.5.3. ## What is the new behavior? Updated to React 18 and Stencil react-output-target 1.5.0. - Types updated for compatibility with type changes in React 18. - `@testing-library/react-hooks` was moved to `@testing-library/react` so this dependency has been removed. - `reflect: true` added to `value` prop on ion-radio. Props without reflection enabled are not rendered in HTML with updated react-output-target. Issue: stenciljs/output-targets#476 BREAKING CHANGE: Drops support for React 17. Applications will need to update to React 18 to be officially supported by Ionic Framework.
…31078) Issue number: internal Co-authored-by: Brandy Smith <brandyscarney@users.noreply.github.com>
Issue number: resolves #24177 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? Currently, Ionic Framework React Router only supports React Router 5. This has many issues and unsupported/broken features. ## What is the new behavior? With this change, Ionic Framework will support React Router 6 while still supporting transitions in the same way a native app does. Most of what caused this change to take a long time is that React Router 5 and React Router 6 have fundamental differences in how they handle components once they're no longer part of the view. In this change, we move away from relying on React Router directly so much and have our own implementation for deciding how views get dealt with during navigation and when they're cleaned up, allowing for us to still transition between them like we need to while still using React Router as much as we possibly can. This change will also lay the foundation for the migration to React Router 7, which will ideally be easier since most of the hard work has been dealt with here. ## Does this introduce a breaking change? - [X] Yes - [ ] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Current dev build (last updated 2026-04-27): > **⚠️ WARNING:** If you're going to use this dev build on an existing react project, you'll need to migrate to React Router 6. Migrating a large project at this point might be a bad idea since this will not release until v9, which will require further migrations and have other breaking changes! I have a preview of migration documentation for this [here](https://ionic-docs-git-v9-react-router-ionic1.vercel.app/docs/updating/9-0#react-router). ``` 8.8.4-dev.11777318673.18d001f6 ``` The dev build linked above will be the last one for this branch alone. Everything going forward will be the major-9.0 branch, which may include large breaking changes. --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com> Co-authored-by: Sean Perkins <13732623+sean-perkins@users.noreply.github.com>
Issue number: internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? `@stencil/react-output-target` is pinned to exactly `1.5.0` in `core/package.json` and `packages/react/package.json`. This was a defensive pin after `1.5.1` shipped a regression that dropped the `Partial<C>` wrapper from `StencilProps`, which broke everything in our react apps by [requiring all props to be defined](https://github.com/ionic-team/ionic-framework/actions/runs/25007199736/job/73233599812?pr=30831). Exact-pinning meant we couldn't pick up future patches in the `1.5.x` line. ## What is the new behavior? Both `package.json` ranges move to `^1.5.2`. Upstream fully reverted [PR #788](stenciljs/output-targets#788) in [`1.5.2`](https://github.com/stenciljs/output-targets/blob/main/packages/react/CHANGELOG.md), so the generator emits `Components.${tag}` again and the runtime restores `Partial<C>`. Regenerating `packages/react/src/components/components.ts` against the `1.5.2` generator and running prettier produces a file byte-identical to the one committed under `1.5.0`, so the published shape is unchanged. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> The original break, the upstream conversation, and the rationale for staying on the `1.5.x` line (rather than taking the new "errors on truly required props" behavior) are all in the [PR #788 thread](stenciljs/output-targets#788).
# Conflicts: # packages/react-router/package-lock.json # packages/react/package-lock.json # packages/react/package.json
Sync major-9.0 with main.
…ation (#31105) Issue number: internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? Currently, reviewing react router based PRs isn't possible using the vercel preview link ## What is the new behavior? This PR adds support for the preview link to support react router 6 and tries to fix some weird edge cases where navigating to a route would sometimes not show the tests in that route ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information The new React Router preview app shares the React package gate (`REACT_PKG_OK`) because `@ionic/react-router` is built inside `build_react_pkgs()`. Test app builds run in parallel under that gate, then `wait` reports per-app failures. Preview URLs (once Vercel builds this branch): - React Router test app: https://ionic-framework-git-chore-vercel-add-rr-ionic1.vercel.app/react-router/ - Dir-index nav fix demo: https://ionic-framework-git-chore-vercel-add-rr-ionic1.vercel.app/src/components/progress-bar/test (no trailing slash; clicking `basic/`, `buffer/`, `determinate/` should now land on the test page instead of the root landing)
Sync major-9.0 with main.
Ionic's vue-output-target uses Stencil's vue-output-target version 0.10.7 and 0.10.8. - Update to `@stencil/vue-output-target@0.13.1`. - Change Vue model event names from kebab-case to camelCase. Stencil vue-output-target no longer converts them automatically. - Generated proxies file has been updated by latest vue-output-target.
# Conflicts: # packages/react-router/package-lock.json # packages/react/package-lock.json # packages/react/package.json # packages/vue/package-lock.json # packages/vue/package.json
Sync major-9.0 with main.
Issue number: internal --------- ## What is the current behavior? `@ionic/vue-router` and `@ionic/vue` build against vue-router 4 ## What is the new behavior? Bumps `vue-router` to `^5.0.6` and `vue` to `^3.5.0` (vue-router 5 raises its peer to `^3.5.0`). We also added Playwright tests for Vue router that are in full parity with the previous Jest and removed the Jest tests and replaced them with Playwright. This PR also makes the current Vue test app, which is also used for the Vue Router automated tests, get rebuilt with the current PR version for testing in the Vercel preview links. ## Does this introduce a breaking change? - [X] Yes - [ ] No Consumer apps that pin `vue-router` themselves need to upgrade to `^5.0.0`, and apps that explicitly pin `vue` need to bump to `^3.5.0` ## Other information CI changes: CI no longer runs `npm run test.spec` (the script and Jest devDeps are gone), and now runs playwright tests Preview (Vue + Vue Router test app, demos both packages from this PR): https://ionic-framework-git-feat-vue-router-upgrade-ionic1.vercel.app/vue/
…31134) Issue number: internal --------- ## What is the current behavior? After the user navigates back in browser history and then pushes a new route, `ion-back-button` on a tab child page silently fails. ## What is the new behavior? When the computed history delta would be non-negative, fall back to `router.replace` on `prevInfo.pathname` (or to `defaultHref` if `prevInfo` has no pathname). `incomingRouteParams` was already set to a pop/back above, so the view stack still animates the transition as a back navigation, and the params are consumed by the resulting `afterEach` hook instead of leaking into the next navigation. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
…ne (#31137) Issue number: resolves #24074 --------- ## What is the current behavior? In vue-router, a `router.push` performed with `routerDirection="none"` doesn't set `pushedByRoute` on the resulting route info. The next page's `ion-back-button` then can't find a previous entry and falls back to `defaultHref` instead of going back through history. The same bug was previously fixed for react-router in v6, this is the vue version of the fix (Angular doesn't need this fix because the router works very differently) ## What is the new behavior? `createIonRouter` now sets `pushedByRoute` from the leaving location when the navigation is `routerAction === "push"`, `routerDirection === "none"`, and stays within the same tab context. Cross-tab pushes with direction `none` still go through the existing tab-aware branch, which has different `pushedByRoute` semantics After the fix, `ion-back-button` returns to the prior page through history and only falls back to `defaultHref` when there's genuinely no history to pop. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information New Playwright spec `direction-none-back.spec.ts` covers both the `forward` and `none` paths and asserts the back button lands on Page A, not the `defaultHref` fallback [Test Page](https://ionic-framework-git-fw-7145-ionic1.vercel.app/vue/direction-none-back/a)
<!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Stencil nightly build uses the Stencil react-output-target version specified in Ionic's `package.json`s. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> Install latest Stencil react-output-target in nightly build. - Run `npm run lint` in `packages/react` to format generated `compontents.ts`. Otherwise it will be reported by `git diff`. ## Does this introduce a breaking change? - [ ] Yes - [X] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> --------- Co-authored-by: Shane <shane@shanessite.net>
…#31145) Issue number: resolves #29413 --------- ## What is the current behavior? In `viewStacks.ts`, `unmountLeavingViews` and `mountIntermediaryViews` walk the outlet's view stack using `startIndex - delta` (or `startIndex + delta`) as the loop end, with no bound on `viewStack.length`. `delta` comes from the popstate event's history delta. Apps that mount `<ion-tabs>` at the root with no outer `<ion-router-outlet>` only have one outlet registered, so `usingLinearNavigation` is true and these helpers actually run. Each tab switch adds a browser history entry but reuses existing view items, so `|delta|` can easily exceed the stack depth above the entering view. The loop then reads `viewStack[i]` as `undefined` and throws `TypeError: viewItem is undefined` from `viewItem.mount = false`. The navigation aborts mid-transition, which is what surfaces the secondary `enteringEl is undefined` warning and leaves that route stuck ## What is the new behavior? Both helpers bail when the entering view item isn't in the stack (`startIndex === -1`) and clamp the loop end to `Math.min(viewStack.length, ...)`, so a delta that overruns the stack stops at the last real view item instead of indexing past the end. A new Vitest spec at `packages/vue/test/base/tests/unit/tabs-single-outlet.spec.ts` mounts `<ion-tabs>` as the app root with flat routes, builds up history across tabs and sub-pages, then calls `router.go(-6)`. Without the fix the spec catches the unhandled `TypeError` from `viewStacks.ts` ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information Current dev build: ``` 8.8.7-dev.11778707797.1723d277 ```
Issue number: resolves #29705 --------- ## What is the current behavior? In a Vue tabs app, clicking the active tab button after landing directly on a tab child (browser refresh on `/tabs/tab1/child`, or browser back from an external `window.location.href`) is a silent no-op. `IonTabBar.checkActiveTab` only records the active tab's `currentHref` when there's a `prevActiveTab`, so the initial deep load leaves `currentHref` matching `originalHref` and the `originalHref !== currentHref` guard in `IonTabButton.onClick` skips `resetTab`. Even past that guard, `resetTab` issues `router.go(routeInfo.position - currentHistoryPosition)`, which resolves to `router.go(0)` when the tab's only history entry is the current page. ## What is the new behavior? `IonTabBar.checkActiveTab` now records `currentHref` on initial setup so the tab state reflects the deep-loaded URL. `resetTab` takes the tab's `originalHref` and, when the first history entry for the tab is the current entry, falls back to `handleNavigate(originalHref, "pop", "back", undefined, tab)`. The `pop` action routes through `router.replace`, so the deep-loaded child entry is replaced rather than stacked on top and no stale child entry stays in browser history. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information Manual test: [/vue/tabs/tab1/childone](https://ionic-framework-git-fw-6452-ionic1.vercel.app/vue/tabs/tab1/childone). Load that URL directly, click the Tab 1 button, you should land on `/vue/tabs/tab1`
#31142) Issue number: resolves #24995 --------- ## What is the current behavior? In `createIonRouter`, the replace-action branch resolves `routeInfo.routerDirection` and `routeInfo.routerAnimation` by preferring the leaving route's values over the ones the caller passed in. The leaving route wins whenever it has any value set, so `useIonRouter.navigate(url, 'forward', 'replace')` called after a back navigation ends up with `routerDirection: 'back'`, and the same call from the initial route ends up with `'none'`. The caller's explicit direction is silently discarded. ## What is the new behavior? The precedence is flipped: the caller's `routerDirection` and `routerAnimation` are kept, and the leaving route's values are only used as a fallback when nothing was provided. `forward + replace` now animates forward regardless of how the user got to the leaving page, and the default `useIonRouter.replace(url)` keeps the `'root'` direction it was constructed with instead of inheriting whatever the leaving route had recorded. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information
…ch (#31139) Issue number: resolves #25141 --------- ## What is the current behavior? Navigate from `/tabs/tab1` into `/tabs/tab1/childone`, click Tab 2, then click Tab 1 again. Tab 1's child page is re-activated. Press the browser back button and the URL becomes `/tabs/tab2`, but `ion-router-outlet` still shows `tab1`. URL and displayed page are out of sync. The pop branch in `handleHistoryChange` doesn't notice that the browser's linear predecessor and the in-tab back target are different, so the new routeInfo grabs its pathname from `location.path` while the outlet renders the incorrect view ## What is the new behavior? When the pop destination differs from the in-tab back target and either side is in a tab context, the router calls `handleNavigate` to replace the URL with the in-tab predecessor's pathname. The outlet keeps rendering the in-tab page and the URL now matches what you see ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Companion to the React Router 6 fix in [#30831](#30831), which fixed the problem in Ionic React Router
…ent instance (#31150) Issue number: resolves #25013 --------- ## What is the current behavior? A `beforeRouteEnter(to, from, next)` guard that calls `next((vm) => vm.member = 5)` never runs the callback. Vue Router's `<router-view>` invokes whatever ends up in `matchedRoute.enterCallbacks[name]` from a post-flush watcher once the view's instance is set, but `IonRouterOutlet` does its own rendering and skips that step. `matchedRoute.instances.default` gets populated in `registerIonPage`, so internal guards still fire, but the queued enter callbacks sit there unused ## What is the new behavior? `createViewStacks`'s `registerIonPage` now reads `matchedRoute.enterCallbacks.default`, resets it, and runs each callback with the resolved instance. The reset happens before invocation so a callback that triggers a synchronous navigation can push fresh entries onto the next matched record without getting wiped. There's a new Vitest case in `routing.spec.ts` that navigates into a page whose `beforeRouteEnter` does `next((vm) => { vm.member = 5 })` and asserts both the callback ran with the instance and the mutation reached the DOM ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information
# Conflicts: # core/src/components/alert/alert.tsx # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-action-sheet-diff-ios-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-action-sheet-diff-md-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-menu-diff-ios-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-menu-diff-md-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-picker-diff-ios-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-picker-diff-md-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-toast-diff-ios-ltr-Mobile-Safari-linux.png # core/src/components/app/test/safe-area/app.e2e.ts-snapshots/app-toast-diff-md-ltr-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-basic-ios-ltr-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-basic-ios-rtl-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-basic-md-ltr-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-basic-md-rtl-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-value-selected-ios-ltr-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-value-selected-ios-rtl-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-value-selected-md-ltr-Mobile-Safari-linux.png # core/src/components/picker-legacy/test/basic/picker.e2e.ts-snapshots/picker-value-selected-md-rtl-Mobile-Safari-linux.png # packages/angular/test/base/src/app/standalone/home-page/home-page.component.html # packages/react-router/package-lock.json # packages/react/package-lock.json # packages/react/package.json # packages/react/test/base/src/App.tsx # packages/vue-router/package-lock.json # packages/vue/package-lock.json # packages/vue/package.json
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@capacitor/core](https://capacitorjs.com) ([source](https://redirect.github.com/ionic-team/capacitor)) | [`8.5.0` → `8.5.1`](https://renovatebot.com/diffs/npm/@capacitor%2fcore/8.5.0/8.5.1) |  |  | --- ### Release Notes <details> <summary>ionic-team/capacitor (@​capacitor/core)</summary> ### [`v8.5.1`](https://redirect.github.com/ionic-team/capacitor/blob/HEAD/CHANGELOG.md#851-2026-08-31) [Compare Source](https://redirect.github.com/ionic-team/capacitor/compare/8.5.0...8.5.1) ##### Bug Fixes - block navigation to the internal HTTP proxy path ([ee586ae](https://redirect.github.com/ionic-team/capacitor/commit/ee586ae680887ba99d066616f976db149542d922)) - **cli:** use POSIX paths in CapApp-SPM Package.swift ([#​8549](https://redirect.github.com/ionic-team/capacitor/issues/8549)) ([5e5bb3b](https://redirect.github.com/ionic-team/capacitor/commit/5e5bb3befc312477900252ab07e23b596f8cb0d1)) - **core:** prevent removeListener from removing wrong listener ([#​8271](https://redirect.github.com/ionic-team/capacitor/issues/8271)) ([5ac4dd6](https://redirect.github.com/ionic-team/capacitor/commit/5ac4dd613ae989d8dc8738ea25b77efbd4fa21fe)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40OS4wIiwidXBkYXRlZEluVmVyIjoiNDQuNDkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: resolves #31406 --------- ## What is the current behavior? The `ion-router-outlet` and `ion-tabs` components didn't declare a `changeDetection` strategy, so the Angular partial linker filled one in. An Angular 22 linker fills in `OnPush` when our emitted declaration is stamped 22 or later, while Angular 18 through 21 linkers fill in `Default`. Bumping this package's own Angular version to 22 was enough to flip both components for every Angular 22 consumer. A clean `OnPush` view stops a tick traversing into anything below it, so on Angular 22 with Zone.js the routed page inside the outlet never re-rendered. State set as a plain field after an `await` stayed stale. ## What is the new behavior? Every `@Component` in `packages/angular/src` now declares its strategy explicitly, so the linker can't pick one for us. The `ion-router-outlet` and `ion-tabs` components are `Default` because routed pages are created inside their own views; everything else is `OnPush`, including `ion-nav`, whose pages the delegate attaches as root views instead. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information This PR also adds an ng22-zone app to prevent future regressions like this one. ## Current dev build: ``` 9.0.2-dev.11788201761.1a20dc3a ```
## What is the current behavior? 1. Missed code completion for imported Ionic Vue components in WebStorm a. Reason - invalid Web Types for `@ionic/vue` b. Related issue - [WEB-53833](https://youtrack.jetbrains.com/issue/WEB-53833) ## What is the new behavior? 1. Fine code completion for imported Ionic Vue components in WebStorm ## Does this introduce a breaking change? - [ ] Yes - [x] No --------- Co-authored-by: ShaneK <shane@shanessite.net>
## What is the current behavior?
`sanitizeDOMString` (`core/src/utils/sanitization/index.ts`) blocks
untrusted HTML containing `onload=` before it reaches `innerHTML`,
because `onload` can fire synchronously while the string is being parsed
into the working document fragment — before the later
attribute-allowlist pass runs. The check is a plain lowercase substring
match (`untrustedString.includes('onload=')`), so it misses `onLoad=`,
`ONLOAD=`, or `onload =` (whitespace before `=`) even though HTML parses
all of those as the same event handler.
## What is the new behavior?
-
- Replaced the substring check with a case-insensitive regex that also
tolerates whitespace around `=` (`/onload\s*=/i`), matching how HTML
actually parses attribute names.
- Added a test covering the case and whitespace variants.
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
## Other information
`sanitizeDOMString` is used by `ion-toast`, `ion-loading`, the
`ion-alert` message, `ion-refresher-content`, and
`ion-infinite-scroll-content` to sanitize developer-supplied HTML
strings that may embed end-user input (e.g. another user's display name
rendered in a toast/alert). This closes a gap where a payload like `<svg
onLoad=...>` could bypass the intended guard and reach `innerHTML`
unfiltered.
I didn't find an existing `SECURITY.md` or private vulnerability
reporting channel enabled on this repo, so opening this directly as a PR
with the fix rather than filing a separate public issue describing the
bypass.
---------
Co-authored-by: ShaneK <shane@shanessite.net>
v9.0.2
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
ShaneK
marked this pull request as draft
September 2, 2026 20:25
The merge moved main's slotted margin rules into the shared textarea.common.scss, which only imports themes/mixins and so cannot reach $form-control-label-margin from themes/native/native.globals. That broke the sass build. The rules already exist, namespaced, in textarea.native.scss where next relocated them. Also regenerates api.txt and the Angular proxies from a real core build: ion-searchbar's cancelButtonIcon signature and the standalone directive inputs were stale after the merge, and ion-divider/ion-gallery/ ion-gallery-item needed standalone: false in the lazy proxies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sync
nextwithmain.This is the first sync since
maincrossed the v9 boundary (mainis 9.0.2,nextwas 8.8.19), so it is much larger than a routine sync: 123 commits and 256 conflicts.Resolution policy
mainwins for code,nextwins for styling. Where the two could not both be kept,main's render tree and component logic were taken andnext's theme-aware styling was layered on top.Notable resolutions
ion-select/ion-input/ion-textareadiverged on render tree, not style. Tookmain's v9 structure (.select-start/.select-control/.select-end,StartContainerController, booleanautocorrect, floating labels that no longer float for slotted content) since BREAKING.md documentspart="inner"as removed. Keptnext's theme-aware derived values (getFill(),getShape(),getSize(),getLabelPlacement(), thethemeclass map, the theme-aware clear icon). The ionic-theme outline hook (<div class="input-outline">etc.) is gone from these three and needs re-adding against the new DOM as part of the ionic-modular work.picker-legacy/picker-legacy-column: acceptedmain's removal. Also droppedion-picker-legacyfromOVERLAY_FOCUS_TRAP_SELECTORand from the Vue overlay container list.packages/reactproxies.ts->components.ts:maindeletedproxies.tswith the react-output-target 1.5.0 upgrade. Portednext'sIonDivider/IonGallery/IonGalleryIteminto the generatedcomponents.tsformat, and updated thebuild-corearchive path.mainmovedpackages/angular/standalone/topackages/angular/src/standalone/with per-component files. Addedion-divider.ts,ion-gallery.ts,ion-gallery-item.tsand registered them.proxies.ts: 83 conflicts. 79 resolved asnext'sinputsline plusmain's addedoutputs/standalone: false. Four by hand: droppedIonPickerLegacy,ion-select-optioninputs are the union,ion-tabkeepsmain's requiredtabinput plusnext'smode/theme.datetime:nextmoved the color rules intodatetime.native.scsswhilemainrewrote them in the shared file for the removal of the defaultcolorprop. Keptnext's file split and portedmain'sion-color(primary, base)default plus:host(.ion-color)override into the native theme.alert.common.scss: rename detection appendedmain's copy of the label-placement and justify rules on top ofnext's identical copy. Removed the duplicate.select.tsx: the merge left two identical copies ofwrapSelectTextNodes. Removed one.next's Textarea Shadow DOM note folded intomain's Textarea section, TOC regenerated.next's, matching the precedent set in 17ab188. Eight orphanedtextareafill Firefox baselines stayed deleted sincenext's fill spec is LTR-only and has no non-custom shaped cases.core/package-lock.json: regenerated frommain's lockfile against the mergedpackage.json.@playwright/teststays at 1.62.1 to match the CI image.Verification
tsc --noEmitoncore/srcis clean (spec-file errors are pre-existing tsconfig noise, present on untouched files too).stencil test --spec: 86 suites, 819 tests, all passing.Before merging
next's and none of them match the merged code. Runnpm run test.e2e.docker.update-snapshots.core/api.txt,core/src/components.d.ts, and the framework proxy files.@phosphor-icons/coreis not installed locally so no build was run here.