Skip to content

Commit 67d8c77

Browse files
authored
fix(components): add isDisabled prop to Perceivable and clear press responders (#1686)
1 parent 7946753 commit 67d8c77

File tree

4 files changed

+89
-21
lines changed

4 files changed

+89
-21
lines changed

.changeset/small-socks-bet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@launchpad-ui/components": patch
3+
---
4+
5+
Add `isDisabled` prop to `Perceivable` and clear press responders

packages/components/__tests__/Perceivable.spec.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, expect, it, vi } from 'vitest';
22

33
import { render, screen, userEvent, waitFor } from '../../../test/utils';
4-
import { Button, Form, Perceivable, Tooltip, TooltipTrigger } from '../src';
4+
import { Button, DialogTrigger, Form, Perceivable, Popover, Tooltip, TooltipTrigger } from '../src';
55

66
describe('Perceivable', () => {
77
it('sets aria-disabled', async () => {
@@ -56,4 +56,23 @@ describe('Perceivable', () => {
5656
await user.click(screen.getByRole('button', { hidden: true }));
5757
expect(spy).not.toHaveBeenCalled();
5858
});
59+
60+
it('clears press responders', async () => {
61+
const user = userEvent.setup();
62+
63+
render(
64+
<DialogTrigger>
65+
<Perceivable>
66+
<TooltipTrigger>
67+
<Button onPress={() => console.log('Pressed')}>Button</Button>
68+
<Tooltip placement="right">Message</Tooltip>
69+
</TooltipTrigger>
70+
<Popover>Message</Popover>
71+
</Perceivable>
72+
</DialogTrigger>,
73+
);
74+
75+
await user.click(screen.getByRole('button', { hidden: true }));
76+
expect(await screen.queryByRole('dialog')).toBeNull();
77+
});
5978
});

packages/components/src/Perceivable.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ import type { KeyboardEvents, PressEvents } from '@react-types/shared';
22
import type { ReactNode } from 'react';
33

44
import { FocusableProvider } from '@react-aria/focus';
5+
import { ClearPressResponder } from '@react-aria/interactions';
56
import { createContext } from 'react';
67
import { Provider } from 'react-aria-components';
78

89
interface InteractionProps extends KeyboardEvents, PressEvents {}
910

1011
interface PerceivableProps {
11-
children: ReactNode;
12+
children?: ReactNode;
13+
isDisabled?: boolean;
1214
}
1315

1416
const PerceivableContext = createContext<InteractionProps>({});
1517

16-
const Perceivable = ({ children }: PerceivableProps) => {
18+
const Perceivable = ({ children, isDisabled = true }: PerceivableProps) => {
1719
const props = {
1820
onPress: undefined,
1921
onPressStart: undefined,
@@ -27,12 +29,14 @@ const Perceivable = ({ children }: PerceivableProps) => {
2729
type: 'button',
2830
};
2931

30-
return (
32+
return isDisabled ? (
3133
<Provider values={[[PerceivableContext, { ...props }]]}>
3234
<FocusableProvider aria-disabled="true" data-lp="">
33-
{children}
35+
<ClearPressResponder>{children}</ClearPressResponder>
3436
</FocusableProvider>
3537
</Provider>
38+
) : (
39+
children
3640
);
3741
};
3842

packages/components/stories/recipes/composition.stories.tsx

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import { ButtonGroup } from '../../src/ButtonGroup';
1414
import { ComboBox, ComboBoxClearButton } from '../../src/ComboBox';
1515
import { Dialog, DialogTrigger } from '../../src/Dialog';
1616
import { Group } from '../../src/Group';
17+
import { Heading } from '../../src/Heading';
1718
import { IconButton } from '../../src/IconButton';
1819
import { Input } from '../../src/Input';
1920
import { Label } from '../../src/Label';
2021
import { ListBox, ListBoxItem } from '../../src/ListBox';
22+
import { Modal, ModalOverlay } from '../../src/Modal';
2123
import { Perceivable } from '../../src/Perceivable';
2224
import { Popover } from '../../src/Popover';
2325
import { RadioButton } from '../../src/RadioButton';
@@ -26,6 +28,7 @@ import { RadioIconButton } from '../../src/RadioIconButton';
2628
import { Select, SelectValue } from '../../src/Select';
2729
import { Text } from '../../src/Text';
2830
import { ToastRegion, toastQueue } from '../../src/Toast';
31+
import { ToggleButton } from '../../src/ToggleButton';
2932
import { Tooltip, TooltipTrigger } from '../../src/Tooltip';
3033

3134
const Container = (props: ComponentPropsWithoutRef<typeof Fragment>) => <>{props.children}</>;
@@ -217,6 +220,7 @@ export const ListBoxTooltip: Story = {
217220

218221
export const DisabledWithTooltip: Story = {
219222
render: () => {
223+
const [isDisabled, setIsDisabled] = useState(true);
220224
return (
221225
<div
222226
style={{
@@ -225,22 +229,58 @@ export const DisabledWithTooltip: Story = {
225229
gap: vars.spacing[400],
226230
}}
227231
>
228-
<Perceivable>
229-
<TooltipTrigger>
230-
<Button onPress={() => console.log('Pressed')}>Button</Button>
231-
<Tooltip placement="right">Message</Tooltip>
232-
</TooltipTrigger>
233-
<TooltipTrigger>
234-
<IconButton
235-
icon="add"
236-
size="small"
237-
variant="minimal"
238-
aria-label="create"
239-
onPress={() => console.log('Pressed')}
240-
/>
241-
<Tooltip placement="right">Message</Tooltip>
242-
</TooltipTrigger>
243-
</Perceivable>
232+
<ToggleButton isSelected={isDisabled} onChange={setIsDisabled}>
233+
Restrict
234+
</ToggleButton>
235+
<DialogTrigger>
236+
<Perceivable isDisabled={isDisabled}>
237+
<TooltipTrigger>
238+
<Button onPress={() => console.log('Pressed')}>Button</Button>
239+
<Tooltip placement="right">Message</Tooltip>
240+
</TooltipTrigger>
241+
<Popover>Message</Popover>
242+
</Perceivable>
243+
</DialogTrigger>
244+
<DialogTrigger>
245+
<Perceivable isDisabled={isDisabled}>
246+
<TooltipTrigger>
247+
<IconButton
248+
icon="add"
249+
size="small"
250+
variant="minimal"
251+
aria-label="create"
252+
onPress={() => console.log('Pressed')}
253+
/>
254+
<Tooltip placement="right">Message</Tooltip>
255+
</TooltipTrigger>
256+
<ModalOverlay>
257+
<Modal>
258+
<Dialog>
259+
{({ close }) => (
260+
<>
261+
<div slot="header">
262+
<Heading slot="title">Title</Heading>
263+
<IconButton
264+
aria-label="close"
265+
icon="cancel"
266+
size="small"
267+
variant="minimal"
268+
onPress={close}
269+
/>
270+
<Text slot="subtitle">Subtitle</Text>
271+
</div>
272+
<div slot="body">Body text</div>
273+
<div slot="footer">
274+
<Button slot="close">Cancel</Button>
275+
<Button variant="primary">Confirm</Button>
276+
</div>
277+
</>
278+
)}
279+
</Dialog>
280+
</Modal>
281+
</ModalOverlay>
282+
</Perceivable>
283+
</DialogTrigger>
244284
</div>
245285
);
246286
},

0 commit comments

Comments
 (0)