Skip to content

VIDCS-3681: Bump up React version #154

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 14 commits into from
May 6, 2025
Merged

Conversation

behei-vonage
Copy link
Contributor

@behei-vonage behei-vonage commented Apr 28, 2025

What is this PR doing?

This PR bumps up version of react used in the project from 18 to 19.

How should this be manually tested?

A few things need to be tested as a part of this PR.
Given that there is a version update of the framework that we use, it'd be great to check that the basic functionality works as expected - multiple people in the meeting room, participant list/chat/report issue functionality works, changing audio/video sources works etc.
CI passes

What are the relevant tickets?

A maintainer will add this ticket number.

Resolves VIDCS-3681

Checklist

[ ] Branch is based on develop (not main).
[ ] Resolves a Known Issue.
[ ] If yes, did you remove the item from the docs/KNOWN_ISSUES.md?
[ ] Resolves an item reported in Issues.
If yes, which issue? Issue Number?

@behei-vonage behei-vonage self-assigned this Apr 28, 2025
@@ -17,7 +17,7 @@ import DeviceStore from '../../../utils/DeviceStore';
const usePublisherOptions = (): PublisherProperties | null => {
const { user } = useUserContext();
const [publisherOptions, setPublisherOptions] = useState<PublisherProperties | null>(null);
const deviceStoreRef = useRef<DeviceStore>();
const deviceStoreRef = useRef<DeviceStore | null>(null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this was changed in a few places. it is enforced in React 19

Comment on lines 114 to 117
expect((outputDevicesElement.firstChild as HTMLOptionElement).selected).toBe(true);
expect((outputDevicesElement.firstChild as Element).classList.contains('Mui-selected')).toBe(
true
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this was returning undefined after an update and needed to be changed on how we access the selected property.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be more specific than Element? Should this be a div, an li, span, or is it unknown so an HTMLElement?

@@ -22,16 +22,23 @@ describe('GoodBye', () => {
});

it('should render', () => {
render(<GoodBye />, { wrapper: BrowserRouter });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: wrapper: BrowserRouter is no longer supported. the change is fairly simple though, just had to directly wrap the Goodbye component with BrowserRouter

Comment on lines 36 to 42
react({
babel: {
plugins: isTest
? [] // Disable babel-plugin-react-compiler in test environment
: [['babel-plugin-react-compiler', { target: '19' }]],
},
}),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this was added because the React Compiler was breaking the tests by injecting things like UseMemoCache that are undefined in the vitest environment.
target set to 19 refers to the React version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a recommended workaround? Are there more people dealing with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot suggested this workaround. there isn't a whole lot of people dealing with this quite yet since it's a fairly new plugin

"@types/react-router-dom": "^5.3.3",
"@types/ua-parser-js": "^0.7.39",
"@types/uuid": "^9.0.8",
"@vitest/coverage-v8": "^1.3.0",
"@vitest/ui": "^1.3",
"babel-plugin-react-compiler": "19.1.0-rc.1",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: please do not be scared that the name of the React Compiler starts with babel-plugin. While it works in babel, it is also fully supported with vite - as you can see, no babel.config files have been introduced and it is all handled within vite.config.ts

@behei-vonage behei-vonage added the do-not-review Do not review label Apr 28, 2025
@@ -1,6 +1,6 @@
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, Mock, Mocked, vi } from 'vitest';
import { MutableRefObject } from 'react';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: MutableRefObject has been discontinued in React 19. SonarCloud is very unhappy about keeping this even though it is not currently a breaking change. This has been replaced with RefObjects. All other changes related to MutableRefObject are for the same reason.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm, there's no way to disambiguate between mutable and immutable ref objects?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, they're all refobjects now and it is mutable

@behei-vonage behei-vonage requested a review from Copilot April 28, 2025 17:20
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR upgrades the React version from 18 to 19 and introduces the React Compiler via a Babel plugin, while also refactoring several type definitions by replacing MutableRefObject with RefObject in both production code and tests. Additionally, tests are updated to properly wrap components with BrowserRouter for rendering.

Reviewed Changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated no comments.

Show a summary per file
File Description
frontend/vite.config.ts Added "isTest" flag and Babel plugin configuration for React Compiler.
frontend/src/utils/helpers/getLayoutBoxes/getLayoutBoxes.ts Replaced MutableRefObject with RefObject in ref type.
frontend/src/utils/helpers/getLayoutBoxes/getLayoutBoxes.spec.ts Updated ref type in tests accordingly.
frontend/src/pages/UnsupportedBrowserPage/UnsupportedBrowserPage.spec.tsx Wrapped component in BrowserRouter for improved rendering in tests.
frontend/src/pages/GoodBye/GoodBye.spec.tsx Wrapped GoodBye in BrowserRouter and updated expectations for UI.
frontend/src/hooks/useToolbarButtons.tsx Updated ref types to RefObject.
frontend/src/hooks/useElementDimensions.tsx Updated ref type and initialized the ResizeObserver ref explicitly.
frontend/src/hooks/useChat.tsx Changed default value logic using the nullish coalescing operator.
frontend/src/hooks/useArchives.tsx Initialized the pollingIntervalRef with undefined.
frontend/src/hooks/tests/useChat.spec.tsx Updated ref type in tests.
frontend/src/components/MeetingRoom/DeviceSettingsMenu/DeviceSettingsMenu.tsx Adjusted ref type in props for anchorRef to support null value.
frontend/src/components/MeetingRoom/DeviceSettingsMenu/DeviceSettingsMenu.spec.tsx Updated tests to check for CSS class "Mui-selected" instead of HTML select state.
frontend/src/components/MeetingRoom/DeviceControlButton/DeviceControlButton.tsx Adjusted ref type in DeviceControlButton for anchorRef.
frontend/src/Context/PublisherProvider/usePublisherOptions/usePublisherOptions.tsx Changed deviceStoreRef type to include null initially.
Files not reviewed (1)
  • frontend/package.json: Language not supported
Comments suppressed due to low confidence (1)

frontend/src/components/MeetingRoom/DeviceSettingsMenu/DeviceSettingsMenu.spec.tsx:115

  • [nitpick] If this assertion is synchronous, consider removing the 'await' keyword to improve test clarity and consistency.
await expect((outputDevicesElement.firstChild as Element).classList.contains('Mui-selected')).toBe(true);

@behei-vonage behei-vonage removed the do-not-review Do not review label Apr 28, 2025
Copy link
Contributor

@maikthomas maikthomas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one, good to keep up to date 👍 Left a couple q's

@@ -40,7 +40,7 @@ const HelperText = ({
};

return (
<Box display="flex" justifyContent="space-between" width="100%">
<Box component="span" display="flex" justifyContent="space-between" width="100%">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this a span before? I think technically spans aren't meant to contain child elements

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was just a <p> before with some <div>s inside of it and it was failing with:

In HTML, <div> cannot be a descendant of <p>.
This will cause a hydration error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be okay. The component will be a span containing more spans.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics are odd. Box implies "block level" element. By contrast, span implies "inlined" elements, like Mike mentioned.

Do we need to add component="span"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I think we need to add that component="span" here since this HelperText component is used inside of another component, FeedbackForm. the FeedbackForm consists out of a bunch of <p>s and nesting a <div> inside of it causes a hydration error (see comment above). That's why we need to set Box components to span so rendering looks like this:

<p>
    <span>...</span>
    <span>...</span>
</p>
<p>
    ....
</p>

Comment on lines 36 to 42
react({
babel: {
plugins: isTest
? [] // Disable babel-plugin-react-compiler in test environment
: [['babel-plugin-react-compiler', { target: '19' }]],
},
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a recommended workaround? Are there more people dealing with this?

@behei-vonage behei-vonage requested a review from maikthomas April 30, 2025 13:43
Copy link
Contributor

@cpettet cpettet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far! Just have a few comments/questions. Let me know what you think!

Comment on lines 114 to 117
expect((outputDevicesElement.firstChild as HTMLOptionElement).selected).toBe(true);
expect((outputDevicesElement.firstChild as Element).classList.contains('Mui-selected')).toBe(
true
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be more specific than Element? Should this be a div, an li, span, or is it unknown so an HTMLElement?

@@ -120,7 +123,9 @@ describe('DeviceSettingsMenu Component', () => {
await act(() => (outputDevicesElement.children[2] as HTMLOptionElement).click?.());

expect(mockSetAudioOutputDevice).toHaveBeenCalledWith(audioOutputDevices[2].deviceId);
await expect((outputDevicesElement.children[2] as HTMLOptionElement).selected).toBe(true);
expect((outputDevicesElement.children[2] as Element).classList.contains('Mui-selected')).toBe(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to check the visual indicator we use is present for this output device? I'm not a big fan of checking the presence of a class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a particular issue with checking for a class. I will add an extra expect to check that the following element is not currently selected, though, to be extra certain

@@ -40,7 +40,7 @@ const HelperText = ({
};

return (
<Box display="flex" justifyContent="space-between" width="100%">
<Box component="span" display="flex" justifyContent="space-between" width="100%">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be okay. The component will be a span containing more spans.

@behei-vonage behei-vonage requested a review from cpettet April 30, 2025 21:03
cpettet
cpettet previously approved these changes May 2, 2025
Copy link
Contributor

@cpettet cpettet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Great job! :shipit:

@v-kpheng
Copy link
Collaborator

v-kpheng commented May 2, 2025

@behei-vonage, some nice initiative here 💪

That said, some questions/comments:

  • Using a de-facto alpha release makes me feel iffy. I know we're using some beta packages too, but still
  • Do we have measurements to contrast how much more responsive the changes make VERA? If not, then maybe we can add telemetry to gauge the improvements these changes make

@behei-vonage behei-vonage changed the title VIDCS-3681: Bump up React version and introduce React Compiler VIDCS-3681: Bump up React version May 2, 2025
@behei-vonage behei-vonage requested review from cpettet and v-kpheng May 2, 2025 20:18
Copy link
Collaborator

@v-kpheng v-kpheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 💪

That said, left some questions/comments. Thanks! 🙏

"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@vitejs/plugin-react": "^4.4.1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need to be upgraded as well? If so, it's because the new version of React requires this, yes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, react 19 requires these packages to be upgraded

@@ -1,6 +1,6 @@
import { act, queryByText, render, screen, waitFor } from '@testing-library/react';
import { describe, beforeEach, it, Mock, vi, expect, afterAll } from 'vitest';
import { MutableRefObject } from 'react';
import { RefObject } from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For React 19, all RefObjects are implicitly mutable now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is correct, mutablerefobject has been deprecated and it's all on refobject now

@@ -40,7 +40,7 @@ const HelperText = ({
};

return (
<Box display="flex" justifyContent="space-between" width="100%">
<Box component="span" display="flex" justifyContent="space-between" width="100%">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics are odd. Box implies "block level" element. By contrast, span implies "inlined" elements, like Mike mentioned.

Do we need to add component="span"?

@@ -1,6 +1,6 @@
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, Mock, Mocked, vi } from 'vitest';
import { MutableRefObject } from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm, there's no way to disambiguate between mutable and immutable ref objects?

@behei-vonage behei-vonage requested a review from v-kpheng May 5, 2025 19:13
Copy link

sonarqubecloud bot commented May 5, 2025

Copy link
Collaborator

@v-kpheng v-kpheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 💪 🚀

Copy link
Contributor

@cpettet cpettet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Great job! :shipit:

@dwivedisachin
Copy link
Collaborator

Tested LGTM!! 🚀

@behei-vonage behei-vonage merged commit 56c9ee2 into develop May 6, 2025
6 of 7 checks passed
@behei-vonage behei-vonage deleted the behei-vonage/vidcs-3681-bump branch May 6, 2025 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants