Skip to content

Commit 187a73e

Browse files
authored
Support nested focusable elements in VisuallyHidden with isFocusable option (#3572)
Support nested focusable elements in VisuallyHidden with isFocusable option
1 parent f6f39ee commit 187a73e

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

packages/@react-aria/visually-hidden/src/VisuallyHidden.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import {DOMAttributes} from '@react-types/shared';
1414
import {mergeProps} from '@react-aria/utils';
1515
import React, {CSSProperties, JSXElementConstructor, ReactNode, useMemo, useState} from 'react';
16-
import {useFocus} from '@react-aria/interactions';
16+
import {useFocusWithin} from '@react-aria/interactions';
1717

1818
export interface VisuallyHiddenProps extends DOMAttributes {
1919
/** The content to visually hide. */
@@ -57,9 +57,9 @@ export function useVisuallyHidden(props: VisuallyHiddenProps = {}): VisuallyHidd
5757
} = props;
5858

5959
let [isFocused, setFocused] = useState(false);
60-
let {focusProps} = useFocus({
60+
let {focusWithinProps} = useFocusWithin({
6161
isDisabled: !isFocusable,
62-
onFocusChange: setFocused
62+
onFocusWithinChange: (val) => setFocused(val)
6363
});
6464

6565
// If focused, don't hide the element.
@@ -75,7 +75,7 @@ export function useVisuallyHidden(props: VisuallyHiddenProps = {}): VisuallyHidd
7575

7676
return {
7777
visuallyHiddenProps: {
78-
...focusProps,
78+
...focusWithinProps,
7979
style: combinedStyles
8080
}
8181
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
import React from 'react';
14+
import {render} from '@react-spectrum/test-utils';
15+
import userEvent from '@testing-library/user-event';
16+
import {VisuallyHidden} from '../';
17+
18+
describe('VisuallyHidden', function () {
19+
beforeAll(() => {
20+
jest.useFakeTimers();
21+
});
22+
it('hides element', function () {
23+
let {getAllByRole} = render(
24+
<>
25+
<button>This is button A</button>
26+
<VisuallyHidden>
27+
<button>
28+
With no isFocusable, I should not show my text on focus
29+
</button>
30+
</VisuallyHidden>
31+
<button>This is button C</button>
32+
</>
33+
);
34+
35+
let buttons = getAllByRole('button');
36+
userEvent.tab();
37+
38+
expect(document.activeElement).toBe(buttons[0]);
39+
let hiddenStyle = buttons[1].parentElement.getAttribute('style');
40+
expect(hiddenStyle.length).toBeGreaterThan(0);
41+
42+
userEvent.tab();
43+
44+
expect(document.activeElement).toBe(buttons[1]);
45+
expect(buttons[1].parentElement.getAttribute('style')).toEqual(hiddenStyle);
46+
});
47+
it('unhides element if focused and isFocusable', function () {
48+
let {getAllByRole} = render(
49+
<>
50+
<button>This is button A</button>
51+
<VisuallyHidden isFocusable>
52+
<button>
53+
With isFocusable, I should show my text on focus
54+
</button>
55+
</VisuallyHidden>
56+
<button>This is button C</button>
57+
</>
58+
);
59+
60+
let buttons = getAllByRole('button');
61+
userEvent.tab();
62+
63+
expect(document.activeElement).toBe(buttons[0]);
64+
let hiddenStyle = buttons[1].parentElement.getAttribute('style');
65+
expect(hiddenStyle.length).toBeGreaterThan(0);
66+
67+
userEvent.tab();
68+
69+
expect(document.activeElement).toBe(buttons[1]);
70+
expect(buttons[1].parentElement.getAttribute('style')).not.toEqual(hiddenStyle);
71+
expect(buttons[1].parentElement.getAttribute('style')).toHaveLength(0);
72+
});
73+
});

0 commit comments

Comments
 (0)