From ba40af2c5cc02d699098d93b46a7333ee8bd5156 Mon Sep 17 00:00:00 2001 From: Brandon Goren Date: Fri, 24 Oct 2025 11:19:27 -0400 Subject: [PATCH 1/2] add data attribute to collapsible and accordion --- docs/reference/generated/accordion-panel.json | 3 +++ docs/reference/generated/collapsible-panel.json | 3 +++ packages/react/src/accordion/panel/AccordionPanel.tsx | 3 ++- .../react/src/accordion/panel/AccordionPanelDataAttributes.ts | 4 ++++ packages/react/src/collapsible/panel/CollapsiblePanel.tsx | 4 +++- .../src/collapsible/panel/CollapsiblePanelDataAttributes.ts | 4 ++++ 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/reference/generated/accordion-panel.json b/docs/reference/generated/accordion-panel.json index 24ee5bbbbdb..2fd4e036607 100644 --- a/docs/reference/generated/accordion-panel.json +++ b/docs/reference/generated/accordion-panel.json @@ -39,6 +39,9 @@ "data-disabled": { "description": "Present when the accordion item is disabled." }, + "data-hidden": { + "description": "Present when the panel is hidden." + }, "data-index": { "description": "Indicates the index of the accordion item.", "type": "number" diff --git a/docs/reference/generated/collapsible-panel.json b/docs/reference/generated/collapsible-panel.json index c1595a4c0cd..f5efeb1b195 100644 --- a/docs/reference/generated/collapsible-panel.json +++ b/docs/reference/generated/collapsible-panel.json @@ -36,6 +36,9 @@ "data-closed": { "description": "Present when the collapsible panel is closed." }, + "data-hidden": { + "description": "Present when the panel is hidden." + }, "data-starting-style": { "description": "Present when the panel is animating in." }, diff --git a/packages/react/src/accordion/panel/AccordionPanel.tsx b/packages/react/src/accordion/panel/AccordionPanel.tsx index a792309be61..3229104a8aa 100644 --- a/packages/react/src/accordion/panel/AccordionPanel.tsx +++ b/packages/react/src/accordion/panel/AccordionPanel.tsx @@ -132,8 +132,9 @@ export const AccordionPanel = React.forwardRef(function AccordionPanel( () => ({ ...state, transitionStatus, + hidden: !open, }), - [state, transitionStatus], + [state, transitionStatus, open], ); const element = useRenderElement('div', componentProps, { diff --git a/packages/react/src/accordion/panel/AccordionPanelDataAttributes.ts b/packages/react/src/accordion/panel/AccordionPanelDataAttributes.ts index 21f5fd2a1e9..3bd946447bc 100644 --- a/packages/react/src/accordion/panel/AccordionPanelDataAttributes.ts +++ b/packages/react/src/accordion/panel/AccordionPanelDataAttributes.ts @@ -26,4 +26,8 @@ export enum AccordionPanelDataAttributes { * Present when the panel is animating out. */ endingStyle = TransitionStatusDataAttributes.endingStyle, + /** + * Present when the panel is hidden. + */ + hidden = 'data-hidden', } diff --git a/packages/react/src/collapsible/panel/CollapsiblePanel.tsx b/packages/react/src/collapsible/panel/CollapsiblePanel.tsx index 7450f5f1462..503209463f6 100644 --- a/packages/react/src/collapsible/panel/CollapsiblePanel.tsx +++ b/packages/react/src/collapsible/panel/CollapsiblePanel.tsx @@ -124,9 +124,10 @@ export const CollapsiblePanel = React.forwardRef(function CollapsiblePanel( const panelState: CollapsiblePanel.State = React.useMemo( () => ({ ...state, + hidden: !open, transitionStatus, }), - [state, transitionStatus], + [state, transitionStatus, open], ); const element = useRenderElement('div', componentProps, { @@ -158,6 +159,7 @@ export const CollapsiblePanel = React.forwardRef(function CollapsiblePanel( export interface CollapsiblePanelState extends CollapsibleRoot.State { transitionStatus: TransitionStatus; + hidden: boolean; } export interface CollapsiblePanelProps extends BaseUIComponentProps<'div', CollapsiblePanel.State> { diff --git a/packages/react/src/collapsible/panel/CollapsiblePanelDataAttributes.ts b/packages/react/src/collapsible/panel/CollapsiblePanelDataAttributes.ts index d081ee87c7c..b8328926b91 100644 --- a/packages/react/src/collapsible/panel/CollapsiblePanelDataAttributes.ts +++ b/packages/react/src/collapsible/panel/CollapsiblePanelDataAttributes.ts @@ -17,4 +17,8 @@ export enum CollapsiblePanelDataAttributes { * Present when the panel is animating out. */ endingStyle = TransitionStatusDataAttributes.endingStyle, + /** + * Present when the panel is hidden. + */ + hidden = 'data-hidden' } From 8bc13d3bef8425f315022967d0538b1c83e37197 Mon Sep 17 00:00:00 2001 From: Brandon Goren Date: Fri, 24 Oct 2025 17:56:12 -0400 Subject: [PATCH 2/2] suggested refactor --- .../src/accordion/panel/AccordionPanel.tsx | 22 ++++++++++++++++--- .../collapsible/panel/CollapsiblePanel.tsx | 14 +++++++----- .../src/utils/collapsibleOpenStateMapping.ts | 18 +++++++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/packages/react/src/accordion/panel/AccordionPanel.tsx b/packages/react/src/accordion/panel/AccordionPanel.tsx index 3229104a8aa..d2aec21716e 100644 --- a/packages/react/src/accordion/panel/AccordionPanel.tsx +++ b/packages/react/src/accordion/panel/AccordionPanel.tsx @@ -14,6 +14,23 @@ import { AccordionPanelCssVars } from './AccordionPanelCssVars'; import { useOpenChangeComplete } from '../../utils/useOpenChangeComplete'; import { useRenderElement } from '../../utils/useRenderElement'; import type { TransitionStatus } from '../../utils/useTransitionStatus'; +import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'; +import { AccordionPanelDataAttributes } from './AccordionPanelDataAttributes'; + +const stateAttributesMapping: StateAttributesMapping = { + ...accordionStateAttributesMapping, + open: (open) => { + const hidden = !open; + const itemMapping = accordionStateAttributesMapping.open?.(open) ?? {}; + if (hidden) { + return { + ...itemMapping, + [AccordionPanelDataAttributes.hidden]: '', + }; + } + return itemMapping; + }, +}; /** * A collapsible panel with the accordion item contents. @@ -132,9 +149,8 @@ export const AccordionPanel = React.forwardRef(function AccordionPanel( () => ({ ...state, transitionStatus, - hidden: !open, }), - [state, transitionStatus, open], + [state, transitionStatus], ); const element = useRenderElement('div', componentProps, { @@ -154,7 +170,7 @@ export const AccordionPanel = React.forwardRef(function AccordionPanel( }, elementProps, ], - stateAttributesMapping: accordionStateAttributesMapping, + stateAttributesMapping, }); const shouldRender = keepMounted || hiddenUntilFound || (!keepMounted && mounted); diff --git a/packages/react/src/collapsible/panel/CollapsiblePanel.tsx b/packages/react/src/collapsible/panel/CollapsiblePanel.tsx index 503209463f6..bdf225a1976 100644 --- a/packages/react/src/collapsible/panel/CollapsiblePanel.tsx +++ b/packages/react/src/collapsible/panel/CollapsiblePanel.tsx @@ -6,12 +6,18 @@ import { BaseUIComponentProps } from '../../utils/types'; import { useRenderElement } from '../../utils/useRenderElement'; import { useCollapsibleRootContext } from '../root/CollapsibleRootContext'; import type { CollapsibleRoot } from '../root/CollapsibleRoot'; -import { collapsibleStateAttributesMapping } from '../root/stateAttributesMapping'; +import { transitionStatusMapping } from '../../utils/stateAttributesMapping'; +import { panelOpenStateMapping } from '../../utils/collapsibleOpenStateMapping'; import { useCollapsiblePanel } from './useCollapsiblePanel'; import { CollapsiblePanelCssVars } from './CollapsiblePanelCssVars'; import { useOpenChangeComplete } from '../../utils/useOpenChangeComplete'; import type { TransitionStatus } from '../../utils/useTransitionStatus'; +import type { StateAttributesMapping } from '../../utils/getStateAttributesProps'; +const stateAttributesMapping: StateAttributesMapping = { + ...panelOpenStateMapping, + ...transitionStatusMapping, +}; /** * A panel with the collapsible contents. * Renders a `
` element. @@ -124,10 +130,9 @@ export const CollapsiblePanel = React.forwardRef(function CollapsiblePanel( const panelState: CollapsiblePanel.State = React.useMemo( () => ({ ...state, - hidden: !open, transitionStatus, }), - [state, transitionStatus, open], + [state, transitionStatus], ); const element = useRenderElement('div', componentProps, { @@ -145,7 +150,7 @@ export const CollapsiblePanel = React.forwardRef(function CollapsiblePanel( }, elementProps, ], - stateAttributesMapping: collapsibleStateAttributesMapping, + stateAttributesMapping, }); const shouldRender = keepMounted || hiddenUntilFound || (!keepMounted && mounted); @@ -159,7 +164,6 @@ export const CollapsiblePanel = React.forwardRef(function CollapsiblePanel( export interface CollapsiblePanelState extends CollapsibleRoot.State { transitionStatus: TransitionStatus; - hidden: boolean; } export interface CollapsiblePanelProps extends BaseUIComponentProps<'div', CollapsiblePanel.State> { diff --git a/packages/react/src/utils/collapsibleOpenStateMapping.ts b/packages/react/src/utils/collapsibleOpenStateMapping.ts index df22f2c525b..641b2337edf 100644 --- a/packages/react/src/utils/collapsibleOpenStateMapping.ts +++ b/packages/react/src/utils/collapsibleOpenStateMapping.ts @@ -33,3 +33,21 @@ export const collapsibleOpenStateMapping = { } satisfies StateAttributesMapping<{ open: boolean; }>; + +export const panelOpenStateMapping = { + open: (open) => { + const panelHidden = !open; + const collapsibleStateMapping = collapsibleOpenStateMapping.open(open); + + if (panelHidden) { + return { + ...collapsibleStateMapping, + [CollapsiblePanelDataAttributes.hidden]: '', + }; + } + + return collapsibleStateMapping; + }, +} satisfies StateAttributesMapping<{ + open: boolean; +}>;