Skip to content

Commit e87ee28

Browse files
authored
Add DisclosureGroup component to RAC (#7063)
1 parent 9421c14 commit e87ee28

File tree

11 files changed

+236
-109
lines changed

11 files changed

+236
-109
lines changed

packages/@react-aria/disclosure/src/useDisclosure.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export interface AriaDisclosureProps {
3030
export interface DisclosureAria {
3131
/** Props for the disclosure button. */
3232
buttonProps: AriaButtonProps,
33-
/** Props for the content element. */
34-
contentProps: HTMLAttributes<HTMLElement>
33+
/** Props for the disclosure panel. */
34+
panelProps: HTMLAttributes<HTMLElement>
3535
}
3636

3737
/**
@@ -84,8 +84,10 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
8484
}
8585
}
8686
},
87-
contentProps: {
87+
panelProps: {
8888
id: contentId,
89+
// This can be overridden at the panel element level.
90+
role: 'group',
8991
'aria-labelledby': triggerId,
9092
hidden: (!supportsBeforeMatch || isControlled) ? !state.isExpanded : true
9193
}

packages/@react-spectrum/accordion/chromatic/Accordion.stories.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,23 @@ export default meta;
2424

2525
export const Template = (args) => (
2626
<Accordion {...args}>
27-
<Disclosure key="files">
27+
<Disclosure id="files">
2828
<DisclosureHeader>
2929
Your files
3030
</DisclosureHeader>
3131
<DisclosurePanel>
3232
files
3333
</DisclosurePanel>
3434
</Disclosure>
35-
<Disclosure key="shared">
35+
<Disclosure id="shared">
3636
<DisclosureHeader>
3737
Shared with you
3838
</DisclosureHeader>
3939
<DisclosurePanel>
4040
shared
4141
</DisclosurePanel>
4242
</Disclosure>
43-
<Disclosure key="last">
43+
<Disclosure id="last">
4444
<DisclosureHeader>
4545
Last item
4646
</DisclosureHeader>

packages/@react-spectrum/accordion/src/Accordion.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@
1111
*/
1212

1313
import {AriaLabelingProps, DOMProps, DOMRef, StyleProps} from '@react-types/shared';
14-
import {Button, DisclosurePanelProps, DisclosureProps, Heading, Disclosure as RACDisclosure, DisclosurePanel as RACDisclosurePanel} from 'react-aria-components';
14+
import {Button, DisclosureGroup, DisclosurePanelProps, DisclosureProps, Heading, Disclosure as RACDisclosure, DisclosurePanel as RACDisclosurePanel} from 'react-aria-components';
1515
import ChevronLeftMedium from '@spectrum-icons/ui/ChevronLeftMedium';
1616
import ChevronRightMedium from '@spectrum-icons/ui/ChevronRightMedium';
1717
import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils';
18-
import {filterDOMProps} from '@react-aria/utils';
1918
import React, {forwardRef, ReactElement} from 'react';
2019
import styles from '@adobe/spectrum-css-temp/components/accordion/vars.css';
2120
import {useLocale} from '@react-aria/i18n';
@@ -31,17 +30,17 @@ function Accordion(props: SpectrumAccordionProps, ref: DOMRef<HTMLDivElement>) {
3130
let {styleProps} = useStyleProps(props);
3231
let domRef = useDOMRef(ref);
3332
return (
34-
<div
35-
{...filterDOMProps(props)}
33+
<DisclosureGroup
34+
{...props}
3635
{...styleProps}
3736
ref={domRef}
3837
className={classNames(styles, 'spectrum-Accordion', styleProps.className)}>
3938
{props.children}
40-
</div>
39+
</DisclosureGroup>
4140
);
4241
}
4342

44-
export interface SpectrumDisclosureProps extends DisclosureProps, DOMProps, AriaLabelingProps {
43+
export interface SpectrumDisclosureProps extends DisclosureProps, AriaLabelingProps {
4544
/** The contents of the disclosure. The first child should be the header, and the second child should be the panel. */
4645
children: [ReactElement<SpectrumDisclosureHeaderProps>, ReactElement<SpectrumDisclosurePanelProps>]
4746
}
@@ -78,11 +77,11 @@ function DisclosurePanel(props: SpectrumDisclosurePanelProps, ref: DOMRef<HTMLDi
7877

7978
export interface SpectrumDisclosureHeaderProps extends DOMProps, AriaLabelingProps {
8079
/**
81-
* The heading level of the accordion header.
80+
* The heading level of the disclosure header.
8281
* @default 3
8382
*/
8483
level?: number,
85-
/** The contents of the accordion header. */
84+
/** The contents of the disclosure header. */
8685
children: React.ReactNode
8786
}
8887

packages/@react-spectrum/accordion/stories/Accordion.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ export type AccordionStory = ComponentStoryObj<typeof Accordion>;
2525
export const Default: AccordionStory = {
2626
render: (args) => (
2727
<Accordion {...args}>
28-
<Disclosure key="files">
28+
<Disclosure id="files">
2929
<DisclosureHeader>
3030
Files
3131
</DisclosureHeader>
3232
<DisclosurePanel>
3333
<p>Files content</p>
3434
</DisclosurePanel>
3535
</Disclosure>
36-
<Disclosure key="people">
36+
<Disclosure id="people">
3737
<DisclosureHeader>
3838
People
3939
</DisclosureHeader>

packages/@react-spectrum/s2/src/Accordion.tsx

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {ContextValue, Provider, SlotProps} from 'react-aria-components';
13+
import {ContextValue, DisclosureGroup, DisclosureGroupProps, SlotProps} from 'react-aria-components';
1414
import {DisclosureContext} from './Disclosure';
15-
import {DOMProps, DOMRef, DOMRefValue, forwardRefType} from '@react-types/shared';
16-
import {filterDOMProps} from '@react-aria/utils';
15+
import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
1716
import {getAllowedOverrides, StylesPropWithHeight, UnsafeStyles} from './style-utils' with { type: 'macro' };
1817
import React, {createContext, forwardRef} from 'react';
1918
import {style} from '../style/spectrum-theme' with { type: 'macro' };
2019
import {useDOMRef} from '@react-spectrum/utils';
2120
import {useSpectrumContextProps} from './useSpectrumContextProps';
2221

23-
export interface AccordionProps extends UnsafeStyles, DOMProps, SlotProps {
22+
export interface AccordionProps extends DisclosureGroupProps, UnsafeStyles, DOMProps, SlotProps {
2423
/** The disclosure elements in the accordion. */
2524
children: React.ReactNode,
2625
/** Spectrum-defined styles, returned by the `style()` macro. */
@@ -36,9 +35,7 @@ export interface AccordionProps extends UnsafeStyles, DOMProps, SlotProps {
3635
*/
3736
density?: 'compact' | 'regular' | 'spacious',
3837
/** Whether the accordion should be displayed with a quiet style. */
39-
isQuiet?: boolean,
40-
/** Whether the accordion should be disabled. */
41-
isDisabled?: boolean
38+
isQuiet?: boolean
4239
}
4340

4441
const accordion = style({
@@ -56,29 +53,23 @@ function Accordion(props: AccordionProps, ref: DOMRef<HTMLDivElement>) {
5653
UNSAFE_className = '',
5754
size = 'M',
5855
density = 'regular',
59-
isQuiet,
60-
isDisabled,
61-
...otherProps
56+
isQuiet
6257
} = props;
63-
const domProps = filterDOMProps(otherProps);
6458
return (
65-
<Provider
66-
values={[
67-
[DisclosureContext, {size, isQuiet, density, isDisabled}]
68-
]}>
69-
<div
70-
{...domProps}
59+
<DisclosureContext.Provider value={{size, isQuiet, density}}>
60+
<DisclosureGroup
61+
{...props}
7162
ref={domRef}
7263
style={UNSAFE_style}
7364
className={(UNSAFE_className ?? '') + accordion(null, props.styles)}>
7465
{props.children}
75-
</div>
76-
</Provider>
66+
</DisclosureGroup>
67+
</DisclosureContext.Provider>
7768
);
7869
}
7970

8071
/**
8172
* An accordion is a container for multiple disclosures.
8273
*/
83-
let _Accordion = /*#__PURE__*/ (forwardRef as forwardRefType)(Accordion);
74+
let _Accordion = forwardRef(Accordion);
8475
export {_Accordion as Accordion};

packages/@react-spectrum/s2/src/Disclosure.tsx

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {AriaLabelingProps, DOMProps, DOMRef, DOMRefValue, forwardRefType} from '@react-types/shared';
13+
import {AriaLabelingProps, DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
1414
import {Button, ContextValue, DisclosureStateContext, Heading, Provider, Disclosure as RACDisclosure, DisclosurePanel as RACDisclosurePanel, DisclosurePanelProps as RACDisclosurePanelProps, DisclosureProps as RACDisclosureProps, useLocale, useSlottedContext} from 'react-aria-components';
1515
import {CenterBaseline} from './CenterBaseline';
1616
import {centerPadding, focusRing, getAllowedOverrides, StyleProps, UnsafeStyles} from './style-utils' with { type: 'macro' };
1717
import Chevron from '../ui-icons/Chevron';
1818
import {filterDOMProps} from '@react-aria/utils';
19-
import React, {createContext, forwardRef, ReactElement, useContext} from 'react';
20-
import {size as sizeValue, style} from '../style/spectrum-theme' with { type: 'macro' };
19+
import {lightDark, size as sizeValue, style} from '../style/spectrum-theme' with { type: 'macro' };
20+
import React, {createContext, forwardRef, ReactNode, useContext} from 'react';
2121
import {useDOMRef} from '@react-spectrum/utils';
2222
import {useSpectrumContextProps} from './useSpectrumContextProps';
2323

24-
25-
export interface DisclosureProps extends RACDisclosureProps, StyleProps, DOMProps {
24+
export interface DisclosureProps extends RACDisclosureProps, StyleProps {
2625
/**
2726
* The size of the disclosure.
2827
* @default "M"
@@ -36,7 +35,7 @@ export interface DisclosureProps extends RACDisclosureProps, StyleProps, DOMProp
3635
/** Whether the disclosure should be displayed with a quiet style. */
3736
isQuiet?: boolean,
3837
/** The contents of the disclosure, consisting of an DisclosureHeader and DisclosurePanel. */
39-
children: [ReactElement<DisclosureHeaderProps>, ReactElement<DisclosurePanelProps>]
38+
children: ReactNode
4039
}
4140

4241
export const DisclosureContext = createContext<ContextValue<Omit<DisclosureProps, 'children'>, DOMRefValue<HTMLDivElement>>>(null);
@@ -66,24 +65,19 @@ function Disclosure(props: DisclosureProps, ref: DOMRef<HTMLDivElement>) {
6665
let {
6766
size = 'M',
6867
density = 'regular',
69-
isQuiet, isDisabled
70-
} = props;
71-
let domRef = useDOMRef(ref);
72-
let {
68+
isQuiet,
7369
UNSAFE_style,
74-
UNSAFE_className = '',
75-
...otherProps
70+
UNSAFE_className = ''
7671
} = props;
77-
const domProps = filterDOMProps(otherProps);
72+
let domRef = useDOMRef(ref);
7873

7974
return (
8075
<Provider
8176
values={[
82-
[DisclosureContext, {size, isQuiet, density, isDisabled}]
77+
[DisclosureContext, {size, isQuiet, density}]
8378
]}>
8479
<RACDisclosure
85-
{...domProps}
86-
isDisabled={isDisabled}
80+
{...props}
8781
ref={domRef}
8882
style={UNSAFE_style}
8983
className={(UNSAFE_className ?? '') + disclosure({isQuiet}, props.styles)}>
@@ -96,7 +90,7 @@ function Disclosure(props: DisclosureProps, ref: DOMRef<HTMLDivElement>) {
9690
/**
9791
* A disclosure is a collapsible section of content. It is composed of a a header with a heading and trigger button, and a panel that contains the content.
9892
*/
99-
let _Disclosure = /*#__PURE__*/ (forwardRef as forwardRefType)(Disclosure);
93+
let _Disclosure = forwardRef(Disclosure);
10094
export {_Disclosure as Disclosure};
10195

10296
export interface DisclosureHeaderProps extends UnsafeStyles, DOMProps {
@@ -105,6 +99,7 @@ export interface DisclosureHeaderProps extends UnsafeStyles, DOMProps {
10599
* @default 3
106100
*/
107101
level?: number,
102+
/** The contents of the disclosure header. */
108103
children: React.ReactNode
109104
}
110105

@@ -171,18 +166,17 @@ const buttonStyles = style({
171166
width: 'full',
172167
backgroundColor: {
173168
default: 'transparent',
174-
isFocusVisible: 'transparent-black-100',
175-
isHovered: 'transparent-black-100'
169+
isFocusVisible: lightDark('transparent-black-100', 'transparent-white-100'),
170+
isHovered: lightDark('transparent-black-100', 'transparent-white-100'),
171+
isPressed: lightDark('transparent-black-100', 'transparent-white-100')
176172
},
173+
transition: 'default',
177174
borderWidth: 0,
178175
borderRadius: {
179-
// Only rounded for keyboard focus and quiet hover.
176+
// Only rounded for keyboard focus and quiet.
180177
default: 'none',
181178
isFocusVisible: 'control',
182-
isQuiet: {
183-
isHovered: 'control',
184-
isFocusVisible: 'control'
185-
}
179+
isQuiet: 'control'
186180
},
187181
textAlign: 'start',
188182
disableTapHighlight: true
@@ -193,8 +187,7 @@ const chevronStyles = style({
193187
isRTL: 180,
194188
isExpanded: 90
195189
},
196-
transitionDuration: '100ms',
197-
transitionProperty: 'rotate',
190+
transition: 'default',
198191
'--iconPrimary': {
199192
type: 'fill',
200193
value: 'currentColor'
@@ -222,7 +215,7 @@ function DisclosureHeader(props: DisclosureHeaderProps, ref: DOMRef<HTMLDivEleme
222215
ref={domRef}
223216
style={UNSAFE_style}
224217
className={(UNSAFE_className ?? '') + headingStyle}>
225-
<Button className={({isHovered, isFocused, isFocusVisible, isDisabled}) => buttonStyles({size, isHovered, isFocused, isFocusVisible, density, isQuiet, isDisabled})} slot="trigger">
218+
<Button className={(renderProps) => buttonStyles({...renderProps, size, density, isQuiet})} slot="trigger">
226219
<CenterBaseline>
227220
<Chevron size={size} className={chevronStyles({isExpanded, isRTL})} aria-hidden="true" />
228221
</CenterBaseline>
@@ -235,7 +228,7 @@ function DisclosureHeader(props: DisclosureHeaderProps, ref: DOMRef<HTMLDivEleme
235228
/**
236229
* A header for a disclosure. Contains a heading and a trigger button to expand/collapse the panel.
237230
*/
238-
let _DisclosureHeader = /*#__PURE__*/ (forwardRef as forwardRefType)(DisclosureHeader);
231+
let _DisclosureHeader = forwardRef(DisclosureHeader);
239232
export {_DisclosureHeader as DisclosureHeader};
240233

241234
export interface DisclosurePanelProps extends RACDisclosurePanelProps, UnsafeStyles, DOMProps, AriaLabelingProps {
@@ -286,6 +279,6 @@ function DisclosurePanel(props: DisclosurePanelProps, ref: DOMRef<HTMLDivElement
286279
/**
287280
* A disclosure panel is a collapsible section of content that is hidden until the disclosure is expanded.
288281
*/
289-
let _DisclosurePanel = /*#__PURE__*/ (forwardRef as forwardRefType)(DisclosurePanel);
282+
let _DisclosurePanel = forwardRef(DisclosurePanel);
290283
export {_DisclosurePanel as DisclosurePanel};
291284

0 commit comments

Comments
 (0)