Skip to content

Commit

Permalink
Update react-scripts and Typescript to version 4 (#85)
Browse files Browse the repository at this point in the history
* Update react-scripts and typescript to new major versions

* Update to react-scripts 4 and Typescript 4

* Fix lint error where useState was being called conditionally

* Fix lint error where no type was provided for useParams

* Remove ref hack that's no longer needed

* Hack to fix proxy weirdness

* Set resetMocks to false in Jest

CRA 4 defaults this to true, which broke jest-mock-axios and therefore every test we have.

* Squash warnings

* Use Cookies library to set cookies in auth tests

This fixes a type error we were getting on deleting window.document.cookie, which is invalid, since that field is not optional.

* Go back to export default

The client manifest generation script relies on export default being there, so we just ignore the eslint rule.

* Switch to storing config data in a JSON file

This avoids the eslint error we were getting in config.ts before, and makes the manifest generation code less fragile

* Fix formatting
  • Loading branch information
ktrieu authored Feb 15, 2021
1 parent 12ffe92 commit c36d9f5
Show file tree
Hide file tree
Showing 19 changed files with 9,886 additions and 8,356 deletions.
9 changes: 0 additions & 9 deletions .eslintrc.json

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
.env.development.local
.env.test.local
.env.production.local
.eslintcache

npm-debug.log*
yarn-debug.log*
Expand Down
18,057 changes: 9,783 additions & 8,274 deletions package-lock.json

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"coverageReporters": [
"text",
"lcov"
]
],
"resetMocks": false
},
"dependencies": {
"@babel/cli": "^7.10.1",
Expand All @@ -29,7 +30,7 @@
"@types/react-dom": "^16.9.5",
"@types/react-helmet": "^6.0.0",
"@types/react-router-config": "^5.0.1",
"@types/react-router-dom": "^5.1.3",
"@types/react-router-dom": "^5.1.7",
"axios": "^0.21.1",
"babel-plugin-transform-assets": "^1.0.2",
"bootstrap": "^4.5.0",
Expand All @@ -50,12 +51,12 @@
"react-helmet": "^6.0.0",
"react-hook-form": "^5.6.1",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.4.3",
"react-scripts": "^4.0.1",
"serialize-javascript": "^3.1.0",
"slate": "^0.58.3",
"slate-history": "^0.58.4",
"slate-react": "^0.58.4",
"typescript": "~3.9.7"
"typescript": "^4.1.3"
},
"scripts": {
"start": "react-scripts start",
Expand All @@ -67,7 +68,16 @@
"size": "npm run build && size-limit"
},
"eslintConfig": {
"extends": "react-app"
"extends": [
"react-app",
"prettier"
],
"parserOptions": {
"ecmaVersion": 2018
},
"env": {
"es2017": true
}
},
"browserslist": {
"production": [
Expand All @@ -94,7 +104,6 @@
"@types/scheduler": "^0.16.1",
"@types/serialize-javascript": "^1.5.0",
"core-js": "^3.6.5",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"glob": "^7.1.6",
"jest-mock-axios": "^4.0.0",
Expand Down
8 changes: 2 additions & 6 deletions scripts/generate-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
const fs = require("fs");
const path = require("path");

const configPath = path.resolve(__dirname, "..", "src", "config.ts");
const configPath = path.resolve(__dirname, "..", "src", "config", "config.json");
const manifestPath = path.resolve(
__dirname,
"..",
Expand All @@ -21,11 +21,7 @@ console.log("Loading config from", configPath);

// Because TypeScript uses ES6 modules, we can't exactly load the contents of the config file
// So instead, we read the source and evaluate it.
const config = Function(
`"use strict"; return ${fs
.readFileSync(configPath, "utf-8")
.replace("export default", "")}`
)();
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"))

console.log("Loaded config:");
console.dir(config);
Expand Down
5 changes: 3 additions & 2 deletions src/auth/__tests__/Auth.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { User } from "../../shared/types";
import { testAdmin, testUser } from "../../shared/test-utils";
import { authReducer, CSRF_COOKIE } from "../Auth";
import Cookies from "js-cookie";

describe("authReducer", () => {
let authState: { user: User | null; csrfToken: string | null };

beforeAll(() => {
window.document.cookie = `${CSRF_COOKIE}=csrf`;
Cookies.set(CSRF_COOKIE, 'csrf')
});

beforeEach(() => {
Expand All @@ -22,7 +23,7 @@ describe("authReducer", () => {
});

afterAll(() => {
delete window.document.cookie;
Cookies.remove(CSRF_COOKIE)
});

describe("action:login", () => {
Expand Down
5 changes: 3 additions & 2 deletions src/auth/__tests__/AuthProvider.unit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import { APIResponseFailure, APIError } from "../../shared/types";

// for spies
import * as _a from "../Auth";
import Cookies from "js-cookie";

describe("AuthProvider", () => {
let result: HookResult<AuthContext>;
let auth: AuthContext;

beforeAll(() => {
window.document.cookie = `${CSRF_COOKIE}=csrf`;
Cookies.set(CSRF_COOKIE, 'csrf')
});

beforeEach(() => {
Expand All @@ -36,7 +37,7 @@ describe("AuthProvider", () => {
});

afterAll(() => {
delete window.document.cookie;
Cookies.remove(CSRF_COOKIE)
});

it("provides access to all fields in interface (sanity check)", () => {
Expand Down
39 changes: 0 additions & 39 deletions src/config.ts

This file was deleted.

39 changes: 39 additions & 0 deletions src/config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"baseurl": "http://mathnews.uwaterloo.ca",
"title": "mathNEWS",
"description": "UWaterloo's Bastion of Erudite Thought",
"themeColor": "#000000",
"locale": "en_CA",
"alternateLocales": [],
"icons": {
"favicon": {
"src": "favicon.ico",
"type": "image/x-icon",
"sizes": "64x64 32x32 16x16"
},
"touchIcon": {
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
"largeIcon": {
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
},
"seo": {
"image": "",
"twitter": {
"card": "summary_large_image",
"site": "@UWmathNEWS"
}
},
"manifest": {
"fullName": "",
"start_url": ".",
"display": "standalone",
"backgroundColor": "#ffffff"
},
"googleSiteVerification": ""
}
2 changes: 2 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import config from "./config.json";
export default config;
6 changes: 5 additions & 1 deletion src/dash/EditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ const getEditorRequestState = (
}
};

interface EditorPageRouteParams {
articleId: string
}

const ARTICLE_SAVE_DELAY_MSECS = 10000;

const EditorPage: React.FC<RouteComponentProps> = (props) => {
const { articleId } = useParams();
const { articleId } = useParams<EditorPageRouteParams>();

const [lastSaved, setLastSaved] = useState<Date>(new Date());

Expand Down
6 changes: 5 additions & 1 deletion src/dash/issues/DashIssueDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ const columns: Column<Article>[] = [
},
];

interface DashIssueDetailRouteParams {
issueId: string
}

const DashIssueDetail: React.FC<RouteComponentProps> = (props) => {
const { issueId } = useParams();
const { issueId } = useParams<DashIssueDetailRouteParams>();

const [issue, issueError, issueReqInfo] = useAPI(
useCallback(() => {
Expand Down
5 changes: 1 addition & 4 deletions src/shared/components/ActionLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ import type { LinkProps } from "react-router-dom";
const ActionLink = React.forwardRef<HTMLAnchorElement, LinkProps>(
({ className, children, ...props }, ref) => {
return (
// Link forwards its refs (see
// https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/Link.md#innerref-refobject)
// but this is not recognized by the provided typings, so we must bodge it with a cast
<Link
ref={ref as React.Ref<Link>}
ref={ref}
className={`ActionLink ${className || ""}`}
{...props}
>
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const LinkButton = React.forwardRef<
>(({ variant, className, children, ...props }, ref) => {
return (
<Link
ref={ref as React.Ref<Link>}
ref={ref}
{...(props as LinkProps)}
className={`btn btn-primary Button Button-${variant} ${className || ""}`}
>
Expand Down
4 changes: 3 additions & 1 deletion src/shared/components/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface LoaderProps {
hideFromScreenreaders?: boolean;
}

export default (props: LoaderProps) => {
const Loader: React.FC<LoaderProps> = (props: LoaderProps) => {
if (props.variant === "spinner") {
return (
<div className={`d-flex justify-content-center ${props.className}`}>
Expand All @@ -32,3 +32,5 @@ export default (props: LoaderProps) => {
}
return <></>;
};

export default Loader;
8 changes: 4 additions & 4 deletions src/shared/contexts/ToastContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface ToastManager {
addToasts: (toasts: ToastMessage[]) => void;
}

const ToastManager = createContext<ToastManager>({
const ToastManagerContext = createContext<ToastManager>({
toasts: [],
setToasts: () => {},
setToastsHeuristically: () => {},
Expand Down Expand Up @@ -67,10 +67,10 @@ export const ToastProvider: React.FC = props => {
}).filter(t => t._delay === undefined || t._delay > 100))); // 150ms is the default bootstrap anim duration

return (
<ToastManager.Provider value={{ toasts, setToasts, setToastsHeuristically, addToasts }}>
<ToastManagerContext.Provider value={{ toasts, setToasts, setToastsHeuristically, addToasts }}>
{props.children}
</ToastManager.Provider>
</ToastManagerContext.Provider>
)
};

export const useToast = () => useContext(ToastManager);
export const useToast = () => useContext(ToastManagerContext);
5 changes: 5 additions & 0 deletions src/shared/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const errorsProxyFactory = (errors: { [key: string]: any }): ErrorsProxy => {
return new Proxy(proxiedErrors, {
get(_, prop: string) {
if (prop === "__typeof__") return "proxy";
// HACK: react-refresh scans every export and uses $$typeof to determine
// if it's a React component that it needs to process. Without this, we'll
// throw an error here which breaks initialization.
// TODO: Consider returning undefined instead of throwing an error in the "not found" case.
if (prop === "$$typeof") return undefined;
if (prop.includes(" ")) return prop;
if (prop in memo) return memo[prop];

Expand Down
5 changes: 3 additions & 2 deletions src/shared/form/PasswordField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ export const validatePassword = {
const PasswordField = React.forwardRef<
FormControlElementType,
PasswordFieldProps<any>
>(({ state = useState<boolean>(false), context, ...props }, ref) => {
const [showPassword, setShowPassword] = state;
>(({ state, context, ...props }, ref) => {
const defaultState = useState<boolean>(false)
const [showPassword, setShowPassword] = state ?? defaultState;
return (
<Field
type={showPassword ? "text" : "password"}
Expand Down
15 changes: 11 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
Expand All @@ -13,8 +17,11 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"downlevelIteration": true
"jsx": "react-jsx",
"downlevelIteration": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
"include": [
"src"
]
}

0 comments on commit c36d9f5

Please sign in to comment.