react-spectrum icon indicating copy to clipboard operation
react-spectrum copied to clipboard

Make Disclosure Animatable

Open axelthat opened this issue 1 year ago โ€ข 6 comments

Provide a general summary of the feature here

It would be great to animate height on Disclosure like Bootstrap's Accordion.

๐Ÿค” Expected Behavior?

It would be nice for the library to expose/implement isEntering or isExiting props.

๐Ÿ˜ฏ Current Behavior

isEntering or isExiting props aren't implemented.

๐Ÿ’ Possible Solution

No response

๐Ÿ”ฆ Context

Animated height feels natural for a component like Disclosure/Accordion.

๐Ÿ’ป Examples

No response

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response

axelthat avatar Dec 17 '24 08:12 axelthat

Leaving this here for guidance on how to implement it, in case someone wants to pick this up: https://github.com/adobe/react-spectrum/pull/6931#issuecomment-2313394336

nwidynski avatar Dec 17 '24 10:12 nwidynski

I think this is related https://github.com/adobe/react-spectrum/pull/7488

snowystinger avatar Dec 17 '24 10:12 snowystinger

@snowystinger https://github.com/adobe/react-spectrum/pull/7488 is now merged, but I can't find the isEntering and isExiting data tags in Disclosure. Are there any plans on adding them to Disclosure?

thorbde avatar May 26 '25 09:05 thorbde

It's not a priority for us right now, so the fastest way to get this would be to create a PR for the feature so we can discuss it. Thanks!

snowystinger avatar May 26 '25 22:05 snowystinger

I got it half way working using the grid-template-rows trick. This is basically the whole thing:

<DisclosurePanel
  className={[
    'ease-out-quad grid overflow-hidden transition-[grid-template-rows] duration-500',
    'grid-rows-[1fr] aria-hidden:grid-rows-[0fr]',
    '*:visible aria-hidden:*:invisible',
  ].join(' ')}
>
  <div className="min-h-0 transition-[visibility]">{children}</div>
</DisclosurePanel>

Half way because it is not animation when closing. Didn't find out yet why ๐Ÿ™ˆ

https://github.com/user-attachments/assets/0719f639-d255-4b44-9f6c-9e47beeca900


Edit: I think I found it. The hidden attribute is set and immediately hides the panel (https://github.com/adobe/react-spectrum/blob/79505ba223825b329cdba614f3c1ff1cd7f89659/packages/%40react-aria/disclosure/src/useDisclosure.ts#L117). Is there a way to override this behavior or is this required for a11y?

sebald avatar May 28 '25 11:05 sebald

Maybe we could prevent the hidden attribute from being applied until the transitionend event? I don't think that applying it after a small delay is a problem, but would be useful to test that theory.

snowystinger avatar May 29 '25 00:05 snowystinger

I've tried incorporating a delay in my own component and it seems to work well. I've got the delay set with the same duration as my animation transition (transition: grid-template-rows 200ms), which is controlled using the visuallyExpanded class (here using the classnames library to concatenate classes):

export const MyDisclosure = (props: DisclosureProps) => {
  const [visuallyExpanded, setVisuallyExpanded] = useState(props.isExpanded);

  const handleExpansionChange = async (isExpanded: boolean) => {
    setVisuallyExpanded(isExpanded);

    const timeout = isExpanded ? 0 : 200;

    setTimeout(() => {
      props.onExpandedChange?.(isExpanded);
    }, timeout);
  };

  const classNames = cn('disclosure', { visuallyExpanded: visuallyExpanded });

  return <Disclosure {...props} className={classNames} onExpandedChange={handleExpansionChange} />;
};

edit: I've tried this a couple of different ways and the two states can get out of line with one another with fast-clicking. Thinking cap still on.

edit2: A useEffect to deal with state misalignment fixed the issues. Not ideal but works for me for now.

elstoc avatar Jul 17 '25 18:07 elstoc

See #8867 which implements support for this. It adds a CSS variable that gets set to a pixel equivalent of the auto width and height when the disclosure is opening or closing, which you can use with CSS transitions. This seemed simpler than adding an entering/exiting state. Are there any use cases where this will be insufficient?

devongovett avatar Sep 15 '25 18:09 devongovett