Fix details animation by preventing overflow during animation (#1214)

* don't overflow content; fixes #1149

* fix details overflow; closes #1149
This commit is contained in:
Cory LaViska
2025-07-22 14:00:21 -04:00
committed by GitHub
parent 310f7a8c5d
commit 6f6e23c78c
3 changed files with 34 additions and 15 deletions

View File

@@ -13,6 +13,11 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
### New Features {data-no-outline}
- Added the `icon-position` attribute to `<wa-details>` [discuss:1099]
- Added the `animating` custom state to `<wa-details>`
### Bug Fixes and Improvements {data-no-outline}
- Fixed a bug in `<wa-details>` that caused the content to overflow the container when animating [issue:1149]
### Bug Fixes and Improvements {data-no-outline}

View File

@@ -129,8 +129,7 @@ details {
display: none;
}
/* Overflows get clipped during the closing animation if we don't wait until the close is gone. */
:host(:not([open])) .body {
.body.animating {
overflow: hidden;
}
@@ -140,10 +139,3 @@ details {
padding-inline: var(--spacing); /* Add horizontal padding */
padding-block-end: var(--spacing); /* Add bottom padding */
}
@keyframes show {
from {
}
to {
}
}

View File

@@ -1,5 +1,7 @@
import type { PropertyValues } from 'lit';
import { html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { WaAfterHideEvent } from '../../events/after-hide.js';
import { WaAfterShowEvent } from '../../events/after-show.js';
import { WaHideEvent } from '../../events/hide.js';
@@ -40,6 +42,8 @@ import styles from './details.css';
* @cssproperty --spacing - The amount of space around and between the details' content. Expects a single value.
* @cssproperty [--show-duration=200ms] - The show duration to use when applying built-in animation classes.
* @cssproperty [--hide-duration=200ms] - The hide duration to use when applying built-in animation classes.
*
* @cssstate animating - Applied when the details is animating expand/collapse.
*/
@customElement('wa-details')
export default class WaDetails extends WebAwesomeElement {
@@ -53,6 +57,8 @@ export default class WaDetails extends WebAwesomeElement {
@query('.body') body: HTMLElement;
@query('.expand-icon-slot') expandIconSlot: HTMLSlotElement;
@state() isAnimating = false;
/**
* Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you
* can use the `show()` and `hide()` methods and this attribute will reflect the details' open state.
@@ -74,6 +80,11 @@ export default class WaDetails extends WebAwesomeElement {
/** The position of the expand/collapse icon. */
@property({ attribute: 'icon-position', reflect: true }) iconPosition: 'start' | 'end' = 'end';
disconnectedCallback() {
super.disconnectedCallback();
this.detailsObserver?.disconnect();
}
firstUpdated() {
this.body.style.height = this.open ? 'auto' : '0';
if (this.open) {
@@ -94,9 +105,10 @@ export default class WaDetails extends WebAwesomeElement {
this.detailsObserver.observe(this.details, { attributes: true });
}
disconnectedCallback() {
super.disconnectedCallback();
this.detailsObserver?.disconnect();
updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has('isAnimating')) {
this.customStates.set('animating', this.isAnimating);
}
}
private handleSummaryClick(event: MouseEvent) {
@@ -171,6 +183,7 @@ export default class WaDetails extends WebAwesomeElement {
// Close other details with the same name
this.closeOthersWithSameName();
this.isAnimating = true;
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--show-duration'));
// We can't animate to 'auto', so use the scroll height for now
await animate(
@@ -185,6 +198,7 @@ export default class WaDetails extends WebAwesomeElement {
},
);
this.body.style.height = 'auto';
this.isAnimating = false;
this.dispatchEvent(new WaAfterShowEvent());
} else {
@@ -197,6 +211,7 @@ export default class WaDetails extends WebAwesomeElement {
return;
}
this.isAnimating = true;
const duration = parseDuration(getComputedStyle(this.body).getPropertyValue('--hide-duration'));
// We can't animate from 'auto', so use the scroll height for now
await animate(
@@ -208,7 +223,7 @@ export default class WaDetails extends WebAwesomeElement {
{ duration, easing: 'linear' },
);
this.body.style.height = 'auto';
this.isAnimating = false;
this.details.open = false;
this.dispatchEvent(new WaAfterHideEvent());
}
@@ -271,7 +286,14 @@ export default class WaDetails extends WebAwesomeElement {
</span>
</summary>
<div class="body" role="region" aria-labelledby="header">
<div
class=${classMap({
body: true,
animating: this.isAnimating,
})}
role="region"
aria-labelledby="header"
>
<slot part="content" id="content" class="content"></slot>
</div>
</details>