Skip to content

Commit db6c2b6

Browse files
committed
fix(accordion): trying to prevent animation based on interaction rather than render
1 parent f84c484 commit db6c2b6

File tree

1 file changed

+55
-20
lines changed

1 file changed

+55
-20
lines changed

core/src/components/accordion/accordion.tsx

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ export class Accordion implements ComponentInterface {
4141
private accordionGroupEl?: HTMLIonAccordionGroupElement | null;
4242
private updateListener = (ev: CustomEvent<AccordionGroupChangeEventDetail>) => {
4343
const initialUpdate = ev.detail?.initial ?? false;
44+
console.log('[Accordion]', this.value, 'updateListener - initial:', initialUpdate, 'hasInteracted:', this.hasInteracted, 'hasEverBeenExpanded:', this.hasEverBeenExpanded);
45+
/**
46+
* If this is not an initial update (meaning it's from a user interaction
47+
* or programmatic call after load), mark that we've had an interaction.
48+
* This enables animations for this and future updates.
49+
*/
50+
if (!initialUpdate) {
51+
console.log('[Accordion]', this.value, 'Setting hasInteracted to true');
52+
this.hasInteracted = true;
53+
}
4454
this.updateState(initialUpdate);
4555
};
4656
private contentEl: HTMLDivElement | undefined;
@@ -55,12 +65,18 @@ export class Accordion implements ComponentInterface {
5565
@State() isNext = false;
5666
@State() isPrevious = false;
5767
/**
58-
* Tracks whether the component has completed its initial render.
59-
* Animations are disabled until after the first render completes.
60-
* This prevents the accordion from animating when it starts
61-
* expanded or collapsed on initial load.
68+
* Tracks whether a user-initiated interaction has occurred.
69+
* Animations are disabled until the first interaction happens.
70+
* This prevents the accordion from animating when it's programmatically
71+
* set to an expanded or collapsed state on initial load.
72+
*/
73+
@State() hasInteracted = false;
74+
75+
/**
76+
* Tracks if this accordion has ever been expanded.
77+
* Used to prevent the first expansion from animating.
6278
*/
63-
@State() hasRendered = false;
79+
private hasEverBeenExpanded = false;
6480

6581
/**
6682
* The value of the accordion. Defaults to an autogenerated
@@ -130,17 +146,6 @@ export class Accordion implements ComponentInterface {
130146
});
131147
}
132148

133-
componentDidRender() {
134-
/**
135-
* After the first render completes, mark that we've rendered.
136-
* Setting this state property triggers a re-render, at which point
137-
* animations will be enabled. This ensures animations are disabled
138-
* only for the initial render, avoiding unwanted animations on load.
139-
*/
140-
if (!this.hasRendered) {
141-
this.hasRendered = true;
142-
}
143-
}
144149

145150
private setItemDefaults = () => {
146151
const ionItem = this.getSlottedHeaderIonItem();
@@ -236,21 +241,37 @@ export class Accordion implements ComponentInterface {
236241
};
237242

238243
private expandAccordion = (initialUpdate = false) => {
244+
console.log('[Accordion]', this.value, 'expandAccordion - initialUpdate:', initialUpdate, 'state:', this.state);
239245
const { contentEl, contentElWrapper } = this;
240246
if (initialUpdate || contentEl === undefined || contentElWrapper === undefined) {
241247
this.state = AccordionState.Expanded;
248+
/**
249+
* Mark that this accordion has been expanded at least once.
250+
* Do this even on initial expansion so future interactions animate.
251+
*/
252+
this.hasEverBeenExpanded = true;
253+
console.log('[Accordion]', this.value, 'expandAccordion early return - hasEverBeenExpanded set to true');
242254
return;
243255
}
244256

245257
if (this.state === AccordionState.Expanded) {
258+
console.log('[Accordion]', this.value, 'expandAccordion - already expanded, returning');
246259
return;
247260
}
248261

249262
if (this.currentRaf !== undefined) {
250263
cancelAnimationFrame(this.currentRaf);
251264
}
252265

266+
/**
267+
* Mark that this accordion has been expanded at least once.
268+
* This allows subsequent expansions to animate.
269+
*/
270+
this.hasEverBeenExpanded = true;
271+
console.log('[Accordion]', this.value, 'expandAccordion - hasEverBeenExpanded set to true');
272+
253273
if (this.shouldAnimate()) {
274+
console.log('[Accordion]', this.value, 'expandAccordion - will animate');
254275
raf(() => {
255276
this.state = AccordionState.Expanding;
256277

@@ -315,11 +336,16 @@ export class Accordion implements ComponentInterface {
315336
*/
316337
private shouldAnimate = () => {
317338
/**
318-
* Don't animate until after the first render cycle completes.
339+
* Don't animate until after the first user interaction.
319340
* This prevents animations on initial load when accordions
320-
* start in an expanded or collapsed state.
341+
* start in an expanded or collapsed state programmatically.
342+
*
343+
* Additionally, don't animate the very first expansion even if
344+
* hasInteracted is true. This handles edge cases like React StrictMode
345+
* where effects run twice and might incorrectly mark as interacted.
321346
*/
322-
if (!this.hasRendered) {
347+
if (!this.hasInteracted || !this.hasEverBeenExpanded) {
348+
console.log('[Accordion]', this.value, 'shouldAnimate: false - hasInteracted:', this.hasInteracted, 'hasEverBeenExpanded:', this.hasEverBeenExpanded);
323349
return false;
324350
}
325351

@@ -418,6 +444,12 @@ export class Accordion implements ComponentInterface {
418444

419445
if (disabled || readonly) return;
420446

447+
/**
448+
* Mark that the user has interacted with the accordion.
449+
* This enables animations for all future state changes.
450+
*/
451+
this.hasInteracted = true;
452+
421453
if (accordionGroupEl) {
422454
/**
423455
* Because the accordion group may or may
@@ -438,6 +470,9 @@ export class Accordion implements ComponentInterface {
438470
const expanded = this.state === AccordionState.Expanded || this.state === AccordionState.Expanding;
439471
const headerPart = expanded ? 'header expanded' : 'header';
440472
const contentPart = expanded ? 'content expanded' : 'content';
473+
const shouldAnimate = this.shouldAnimate();
474+
475+
console.log('[Accordion]', this.value, 'render - state:', this.state, 'shouldAnimate:', shouldAnimate, 'hasInteracted:', this.hasInteracted, 'hasEverBeenExpanded:', this.hasEverBeenExpanded);
441476

442477
this.setAria(expanded);
443478

@@ -456,7 +491,7 @@ export class Accordion implements ComponentInterface {
456491
'accordion-disabled': disabled,
457492
'accordion-readonly': readonly,
458493

459-
'accordion-animated': this.shouldAnimate(),
494+
'accordion-animated': shouldAnimate,
460495
}}
461496
>
462497
<div

0 commit comments

Comments
 (0)