Skip to content

Commit 233900c

Browse files
lixiaoyanclaudesnowystinger
authored
fix: use attribute presence check for data-react-aria-top-layer in MutationObserver (adobe#9763)
* fix: use attribute presence check for data-react-aria-top-layer in MutationObserver Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add test * remove unnecessary comment --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Robert Snow <snowystinger@gmail.com>
1 parent dda1be5 commit 233900c

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

packages/@react-aria/overlays/src/ariaHideOutside.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import {shadowDOM} from '@react-stately/flags';
1515

1616
const supportsInert = typeof HTMLElement !== 'undefined' && 'inert' in HTMLElement.prototype;
1717

18+
function isAlwaysVisibleNode(node: HTMLElement | SVGElement): boolean {
19+
return node.dataset.liveAnnouncer === 'true' || node.dataset.reactAriaTopLayer !== undefined;
20+
}
21+
1822
interface AriaHideOutsideOptions {
1923
root?: Element,
2024
shouldUseInert?: boolean
@@ -175,7 +179,7 @@ export function ariaHideOutside(targets: Element[], options?: AriaHideOutsideOpt
175179
for (let node of change.addedNodes) {
176180
if (
177181
(node instanceof HTMLElement || node instanceof SVGElement) &&
178-
(node.dataset.liveAnnouncer === 'true' || node.dataset.reactAriaTopLayer === 'true')
182+
isAlwaysVisibleNode(node)
179183
) {
180184
visibleNodes.add(node);
181185
} else if (node instanceof Element) {
@@ -218,7 +222,7 @@ export function ariaHideOutside(targets: Element[], options?: AriaHideOutsideOpt
218222
for (let node of change.addedNodes) {
219223
if (
220224
(node instanceof HTMLElement || node instanceof SVGElement) &&
221-
(node.dataset.liveAnnouncer === 'true' || node.dataset.reactAriaTopLayer === 'true')
225+
isAlwaysVisibleNode(node)
222226
) {
223227
visibleNodes.add(node);
224228
} else if (node instanceof Element) {

packages/@react-aria/overlays/test/ariaHideOutside.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,35 @@ describe('ariaHideOutside', function () {
239239
expect(getAllByRole('checkbox')).toHaveLength(1);
240240
});
241241

242+
it('should recognize dynamically added top layer element with data-react-aria-top-layer="" (attribute presence)', async function () {
243+
let Test = props => (
244+
<>
245+
<button>Button</button>
246+
{props.show && (
247+
<>
248+
<div role="status" data-react-aria-top-layer="">Top layer (empty attr)</div>
249+
<input type="checkbox" />
250+
</>
251+
)}
252+
</>
253+
);
254+
255+
let {getByRole, getAllByRole, rerender} = render(<Test />);
256+
257+
let button = getByRole('button');
258+
let revert = ariaHideOutside([button]);
259+
260+
rerender(<Test show />);
261+
262+
// MutationObserver is async
263+
await Promise.resolve();
264+
expect(getByRole('status')).not.toHaveAttribute('aria-hidden');
265+
expect(getByRole('checkbox', {hidden: true})).toHaveAttribute('aria-hidden', 'true');
266+
267+
revert();
268+
expect(getAllByRole('checkbox')).toHaveLength(1);
269+
});
270+
242271
it('should handle when a new element is added inside a target element', async function () {
243272
let Test = props => (
244273
<>

0 commit comments

Comments
 (0)