Skip to content

Commit 48b8212

Browse files
committed
website: Update react to 18.3.1
1 parent e7ff3c4 commit 48b8212

File tree

9 files changed

+547
-121
lines changed

9 files changed

+547
-121
lines changed

website/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@babel/preset-env": "7.22.10",
3535
"@babel/preset-react": "7.22.5",
3636
"@babel/preset-typescript": "7.22.5",
37+
"@cfaester/enzyme-adapter-react-18": "0.8.0",
3738
"@packtracker/webpack-plugin": "2.3.0",
3839
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
3940
"@svgr/webpack": "6.5.1",
@@ -48,9 +49,9 @@
4849
"@types/lodash": "4.14.197",
4950
"@types/mousetrap": "1.6.15",
5051
"@types/no-scroll": "2.1.2",
51-
"@types/react": "17.0.64",
52+
"@types/react": "18.3.12",
5253
"@types/react-beautiful-dnd": "13.1.8",
53-
"@types/react-dom": "17.0.20",
54+
"@types/react-dom": "18.3.1",
5455
"@types/react-helmet": "6.1.11",
5556
"@types/react-kawaii": "0.17.3",
5657
"@types/react-loadable": "5.5.11",
@@ -64,7 +65,6 @@
6465
"@typescript-eslint/eslint-plugin": "4.33.0",
6566
"@typescript-eslint/parser": "4.33.0",
6667
"@vercel/node": "1.15.4",
67-
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
6868
"babel-jest": "29.6.2",
6969
"babel-loader": "9.1.3",
7070
"babel-plugin-dynamic-import-node": "2.3.3",
@@ -150,9 +150,9 @@
150150
"nusmoderator": "3.0.0",
151151
"p-queue": "6.6.2",
152152
"query-string": "6.14.1",
153-
"react": "17.0.2",
153+
"react": "18.3.1",
154154
"react-beautiful-dnd": "13.1.1",
155-
"react-dom": "17.0.2",
155+
"react-dom": "18.3.1",
156156
"react-feather": "2.0.10",
157157
"react-helmet": "6.1.0",
158158
"react-kawaii": "0.18.0",

website/scripts/test.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
const { configure } = require('enzyme');
2-
const { setAutoFreeze } = require('immer');
3-
const Adapter = require('@wojtekmaj/enzyme-adapter-react-17');
4-
require('@testing-library/jest-dom');
1+
import Adapter from '@cfaester/enzyme-adapter-react-18';
2+
import { configure } from 'enzyme';
3+
import { setAutoFreeze } from 'immer';
4+
5+
import '@testing-library/jest-dom';
6+
7+
import { TextEncoder } from 'util';
8+
9+
Object.defineProperty(global, 'TextEncoder', {
10+
value: TextEncoder,
11+
});
512

613
configure({ adapter: new Adapter() });
714

website/src/entry/export/main.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createRef } from 'react';
2-
import ReactDOM from 'react-dom';
2+
import { createRoot } from 'react-dom/client';
33
import { Store } from 'redux';
44

55
import { Module } from 'types/modules';
@@ -53,7 +53,8 @@ const render = () => {
5353
const appElement = document.getElementById('app');
5454
if (!appElement) throw new Error('#app not found');
5555

56-
ReactDOM.render(<TimetableOnly store={store} ref={timetableRef} />, appElement);
56+
const root = createRoot(appElement);
57+
root.render(<TimetableOnly store={store} ref={timetableRef} />);
5758
};
5859

5960
render();

website/src/entry/main.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'bootstrapping/sentry';
66
// See: https://github.com/zloirock/core-js/issues/579#issuecomment-504325213
77
import 'core-js/es/promise/finally';
88

9-
import ReactDOM from 'react-dom';
9+
import { createRoot } from 'react-dom/client';
1010
import ReactModal from 'react-modal';
1111

1212
import configureStore from 'bootstrapping/configure-store';
@@ -25,7 +25,9 @@ subscribeOnlineEvents(store);
2525
// Initialize ReactModal
2626
ReactModal.setAppElement('#app');
2727

28-
ReactDOM.render(<App store={store} persistor={persistor} />, document.getElementById('app'));
28+
const container = document.getElementById('app');
29+
const root = createRoot(container);
30+
root.render(<App store={store} persistor={persistor} />);
2931

