Skip to content

Commit 53f9158

Browse files
committed
fix collection index incrementing when performing insertBefore
previously was only checking elements in the collection after the loading spinner node, thus items loaded async into the collection all remained with incorrect index, resulting in incorrect aria-rowIndex
1 parent f7a1e20 commit 53f9158

File tree

3 files changed

+12
-18
lines changed

3 files changed

+12
-18
lines changed

packages/@react-aria/collections/src/Document.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ export class BaseNode<T> {
155155
newNode.nextSibling = referenceNode;
156156
newNode.previousSibling = referenceNode.previousSibling;
157157
newNode.index = referenceNode.index;
158-
159158
if (this.firstChild === referenceNode) {
160159
this.firstChild = newNode;
161160
} else if (referenceNode.previousSibling) {
@@ -165,15 +164,15 @@ export class BaseNode<T> {
165164
referenceNode.previousSibling = newNode;
166165
newNode.parentNode = referenceNode.parentNode;
167166

168-
this.invalidateChildIndices(referenceNode);
167+
this.invalidateChildIndices(newNode);
169168
this.ownerDocument.queueUpdate();
170169
}
171170

172171
removeChild(child: ElementNode<T>): void {
173172
if (child.parentNode !== this || !this.ownerDocument.isMounted) {
174173
return;
175174
}
176-
175+
177176
if (child.nextSibling) {
178177
this.invalidateChildIndices(child.nextSibling);
179178
child.nextSibling.previousSibling = child.previousSibling;
@@ -279,7 +278,7 @@ export class ElementNode<T> extends BaseNode<T> {
279278
this.node = this.node.clone();
280279
this.isMutated = true;
281280
}
282-
281+
283282
this.ownerDocument.markDirty(this);
284283
return this.node;
285284
}
@@ -497,7 +496,7 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
497496
if (this.dirtyNodes.size === 0 || this.queuedRender) {
498497
return;
499498
}
500-
499+
501500
// Only trigger subscriptions once during an update, when the first item changes.
502501
// React's useSyncExternalStore will call getCollection immediately, to check whether the snapshot changed.
503502
// If so, React will queue a render to happen after the current commit to our fake DOM finishes.

packages/@react-aria/virtualizer/src/ScrollView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
199199
// Update visible rect when the content size changes, in case scrollbars need to appear or disappear.
200200
let lastContentSize = useRef<Size | null>(null);
201201
let [update, setUpdate] = useState({});
202-
203202
useLayoutEffect(() => {
204203
if (!isUpdatingSize.current && (lastContentSize.current == null || !contentSize.equals(lastContentSize.current))) {
205204
// React doesn't allow flushSync inside effects, so queue a microtask.
@@ -210,8 +209,8 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
210209
// https://github.com/reactwg/react-18/discussions/102
211210
// @ts-ignore
212211
if (typeof IS_REACT_ACT_ENVIRONMENT === 'boolean' ? IS_REACT_ACT_ENVIRONMENT : typeof jest !== 'undefined') {
213-
// Queue call of updateSize to happen in a separate render but within the same act so that RAC virtualized ComboBoxes and Selects
214-
// work properly
212+
// This is so we update size in a separate render but within the same act. Needs to be setState instead of refs
213+
// due to strict mode.
215214
setUpdate({});
216215
lastContentSize.current = contentSize;
217216
return
@@ -223,6 +222,7 @@ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement
223222
lastContentSize.current = contentSize;
224223
});
225224

225+
// Will only run in tests, needs to be in separate effect so it is properly run in the next render in strict mode.
226226
useLayoutEffect(() => {
227227
updateSize(fn => fn());
228228
}, [update])

packages/react-aria-components/test/Table.test.js

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,23 +2031,13 @@ describe('Table', () => {
20312031
beforeAll(() => {
20322032
clientWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 100);
20332033
clientHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 100);
2034-
// jest.spyOn(window.HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() => 150);
2035-
// jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 100);
2036-
// jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementationOnce(() => 0).mockImplementation(function () {
2037-
// if (this.getAttribute('role') === 'grid') {
2038-
// return 50;
2039-
// }
2040-
2041-
// return 25;
2042-
// });
20432034
});
20442035

20452036
beforeEach(() => {
20462037
act(() => {jest.runAllTimers();});
20472038
});
20482039

20492040
afterAll(function () {
2050-
// TODO fix these
20512041
clientWidth.mockReset();
20522042
clientHeight.mockReset();
20532043
});
@@ -2137,6 +2127,11 @@ describe('Table', () => {
21372127
expect(sentinelParentStyles.top).toBe('0px');
21382128
expect(sentinelParentStyles.height).toBe('30px');
21392129
});
2130+
2131+
it('should have the correct row indicies after loading more items', async () => {
2132+
// TODO: first render without items and is loading
2133+
// then render with items and double check
2134+
});
21402135
});
21412136
});
21422137

0 commit comments

Comments
 (0)