Skip to content

Commit

Permalink
fix: remove flicker in AccordionItem (#9818) (#14776)
Browse files Browse the repository at this point in the history
* fix: remove flicker in AccordionItem (#9818)

Removes the flickering effect when using AccordionItem as a controlled
component

* fix: rework accordion component animation (#9818)

* refactor: use ref hook for accordion content

* test: update accordion component snapshots

* docs: fix extra missing curly brace in .all-contributorsrc

* docs: remove accordion controlled test story

---------

Co-authored-by: Andrea N. Cardona <cardona.n.andrea@gmail.com>
Co-authored-by: Alison Joseph <alison.joseph@us.ibm.com>
  • Loading branch information
3 people committed Nov 30, 2023
1 parent 99afd8c commit 448c0ef
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 63 deletions.
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,15 @@
"code"
]
},
{
"login": "cesardlinx",
"name": "David Padilla",
"avatar_url": "https://avatars.githubusercontent.com/u/25573926?v=4",
"profile": "https://www.davidpadilla.dev/",
"contributions": [
"code"
]
},
{
"login": "allisonishida",
"name": "Allison Ishida",
Expand Down
183 changes: 183 additions & 0 deletions README.md

Large diffs are not rendered by default.

39 changes: 25 additions & 14 deletions packages/react/src/components/Accordion/AccordionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import React, {
ReactElement,
ReactNode,
useContext,
useRef,
useState,
} from 'react';
import { Text } from '../Text';
Expand Down Expand Up @@ -87,7 +88,7 @@ interface AccordionItemProps {
* The callback function to run on the `onAnimationEnd`
* event for the list item.
*/
handleAnimationEnd?: AnimationEventHandler<HTMLLIElement>;
handleAnimationEnd?: AnimationEventHandler<HTMLElement>;
}

interface AccordionToggleProps {
Expand Down Expand Up @@ -117,8 +118,6 @@ function AccordionItem({
...rest
}: PropsWithChildren<AccordionItemProps>) {
const [isOpen, setIsOpen] = useState(open);
const [prevIsOpen, setPrevIsOpen] = useState(open);
const [animation, setAnimation] = useState('');
const accordionState = useContext(AccordionContext);

const disabledIsControlled = typeof controlledDisabled === 'boolean';
Expand All @@ -131,24 +130,32 @@ function AccordionItem({
const className = cx({
[`${prefix}--accordion__item`]: true,
[`${prefix}--accordion__item--active`]: isOpen,
[`${prefix}--accordion__item--${animation}`]: animation,
[`${prefix}--accordion__item--disabled`]: disabled,
[customClassName]: !!customClassName,
});

const Toggle = renderToggle || renderExpando; // remove renderExpando in next major release

if (open !== prevIsOpen) {
setAnimation(isOpen ? 'collapsing' : 'expanding');
setIsOpen(open);
setPrevIsOpen(open);
}
const content = useRef<HTMLDivElement>(null);

// When the AccordionItem heading is clicked, toggle the open state of the
// panel
function onClick(event) {
// type guard for ref
if (!content.current) {
return;
}

if (isOpen) {
// accordion closes
content.current.style.maxBlockSize = '';
} else {
// accordion opens
content.current.style.maxBlockSize =
content.current.scrollHeight + 15 + 'px';
}

const nextValue = !isOpen;
setAnimation(isOpen ? 'collapsing' : 'expanding');
setIsOpen(nextValue);
if (onHeadingClick) {
// TODO: normalize signature, potentially:
Expand All @@ -168,11 +175,10 @@ function AccordionItem({
if (handleAnimationEnd) {
handleAnimationEnd(event);
}
setAnimation('');
}

return (
<li className={className} {...rest} onAnimationEnd={onAnimationEnd}>
<li className={className} {...rest}>
<Toggle
disabled={disabled}
aria-controls={id}
Expand All @@ -186,8 +192,13 @@ function AccordionItem({
{title}
</Text>
</Toggle>
<div id={id} className={`${prefix}--accordion__content`}>
{children}
<div
ref={content}
className={`${prefix}--accordion__wrapper`}
onTransitionEnd={onAnimationEnd}>
<div id={id} className={`${prefix}--accordion__content`}>
{children}
</div>
</div>
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ exports[`Accordion should render 1`] = `
</div>
</button>
<div
class="cds--accordion__content"
id="accordion-item-:r0:"
class="cds--accordion__wrapper"
>
Panel A
<div
class="cds--accordion__content"
id="accordion-item-:r0:"
>
Panel A
</div>
</div>
</li>
<li
Expand Down Expand Up @@ -75,10 +79,14 @@ exports[`Accordion should render 1`] = `
</div>
</button>
<div
class="cds--accordion__content"
id="accordion-item-:r1:"
class="cds--accordion__wrapper"
>
Panel B
<div
class="cds--accordion__content"
id="accordion-item-:r1:"
>
Panel B
</div>
</div>
</li>
<li
Expand Down Expand Up @@ -113,10 +121,14 @@ exports[`Accordion should render 1`] = `
</div>
</button>
<div
class="cds--accordion__content"
id="accordion-item-:r2:"
class="cds--accordion__wrapper"
>
Panel C
<div
class="cds--accordion__content"
id="accordion-item-:r2:"
>
Panel C
</div>
</div>
</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ exports[`AccordionItem renders as expected - Component API should render and mat
</div>
</button>
<div
class="cds--accordion__content"
id="accordion-item-:r1:"
class="cds--accordion__wrapper"
>
Lorem ipsum.
<div
class="cds--accordion__content"
id="accordion-item-:r1:"
>
Lorem ipsum.
</div>
</div>
</li>
</div>
Expand Down
50 changes: 13 additions & 37 deletions packages/styles/scss/components/accordion/_accordion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ $content-padding: 0 0 0 $spacing-05 !default;
display: list-item;
overflow: visible;
border-block-start: 1px solid $border-subtle;
transition: all $duration-fast-02 motion(standard, productive);

&:last-child {
border-block-end: 1px solid $border-subtle;
Expand Down Expand Up @@ -144,11 +143,18 @@ $content-padding: 0 0 0 $spacing-05 !default;
text-align: start;
}

.#{$prefix}--accordion__wrapper {
// Properties for when the accordion closes
padding: 0;
max-block-size: 0;
opacity: 0;
transition: all $duration-fast-02 motion(entrance, productive);
writing-mode: horizontal-tb;
}

.#{$prefix}--accordion__content {
display: none;
overflow: hidden;
padding-inline: layout.density('padding-inline');
// Transition property for when the accordion closes
transition: padding motion(standard, productive) $duration-fast-02;

// Custom breakpoints based on issue #4993
@include breakpoint-up(480px) {
Expand Down Expand Up @@ -189,44 +195,14 @@ $content-padding: 0 0 0 $spacing-05 !default;
display: block;
}

@keyframes collapse-accordion {
0% {
@include -content-visible;
}

100% {
@include -content-hidden;
}
}

@keyframes expand-accordion {
0% {
@include -content-hidden;
}

100% {
@include -content-visible;
}
}

.#{$prefix}--accordion__item--collapsing .#{$prefix}--accordion__content {
animation: $duration-fast-02 motion(standard, productive) collapse-accordion;
}

.#{$prefix}--accordion__item--expanding .#{$prefix}--accordion__content {
animation: $duration-fast-02 motion(standard, productive) expand-accordion;
}

.#{$prefix}--accordion__item--active {
overflow: visible;

.#{$prefix}--accordion__content {
display: block;
.#{$prefix}--accordion__wrapper {
// Properties for when the accordion opens
opacity: 1;
padding-block: $spacing-03;
padding-block-end: $spacing-06;
// Transition property for when the accordion opens
transition: padding-top motion(entrance, productive) $duration-fast-02,
padding-bottom motion(entrance, productive) $duration-fast-02;
}

.#{$prefix}--accordion__arrow {
Expand Down

0 comments on commit 448c0ef

Please sign in to comment.