Make Disclosure Animatable
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
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
I think this is related https://github.com/adobe/react-spectrum/pull/7488
@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?
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!
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?
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.
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.
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?