Skip to content
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

[Issue #3434] React USWDS, React, Next.JS upgrade #3515

Merged
merged 25 commits into from
Jan 28, 2025

Conversation

acouch
Copy link
Collaborator

@acouch acouch commented Jan 14, 2025

Summary

Fixes #3434

Time to review: 45 mins

Changes proposed

Upgrade to React 19, Next 15, and the following

packages
package acouch/issue-3434-uswds-react-upgrade main
@ianvs/prettier-plugin-sort-imports 4.4.1 4.3.1
@next/third-parties 15.1.4 14.3.0-canary.87
@playwright/test 1.49.1 1.47.2
@storybook/addon-essentials 8.4.7 8.4.2
@storybook/react 8.4.7 8.4.2
@testing-library/react 16.1.0 16.0.1
@trussworks/react-uswds 9.1.0 github.com/trussworks/react-uswds.git#58f3089739ac5ee415c11cfbe179daad7a625971)
@types/lodash 4.17.14 4.17.13
@types/newrelic 9.14.7 9.14.6
@types/node-fetch 2.6.12 2.6.11
@types/node 22.10.6 22.10.5
@types/react-dom 19.0.3 18.3.1
@types/react 19.0.7 18.3.11
@uswds/uswds 3.11.0 3.8.2
eslint-config-next 13.5.8 13.5.7
eslint-plugin-jest-dom 5.5.0 5.4.0
focus-trap-react 10.3.1 10.3.0
isomorphic-dompurify 2.20.0 2.15.0
newrelic 12.10.0 12.7.0
next-intl 3.26.3 3.26.2
next 15.1.4 14.2.22
postcss 8.5.1 8.4.31
prettier 3.4.2 3.3.3
react-dom 19.0.018.3.1
react 19.0.0 18.3.1
sass 1.77.0 1.78.0
typescript 5.7.3 5.6.2
zod 3.24.1 3.23.8

Temporarily removes @storybookjs/addon-designs

Since it is not yet react 19 compatible.

USWDS Upgrade

There are some changes upgrading uswds 3.8.0 to 3.11.0.

  1. Change in padding for alert

This results from a breaking change from this PR: uswds/uswds#5636

from:
image

to:
image

I've left this as is to stay aligned with USWDS.

  1. Change in padding in header

There was a change in padding in the header from this breaking PR: uswds/uswds#6037 that fixes an issue with the ordering of the search bar which we don't use.

After upgrading USWDS, the header has an extra .25rem padding:

image

I overwrote that change for simpler in this PR: https://github.com/HHS/simpler-grants-gov/pull/3515/files#diff-992305390a5bf9f2ade09df84638c6473249440c5612ae3a328b1b174aa6b7edR439

But can remove that if you'd rather stay up-to-date with USWDS.

Context for reviewers

  • I initially tried just upgrading to Next 15, but ran into issues.
  • This ties us to a commit in uswds-react, this was discussed internally as a viable temporary path

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was autogen

@@ -22,7 +21,7 @@ export type ValidationErrors = {
export default function SubscriptionForm() {
const t = useTranslations("Subscribe");

const [state, formAction] = useFormState(subscribeEmail, {
const [state, formAction] = useActionState(subscribeEmail, {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@@ -61,7 +61,8 @@ export const createSession = async (token: string) => {
}
const expiresAt = newExpirationDate();
const session = await encrypt(token, expiresAt, clientJwtKey);
cookies().set("session", session, {
const cookie = await cookies();
Copy link
Collaborator Author

@acouch acouch Jan 14, 2025

Choose a reason for hiding this comment

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

Dynamic APIs are:

  • The params and searchParams props that get provided to pages, layouts, metadata APIs, and route handlers.
  • cookies(), draftMode(), and headers() from next/headers

In Next 15, these APIs have been made asynchronous. You can read more about this in the Next.js 15 Upgrade Guide.

from: https://nextjs.org/docs/messages/sync-dynamic-apis

@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch 2 times, most recently from 27d030f to ae3527a Compare January 17, 2025 13:25
@acouch
Copy link
Collaborator Author

acouch commented Jan 21, 2025

I've got tests working for upgrading to React 19 and Next.JS 15.

Currently working on:

✅ 1. Couple of unexpected CSS changes image
✅ 2. Errors in the console compiling SCSS
⚠ ./src/styles/styles.scss.webpack[javascript/auto]!=!./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[13].oneOf[11].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[13].oneOf[11].use[3]!./node_modules/next/dist/build/webpack/loaders/resolve-url-loader/index.js??ruleSet[1].rules[13].oneOf[11].use[4]!./node_modules/next/dist/compiled/sass-loader/cjs.js??ruleSet[1].rules[13].oneOf[11].use[5]!./src/styles/styles.scss
Deprecation Warning on line 13, column 6 of file:///Users/partisan/workshop/grants/simpler-grants-gov2/frontend/node_modules/@uswds/uswds/packages/uswds-core/src/styles/functions/units/rem-to-user-em.scss:13:6:
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
Use math.unit instead.

More info and automated migrator: https://sass-lang.com/d/import

13 |   @if unit($grid-in-rem) != "rem" {

node_modules/@uswds/uswds/packages/uswds-core/src/styles/functions/units/rem-to-user-em.scss 14:7  rem-to-user-em()
node_modules/@uswds/uswds/packages/uswds-core/src/styles/mixins/helpers/at-media.scss 17:12        at-media()
node_modules/@uswds/uswds/packages/usa-display/src/styles/_usa-display.scss 7:3                    @forward
node_modules/@uswds/uswds/packages/usa-display/src/styles/_index.scss 4:1                          @forward
node_modules/@uswds/uswds/packages/usa-display/_index.scss 5:1                                     @forward
node_modules/@uswds/uswds/packages/uswds-typography/_index.scss 5:1                                @forward
node_modules/@uswds/uswds/packages/uswds/_index.scss 13:1                                          @forward
src/styles/styles.scss 2:1                                                                         root stylesheet

✅ Better documenting PR / changes

❌ 4. Storybook works but could possibly get it working with the pages image

@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch 2 times, most recently from b1a883b to cfb20a7 Compare January 22, 2025 19:28
@acouch
Copy link
Collaborator Author

acouch commented Jan 22, 2025

FYI, there are some changes upgrading uswds 3.8.0 to 3.11.0

from:
image

to:
image

@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch 2 times, most recently from 5b726ca to 4dadf33 Compare January 23, 2025 16:24
"@storybook/addon-designs",
"@chromatic-com/storybook",
],
addons: ["@storybook/addon-essentials", "@chromatic-com/storybook"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Temp remove @storybook/addon-designs until it is React 19 compatible

Copy link
Collaborator

Choose a reason for hiding this comment

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

sounds good - are we actively using this in any case, or can we just remove it for good? Since there's not even an issue to add React 19 support I wouldn't be too confident that it'll come any time soon

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In practice it is "removed for good" as it is removed from npm and there is no config left.

experimental: {
serverComponentsExternalPackages: ["newrelic"],
},
serverExternalPackages: ["newrelic"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

params: { locale },
}: LocalizedPageProps) {
export async function generateMetadata({ params }: LocalizedPageProps) {
const { locale } = await params;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Lots of changes due to Async Request APIs (Breaking change)

@@ -1,5 +1,3 @@
import React from "react";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No longer needed, removed when running codemon upgrade script.

props: P & WithFeatureFlagProps,
) => {
const searchParams = props.searchParams || {};
const searchParams = (await props.searchParams) || {};
const cookiesRead = await cookies();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@doug-s-nava used cookiesRead and *Read a couple of times. Not wedded to that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

not a blocker but I think something like "resolvedCookies" would be more easily understandble

@@ -10,7 +10,7 @@ in the form $setting: value,
@use "uswds-core" with (
// Disable scary but mostly irrelevant warnings:
$theme-show-notifications: false,

$theme-show-compile-warnings: false,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Necessary to suppress a number of Sass warnings about upcoming versions. We are tied to uswds which is where a lot of them originated.

@@ -25,6 +25,13 @@ const mockSetFeatureFlag = jest.fn(
},
);

jest.mock("react", () => ({
...jest.requireActual<typeof import("react")>("react"),
use: jest.fn(() => ({
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Necessary for pages that "use" use()

Copy link
Collaborator

Choose a reason for hiding this comment

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

a larger discussion for another time probably, but with stuff like this would it make more sense to mock it globally?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I created a ticket: #3657

@@ -0,0 +1,30 @@
import { render, screen } from "@testing-library/react";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ended up adding this in order to test an async page.

@@ -96,6 +96,7 @@ describe("getSession", () => {
describe("createSession", () => {
afterEach(() => jest.clearAllMocks());
// to get this to work we'd need to manage resetting all modules before the test, which is a bit of a pain
// eslint-disable-next-line jest/no-disabled-tests
it.skip("initializes session secrets if necessary", async () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These show up locally if you run the linter.

const OriginalComponent = jest.fn();
const searchParams = { any: "param" };
const WrappedComponent = withFeatureFlag(
OriginalComponent,
"searchOff",
identity,
);
render(<WrappedComponent searchParams={searchParams} />);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a little gnarly. The FF wrapper is now async since it uses cookies and takes params.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, this component in general is pretty gnarly. I'd welcome just ignoring typescript on this file if that'd make things easier. Maybe just make it a js file, or do we have a rule against that?

@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch from f8fb701 to ef60f39 Compare January 23, 2025 16:51
@acouch acouch marked this pull request as ready for review January 23, 2025 17:27
@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch from ef60f39 to a692ad5 Compare January 23, 2025 17:51
@acouch acouch force-pushed the acouch/issue-3434-uswds-react-upgrade branch from 84b6bac to 2574e3e Compare January 27, 2025 22:23
@acouch acouch merged commit 3832fa9 into main Jan 28, 2025
19 checks passed
@acouch acouch deleted the acouch/issue-3434-uswds-react-upgrade branch January 28, 2025 15:50
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.

Upgrade USWDS React library to React 19
3 participants