@@ -5,7 +5,6 @@ import { chevronDown } from 'ionicons/icons';
55
66import  {  config  }  from  '../../global/config' ; 
77import  {  getIonMode  }  from  '../../global/ionic-global' ; 
8- import  type  {  AccordionGroupChangeEventDetail  }  from  '../accordion-group/accordion-group-interface' ; 
98
109const  enum  AccordionState  { 
1110  Collapsed  =  1  <<  0 , 
@@ -39,9 +38,39 @@ const enum AccordionState {
3938} ) 
4039export  class  Accordion  implements  ComponentInterface  { 
4140  private  accordionGroupEl ?: HTMLIonAccordionGroupElement  |  null ; 
42-   private  updateListener  =  ( ev : CustomEvent < AccordionGroupChangeEventDetail > )  =>  { 
43-     const  initialUpdate  =  ev . detail ?. initial  ??  false ; 
44-     this . updateState ( initialUpdate ) ; 
41+   private  updateListener  =  ( )  =>  { 
42+     /** 
43+      * Determine if this update will cause an actual state change. 
44+      * We only want to mark as "interacted" if the state is changing. 
45+      */ 
46+     const  accordionGroup  =  this . accordionGroupEl ; 
47+     if  ( accordionGroup )  { 
48+       const  value  =  accordionGroup . value ; 
49+       const  accordionValue  =  this . value ; 
50+       const  shouldExpand  =  Array . isArray ( value )  ? value . includes ( accordionValue )  : value  ===  accordionValue ; 
51+       const  isExpanded  =  this . state  ===  AccordionState . Expanded  ||  this . state  ===  AccordionState . Expanding ; 
52+       const  stateWillChange  =  shouldExpand  !==  isExpanded ; 
53+ 
54+       /** 
55+        * Only mark as interacted if: 
56+        * 1. This is not the first update we've received with a defined value 
57+        * 2. The state is actually changing (prevents redundant updates from enabling animations) 
58+        */ 
59+       if  ( this . hasReceivedFirstUpdate  &&  stateWillChange )  { 
60+         this . hasInteracted  =  true ; 
61+       } 
62+ 
63+       /** 
64+        * Only count this as the first update if the group value is defined. 
65+        * This prevents the initial undefined value from the group's componentDidLoad 
66+        * from being treated as the first real update. 
67+        */ 
68+       if  ( value  !==  undefined )  { 
69+         this . hasReceivedFirstUpdate  =  true ; 
70+       } 
71+     } 
72+ 
73+     this . updateState ( ) ; 
4574  } ; 
4675  private  contentEl : HTMLDivElement  |  undefined ; 
4776  private  contentElWrapper : HTMLDivElement  |  undefined ; 
@@ -55,12 +84,24 @@ export class Accordion implements ComponentInterface {
5584  @State ( )  isNext  =  false ; 
5685  @State ( )  isPrevious  =  false ; 
5786  /** 
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. 
87+    * Tracks whether a user-initiated interaction  has occurred . 
88+    * Animations are disabled until the first interaction happens . 
89+    * This prevents the accordion from animating when it's programmatically  
90+    * set to an  expanded or collapsed state  on initial load. 
6291   */ 
63-   @State ( )  hasRendered  =  false ; 
92+   @State ( )  hasInteracted  =  false ; 
93+ 
94+   /** 
95+    * Tracks if this accordion has ever been expanded. 
96+    * Used to prevent the first expansion from animating. 
97+    */ 
98+   private  hasEverBeenExpanded  =  false ; 
99+ 
100+   /** 
101+    * Tracks if this accordion has received its first update from the group. 
102+    * Used to distinguish initial programmatic sets from user interactions. 
103+    */ 
104+   private  hasReceivedFirstUpdate  =  false ; 
64105
65106  /** 
66107   * The value of the accordion. Defaults to an autogenerated 
@@ -99,7 +140,7 @@ export class Accordion implements ComponentInterface {
99140  connectedCallback ( )  { 
100141    const  accordionGroupEl  =  ( this . accordionGroupEl  =  this . el ?. closest ( 'ion-accordion-group' ) ) ; 
101142    if  ( accordionGroupEl )  { 
102-       this . updateState ( true ) ; 
143+       this . updateState ( ) ; 
103144      addEventListener ( accordionGroupEl ,  'ionValueChange' ,  this . updateListener ) ; 
104145    } 
105146  } 
@@ -130,18 +171,6 @@ export class Accordion implements ComponentInterface {
130171    } ) ; 
131172  } 
132173
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-   } 
144- 
145174  private  setItemDefaults  =  ( )  =>  { 
146175    const  ionItem  =  this . getSlottedHeaderIonItem ( ) ; 
147176    if  ( ! ionItem )  { 
@@ -235,10 +264,16 @@ export class Accordion implements ComponentInterface {
235264    ionItem . appendChild ( iconEl ) ; 
236265  } ; 
237266
238-   private  expandAccordion  =  ( initialUpdate   =   false )  =>  { 
267+   private  expandAccordion  =  ( )  =>  { 
239268    const  {  contentEl,  contentElWrapper }  =  this ; 
240-     if  ( initialUpdate  ||  contentEl  ===  undefined  ||  contentElWrapper  ===  undefined )  { 
269+ 
270+     /** 
271+      * If the content elements aren't available yet, just set the state. 
272+      * This happens on initial render before the DOM is ready. 
273+      */ 
274+     if  ( contentEl  ===  undefined  ||  contentElWrapper  ===  undefined )  { 
241275      this . state  =  AccordionState . Expanded ; 
276+       this . hasEverBeenExpanded  =  true ; 
242277      return ; 
243278    } 
244279
@@ -250,6 +285,12 @@ export class Accordion implements ComponentInterface {
250285      cancelAnimationFrame ( this . currentRaf ) ; 
251286    } 
252287
288+     /** 
289+      * Mark that this accordion has been expanded at least once. 
290+      * This allows subsequent expansions to animate. 
291+      */ 
292+     this . hasEverBeenExpanded  =  true ; 
293+ 
253294    if  ( this . shouldAnimate ( ) )  { 
254295      raf ( ( )  =>  { 
255296        this . state  =  AccordionState . Expanding ; 
@@ -270,9 +311,14 @@ export class Accordion implements ComponentInterface {
270311    } 
271312  } ; 
272313
273-   private  collapseAccordion  =  ( initialUpdate   =   false )  =>  { 
314+   private  collapseAccordion  =  ( )  =>  { 
274315    const  {  contentEl }  =  this ; 
275-     if  ( initialUpdate  ||  contentEl  ===  undefined )  { 
316+ 
317+     /** 
318+      * If the content element isn't available yet, just set the state. 
319+      * This happens on initial render before the DOM is ready. 
320+      */ 
321+     if  ( contentEl  ===  undefined )  { 
276322      this . state  =  AccordionState . Collapsed ; 
277323      return ; 
278324    } 
@@ -315,11 +361,15 @@ export class Accordion implements ComponentInterface {
315361   */ 
316362  private  shouldAnimate  =  ( )  =>  { 
317363    /** 
318-      * Don't animate until after the first render cycle completes . 
364+      * Don't animate until after the first user interaction . 
319365     * This prevents animations on initial load when accordions 
320-      * start in an expanded or collapsed state. 
366+      * start in an expanded or collapsed state programmatically. 
367+      * 
368+      * Additionally, don't animate the very first expansion even if 
369+      * hasInteracted is true. This handles edge cases like React StrictMode 
370+      * where effects run twice and might incorrectly mark as interacted. 
321371     */ 
322-     if  ( ! this . hasRendered )  { 
372+     if  ( ! this . hasInteracted   ||   ! this . hasEverBeenExpanded )  { 
323373      return  false ; 
324374    } 
325375
@@ -344,7 +394,7 @@ export class Accordion implements ComponentInterface {
344394    return  true ; 
345395  } ; 
346396
347-   private  updateState  =  async  ( initialUpdate   =   false )  =>  { 
397+   private  updateState  =  async  ( )  =>  { 
348398    const  accordionGroup  =  this . accordionGroupEl ; 
349399    const  accordionValue  =  this . value ; 
350400
@@ -357,10 +407,10 @@ export class Accordion implements ComponentInterface {
357407    const  shouldExpand  =  Array . isArray ( value )  ? value . includes ( accordionValue )  : value  ===  accordionValue ; 
358408
359409    if  ( shouldExpand )  { 
360-       this . expandAccordion ( initialUpdate ) ; 
410+       this . expandAccordion ( ) ; 
361411      this . isNext  =  this . isPrevious  =  false ; 
362412    }  else  { 
363-       this . collapseAccordion ( initialUpdate ) ; 
413+       this . collapseAccordion ( ) ; 
364414
365415      /** 
366416       * When using popout or inset, 
@@ -418,6 +468,12 @@ export class Accordion implements ComponentInterface {
418468
419469    if  ( disabled  ||  readonly )  return ; 
420470
471+     /** 
472+      * Mark that the user has interacted with the accordion. 
473+      * This enables animations for all future state changes. 
474+      */ 
475+     this . hasInteracted  =  true ; 
476+ 
421477    if  ( accordionGroupEl )  { 
422478      /** 
423479       * Because the accordion group may or may 
0 commit comments