3032
if (
3133
((NUSMODS_ENV === 'preview' || NUSMODS_ENV === 'staging' || NUSMODS_ENV === 'production') &&

website/src/test-utils/wait.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { CommonWrapper } from 'enzyme';
2+
import { act } from '@testing-library/react';
3+
4+
// Hack to get Enzyme to work with React 18
5+
// Source: https://github.com/enzymejs/enzyme/issues/2524
6+
// TODO: Replace Enzyme entirely along with this hack.
7+
export const waitForComponentToPaint = async (wrapper?: CommonWrapper): Promise<void> => {
8+
await act(async () => {
9+
await new Promise<void>((resolve) => {
10+
window.setTimeout(() => {
11+
/*
12+
* Make sure it is the last task in the queue.
13+
* https://dmitripavlutin.com/javascript-promises-settimeout/
14+
*/
15+
window.setTimeout(resolve, 1);
16+
}, 1);
17+
});
18+
});
19+
wrapper?.update();
20+
};

website/src/views/mpe/form/ModulesSelect.test.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { mount, shallow } from 'enzyme';
22
import Downshift from 'downshift';
33
import Modal from 'views/components/Modal';
4+
import { waitForComponentToPaint } from 'test-utils/wait';
45
import { ModulesSelectComponent } from './ModulesSelect';
56

67
const modules = [
@@ -32,9 +33,10 @@ const commonProps = {
3233
};
3334

3435
describe(ModulesSelectComponent, () => {
35-
it('should show results on input value change', () => {
36+
it('should show results on input value change', async () => {
3637
const wrapper = mount(<ModulesSelectComponent {...commonProps} matchBreakpoint />);
3738
wrapper.setState({ isOpen: true });
39+
await waitForComponentToPaint(wrapper);
3840
const input = wrapper.find('input');
3941
expect(wrapper.find('li')).toHaveLength(0);
4042
input.simulate('change', { target: { value: 'T' } });
@@ -43,17 +45,19 @@ describe(ModulesSelectComponent, () => {
4345
expect(wrapper.find('li')).toHaveLength(0);
4446
});
4547

46-
it('should indicate module is added', () => {
48+
it('should indicate module is added', async () => {
4749
const wrapper = mount(<ModulesSelectComponent {...commonProps} matchBreakpoint />);
4850
wrapper.setState({ isOpen: true, inputValue: 'T' });
51+
await waitForComponentToPaint(wrapper);
4952
const result = wrapper.find('li').at(1);
5053
expect(result.prop('disabled')).toBe(true);
5154
expect(result.find('.badge').exists()).toBe(true);
5255
});
5356

54-
it('should call onChange when module is selected', () => {
57+
it('should call onChange when module is selected', async () => {
5558
const wrapper = mount(<ModulesSelectComponent {...commonProps} matchBreakpoint />);
5659
wrapper.setState({ isOpen: true, inputValue: 'T' });
60+
await waitForComponentToPaint(wrapper);
5761
wrapper.find('li').first().simulate('click');
5862
expect(commonProps.onChange).toHaveBeenCalledWith(modules[0].moduleCode);
5963
// remain open

website/src/views/timetable/TimetableContainer.tsx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import type { SemTimetableConfig } from 'types/timetables';
1111

1212
import { selectSemester } from 'actions/settings';
1313
import { getSemesterTimetableColors, getSemesterTimetableLessons } from 'selectors/timetables';
14-
import {
15-
fetchTimetableModules,
16-
setHiddenModulesFromImport,
17-
setTimetable,
18-
} from 'actions/timetables';
14+
import { fetchTimetableModules, setTimetable } from 'actions/timetables';
1915
import { openNotification } from 'actions/app';
2016
import { undo } from 'actions/undoHistory';
2117
import { getModuleCondensed } from 'selectors/moduleBank';
@@ -156,7 +152,7 @@ export const TimetableContainerComponent: FC = () => {
156152
);
157153

158154
const importedHidden = useMemo(
159-
() => (semester && params.action ? deserializeHidden(location.search) : []),
155+
() => (semester && params.action ? deserializeHidden(location.search) : null),
160156
[semester, params.action, location.search],
161157
);
162158

@@ -167,12 +163,6 @@ export const TimetableContainerComponent: FC = () => {
167163
}
168164
}, [dispatch, importedTimetable]);
169165

170-
useEffect(() => {
171-
if (importedHidden) {
172-
dispatch(setHiddenModulesFromImport(importedHidden));
173-
}
174-
}, [dispatch, importedHidden]);
175-
176166
const isLoading = useMemo(() => {
177167
// Check that all modules are fully loaded into the ModuleBank
178168
const isValidModule = (moduleCode: ModuleCode) => !!getModule(moduleCode);
@@ -211,6 +201,7 @@ export const TimetableContainerComponent: FC = () => {
211201
key={semester}
212202
semester={semester}
213203
timetable={displayedTimetable}
204+
hiddenImportedModules={importedHidden}
214205
colors={filledColors}
215206
header={
216207
<>

website/src/views/timetable/TimetableContent.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type OwnProps = {
6666
semester: Semester;
6767
timetable: SemTimetableConfig;
6868
colors: ColorMapping;
69+
hiddenImportedModules: ModuleCode[] | null;
6970
};
7071

7172
type Props = OwnProps & {
@@ -456,7 +457,8 @@ function mapStateToProps(state: StoreState, ownProps: OwnProps) {
456457

457458
// Determine the key to check for hidden modules based on readOnly status
458459
const hiddenModulesKey = readOnly ? HIDDEN_IMPORTED_SEM : semester;
459-
const hiddenInTimetable = state.timetables.hidden[hiddenModulesKey] || [];
460+
const hiddenInTimetable =
461+
ownProps.hiddenImportedModules ?? (state.timetables.hidden[hiddenModulesKey] || []);
460462

461463
return {
462464
semester,

0 commit comments

Comments
 (0)