Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions packages/@adobe/spectrum-css-temp/components/accordion/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ governing permissions and limitations under the License.

border-bottom: var(--spectrum-accordion-item-border-size) solid transparent;

&:first-of-type {
border-top: var(--spectrum-accordion-item-border-size) solid var(--spectrum-alias-border-color);

&.in-accordion, &:first-of-type {
border-top: var(--spectrum-accordion-item-border-size) solid transparent;
}

&.spectrum-Accordion-item--quiet, &.spectrum-Accordion-item--quiet:first-of-type {
border-radius: var(--spectrum-global-dimension-static-size-100);
border-color: transparent;
}
}
Expand Down Expand Up @@ -97,17 +100,18 @@ governing permissions and limitations under the License.
width: 100%;
border-style: solid;
border-color: transparent;
border-radius: var(--spectrum-global-dimension-static-size-100);

&.focus-ring {
border-radius: var(--spectrum-global-dimension-static-size-100);
outline: solid 2px var(--spectrum-accordion-item-focus-ring-color);
outline-offset: -4px;
}
}

.spectrum-Accordion-itemContent {
padding: 0 var(--spectrum-accordion-item-content-padding) var(--spectrum-accordion-item-content-padding) var(--spectrum-accordion-item-content-padding);
display: none;
.spectrum-Accordion-item.is-expanded {
.spectrum-Accordion-itemContent {
padding: 0 var(--spectrum-accordion-item-content-padding) var(--spectrum-accordion-item-content-padding) var(--spectrum-accordion-item-content-padding);
}
}

.spectrum-Accordion-item {
Expand Down
22 changes: 11 additions & 11 deletions packages/@adobe/spectrum-css-temp/components/accordion/skin.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ governing permissions and limitations under the License.

:root {
--spectrum-accordion-text-color-disabled: var(--spectrum-alias-text-color-disabled);
--spectrum-accordion-background-color-down: var(--spectrum-global-color-gray-300);
}

.spectrum-Accordion-item {
Expand All @@ -25,20 +26,14 @@ governing permissions and limitations under the License.
.spectrum-Accordion-itemHeader {
color: var(--spectrum-alias-text-color);

&:hover {
&:hover, &:focus-visible {
color: var(--spectrum-alias-text-color-hover);

background-color: var(--spectrum-accordion-background-color-hover);
}
}

.spectrum-Accordion-item {
&.is-expanded {
.spectrum-Accordion-itemHeader {
&:hover {
background-color: transparent;
}
}
&.is-pressed {
color: var(--spectrum-accordion-text-color-down);
background-color: var(--spectrum-accordion-background-color-down);
}
}

Expand All @@ -54,8 +49,13 @@ governing permissions and limitations under the License.
}
@media (forced-colors: active) {
.spectrum-Accordion-itemHeader {
border: none;
&:focus-visible {
outline: 3px solid CanvasText;
outline: 2px solid CanvasText;
}
}

.spectrum-Accordion-item.spectrum-Accordion-item--quiet {
border: none
}
}
20 changes: 9 additions & 11 deletions packages/@react-aria/disclosure/docs/anatomy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 17 additions & 3 deletions packages/@react-aria/disclosure/docs/useDisclosure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,24 @@ This example displays a basic disclosure with a button that toggles the visibili
import {useDisclosureState} from '@react-stately/disclosure';
import {useDisclosure} from '@react-aria/disclosure';
import {useButton} from '@react-aria/button';
import {mergeProps, useFocusRing} from 'react-aria';

function Disclosure(props) {
let state = useDisclosureState(props);
let panelRef = React.useRef<HTMLDivElement | null>(null);
let triggerRef = React.useRef<HTMLButtonElement | null>(null);
let {buttonProps: triggerProps, panelProps} = useDisclosure(props, state, panelRef);
let {buttonProps} = useButton(triggerProps, triggerRef);
let {isFocusVisible, focusProps} = useFocusRing();

return (
<div className="disclosure">
<h3>
<button className="trigger" ref={triggerRef} {...buttonProps}>
<button
className="trigger"
ref={triggerRef}
{...mergeProps(buttonProps, focusProps)}
style={{outline: isFocusVisible? '2px solid dodgerblue' : 'none'}}>
<svg viewBox="0 0 24 24">
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
Expand All @@ -109,6 +115,8 @@ function Disclosure(props) {
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>

```css
@import "@react-aria/example-theme";

.disclosure {
.trigger {
background: none;
Expand All @@ -119,6 +127,7 @@ function Disclosure(props) {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-color);

svg {
rotate: 0deg;
Expand Down Expand Up @@ -187,7 +196,7 @@ A disclosure can be disabled with the `isDisabled` prop. This will disable the t

A disclosure group (i.e. accordion) is a set of disclosures where only one disclosure can be expanded at a time. The following example shows how to create a `DisclosureGroup` component with the `useDisclosureGroupState` hook. We'll also create a `DisclosureItem` component that uses the `DisclosureGroupState` context for managing its state.

```tsx example export=true
```tsx example export=true render=false
import {useDisclosureGroupState} from '@react-stately/disclosure';
import {useId} from '@react-aria/utils';

Expand Down Expand Up @@ -231,11 +240,16 @@ function DisclosureItem(props) {
isDisabled
}, state, panelRef);
let {buttonProps} = useButton(triggerProps, triggerRef);
let {isFocusVisible, focusProps} = useFocusRing();

return (
<div className="disclosure">
<h3>
<button className="trigger" ref={triggerRef} {...buttonProps}>
<button
className="trigger"
ref={triggerRef}
{...mergeProps(buttonProps, focusProps)}
style={{outline: isFocusVisible? '2px solid dodgerblue' : 'none'}}>
<svg viewBox="0 0 24 24">
<path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-aria/disclosure/src/useDisclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,8 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
}
},
isDisabled,
onKeyDown(e) {
if (!isDisabled && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault();
onPressStart(e) {
if (e.pointerType === 'keyboard' && !isDisabled) {
state.toggle();
}
}
Expand All @@ -114,6 +113,7 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
// This can be overridden at the panel element level.
role: 'group',
'aria-labelledby': triggerId,
'aria-hidden': !state.isExpanded,
hidden: supportsBeforeMatch ? true : !state.isExpanded
}
};
Expand Down
14 changes: 6 additions & 8 deletions packages/@react-aria/disclosure/test/useDisclosure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/
import {actHook as act, renderHook} from '@react-spectrum/test-utils-internal';
import {KeyboardEvent, PressEvent} from '@react-types/shared';
import {PressEvent} from '@react-types/shared';
import {useDisclosure} from '../src/useDisclosure';
import {useDisclosureState} from '@react-stately/disclosure';

Expand All @@ -32,6 +32,7 @@ describe('useDisclosure', () => {

expect(buttonProps['aria-expanded']).toBe(false);
expect(panelProps.hidden).toBe(true);
expect(panelProps['aria-hidden']).toBe(true);
});

it('should return correct aria attributes when expanded', () => {
Expand All @@ -46,7 +47,7 @@ describe('useDisclosure', () => {
expect(panelProps.hidden).toBe(false);
});

it('should handle expanding on press event', () => {
it('should handle expanding on press event (with mouse)', () => {
let {result} = renderHook(() => {
let state = useDisclosureState({});
let disclosure = useDisclosure({}, state, ref);
Expand All @@ -60,22 +61,19 @@ describe('useDisclosure', () => {
expect(result.current.state.isExpanded).toBe(true);
});

it('should handle expanding on keydown event', () => {
it('should handle expanding on press start event (with keyboard)', () => {
let {result} = renderHook(() => {
let state = useDisclosureState({});
let disclosure = useDisclosure({}, state, ref);
return {state, disclosure};
});

let preventDefault = jest.fn();
let event = (e: Partial<KeyboardEvent>) => ({...e, preventDefault} as KeyboardEvent);
let event = (e: Partial<PressEvent>) => (e as PressEvent);

act(() => {
result.current.disclosure.buttonProps.onKeyDown?.(event({key: 'Enter', preventDefault}) as KeyboardEvent);
result.current.disclosure.buttonProps.onPressStart?.(event({pointerType: 'keyboard'}) as PressEvent);
});

expect(preventDefault).toHaveBeenCalledTimes(1);

expect(result.current.state.isExpanded).toBe(true);
});

Expand Down
13 changes: 12 additions & 1 deletion packages/@react-spectrum/accordion/docs/Accordion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default Layout;
import docs from 'docs:@react-spectrum/accordion';
import {HeaderInfo, PropTable, TypeLink, PageDescription} from '@react-spectrum/docs';
import packageData from '@react-spectrum/accordion/package.json';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh oops that's leftover from trying something else, i can follow-up on it


---
category: Navigation
Expand Down Expand Up @@ -90,7 +91,7 @@ function ControlledExpansion() {
}
```

## Expansion
## Expanded

By default, only one disclosure will be expanded at a time. To expand multiple disclosures, apply the `allowsMultipleExpanded` prop to Accordion.

Expand All @@ -112,8 +113,18 @@ By default, only one disclosure will be expanded at a time. To expand multiple d
```
## Props

### Disclosure
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Disclosure
### Accordion


<PropTable component={docs.exports.Accordion} links={docs.links} />

### Disclosure Panel

<PropTable component={docs.exports.DisclosurePanel} links={docs.links} />

### Disclosure Header

<PropTable component={docs.exports.DisclosureHeader} links={docs.links} />

## Visual Options

### Disabled
Expand Down
28 changes: 19 additions & 9 deletions packages/@react-spectrum/accordion/src/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import {AriaLabelingProps, DOMProps, DOMRef, StyleProps} from '@react-types/shared';
import {Button, UNSTABLE_DisclosureGroup as DisclosureGroup, DisclosureGroupProps, DisclosurePanelProps, DisclosureProps, Heading, UNSTABLE_Disclosure as RACDisclosure, UNSTABLE_DisclosurePanel as RACDisclosurePanel} from 'react-aria-components';
import {Button, DisclosureGroup, DisclosureGroupProps, DisclosurePanelProps, DisclosureProps, Heading, Disclosure as RACDisclosure, DisclosurePanel as RACDisclosurePanel} from 'react-aria-components';
import ChevronLeftMedium from '@spectrum-icons/ui/ChevronLeftMedium';
import ChevronRightMedium from '@spectrum-icons/ui/ChevronRightMedium';
import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils';
Expand Down Expand Up @@ -46,7 +46,7 @@ function Accordion(props: SpectrumAccordionProps, ref: DOMRef<HTMLDivElement>) {
);
}

export interface SpectrumDisclosureProps extends Omit<DisclosureProps, 'className' | 'style' | 'children'>, AriaLabelingProps {
export interface SpectrumDisclosureProps extends Omit<DisclosureProps, 'className' | 'style' | 'children'>, AriaLabelingProps, StyleProps {
/** Whether the Disclosure should be displayed with a quiet style. */
isQuiet?: boolean,
/** The contents of the disclosure. The first child should be the header, and the second child should be the panel. */
Expand All @@ -55,37 +55,45 @@ export interface SpectrumDisclosureProps extends Omit<DisclosureProps, 'classNam

function Disclosure(props: SpectrumDisclosureProps, ref: DOMRef<HTMLDivElement>) {
props = useProviderProps(props);
let {styleProps} = useStyleProps(props);
let domRef = useDOMRef(ref);
let accordionContext = React.useContext(InternalAccordionContext)!;
return (
<RACDisclosure
{...props}
{...styleProps}
ref={domRef}
className={({isExpanded, isDisabled}) => classNames(styles, 'spectrum-Accordion-item', {
'spectrum-Accordion-item--quiet': accordionContext?.isQuiet ?? props.isQuiet,
'is-expanded': isExpanded,
'is-disabled': isDisabled
})}>
'is-disabled': isDisabled,
'in-accordion': accordionContext != null
}, styleProps.className)}>
{props.children}
</RACDisclosure>
);
}

export interface SpectrumDisclosurePanelProps extends Omit<DisclosurePanelProps, 'className' | 'style' | 'children'>, DOMProps, AriaLabelingProps {
export interface SpectrumDisclosurePanelProps extends Omit<DisclosurePanelProps, 'className' | 'style' | 'children'>, DOMProps, AriaLabelingProps, StyleProps {
/** The contents of the accordion panel. */
children: React.ReactNode
}

function DisclosurePanel(props: SpectrumDisclosurePanelProps, ref: DOMRef<HTMLDivElement>) {
let {styleProps} = useStyleProps(props);
let domRef = useDOMRef(ref);
return (
<RACDisclosurePanel ref={domRef} className={classNames(styles, 'spectrum-Accordion-itemContent')} {...props}>
<RACDisclosurePanel
ref={domRef}
{...styleProps as Omit<React.HTMLAttributes<HTMLElement>, 'role'>}
className={classNames(styles, 'spectrum-Accordion-itemContent', styleProps.className)}
{...props}>
{props.children}
</RACDisclosurePanel>
);
}

export interface SpectrumDisclosureHeaderProps extends DOMProps, AriaLabelingProps {
export interface SpectrumDisclosureHeaderProps extends DOMProps, AriaLabelingProps, StyleProps {
/**
* The heading level of the disclosure header.
* @default 3
Expand All @@ -96,15 +104,17 @@ export interface SpectrumDisclosureHeaderProps extends DOMProps, AriaLabelingPro
}

function DisclosureHeader(props: SpectrumDisclosureHeaderProps, ref: DOMRef<HTMLHeadingElement>) {
let {styleProps} = useStyleProps(props);
let {level = 3} = props;
let {direction} = useLocale();
let domRef = useDOMRef(ref);
return (
<Heading ref={domRef} level={level} className={classNames(styles, 'spectrum-Accordion-itemHeading')}>
<Heading ref={domRef} level={level} {...styleProps} className={classNames(styles, 'spectrum-Accordion-itemHeading', styleProps.className)}>
<Button
slot="trigger"
className={({isHovered, isFocusVisible}) => classNames(styles, 'spectrum-Accordion-itemHeader', {
className={({isHovered, isFocusVisible, isPressed}) => classNames(styles, 'spectrum-Accordion-itemHeader', {
'is-hovered': isHovered,
'is-pressed': isPressed,
'focus-ring': isFocusVisible
})}>
{direction === 'ltr' ? (
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/src/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import {ContextValue, UNSTABLE_DisclosureGroup as DisclosureGroup, DisclosureGroupProps, SlotProps} from 'react-aria-components';
import {ContextValue, DisclosureGroup, DisclosureGroupProps, SlotProps} from 'react-aria-components';
import {DisclosureContext} from './Disclosure';
import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
import {getAllowedOverrides, StylesPropWithHeight, UnsafeStyles} from './style-utils' with { type: 'macro' };
Expand Down
Loading