Skip to content

Commit 521d077

Browse files
chore(git): update 8.5 to be in sync with main (#30224)
2 parents 41da4c3 + ecaeb39 commit 521d077

File tree

33 files changed

+332
-29
lines changed

33 files changed

+332
-29
lines changed

.github/workflows/assign-issues.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
issues: write
1212
steps:
1313
- name: 'Auto-assign issue'
14-
uses: pozil/auto-assign-issue@c015a6a3f410f12f58255c3d085fd774312f7a2f # v2.1.2
14+
uses: pozil/auto-assign-issue@39c06395cbac76e79afc4ad4e5c5c6db6ecfdd2e # v2.2.0
1515
with:
1616
assignees: brandyscarney, thetaPC, joselrio, rugoncalves, BenOsodrac, JoaoFerreira-FrontEnd, OS-giulianasilva, tanner-reits
1717
numOfAssignee: 1

core/package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"@capacitor/haptics": "^6.0.0",
4242
"@capacitor/keyboard": "^6.0.0",
4343
"@capacitor/status-bar": "^6.0.0",
44-
"@clack/prompts": "^0.9.0",
44+
"@clack/prompts": "^0.10.0",
4545
"@ionic/eslint-config": "^0.3.0",
4646
"@ionic/prettier-config": "^2.0.0",
4747
"@playwright/test": "^1.46.1",

core/src/components/segment-button/segment-button.tsx

+43-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ComponentInterface } from '@stencil/core';
22
import { Component, Element, Host, Prop, Method, State, Watch, forceUpdate, h } from '@stencil/core';
33
import type { ButtonInterface } from '@utils/element-interface';
44
import type { Attributes } from '@utils/helpers';
5-
import { addEventListener, removeEventListener, inheritAttributes } from '@utils/helpers';
5+
import { addEventListener, removeEventListener, inheritAttributes, getNextSiblingOfType } from '@utils/helpers';
66
import { hostContext } from '@utils/theme';
77

88
import { getIonMode } from '../../global/ionic-global';
@@ -65,7 +65,41 @@ export class SegmentButton implements ComponentInterface, ButtonInterface {
6565
this.updateState();
6666
}
6767

