Skip to content

Commit 9421c14

Browse files
Accordion (#6931)
* initialize accordion item * update yarn.lock * better hidden=until-found support * allow passing in panel ref * comments * use RAC in v3 Accordion * use disclosure hooks * initialize S2 Accordion * fix lint * add exports * lint * fix chevron color in dark mode * fix version of @react-stately/accordion * keep aria-controls even when closed * add isFocusVisibleWithin to AccordionPanel * fix panel height * fix versions * add to ts strict config * change open/close to expand/collapse * move to disclosure package * fix chevron shrinking on wrapping header text * support for disabled in S2 * clear ButtonContext in panel * update colors * lint * fix v3 chevron not rotating * don't open via onbeforematch if disabled * add disableTapHighlight * open disclosure onKeyDown * add getAllowedOverrides * remove outer header component * support level in S2 header * support level in v3 header * switch divider to use border * simplify focus ring and padding styles * update AccordionItem children types to enforce two React elements * remove Header from RAC example * scale font size * update yarn.lock * fix keydown interaction + types * support size, density, isQuiet, and isDisabled on individual items, but use group prop if available * enforce minWidth at item level * add isFocusVisibleWithin to item * fix chevron in RTL * revert changes to @react-aria/accordion * fix v3 chevron in RTL * fix packages/imports * use 'group' as default role * deprecate useAccordion and useAccordionItem * update prop JSDocs * update yarn.lock * fix v3 refs * add v3 tests back * update imports * remove @ts-ignore * Revert "remove @ts-ignore" This reverts commit 88c1604. * fix stories * fix context for individual accordion item * fix defaults for individual item * add story for individual item * make paddingTop and paddingBottom equal * fix story * change triggerProps to buttonProps * add optional ref to useDisclosure * add SSR check * use useEvent to add beforematch listener * rename AccordionGroup to Accordion * rename Disclosure to AccordionItem * rename AccordionPanel to DisclosurePanel * more renaming * rename RAC Accordion file to Disclosure * comment updates * package.json updates * yarn.lock * update render props * add DEFAULT_SLOT * use fontRelative for border radius * style macro updates * lint * update codemod * fix tests * use control for borderRadius * use ref instead of contentRef prop * use values on grid * lint * yarn.lock * fix refs * lint * revert renames in useAccordion * move Disclosure to its own file * reanme RAC Accordion stories to Disclosure * fix styles prop type * fix imports * update styles type on Disclosure * add dedicated story for S2 Disclosure * Slight fixes * Center baseline and simplify padding by changing the minHeight instead It uses more normal numbers * fix import * update lockfile --------- Co-authored-by: Devon Govett <[email protected]>
1 parent 6193c40 commit 9421c14

File tree

34 files changed

+1328
-212
lines changed

34 files changed

+1328
-212
lines changed

packages/@adobe/spectrum-css-temp/components/accordion/index.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ governing permissions and limitations under the License.
3232
.spectrum-Accordion-itemIndicator {
3333
display: block;
3434

35-
padding-inline-start: var(--spectrum-accordion-icon-spacing);
35+
padding-inline-start: var(--spectrum-accordion-icon-gap);
3636
padding-inline-end: var(--spectrum-accordion-icon-gap);
3737

3838
transition: transform ease var(--spectrum-global-animation-duration-100);
@@ -66,7 +66,7 @@ governing permissions and limitations under the License.
6666
box-sizing: border-box;
6767
/* left padding takes into account the icon's size as well as the focus state's left border */
6868
padding-block: var(--spectrum-accordion-item-title-padding-y);
69-
padding-inline-start: 2px;
69+
padding-inline-start: var(--spectrum-accordion-icon-gap);
7070
padding-inline-end: var(--spectrum-accordion-item-padding);
7171
margin: 0;
7272

@@ -111,7 +111,7 @@ governing permissions and limitations under the License.
111111
}
112112

113113
.spectrum-Accordion-item {
114-
&.is-open {
114+
&.is-expanded {
115115
> .spectrum-Accordion-itemHeading {
116116
> .spectrum-Accordion-itemHeader {
117117
> .spectrum-Accordion-itemIndicator {
@@ -126,7 +126,7 @@ governing permissions and limitations under the License.
126126
}
127127

128128
> .spectrum-Accordion-itemHeader::after {
129-
/* No bottom border when open, so be less tall */
129+
/* No bottom border when expanded, so be less tall */
130130
height: var(--spectrum-accordion-item-height-actual);
131131
}
132132

packages/@adobe/spectrum-css-temp/components/accordion/skin.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ governing permissions and limitations under the License.
3939
}
4040

4141
.spectrum-Accordion-item {
42-
&.is-open {
42+
&.is-expanded {
4343
.spectrum-Accordion-itemHeader {
4444
&:hover {
4545
background-color: transparent;

packages/@react-aria/accordion/src/useAccordion.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export interface AccordionItemAria {
3434
regionProps: DOMAttributes
3535
}
3636

37+
/**
38+
* @deprecated Use useDisclosure from `@react-aria/disclosure` instead.
39+
*/
3740
export function useAccordionItem<T>(props: AccordionItemAriaProps<T>, state: TreeState<T>, ref: RefObject<HTMLButtonElement | null>): AccordionItemAria {
3841
let {item} = props;
3942
let buttonId = useId();
@@ -65,6 +68,9 @@ export function useAccordionItem<T>(props: AccordionItemAriaProps<T>, state: Tre
6568
};
6669
}
6770

71+
/**
72+
* @deprecated Use useDisclosure from `@react-aria/disclosure` instead.
73+
*/
6874
export function useAccordion<T>(props: AriaAccordionProps<T>, state: TreeState<T>, ref: RefObject<HTMLDivElement | null>): AccordionAria {
6975
let {listProps} = useSelectableList({
7076
...props,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# @react-aria/disclosure
2+
3+
This package is part of [react-spectrum](https://github.com/adobe/react-spectrum). See the repo for more details.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
export * from './src';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@react-aria/disclosure",
3+
"version": "3.0.0-alpha.0",
4+
"description": "Spectrum UI components in React",
5+
"license": "Apache-2.0",
6+
"main": "dist/main.js",
7+
"module": "dist/module.js",
8+
"exports": {
9+
"types": "./dist/types.d.ts",
10+
"import": "./dist/import.mjs",
11+
"require": "./dist/main.js"
12+
},
13+
"types": "dist/types.d.ts",
14+
"source": "src/index.ts",
15+
"files": [
16+
"dist",
17+
"src"
18+
],
19+
"sideEffects": false,
20+
"repository": {
21+
"type": "git",
22+
"url": "https://github.com/adobe/react-spectrum"
23+
},
24+
"dependencies": {
25+
"@react-aria/button": "^3.9.8",
26+
"@react-aria/selection": "^3.19.3",
27+
"@react-aria/ssr": "^3.9.5",
28+
"@react-aria/utils": "^3.25.2",
29+
"@react-stately/disclosure": "3.0.0-alpha.0",
30+
"@react-stately/toggle": "^3.7.7",
31+
"@react-stately/tree": "^3.8.4",
32+
"@react-types/button": "^3.9.6",
33+
"@react-types/shared": "^3.24.1",
34+
"@swc/helpers": "^0.5.0"
35+
},
36+
"peerDependencies": {
37+
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0",
38+
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
39+
},
40+
"publishConfig": {
41+
"access": "public"
42+
}
43+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
export {useDisclosure} from './useDisclosure';
13+
export type {DisclosureAria, AriaDisclosureProps} from './useDisclosure';
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {AriaButtonProps} from '@react-types/button';
14+
import {DisclosureState} from '@react-stately/disclosure';
15+
import {HTMLAttributes, RefObject, useEffect} from 'react';
16+
import {useEvent, useId} from '@react-aria/utils';
17+
import {useIsSSR} from '@react-aria/ssr';
18+
19+
export interface AriaDisclosureProps {
20+
/** Whether the disclosure is disabled. */
21+
isDisabled?: boolean,
22+
/** Handler that is called when the disclosure's expanded state changes. */
23+
onExpandedChange?: (isExpanded: boolean) => void,
24+
/** Whether the disclosure is expanded (controlled). */
25+
isExpanded?: boolean,
26+
/** Whether the disclosure is expanded by default (uncontrolled). */
27+
defaultExpanded?: boolean
28+
}
29+
30+
export interface DisclosureAria {
31+
/** Props for the disclosure button. */
32+
buttonProps: AriaButtonProps,
33+
/** Props for the content element. */
34+
contentProps: HTMLAttributes<HTMLElement>
35+
}
36+
37+
/**
38+
* Provides the behavior and accessibility implementation for a disclosure component.
39+
* @param props - Props for the disclosure.
40+
* @param state - State for the disclosure, as returned by `useDisclosureState`.
41+
* @param ref - A ref for the disclosure content.
42+
*/
43+
export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState, ref?: RefObject<Element | null>): DisclosureAria {
44+
let {
45+
isDisabled
46+
} = props;
47+
let triggerId = useId();
48+
let contentId = useId();
49+
let isControlled = props.isExpanded !== undefined;
50+
let isSSR = useIsSSR();
51+
let supportsBeforeMatch = !isSSR && 'onbeforematch' in document.body;
52+
53+
// @ts-ignore https://github.com/facebook/react/pull/24741
54+
useEvent(ref, 'beforematch', supportsBeforeMatch ? () => state.expand() : null);
55+
56+
useEffect(() => {
57+
// Until React supports hidden="until-found": https://github.com/facebook/react/pull/24741
58+
if (supportsBeforeMatch && ref?.current && !isControlled && !isDisabled) {
59+
if (state.isExpanded) {
60+
// @ts-ignore
61+
ref.current.hidden = undefined;
62+
} else {
63+
// @ts-ignore
64+
ref.current.hidden = 'until-found';
65+
}
66+
}
67+
}, [isControlled, ref, props.isExpanded, state, supportsBeforeMatch, isDisabled]);
68+
69+
return {
70+
buttonProps: {
71+
id: triggerId,
72+
'aria-expanded': state.isExpanded,
73+
'aria-controls': contentId,
74+
onPress: (e) => {
75+
if (e.pointerType !== 'keyboard') {
76+
state.toggle();
77+
}
78+
},
79+
isDisabled,
80+
onKeyDown(e) {
81+
if (!isDisabled && (e.key === 'Enter' || e.key === ' ')) {
82+
e.preventDefault();
83+
state.toggle();
84+
}
85+
}
86+
},
87+
contentProps: {
88+
id: contentId,
89+
'aria-labelledby': triggerId,
90+
hidden: (!supportsBeforeMatch || isControlled) ? !state.isExpanded : true
91+
}
92+
};
93+
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@
1111
*/
1212

1313
import {Meta} from '@storybook/react';
14-
import {SpectrumAccordionProps} from '@react-types/accordion';
14+
import {SpectrumAccordionProps} from '../src/Accordion';
1515
import {Template} from '../chromatic/Accordion.stories';
1616

17-
const meta: Meta<SpectrumAccordionProps<object>> = {
17+
const meta: Meta<SpectrumAccordionProps> = {
1818
title: 'Accordion'
1919
};
2020

2121
export default meta;
2222

2323
export const Default = {
24-
render: Template,
25-
args: {defaultExpandedKeys: ['shared'], disabledKeys: ['last']}
24+
render: Template
2625
};

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

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

13-
import {Accordion, Item} from '../';
13+
import {Accordion, Disclosure, DisclosureHeader, DisclosurePanel} from '../';
1414
import {Meta} from '@storybook/react';
1515
import React from 'react';
16-
import {SpectrumAccordionProps} from '@react-types/accordion';
1716

18-
const meta: Meta<SpectrumAccordionProps<object>> = {
17+
const meta: Meta = {
1918
title: 'Accordion',
20-
component: Accordion,
19+
component: Disclosure,
2120
excludeStories: ['Template']
2221
};
2322

2423
export default meta;
2524

2625
export const Template = (args) => (
2726
<Accordion {...args}>
28-
<Item key="files" title="Your files">
29-
files
30-
</Item>
31-
<Item key="shared" title="Shared with you">
32-
shared
33-
</Item>
34-
<Item key="last" title="Last item">
35-
last
36-
</Item>
27+
<Disclosure key="files">
28+
<DisclosureHeader>
29+
Your files
30+
</DisclosureHeader>
31+
<DisclosurePanel>
32+
files
33+
</DisclosurePanel>
34+
</Disclosure>
35+
<Disclosure key="shared">
36+
<DisclosureHeader>
37+
Shared with you
38+
</DisclosureHeader>
39+
<DisclosurePanel>
40+
shared
41+
</DisclosurePanel>
42+
</Disclosure>
43+
<Disclosure key="last">
44+
<DisclosureHeader>
45+
Last item
46+
</DisclosureHeader>
47+
<DisclosurePanel>
48+
last
49+
</DisclosurePanel>
50+
</Disclosure>
3751
</Accordion>
3852
);
3953

4054
export const Default = {
4155
render: Template
4256
};
4357

44-
export const ExpandedKeys = {
45-
render: Template,
46-
args: {defaultExpandedKeys: ['shared']}
47-
};
48-
49-
export const DisabledKeys = {
50-
render: Template,
51-
args: {disabledKeys: ['shared']}
52-
};
58+
// TODO: more stories

0 commit comments

Comments
 (0)