Skip to content

Commit 58c2a8a

Browse files
committed
fix(accordion): skip initial animation
1 parent e4fc33f commit 58c2a8a

File tree

4 files changed

+74
-30
lines changed

4 files changed

+74
-30
lines changed

core/src/components/accordion-group/accordion-group-interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface AccordionGroupChangeEventDetail<T = any> {
22
value: T;
3+
initial?: boolean;
34
}
45

56
export interface AccordionGroupCustomEvent<T = any> extends CustomEvent {

core/src/components/accordion-group/accordion-group.tsx

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,31 +75,7 @@ export class AccordionGroup implements ComponentInterface {
7575

7676
@Watch('value')
7777
valueChanged() {
78-
const { value, multiple } = this;
79-
80-
if (!multiple && Array.isArray(value)) {
81-
/**
82-
* We do some processing on the `value` array so
83-
* that it looks more like an array when logged to
84-
* the console.
85-
* Example given ['a', 'b']
86-
* Default toString() behavior: a,b
87-
* Custom behavior: ['a', 'b']
88-
*/
89-
printIonWarning(
90-
`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
91-
92-
Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
93-
`,
94-
this.el
95-
);
96-
}
97-
98-
/**
99-
* Do not use `value` here as that will be
100-
* not account for the adjustment we make above.
101-
*/
102-
this.ionValueChange.emit({ value: this.value });
78+
this.emitValueChange(false);
10379
}
10480

10581
@Watch('disabled')
@@ -184,11 +160,10 @@ export class AccordionGroup implements ComponentInterface {
184160
* it is possible for the value to be set after the Web Component
185161
* initializes but before the value watcher is set up in Stencil.
186162
* As a result, the watcher callback may not be fired.
187-
* We work around this by manually calling the watcher
188-
* callback when the component has loaded and the watcher
189-
* is configured.
163+
* We work around this by manually emitting a value change when the component
164+
* has loaded and the watcher is configured.
190165
*/
191-
this.valueChanged();
166+
this.emitValueChange(true);
192167
}
193168

194169
/**
@@ -276,6 +251,34 @@ export class AccordionGroup implements ComponentInterface {
276251
return Array.from(this.el.querySelectorAll(':scope > ion-accordion')) as HTMLIonAccordionElement[];
277252
}
278253

254+
private emitValueChange(initial: boolean) {
255+
const { value, multiple } = this;
256+
257+
if (!multiple && Array.isArray(value)) {
258+
/**
259+
* We do some processing on the `value` array so
260+
* that it looks more like an array when logged to
261+
* the console.
262+
* Example given ['a', 'b']
263+
* Default toString() behavior: a,b
264+
* Custom behavior: ['a', 'b']
265+
*/
266+
printIonWarning(
267+
`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
268+
269+
Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
270+
`,
271+
this.el
272+
);
273+
}
274+
275+
/**
276+
* Do not use `value` here as that will not account
277+
* for the adjustment we make above.
278+
*/
279+
this.ionValueChange.emit({ value: this.value, initial });
280+
}
281+
279282
render() {
280283
const { disabled, readonly, expand } = this;
281284
const mode = getIonMode(this);

core/src/components/accordion/accordion.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { chevronDown } from 'ionicons/icons';
55

66
import { config } from '../../global/config';
77
import { getIonMode } from '../../global/ionic-global';
8+
import type { AccordionGroupChangeEventDetail } from '../accordion-group/accordion-group-interface';
89

910
const enum AccordionState {
1011
Collapsed = 1 << 0,
@@ -38,7 +39,10 @@ const enum AccordionState {
3839
})
3940
export class Accordion implements ComponentInterface {
4041
private accordionGroupEl?: HTMLIonAccordionGroupElement | null;
41-
private updateListener = () => this.updateState(false);
42+
private updateListener = (ev: CustomEvent<AccordionGroupChangeEventDetail>) => {
43+
const initialUpdate = ev.detail?.initial ?? false;
44+
this.updateState(initialUpdate);
45+
};
4246
private contentEl: HTMLDivElement | undefined;
4347
private contentElWrapper: HTMLDivElement | undefined;
4448
private headerEl: HTMLDivElement | undefined;

core/src/components/accordion/test/accordion.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { newSpecPage } from '@stencil/core/testing';
22

3+
import type { AccordionGroupChangeEventDetail } from '../../accordion-group/accordion-group-interface';
34
import { AccordionGroup } from '../../accordion-group/accordion-group';
45
import { Item } from '../../item/item';
56
import { Accordion } from '../accordion';
@@ -200,6 +201,41 @@ it('should set default values if not provided', async () => {
200201
expect(accordion.classList.contains('accordion-collapsed')).toEqual(false);
201202
});
202203

204+
it('should not animate when initial value is set before load', async () => {
205+
const page = await newSpecPage({
206+
components: [Item, Accordion, AccordionGroup],
207+
});
208+
209+
const accordionGroup = page.doc.createElement('ion-accordion-group');
210+
accordionGroup.innerHTML = `
211+
<ion-accordion value="first">
212+
<ion-item slot="header">Label</ion-item>
213+
<div slot="content">Content</div>
214+
</ion-accordion>
215+
<ion-accordion value="second">
216+
<ion-item slot="header">Label</ion-item>
217+
<div slot="content">Content</div>
218+
</ion-accordion>
219+
`;
220+
221+
const details: AccordionGroupChangeEventDetail[] = [];
222+
accordionGroup.addEventListener('ionValueChange', (event: CustomEvent<AccordionGroupChangeEventDetail>) => {
223+
details.push(event.detail);
224+
});
225+
226+
accordionGroup.value = 'first';
227+
page.body.appendChild(accordionGroup);
228+
229+
await page.waitForChanges();
230+
231+
expect(details[0]?.initial).toBe(true);
232+
233+
const firstAccordion = accordionGroup.querySelector('ion-accordion[value="first"]')!;
234+
235+
expect(firstAccordion.classList.contains('accordion-expanded')).toEqual(true);
236+
expect(firstAccordion.classList.contains('accordion-expanding')).toEqual(false);
237+
});
238+
203239
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/27047
204240
it('should not have animated class when animated="false"', async () => {
205241
const page = await newSpecPage({

0 commit comments

Comments
 (0)