Skip to content

fix: remove stale inert after performing keyboard drag and drop in virtualized tree #8574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2025
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
4 changes: 2 additions & 2 deletions packages/@react-aria/overlays/src/ariaHideOutside.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
*/

import {getOwnerWindow} from '@react-aria/utils';

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

interface AriaHideOutsideOptions {
Expand Down Expand Up @@ -72,6 +71,7 @@ export function ariaHideOutside(targets: Element[], options?: AriaHideOutsideOpt
// made for elements with role="row" since VoiceOver on iOS has issues hiding elements with role="row".
// For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).
if (
hiddenNodes.has(node) ||
visibleNodes.has(node) ||
(node.parentElement && hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute('role') !== 'row')
) {
Expand Down Expand Up @@ -136,7 +136,7 @@ export function ariaHideOutside(targets: Element[], options?: AriaHideOutsideOpt

let observer = new MutationObserver(changes => {
for (let change of changes) {
if (change.type !== 'childList' || change.addedNodes.length === 0) {
if (change.type !== 'childList') {
continue;
}

Expand Down
55 changes: 51 additions & 4 deletions packages/@react-aria/overlays/test/ariaHideOutside.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
* governing permissions and limitations under the License.
*/

import {act, render, waitFor} from '@react-spectrum/test-utils-internal';
import {ariaHideOutside} from '../src';
import React from 'react';
import {render, waitFor} from '@react-spectrum/test-utils-internal';
import React, {useState} from 'react';

describe('ariaHideOutside', function () {
it('should hide everything except the provided element [button]', function () {
Expand Down Expand Up @@ -354,10 +354,13 @@ describe('ariaHideOutside', function () {
});

it('should hide everything except the provided element [row]', function () {
let {getAllByRole} = render(
let {getAllByRole, getByTestId} = render(
<div role="grid">
<div role="row">
<div role="gridcell">Cell 1</div>
<div role="gridcell">
<span data-testid="test-span">
Cell 1
</span></div>
</div>
<div role="row">
<div role="gridcell">Cell 2</div>
Expand All @@ -367,13 +370,15 @@ describe('ariaHideOutside', function () {

let cells = getAllByRole('gridcell');
let rows = getAllByRole('row');
let span = getByTestId('test-span');

let revert = ariaHideOutside([rows[1]]);

// Applies aria-hidden to the row and cell despite recursive nature of aria-hidden
// for https://bugs.webkit.org/show_bug.cgi?id=222623
expect(rows[0]).toHaveAttribute('aria-hidden', 'true');
expect(cells[0]).toHaveAttribute('aria-hidden', 'true');
expect(span).not.toHaveAttribute('aria-hidden');
expect(rows[1]).not.toHaveAttribute('aria-hidden', 'true');
expect(cells[1]).not.toHaveAttribute('aria-hidden', 'true');

Expand All @@ -383,5 +388,47 @@ describe('ariaHideOutside', function () {
expect(cells[0]).not.toHaveAttribute('aria-hidden', 'true');
expect(rows[1]).not.toHaveAttribute('aria-hidden', 'true');
expect(cells[1]).not.toHaveAttribute('aria-hidden', 'true');
expect(span).not.toHaveAttribute('aria-hidden');
});

it('should unhide after item reorder', async function () {
function Item(props) {
return (
<div role="presentation">
<div data-testid={props.testid} role="row">
<div role="gridcell" />
</div>
</div>
);
}

function Test() {
let [count, setCount] = useState(0);
let items = ['row1', 'row2', 'row3', 'row4', 'row5'];
if (count === 1) {
items = ['row2', 'row3', 'row4', 'row5', 'row1'];
}

return (
<>
<button onClick={() => setCount((old) => old + 1)}>press</button>
{items.map((item) => <Item testid={item} key={item} />)}
</>

);
}

let {getByRole, getByTestId} = render(
<Test />
);

let button = getByRole('button');
let row = getByTestId('row1');
let revert = ariaHideOutside([button]);

act(() => button.click());
await Promise.resolve();
revert();
expect(row).not.toHaveAttribute('aria-hidden', 'true');
});
});
Loading