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
2 changes: 1 addition & 1 deletion packages/@react-aria/disclosure/src/useDisclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
role: 'group',
'aria-labelledby': triggerId,
'aria-hidden': !state.isExpanded,
hidden: (isSSR || isDisabled) ? (isDisabled || !state.isExpanded) : undefined
hidden: !state.isExpanded || undefined
}
};
}
45 changes: 32 additions & 13 deletions packages/@react-aria/disclosure/test/useDisclosure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ describe('useDisclosure', () => {
expect(result.current.state.isExpanded).toBe(false);
});

it('should keep panel hidden when toggling disabled state', () => {
let {result, rerender} = renderHook(({isDisabled}: {isDisabled: boolean}) => {
let state = useDisclosureState({});
return useDisclosure({isDisabled}, state, ref);
}, {initialProps: {isDisabled: false}});

act(() => {
rerender({isDisabled: true});
});

expect(result.current.panelProps.hidden).toBe(true);

act(() => {
rerender({isDisabled: false});
});

expect(result.current.panelProps.hidden).toBe(true);
});

it('should set correct IDs for accessibility', () => {
let {result} = renderHook(() => {
let state = useDisclosureState({});
Expand All @@ -111,27 +130,27 @@ describe('useDisclosure', () => {
writable: true,
configurable: true
});

const ref = {current: document.createElement('div')};

const {result} = renderHook(() => {
const state = useDisclosureState({});
const disclosure = useDisclosure({}, state, ref);
return {state, disclosure};
});

expect(result.current.state.isExpanded).toBe(false);
expect(ref.current.getAttribute('hidden')).toBe('until-found');

// Simulate the 'beforematch' event
act(() => {
const event = new Event('beforematch', {bubbles: true});
ref.current.dispatchEvent(event);
});

expect(result.current.state.isExpanded).toBe(true);
expect(ref.current.hasAttribute('hidden')).toBe(false);

Object.defineProperty(document.body, 'onbeforematch', {
value: originalOnBeforeMatch,
writable: true,
Expand All @@ -148,31 +167,31 @@ describe('useDisclosure', () => {
writable: true,
configurable: true
});

const ref = {current: document.createElement('div')};

const onExpandedChange = jest.fn();

const {result} = renderHook(() => {
const state = useDisclosureState({isExpanded: false, onExpandedChange});
const disclosure = useDisclosure({isExpanded: false}, state, ref);
return {state, disclosure};
});

expect(result.current.state.isExpanded).toBe(false);
expect(ref.current.getAttribute('hidden')).toBe('until-found');

// Simulate the 'beforematch' event
act(() => {
const event = new Event('beforematch', {bubbles: true});
ref.current.dispatchEvent(event);
});

expect(result.current.state.isExpanded).toBe(false);
expect(ref.current.getAttribute('hidden')).toBe('until-found');
expect(onExpandedChange).toHaveBeenCalledTimes(1);
expect(onExpandedChange).toHaveBeenCalledWith(true);

Object.defineProperty(document.body, 'onbeforematch', {
value: originalOnBeforeMatch,
writable: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,13 @@ export const FieldGroup = forwardRef(function FieldGroup(props: FieldGroupProps,
// Forward focus to input element when clicking on a non-interactive child (e.g. icon or padding)
if (e.pointerType === 'mouse' && !(e.target as Element).closest('button,input,textarea')) {
e.preventDefault();
e.currentTarget.querySelector('input')?.focus();
(e.currentTarget.querySelector('input, textarea') as HTMLElement)?.focus();
}
}}
onPointerUp={e => {
if (e.pointerType !== 'mouse' && !(e.target as Element).closest('button,input,textarea')) {
e.preventDefault();
e.currentTarget.querySelector('input')?.focus();
(e.currentTarget.querySelector('input, textarea') as HTMLElement)?.focus();
}
}}
style={props.UNSAFE_style}
Expand Down
69 changes: 69 additions & 0 deletions packages/react-aria-components/stories/DisclosureGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {Button, Heading} from 'react-aria-components';
import {
Disclosure,
DisclosureGroup,
DisclosurePanel
} from '../src/Disclosure';
import {Meta, StoryFn} from '@storybook/react';
import React from 'react';
import './styles.css';

export default {
title: 'React Aria Components/DisclosureGroup',
component: DisclosureGroup
} as Meta<typeof DisclosureGroup>;

export type DisclosureGroupStory = StoryFn<typeof DisclosureGroup>;

export const DisclosureGroupExample: DisclosureGroupStory = (args) => {
const [isDisabled, setIsDisabled] = React.useState(false);
const toggleDisabled = () => setIsDisabled((d) => !d);

return (
<>
<Button onPress={toggleDisabled}>Toggle Disabled</Button>
<DisclosureGroup {...args}>
<Disclosure isDisabled={isDisabled}>
{({isExpanded}) => (
<>
<Heading level={3}>
<Button slot="trigger">
{isExpanded ? '⬇️' : '➡️'} This is a disclosure header
</Button>
</Heading>
<DisclosurePanel>
<p>This is the content of the disclosure panel.</p>
</DisclosurePanel>
</>
)}
</Disclosure>
<Disclosure isDisabled={isDisabled}>
{({isExpanded}) => (
<>
<Heading level={3}>
<Button slot="trigger">
{isExpanded ? '⬇️' : '➡️'} This is a disclosure header
</Button>
</Heading>
<DisclosurePanel>
<p>This is the content of the disclosure panel.</p>
</DisclosurePanel>
</>
)}
</Disclosure>
</DisclosureGroup>
</>
);
};
4 changes: 2 additions & 2 deletions packages/react-aria-components/test/Disclosure.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('Disclosure', () => {
expect(panel).not.toBeVisible();
});

it('should not expand a disabled disclosure via isExpanded', () => {
it('should expand a disabled disclosure via isExpanded', () => {
const {getByTestId, queryByText} = render(
<Disclosure data-testid="disclosure" isDisabled isExpanded>
<Heading level={3}>
Expand All @@ -112,7 +112,7 @@ describe('Disclosure', () => {
const disclosure = getByTestId('disclosure');
expect(disclosure).toHaveAttribute('data-disabled', 'true');
const panel = queryByText('Content');
expect(panel).not.toBeVisible();
expect(panel).toBeVisible();
});

it('should support controlled isExpanded prop', async () => {
Expand Down
Loading