Skip to content

Commit c35395c

Browse files
authored
merge release-8.6.1 (#30476)
Release v8.6.1
2 parents 5833199 + e27c14a commit c35395c

31 files changed

+279
-162
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,13 @@ jobs:
120120
-d '{
121121
"path": [
122122
"/npm/@ionic/core@6/dist/ionic/ionic.esm.js",
123+
"/npm/@ionic/core@7/dist/ionic/ionic.esm.js",
124+
"/npm/@ionic/core@8/dist/ionic/ionic.esm.js",
123125
"/npm/@ionic/core@latest/dist/ionic/ionic.esm.js",
124126
"/npm/@ionic/core@next/dist/ionic/ionic.esm.js",
125127
"/npm/@ionic/core@6/css/ionic.bundle.css",
128+
"/npm/@ionic/core@7/css/ionic.bundle.css",
129+
"/npm/@ionic/core@8/css/ionic.bundle.css",
126130
"/npm/@ionic/core@latest/css/ionic.bundle.css"
127131
"/npm/@ionic/core@next/css/ionic.bundle.css"
128132
]}'

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.6.1](https://github.com/ionic-team/ionic-framework/compare/v8.6.0...v8.6.1) (2025-06-11)
7+
8+
9+
### Bug Fixes
10+
11+
* **item-sliding:** check for side attribute to avoid an `undefined` value ([#29845](https://github.com/ionic-team/ionic-framework/issues/29845)) ([c38aa07](https://github.com/ionic-team/ionic-framework/commit/c38aa07cf8bfab200b3c071328d893bd1627cde7)), closes [#29499](https://github.com/ionic-team/ionic-framework/issues/29499)
12+
* **modal:** reset footer positioning after content drag and multi-footer support ([#30470](https://github.com/ionic-team/ionic-framework/issues/30470)) ([071b414](https://github.com/ionic-team/ionic-framework/commit/071b414a00f4497ed0baa1431f0bee4b3c7c13fb)), closes [#30468](https://github.com/ionic-team/ionic-framework/issues/30468)
13+
14+
15+
16+
17+
618
# [8.6.0](https://github.com/ionic-team/ionic-framework/compare/v8.5.9...v8.6.0) (2025-06-04)
719

820

core/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.6.1](https://github.com/ionic-team/ionic-framework/compare/v8.6.0...v8.6.1) (2025-06-11)
7+
8+
9+
### Bug Fixes
10+
11+
* **item-sliding:** check for side attribute to avoid an `undefined` value ([#29845](https://github.com/ionic-team/ionic-framework/issues/29845)) ([c38aa07](https://github.com/ionic-team/ionic-framework/commit/c38aa07cf8bfab200b3c071328d893bd1627cde7)), closes [#29499](https://github.com/ionic-team/ionic-framework/issues/29499)
12+
* **modal:** reset footer positioning after content drag and multi-footer support ([#30470](https://github.com/ionic-team/ionic-framework/issues/30470)) ([071b414](https://github.com/ionic-team/ionic-framework/commit/071b414a00f4497ed0baa1431f0bee4b3c7c13fb)), closes [#30468](https://github.com/ionic-team/ionic-framework/issues/30468)
13+
14+
15+
16+
17+
618
# [8.6.0](https://github.com/ionic-team/ionic-framework/compare/v8.5.9...v8.6.0) (2025-06-04)
719

820

core/package-lock.json

Lines changed: 17 additions & 17 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.6.0",
3+
"version": "8.6.1",
44
"description": "Base components for Ionic",
55
"keywords": [
66
"ionic",
@@ -36,7 +36,7 @@
3636
"tslib": "^2.1.0"
3737
},
3838
"devDependencies": {
39-
"@axe-core/playwright": "^4.10.1",
39+
"@axe-core/playwright": "^4.10.2",
4040
"@capacitor/core": "^7.0.0",
4141
"@capacitor/haptics": "^7.0.0",
4242
"@capacitor/keyboard": "^7.0.0",

core/src/components/fab/test/custom-size/fab.e2e.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { configs, test, Viewports } from '@utils/test/playwright';
66
*/
77
configs({ modes: ['ios'] }).forEach(({ title, config, screenshot }) => {
88
test.describe(title('fab: custom size'), () => {
9-
test('should position fabs correctly with custom sizes', async ({ page }) => {
9+
// TODO(FW-6587): Remove skip once the flaky test is fixed
10+
test.skip('should position fabs correctly with custom sizes', async ({ page }) => {
1011
await page.goto(`/src/components/fab/test/custom-size`, config);
1112

1213
await page.setViewportSize(Viewports.tablet.landscape);

core/src/components/item-sliding/item-sliding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export class ItemSliding implements ComponentInterface {
263263
// eslint-disable-next-line custom-rules/no-component-on-ready-method
264264
const option = (item as any).componentOnReady !== undefined ? await item.componentOnReady() : item;
265265

266-
const side = isEndSide(option.side) ? 'end' : 'start';
266+
const side = isEndSide(option.side ?? option.getAttribute('side')) ? 'end' : 'start';
267267

268268
if (side === 'start') {
269269
this.leftOptions = option;

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

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export const createSheetGesture = (
8484
let offset = 0;
8585
let canDismissBlocksGesture = false;
8686
let cachedScrollEl: HTMLElement | null = null;
87-
let cachedFooterEl: HTMLIonFooterElement | null = null;
87+
let cachedFooterEls: HTMLIonFooterElement[] | null = null;
8888
let cachedFooterYPosition: number | null = null;
8989
let currentFooterState: 'moving' | 'stationary' | null = null;
9090
const canDismissMaxStep = 0.95;
@@ -126,9 +126,9 @@ export const createSheetGesture = (
126126
* @param newPosition Whether the footer is in a moving or stationary position.
127127
*/
128128
const swapFooterPosition = (newPosition: 'moving' | 'stationary') => {
129-
if (!cachedFooterEl) {
130-
cachedFooterEl = baseEl.querySelector('ion-footer') as HTMLIonFooterElement | null;
131-
if (!cachedFooterEl) {
129+
if (!cachedFooterEls) {
130+
cachedFooterEls = Array.from(baseEl.querySelectorAll('ion-footer'));
131+
if (!cachedFooterEls.length) {
132132
return;
133133
}
134134
}
@@ -137,57 +137,80 @@ export const createSheetGesture = (
137137

138138
currentFooterState = newPosition;
139139
if (newPosition === 'stationary') {
140-
// Reset positioning styles to allow normal document flow
141-
cachedFooterEl.classList.remove('modal-footer-moving');
142-
cachedFooterEl.style.removeProperty('position');
143-
cachedFooterEl.style.removeProperty('width');
144-
cachedFooterEl.style.removeProperty('height');
145-
cachedFooterEl.style.removeProperty('top');
146-
cachedFooterEl.style.removeProperty('left');
147-
page?.style.removeProperty('padding-bottom');
148-
149-
// Move to page
150-
page?.appendChild(cachedFooterEl);
140+
cachedFooterEls.forEach((cachedFooterEl) => {
141+
// Reset positioning styles to allow normal document flow
142+
cachedFooterEl.classList.remove('modal-footer-moving');
143+
cachedFooterEl.style.removeProperty('position');
144+
cachedFooterEl.style.removeProperty('width');
145+
cachedFooterEl.style.removeProperty('height');
146+
cachedFooterEl.style.removeProperty('top');
147+
cachedFooterEl.style.removeProperty('left');
148+
page?.style.removeProperty('padding-bottom');
149+
150+
// Move to page
151+
page?.appendChild(cachedFooterEl);
152+
});
151153
} else {
152-
// Get both the footer and document body positions
153-
const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
154-
const bodyRect = document.body.getBoundingClientRect();
155-
156-
// Add padding to the parent element to prevent content from being hidden
157-
// when the footer is positioned absolutely. This has to be done before we
158-
// make the footer absolutely positioned or we may accidentally cause the
159-
// sheet to scroll.
160-
const footerHeight = cachedFooterEl.clientHeight;
161-
page?.style.setProperty('padding-bottom', `${footerHeight}px`);
162-
163-
// Apply positioning styles to keep footer at bottom
164-
cachedFooterEl.classList.add('modal-footer-moving');
165-
166-
// Calculate absolute position relative to body
167-
// We need to subtract the body's offsetTop to get true position within document.body
168-
const absoluteTop = cachedFooterElRect.top - bodyRect.top;
169-
const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
170-
171-
// Capture the footer's current dimensions and hard code them during the drag
172-
cachedFooterEl.style.setProperty('position', 'absolute');
173-
cachedFooterEl.style.setProperty('width', `${cachedFooterEl.clientWidth}px`);
174-
cachedFooterEl.style.setProperty('height', `${cachedFooterEl.clientHeight}px`);
175-
cachedFooterEl.style.setProperty('top', `${absoluteTop}px`);
176-
cachedFooterEl.style.setProperty('left', `${absoluteLeft}px`);
177-
178-
// Also cache the footer Y position, which we use to determine if the
179-
// sheet has been moved below the footer. When that happens, we need to swap
180-
// the position back so it will collapse correctly.
181-
cachedFooterYPosition = absoluteTop;
182-
// If there's a toolbar, we need to combine the toolbar height with the footer position
183-
// because the toolbar moves with the drag handle, so when it starts overlapping the footer,
184-
// we need to account for that.
185-
const toolbar = baseEl.querySelector('ion-toolbar') as HTMLIonToolbarElement | null;
186-
if (toolbar) {
187-
cachedFooterYPosition -= toolbar.clientHeight;
188-
}
189-
190-
document.body.appendChild(cachedFooterEl);
154+
let footerHeights = 0;
155+
cachedFooterEls.forEach((cachedFooterEl, index) => {
156+
// Get both the footer and document body positions
157+
const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
158+
const bodyRect = document.body.getBoundingClientRect();
159+
160+
// Calculate the total height of all footers
161+
// so we can add padding to the page element
162+
footerHeights += cachedFooterEl.clientHeight;
163+
164+
// Calculate absolute position relative to body
165+
// We need to subtract the body's offsetTop to get true position within document.body
166+
const absoluteTop = cachedFooterElRect.top - bodyRect.top;
167+
const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
168+
169+
// Capture the footer's current dimensions and store them in CSS variables for
170+
// later use when applying absolute positioning.
171+
cachedFooterEl.style.setProperty('--pinned-width', `${cachedFooterEl.clientWidth}px`);
172+
cachedFooterEl.style.setProperty('--pinned-height', `${cachedFooterEl.clientHeight}px`);
173+
cachedFooterEl.style.setProperty('--pinned-top', `${absoluteTop}px`);
174+
cachedFooterEl.style.setProperty('--pinned-left', `${absoluteLeft}px`);
175+
176+
// Only cache the first footer's Y position
177+
// This is used to determine if the sheet has been moved below the footer
178+
// and needs to be swapped back to stationary so it collapses correctly.
179+
if (index === 0) {
180+
cachedFooterYPosition = absoluteTop;
181+
// If there's a header, we need to combine the header height with the footer position
182+
// because the header moves with the drag handle, so when it starts overlapping the footer,
183+
// we need to account for that.
184+
const header = baseEl.querySelector('ion-header') as HTMLIonHeaderElement | null;
185+
if (header) {
186+
cachedFooterYPosition -= header.clientHeight;
187+
}
188+
}
189+
});
190+
191+
// Apply the pinning of styles after we've calculated everything
192+
// so that we don't cause layouts to shift while calculating the footer positions.
193+
// Otherwise, with multiple footers we'll end up capturing the wrong positions.
194+
cachedFooterEls.forEach((cachedFooterEl) => {
195+
// Add padding to the parent element to prevent content from being hidden
196+
// when the footer is positioned absolutely. This has to be done before we
197+
// make the footer absolutely positioned or we may accidentally cause the
198+
// sheet to scroll.
199+
page?.style.setProperty('padding-bottom', `${footerHeights}px`);
200+
201+
// Apply positioning styles to keep footer at bottom
202+
cachedFooterEl.classList.add('modal-footer-moving');
203+
204+
// Apply our preserved styles to pin the footer
205+
cachedFooterEl.style.setProperty('position', 'absolute');
206+
cachedFooterEl.style.setProperty('width', 'var(--pinned-width)');
207+
cachedFooterEl.style.setProperty('height', 'var(--pinned-height)');
208+
cachedFooterEl.style.setProperty('top', 'var(--pinned-top)');
209+
cachedFooterEl.style.setProperty('left', 'var(--pinned-left)');
210+
211+
// Move the element to the body when everything else is done
212+
document.body.appendChild(cachedFooterEl);
213+
});
191214
}
192215
};
193216

@@ -400,6 +423,14 @@ export const createSheetGesture = (
400423
* is not scrolled to the top.
401424
*/
402425
if (!expandToScroll && detail.deltaY <= 0 && cachedScrollEl && cachedScrollEl.scrollTop > 0) {
426+
/**
427+
* If expand to scroll is disabled, we need to make sure we swap the footer position
428+
* back to stationary so that it will collapse correctly if the modal is dismissed without
429+
* dragging (e.g. through a dismiss button).
430+
* This can cause issues if the user has a modal with content that can be dragged, as we'll
431+
* swap to moving on drag and if we don't swap back here then the footer will get stuck.
432+
*/
433+
swapFooterPosition('stationary');
403434
return;
404435
}
405436

core/src/components/popover/test/basic/popover.e2e.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
2929
await popoverFixture.open('#long-list-popover');
3030
await popoverFixture.screenshot('basic-long-list-popover', screenshot);
3131
});
32-
test('should render no event popover', async () => {
32+
// TODO(FW-6588): Remove skip once the flaky test is fixed
33+
test.skip('should render no event popover', async () => {
3334
await popoverFixture.open('#no-event-popover');
3435
await popoverFixture.screenshot('basic-no-event-popover', screenshot);
3536
});

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.6.0"
6+
"version": "8.6.1"
77
}

0 commit comments

Comments
 (0)