diff --git a/packages/@react-aria/collections/src/Document.ts b/packages/@react-aria/collections/src/Document.ts index d01d4170e91..1385e18a413 100644 --- a/packages/@react-aria/collections/src/Document.ts +++ b/packages/@react-aria/collections/src/Document.ts @@ -460,9 +460,13 @@ export class Document = BaseCollection> extend } updateCollection(): void { - // First, update the indices of dirty element children. + // First, remove disconnected nodes and update the indices of dirty element children. for (let element of this.dirtyNodes) { - element.updateChildIndices(); + if (element instanceof ElementNode && (!element.isConnected || element.isHidden)) { + this.removeNode(element); + } else { + element.updateChildIndices(); + } } // Next, update dirty collection nodes. @@ -471,8 +475,6 @@ export class Document = BaseCollection> extend if (element.isConnected && !element.isHidden) { element.updateNode(); this.addNode(element); - } else { - this.removeNode(element); } element.isMutated = false; diff --git a/packages/react-aria-components/test/ListBox.test.js b/packages/react-aria-components/test/ListBox.test.js index 88070aef887..17e7df8a62e 100644 --- a/packages/react-aria-components/test/ListBox.test.js +++ b/packages/react-aria-components/test/ListBox.test.js @@ -337,6 +337,54 @@ describe('ListBox', () => { expect(getAllByRole('option').map(o => o.textContent)).toEqual(['Hi']); }); + it('should update collection when moving item to a different section', () => { + let {getAllByRole, rerender} = render( + + +
Veggies
+ Lettuce + Tomato + Onion +
+ +
Meats
+ Ham + Tuna + Tofu +
+
+ ); + + let sections = getAllByRole('group'); + let items = within(sections[0]).getAllByRole('option'); + expect(items).toHaveLength(3); + items = within(sections[1]).getAllByRole('option'); + expect(items).toHaveLength(3); + + rerender( + + +
Veggies
+ Lettuce + Tomato + Onion + Ham +
+ +
Meats
+ Tuna + Tofu +
+
+ ); + + sections = getAllByRole('group'); + items = within(sections[0]).getAllByRole('option'); + expect(items).toHaveLength(4); + items = within(sections[1]).getAllByRole('option'); + expect(items).toHaveLength(2); + }); + it('should support autoFocus', () => { let {getByRole} = renderListbox({autoFocus: true}); let listbox = getByRole('listbox');