Issue number: resolves#30735
---------
## What is the current behavior?
Toast's wrapper and content divs do not have part attributes.
## What is the new behavior?
- Adds `wrapper` and `content` parts to Toast.
## 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
This PR was originally opened by @Hurubon here in #30737. It was lost in
`feature-8.8` due to a bad merge, so this PR is to re-apply this feature
so it can go out in 8.8.1 (it was originally supposed to go out in
8.8.0)
Co-authored-by: Hurubon <58346722+Hurubon@users.noreply.github.com>
Co-authored-by: Hurubon <58346722+Hurubon@users.noreply.github.com>
Issue number: internal
---------
## What is the current behavior?
The sheet and card modal can be dragged to view content. However, there
are no events that determine when drag has started or ended.
## What is the new behavior?
- Added drag events for sheet and card modal: `ionDragStart`, `ionDragMove`, `ionDragEnd`
- Added a drag interface
- Added tests
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Shane <shane@shanessite.net>
Co-authored-by: Brandy Smith <brandyscarney@users.noreply.github.com>
Issue number: resolves#29862
---------
## What is the current behavior?
Range exposes a single part for both knobs & pins. This makes it impossible to style the knobs/pins differently when dual knobs is enabled.
## What is the new behavior?
- Fixes a bug where the knobs would swap A & B when they cross over each other
- Fixes the focus behavior so that dual knobs act the same as a single knob range when focusing a knob
- Adds the following classes to the host element when `dualKnobs` is enabled:
- `range-dual-knobs`
- `range-pressed-a` when the knob with name A is pressed
- `range-pressed-b` when the knob with name B is pressed
- `range-pressed-lower` when the lower knob is pressed
- `range-pressed-upper` when the upper knob is pressed
- Adds parts for the following:
- `knob-handle-a` — The container for the knob with the static `A` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `knob-handle-b` — The container for the knob with the static `B` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `knob-handle-lower` — The container for the knob whose current `value` is `lower` when `dualKnobs` is `true`. The lower and upper parts swap which knob handle they refer to when the knobs cross.
- `knob-handle-upper` — The container for the knob whose current `value` is `upper` when `dualKnobs` is `true`. The lower and upper parts swap which knob handle they refer to when the knobs cross.
- `pin-a` — The value indicator above the knob with the static `A` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `pin-b` — The value indicator above the knob with the static `B` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `pin-lower` — The value indicator above the knob whose current `value` is `lower` when `dualKnobs` is `true`. The lower and upper parts swap which pin they refer to when the knobs cross.
- `pin-upper` — The value indicator above the knob whose current `value` is `upper` when `dualKnobs` is `true`. The lower and upper parts swap which pin they refer to when the knobs cross.
- `knob-a` — The visual knob for the static `A` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `knob-b` — The visual knob for the static `B` identity when `dualKnobs` is `true`. This identity does not change, even if the knobs cross and swap which one represents the lower or upper value.
- `knob-lower` — The visual knob whose current `value` is `lower` when `dualKnobs` is `true`. The lower and upper parts swap which knob they refer to when the knobs cross.
- `knob-upper` — The visual knob whose current `value` is `upper` when `dualKnobs` is `true`. The lower and upper parts swap which knob they refer to when the knobs cross.
- `activated` — Added to the knob-handle, knob, and pin when the knob is active. Only one set has this part at a time when `dualKnobs` is `true`.
- `focused` — Added to the knob-handle, knob, and pin that currently has focus. Only one set has this part at a time when `dualKnobs` is `true`.
- `hover` — Added to the knob-handle, knob, and pin when the knob has hover. Only one set has this part at a time when `dualKnobs` is `true`.
- `pressed` — Added to the knob-handle, knob, and pin that is currently being pressed to drag. Only one set has this part at a time when `dualKnobs` is `true`.
- Adds e2e tests for the following:
- customizing label part
- customizing bar parts
- customizing pin parts
- customizing tick parts
- customizing knob parts
- customizing dual knob a & b parts
- customizing dual knob lower & upper parts
- verifies that a & b parts stay on the original elements but lower & upper parts swap when the values swap
- Adds spec tests for the following:
- css classes
- value state classes
- boolean property classes
- pressed state classes
- shadow parts
- static shadow parts
- verifies the shadow parts exist based on the existence of certain range properties
- state shadow parts
- verifies the shadow parts exist based on the state of the range knob (pressed, focused, activated, hover)
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: N/A
---------
## What is the current behavior?
The inner structural element of list-header is not exposed as a shadow part, preventing users from being able to customize its styles directly.
## What is the new behavior?
- Exposes `inner` shadow part
- Adds e2e test coverage for customizing the shadow part
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: N/A
---------
## What is the current behavior?
The inner structural elements of item-option are not exposed as shadow parts, preventing users from being able to customize their styles directly.
## What is the new behavior?
- Exposes `inner` and `container` shadow parts
- Adds e2e test coverage for customizing the shadow parts
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: N/A
---------
## What is the current behavior?
The inner structural elements of item-divider are not exposed as shadow parts, preventing users from being able to customize their styles directly.
## What is the new behavior?
- Exposes `inner` and `container` shadow parts
- Adds e2e test coverage for customizing the shadow parts
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: N/A
---------
## What is the current behavior?
The inner structural elements of item are not exposed as shadow parts, preventing users from being able to customize their styles directly.
## What is the new behavior?
- Exposes `inner` and `container` shadow parts
- Adds e2e test coverage for customizing the shadow parts
- Removes css variables test, combining it with the shadow parts test
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Adds guidelines document establishing standardized naming conventions for CSS Shadow Parts in Ionic Framework components.
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
resolves#30279resolves#30359
## What is the current behavior?
While Ionic's `stencil.config.ts` sets `experimentalSlotFixes: true`,
the fixes never get applied at runtime. Ionic is using an external
runtime, so Ionic components import `defineCustomElement` from
`@stencil/core/internal/client` at runtime and this code has no
awareness of the project's stencil configuration.
This leads to a `NotFoundError` (Failed to execute 'removeChild' on
'Node') when filtering or dynamically removing radios in an
`ion-radio-group`. The error occurs because `ion-radio-group` wraps its
slotted content in an internal `<div>`.
## What is the new behavior?
By setting `externalRuntime: false`, Stencil generates a
project-specific file with `defineCustomElement` that components import.
This file has the project's build settings baked in, correctly applying
slot fixes.
Additionally, the internal wrapper `<div>` around the slotted content in
`ion-radio-group` is removed. With slot fixes correctly applied and the
wrapper removed, radios can be filtered or dynamically removed without
triggering `NotFoundError` or `DOMExceptions`.
## Does this introduce a breaking change?
- [ ] Yes
- [X] No
## Other information
External Runtime is enabled by default and designed for projects that
import Stencil components from multiple sources. This is flawed because
those components will not be running with the runtime settings for which
they were made.
Issue number: resolves#24524
---------
<!-- 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?
There is no way to know when the refresher has fully returned to its
inactive state after a pull gesture. The existing `ionStart` event fires
when pulling begins, but there is no corresponding end event. Watching
the progress property is insufficient because hitting zero doesn’t
necessarily mean the user has completed the pull gesture.
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
Two new events are added to the refresher component:
- `ionPullStart`: Emitted when the user begins pulling down (same as
`ionStart`, which is now deprecated)
- `ionPullEnd`: Emitted when the refresher returns to inactive state,
with a `reason` property of `'complete'` or `'cancel'` indicating
whether the refresh operation completed successfully or was cancelled
This allows you to know both when the user is no longer touching the
screen AND when the refresher is ready to be pulled again.
## 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. -->
Test page:
https://ionic-framework-git-fw-6591-ionic1.vercel.app/src/components/refresher/test/basic/index.html
Current dev build:
```
8.7.17-dev.11770319814.172b4f50
```
---------
Co-authored-by: Patrick McDonald <764290+WhatsThatItsPat@users.noreply.github.com>
Issue number: resolves#30860
## What is the current behavior?
We have flaky tests in an ionic angular project that root cause are not
cleaned up timeouts.
I commented out the timeout in the searchbar componentWillLoad method.
and after several runs no flaky tests at all.
My guess -> test runs faster than the 300ms it takes til the timeout
runs. Everything is cleaned up, but not the ionic timeouts (i think i
saw something similar in other components)
## What is the new behavior?
Timeouts are cleaned on disconnect.
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
## Other information
Testrunner is vitest + angular 20 and latest ionic version 8.x
Issue number: resolves 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?
When a CanDeactivate guard cancels a back navigation (like when it's
initiated by ion-back-button), the NavController’s explicit direction
state (back) is never consumed because consumeTransition() is not called
for canceled navigations. This stale direction leaks into the next
forward navigation, causing it to be incorrectly treated as a back/root
navigation.
## What is the new behavior?
The NavController now listens for NavigationCancel and NavigationError
router events and resets direction, animated, and animationBuilder back
to their defaults. This ensures stale state from a canceled navigation
does not affect subsequent navigations. The reset puts the NavController
into 'auto' mode, which correctly uses guessDirection for the next
navigation.
## 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
Current dev build:
```
8.7.18-dev.11771020096.1ca03a6d
```
Issue number: resolves#28411
---------
<!-- 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?
When a modal is displayed on tablet-sized screens (>= 768px × >= 600px),
the `--ion-safe-area-*` CSS variables are explicitly set to 0px. This
was intended for inset modals that don't touch screen edges, but it
breaks safe area handling on newer iPads with Face ID/home indicators,
causing content to overlap with system UI elements.
## What is the new behavior?
Modals now dynamically handle safe-area insets based on their type and
position. This has to be done because modals that don't touch the edges
cannot have a safe area applied (because it will add unnecessary
padding), but modals that do touch the edges need to apply safe area
correctly or the edges will be obstructed by whatever is in the safe
area.
## 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. -->
[Modals test
page](https://ionic-framework-git-fw-6830-2-ionic1.vercel.app/src/components/modal/test/safe-area/index.html)
[Popovers test
page](https://ionic-framework-git-fw-6830-2-ionic1.vercel.app/src/components/popover/test/safe-area/index.html)
Current dev build:
```
8.7.18-dev.11770674094.18396f54
```
---------
Co-authored-by: ionitron <hi@ionicframework.com>
Issue number: resolves#30290
---------
## What is the current behavior?
The segment view swipe gesture can only be disabled by adding the `disabled` property and setting `opacity: 1`.
## What is the new behavior?
- Adds a new property, `swipeGesture`, to disable swiping on the segment view
- Adds an e2e test which verifies the styles blocking the swipe are correctly applied when `swipeGesture` is `false`
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: resolves#30638
---------
<!-- 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?
When using `ModalController.create()` or `PopoverController.create()` in
Angular, components rendered inside overlays cannot access non-global
services or tokens from the component tree. For example, route-scoped
services or Angular's Dir directive for bidirectional text support are
not accessible from within a modal, requiring complex workarounds with
wrapper components.
## What is the new behavior?
`ModalController.create()` and `PopoverController.create()` now accept
an optional injector property that allows passing a custom Angular
Injector. This enables overlay components to access services and tokens
that are not available in the root injector, such as route-scoped
services or the Dir directive from Angular CDK.
```typescript
const customInjector = Injector.create({
providers: [{ provide: MyService, useValue: myServiceInstance }],
parent: this.injector,
});
```
```typescript
const modal = await this.modalController.create({
component: MyModalComponent,
injector: customInjector,
});
```
## 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. -->
Current dev build:
```
8.7.17-dev.11769628168.11eca7cd
```
<!-- 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. -->
The Stencil Nightly Build workflow tests Ionic with the latest nightly
build of Stencil. The first step of the workflow updates Stencil, builds
Ionic core, and uploads the build files. Later steps download these
build files. Core's updated package.json is not uploaded with the build
files, so later steps are installing an old Stencil version, leading to
conflicts.
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
Add core's package.json to the artifact upload. This will make all later
steps use the correct Stencil version.
Seven of the tests run `git diff` to ensure tests did not cause changes
in tracked files. Core's package.json would register as a change, so a
new step reverts package.json before running `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.
-->
## Other information
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | patch | `v6.0.1` → `v6.0.2` |
---
### Release Notes
<details>
<summary>actions/checkout (actions/checkout)</summary>
###
[`v6.0.2`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v602)
[Compare
Source](https://redirect.github.com/actions/checkout/compare/v6.0.1...v6.0.2)
- Fix tag handling: preserve annotations and explicit fetch-tags by
[@​ericsciple](https://redirect.github.com/ericsciple) in
[#​2356](https://redirect.github.com/actions/checkout/pull/2356)
</details>
---
### Configuration
📅 **Schedule**: Branch creation - "every weekday before 11am" (UTC),
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:eyJjcmVhdGVkSW5WZXIiOiI0Mi44NS4xIiwidXBkYXRlZEluVmVyIjoiNDIuODUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
## What is the current behavior?
Content does not reflect the `fullscreen` property or add a class when it is enabled, making it harder to style.
## What is the new behavior?
- Adds `content-fullscreen` class to content when `fullscreen` is true
- Adds an e2e test verifying the class is applied
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
## What is the current behavior?
Range adds classes to the knobs at `min` and `max`, but the host element doesn't reflect those states.
## What is the new behavior?
- Adds `range-value-min` and `range-value-max` when the value is at the `min` or `max`, respectively.
- Adds a spec test verifying the classes are applied properly.
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: resolves#30908
---------
## What is the current behavior?
Toast with an icon and long message using a stacked layout will wrap the
message below the icon.
## What is the new behavior?
- Apply `flex: 1` to `.toast-content` regardless of layout, which makes
sure the content does not get wrapped under the icon
- Adds an e2e test for a stacked toast with a long message to
`toast/test/layout`
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
## Other information
- Dev build: `8.7.18-dev.11768592717.14a59d2f`
- Preview:
[Layout](https://ionic-framework-git-fw-7035-ionic1.vercel.app/src/components/toast/test/layout/)
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://redirect.github.com/actions/setup-node) |
action | minor | `v6.1.0` → `v6.2.0` |
---
### Release Notes
<details>
<summary>actions/setup-node (actions/setup-node)</summary>
###
[`v6.2.0`](https://redirect.github.com/actions/setup-node/compare/v6.1.0...v6.2.0)
[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v6.1.0...v6.2.0)
</details>
---
### Configuration
📅 **Schedule**: Branch creation - "every weekday before 11am" (UTC),
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:eyJjcmVhdGVkSW5WZXIiOiI0Mi43NC41IiwidXBkYXRlZEluVmVyIjoiNDIuNzQuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: resolves 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?
When using `ion-input` with a label on Android, TalkBack treats the
visual label text as a separate focusable element. This causes the
initial focus to land on the label instead of the input field, creating
a confusing experience for screen reader users.
## What is the new behavior?
The label text wrapper is now hidden from the accessibility tree via
`aria-hidden="true"`, while the native input maintains proper labeling
through `aria-labelledby`. This ensures Android TalkBack focuses
directly on the input field while still announcing the label correctly.
## 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. -->
Current dev build:
```
8.7.16-dev.11767032989.1ae720d0
```
Issue number: resolves internal
---------
## What is the current behavior?
When `ion-tab-bar` is rapidly mounted and unmounted, a race condition in
connectedCallback can cause the keyboard controller to be created after
the component has been disconnected. This results in orphaned event
listeners (`keyboardWillShow`, `keyboardWillHide`) on the window object
that are never cleaned up, causing a memory leak.
## What is the new behavior?
The keyboard controller is now properly destroyed in all scenarios:
- If the component is disconnected while createKeyboardController is
pending, the promise is tracked and destroyed when it resolves
- If a new connectedCallback runs before the previous async completes,
the stale controller is destroyed
The promise tracking pattern ensures only the most recent async
operation assigns its result
## 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. -->
Current dev build:
```
8.7.17-dev.11767895575.16ea7cef
```
I was unable to find a way to create tests that accurately identified if
this problem was occurring. Memory leaks are notoriously difficult to
created automated tests for. I ultimately removed my previous attempts
because I didn't want to give a false sense of security.