68-
connectedCallback() {
68+
private waitForSegmentContent(ionSegment: HTMLIonSegmentElement | null, contentId: string): Promise<HTMLElement> {
69+
return new Promise((resolve, reject) => {
70+
let timeoutId: NodeJS.Timeout | undefined = undefined;
71+
let animationFrameId: number;
72+
73+
const check = () => {
74+
if (!ionSegment) {
75+
reject(new Error(`Segment not found when looking for Segment Content`));
76+
return;
77+
}
78+
79+
const segmentView = getNextSiblingOfType<HTMLIonSegmentViewElement>(ionSegment); // Skip the text nodes
80+
const segmentContent = segmentView?.querySelector(
81+
`ion-segment-content[id="${contentId}"]`
82+
) as HTMLIonSegmentContentElement | null;
83+
if (segmentContent && timeoutId) {
84+
clearTimeout(timeoutId); // Clear the timeout if the segmentContent is found
85+
cancelAnimationFrame(animationFrameId);
86+
resolve(segmentContent);
87+
} else {
88+
animationFrameId = requestAnimationFrame(check); // Keep checking on the next animation frame
89+
}
90+
};
91+
92+
check();
93+
94+
// Set a timeout to reject the promise
95+
timeoutId = setTimeout(() => {
96+
cancelAnimationFrame(animationFrameId);
97+
reject(new Error(`Unable to find Segment Content with id="${contentId} within 1000 ms`));
98+
}, 1000);
99+
});
100+
}
101+
102+
async connectedCallback() {
69103
const segmentEl = (this.segmentEl = this.el.closest('ion-segment'));
70104
if (segmentEl) {
71105
this.updateState();
@@ -76,12 +110,13 @@ export class SegmentButton implements ComponentInterface, ButtonInterface {
76110
// Return if there is no contentId defined
77111
if (!this.contentId) return;
78112

79-
// Attempt to find the Segment Content by its contentId
80-
const segmentContent = document.getElementById(this.contentId) as HTMLIonSegmentContentElement | null;
81-
82-
// If no associated Segment Content exists, log an error and return
83-
if (!segmentContent) {
84-
console.error(`Segment Button: Unable to find Segment Content with id="${this.contentId}".`);
113+
let segmentContent;
114+
try {
115+
// Attempt to find the Segment Content by its contentId
116+
segmentContent = await this.waitForSegmentContent(segmentEl, this.contentId);
117+
} catch (error) {
118+
// If no associated Segment Content exists, log an error and return
119+
console.error('Segment Button: ', (error as Error).message);
85120
return;
86121
}
87122

core/src/components/segment-view/test/basic/index.html

+30
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@
123123
<button class="expand" onClick="changeSegmentContent()">Change Segment Content</button>
124124

125125
<button class="expand" onClick="clearSegmentValue()">Clear Segment Value</button>
126+
127+
<button class="expand" onClick="addSegmentButtonAndContent()">Add New Segment Button & Content</button>
126128
</ion-content>
127129

128130
<ion-footer>
@@ -158,6 +160,34 @@
158160
segment.value = undefined;
159161
});
160162
}
163+
164+
async function addSegmentButtonAndContent() {
165+
const segment = document.querySelector('ion-segment');
166+
const segmentView = document.querySelector('ion-segment-view');
167+
168+
const newButton = document.createElement('ion-segment-button');
169+
const newId = `new-${Date.now()}`;
170+
newButton.setAttribute('content-id', newId);
171+
newButton.setAttribute('value', newId);
172+
newButton.innerHTML = '<ion-label>New Button</ion-label>';
173+
174+
segment.appendChild(newButton);
175+
176+
setTimeout(() => {
177+
// Timeout to test waitForSegmentContent() in segment-button
178+
const newContent = document.createElement('ion-segment-content');
179+
newContent.setAttribute('id', newId);
180+
newContent.innerHTML = 'New Content';
181+
182+
segmentView.appendChild(newContent);
183+
184+
// Necessary timeout to ensure the value is set after the content is added.
185+
// Otherwise, the transition is unsuccessful and the content is not shown.
186+
setTimeout(() => {
187+
segment.setAttribute('value', newId);
188+
}, 200);
189+
}, 200);
190+
}
161191
</script>
162192
</ion-app>
163193
</body>

core/src/components/select/select.tsx

+35-11
Original file line numberDiff line numberDiff line change
@@ -317,19 +317,10 @@ export class Select implements ComponentInterface {
317317
}
318318
this.isExpanded = true;
319319
const overlay = (this.overlay = await this.createOverlay(event));
320-
overlay.onDidDismiss().then(() => {
321-
this.overlay = undefined;
322-
this.isExpanded = false;
323-
this.ionDismiss.emit();
324-
this.setFocus();
325-
});
326320

327-
await overlay.present();
328-
329-
// focus selected option for popovers and modals
330-
if (this.interface === 'popover' || this.interface === 'modal') {
321+
// Add logic to scroll selected item into view before presenting
322+
const scrollSelectedIntoView = () => {
331323
const indexOfSelected = this.childOpts.findIndex((o) => o.value === this.value);
332-
333324
if (indexOfSelected > -1) {
334325
const selectedItem = overlay.querySelector<HTMLElement>(
335326
`.select-interface-option:nth-child(${indexOfSelected + 1})`
@@ -352,6 +343,7 @@ export class Select implements ComponentInterface {
352343
| HTMLIonCheckboxElement
353344
| null;
354345
if (interactiveEl) {
346+
selectedItem.scrollIntoView({ block: 'nearest' });
355347
// Needs to be called before `focusVisibleElement` to prevent issue with focus event bubbling
356348
// and removing `ion-focused` style
357349
interactiveEl.setFocus();
@@ -379,8 +371,40 @@ export class Select implements ComponentInterface {
379371
focusVisibleElement(firstEnabledOption.closest('ion-item')!);
380372
}
381373
}
374+
};
375+
376+
// For modals and popovers, we can scroll before they're visible
377+
if (this.interface === 'modal') {
378+
overlay.addEventListener('ionModalWillPresent', scrollSelectedIntoView, { once: true });
379+
} else if (this.interface === 'popover') {
380+
overlay.addEventListener('ionPopoverWillPresent', scrollSelectedIntoView, { once: true });
381+
} else {
382+
/**
383+
* For alerts and action sheets, we need to wait a frame after willPresent
384+
* because these overlays don't have their content in the DOM immediately
385+
* when willPresent fires. By waiting a frame, we ensure the content is
386+
* rendered and can be properly scrolled into view.
387+
*/
388+
const scrollAfterRender = () => {
389+
requestAnimationFrame(() => {
390+
scrollSelectedIntoView();
391+
});
392+
};
393+
if (this.interface === 'alert') {
394+
overlay.addEventListener('ionAlertWillPresent', scrollAfterRender, { once: true });
395+
} else if (this.interface === 'action-sheet') {
396+
overlay.addEventListener('ionActionSheetWillPresent', scrollAfterRender, { once: true });
397+
}
382398
}
383399

400+
overlay.onDidDismiss().then(() => {
401+
this.overlay = undefined;
402+
this.isExpanded = false;
403+
this.ionDismiss.emit();
404+
this.setFocus();
405+
});
406+
407+
await overlay.present();
384408
return overlay;
385409
}
386410

0 commit comments

Comments
 (0)