Skip to content

Commit 853c60c

Browse files
authored
Upgrade React Transition Group (#3706)
* Upgrade React Transition Group
1 parent 960b3e3 commit 853c60c

File tree

6 files changed

+111
-51
lines changed

6 files changed

+111
-51
lines changed

packages/@react-spectrum/combobox/test/ComboBox.test.js

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,7 @@ describe('ComboBox', function () {
248248
jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 1000);
249249
window.HTMLElement.prototype.scrollIntoView = jest.fn();
250250
jest.spyOn(window.screen, 'width', 'get').mockImplementation(() => 1024);
251-
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => setTimeout(cb, 0));
252-
jest.useFakeTimers('legacy');
251+
jest.useFakeTimers();
253252
});
254253

255254
beforeEach(() => {
@@ -2024,23 +2023,45 @@ describe('ComboBox', function () {
20242023
);
20252024

20262025
let button = getByRole('button');
2026+
let combobox = getByRole('combobox');
20272027

2028-
await waitFor(() => expect(load).toHaveBeenCalledTimes(1));
2028+
expect(load).toHaveBeenCalledTimes(1);
20292029
expect(onOpenChange).toHaveBeenCalledTimes(0);
20302030
expect(onLoadMore).toHaveBeenCalledTimes(0);
20312031

2032+
// open menu
2033+
triggerPress(button);
2034+
// use async act to resolve initial load
20322035
await act(async () => {
2033-
triggerPress(button);
2034-
jest.runAllTimers();
2036+
// advance to open state from Transition
2037+
jest.advanceTimersToNextTimer();
20352038
});
2036-
20372039
let listbox = getByRole('listbox');
20382040
expect(listbox).toBeVisible();
2041+
jest.spyOn(listbox, 'clientHeight', 'get').mockImplementation(() => 100);
2042+
// update size, virtualizer raf kicks in
2043+
act(() => {jest.advanceTimersToNextTimer();});
2044+
// onLoadMore queued by previous timer, run it now
2045+
act(() => {jest.advanceTimersToNextTimer();});
20392046

20402047
expect(onOpenChange).toHaveBeenCalledTimes(1);
20412048
expect(onOpenChange).toHaveBeenCalledWith(true, 'manual');
20422049
expect(onLoadMore).toHaveBeenCalledTimes(1);
2043-
await waitFor(() => expect(load).toHaveBeenCalledTimes(1));
2050+
expect(load).toHaveBeenCalledTimes(1);
2051+
2052+
// close menu
2053+
act(() => {combobox.blur();});
2054+
// raf from virtualizer relayout
2055+
act(() => {jest.advanceTimersToNextTimer();});
2056+
// previous act wraps up onExiting
2057+
// raf
2058+
act(() => {jest.advanceTimersToNextTimer();});
2059+
// raf
2060+
act(() => {jest.advanceTimersToNextTimer();});
2061+
// exited
2062+
act(() => {jest.advanceTimersToNextTimer();});
2063+
2064+
expect(listbox).not.toBeInTheDocument();
20442065
});
20452066

20462067
it('onLoadMore is not called on when previously opened', async () => {
@@ -2052,29 +2073,46 @@ describe('ComboBox', function () {
20522073

20532074
let button = getByRole('button');
20542075
let combobox = getByRole('combobox');
2055-
await waitFor(() => expect(load).toHaveBeenCalledTimes(1));
2076+
expect(load).toHaveBeenCalledTimes(1);
20562077
expect(onOpenChange).toHaveBeenCalledTimes(0);
20572078
expect(onLoadMore).toHaveBeenCalledTimes(0);
20582079

20592080
// this call and the one below are more correct for how the code should
2060-
// behave, the inital call would have a height of zero and after that a measureable height
2081+
// behave, the initial call would have a height of zero and after that a measureable height
20612082
clientHeightSpy.mockRestore();
20622083
clientHeightSpy = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementationOnce(() => 0).mockImplementation(() => 40);
20632084
// open menu
2085+
triggerPress(button);
2086+
// use async act to resolve initial load
20642087
await act(async () => {
2065-
triggerPress(button);
2066-
jest.runAllTimers();
2088+
// advance to open state from Transition
2089+
jest.advanceTimersToNextTimer();
20672090
});
2091+
let listbox = getByRole('listbox');
2092+
expect(listbox).toBeVisible();
2093+
jest.spyOn(listbox, 'clientHeight', 'get').mockImplementation(() => 100);
2094+
// update size, virtualizer raf kicks in
2095+
act(() => {jest.advanceTimersToNextTimer();});
2096+
// onLoadMore queued by previous timer, run it now
2097+
act(() => {jest.advanceTimersToNextTimer();});
20682098

20692099
expect(onOpenChange).toHaveBeenCalledTimes(1);
20702100
expect(onOpenChange).toHaveBeenCalledWith(true, 'manual');
20712101
expect(onLoadMore).toHaveBeenCalledTimes(1);
20722102

20732103
// close menu
2074-
act(() => {
2075-
combobox.blur();
2076-
jest.runAllTimers();
2077-
});
2104+
act(() => {combobox.blur();});
2105+
// raf from virtualizer relayout
2106+
act(() => {jest.advanceTimersToNextTimer();});
2107+
// previous act wraps up onExiting
2108+
// raf
2109+
act(() => {jest.advanceTimersToNextTimer();});
2110+
// raf
2111+
act(() => {jest.advanceTimersToNextTimer();});
2112+
// exited
2113+
act(() => {jest.advanceTimersToNextTimer();});
2114+
2115+
expect(listbox).not.toBeInTheDocument();
20782116

20792117
expect(onOpenChange).toHaveBeenCalledTimes(2);
20802118
expect(onOpenChange).toHaveBeenLastCalledWith(false, undefined);
@@ -2083,10 +2121,18 @@ describe('ComboBox', function () {
20832121
clientHeightSpy.mockRestore();
20842122
clientHeightSpy = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementationOnce(() => 0).mockImplementation(() => 40);
20852123
// reopen menu
2124+
triggerPress(button);
20862125
await act(async () => {
2087-
triggerPress(button);
2088-
jest.runAllTimers();
2126+
// advance to open state from Transition
2127+
jest.advanceTimersToNextTimer();
20892128
});
2129+
listbox = getByRole('listbox');
2130+
expect(listbox).toBeVisible();
2131+
jest.spyOn(listbox, 'clientHeight', 'get').mockImplementation(() => 100);
2132+
// update size, virtualizer raf kicks in
2133+
act(() => {jest.advanceTimersToNextTimer();});
2134+
// onLoadMore queued by previous timer, run it now
2135+
act(() => {jest.advanceTimersToNextTimer();});
20902136

20912137
expect(onOpenChange).toHaveBeenCalledTimes(3);
20922138
expect(onOpenChange).toHaveBeenLastCalledWith(true, 'manual');
@@ -2095,7 +2141,10 @@ describe('ComboBox', function () {
20952141
// because the browser limits the popover height via calculatePosition(),
20962142
// while the test doesn't, causing virtualizer to try to load more
20972143
expect(onLoadMore).toHaveBeenCalledTimes(2);
2098-
await waitFor(() => expect(load).toHaveBeenCalledTimes(1));
2144+
expect(load).toHaveBeenCalledTimes(1);
2145+
2146+
// close menu
2147+
act(() => {combobox.blur();});
20992148
});
21002149
});
21012150

packages/@react-spectrum/list/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"@react-types/grid": "^3.1.5",
5353
"@react-types/shared": "^3.16.0",
5454
"@spectrum-icons/ui": "^3.4.0",
55-
"react-transition-group": "^2.2.0"
55+
"react-transition-group": "^4.4.5"
5656
},
5757
"devDependencies": {
5858
"@adobe/spectrum-css-temp": "^3.0.0-alpha.1",

packages/@react-spectrum/overlays/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@react-stately/overlays": "^3.4.3",
3939
"@react-types/overlays": "^3.6.5",
4040
"@react-types/shared": "^3.16.0",
41-
"react-transition-group": "^2.2.0"
41+
"react-transition-group": "^4.4.5"
4242
},
4343
"devDependencies": {
4444
"@adobe/spectrum-css-temp": "3.0.0-alpha.1"

packages/@react-spectrum/overlays/src/OpenTransition.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import React from 'react';
14-
import Transition from 'react-transition-group/Transition';
14+
import {Transition} from 'react-transition-group';
1515

1616
const OPEN_STATES = {
1717
entering: false,

packages/@react-stately/virtualizer/src/useVirtualizerState.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,37 @@ export function useVirtualizerState<T extends object, V, W>(opts: VirtualizerPro
8080
// eslint-disable-next-line react-hooks/exhaustive-deps
8181
}, []);
8282

83-
return {
83+
let setVisibleRect = useCallback((rect) => {
84+
virtualizer.visibleRect = rect;
85+
}, [virtualizer]);
86+
let startScrolling = useCallback(() => {
87+
virtualizer.startScrolling();
88+
setScrolling(true);
89+
}, [virtualizer]);
90+
let endScrolling = useCallback(() => {
91+
virtualizer.endScrolling();
92+
setScrolling(false);
93+
}, [virtualizer]);
94+
95+
let state = useMemo(() => ({
8496
virtualizer,
8597
visibleViews,
86-
setVisibleRect: useCallback((rect) => {
87-
virtualizer.visibleRect = rect;
88-
}, [virtualizer]),
98+
setVisibleRect,
8999
contentSize,
90100
isAnimating,
91101
isScrolling,
92-
startScrolling: useCallback(() => {
93-
virtualizer.startScrolling();
94-
setScrolling(true);
95-
}, [virtualizer]),
96-
endScrolling: useCallback(() => {
97-
virtualizer.endScrolling();
98-
setScrolling(false);
99-
}, [virtualizer])
100-
};
102+
startScrolling,
103+
endScrolling
104+
}), [
105+
virtualizer,
106+
visibleViews,
107+
setVisibleRect,
108+
contentSize,
109+
isAnimating,
110+
isScrolling,
111+
startScrolling,
112+
endScrolling
113+
]);
114+
115+
return state;
101116
}

yarn.lock

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@
11251125
core-js-pure "^3.0.0"
11261126
regenerator-runtime "^0.13.4"
11271127

1128-
"@babel/[email protected]", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
1128+
"@babel/[email protected]", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
11291129
version "7.12.5"
11301130
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
11311131
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
@@ -8684,12 +8684,13 @@ dom-converter@^0.2:
86848684
dependencies:
86858685
utila "~0.4"
86868686

8687-
dom-helpers@^3.4.0:
8688-
version "3.4.0"
8689-
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
8690-
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
8687+
dom-helpers@^5.0.1:
8688+
version "5.2.1"
8689+
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
8690+
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
86918691
dependencies:
8692-
"@babel/runtime" "^7.1.2"
8692+
"@babel/runtime" "^7.8.7"
8693+
csstype "^3.0.2"
86938694

86948695
dom-serializer@0:
86958696
version "0.2.2"
@@ -18615,11 +18616,6 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.8.6:
1861518616
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1861618617
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
1861718618

18618-
react-lifecycles-compat@^3.0.4:
18619-
version "3.0.4"
18620-
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
18621-
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
18622-
1862318619
react-lowlight@^2.0.0:
1862418620
version "2.0.0"
1862518621
resolved "https://registry.yarnpkg.com/react-lowlight/-/react-lowlight-2.0.0.tgz#3c9fa072c6516add8552f9470287e24e13d38eff"
@@ -18653,15 +18649,15 @@ react-test-renderer@^16.9.0:
1865318649
react-is "^16.8.6"
1865418650
scheduler "^0.16.2"
1865518651

18656-
react-transition-group@^2.2.0:
18657-
version "2.9.0"
18658-
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
18659-
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
18652+
react-transition-group@^4.4.5:
18653+
version "4.4.5"
18654+
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
18655+
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
1866018656
dependencies:
18661-
dom-helpers "^3.4.0"
18657+
"@babel/runtime" "^7.5.5"
18658+
dom-helpers "^5.0.1"
1866218659
loose-envify "^1.4.0"
1866318660
prop-types "^15.6.2"
18664-
react-lifecycles-compat "^3.0.4"
1866518661

1866618662
react@^18.0.0:
1866718663
version "18.1.0"

0 commit comments

Comments
 (0)