diff --git a/.vscode/settings.json b/.vscode/settings.json index eb7a9947b2..65feac3c69 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,7 @@ "piptools", "Prenoun", "Preverb", + "recaptcha", "reportgenerator", "sched", "sillsdev", diff --git a/package.json b/package.json index 433e9ff515..948c50c354 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,7 @@ }, "eslintConfig": { "extends": [ + "plugin:@typescript-eslint/recommended", "plugin:react/recommended", "plugin:react/jsx-runtime", "plugin:react-hooks/recommended", @@ -142,8 +143,9 @@ ], "rules": { "@typescript-eslint/no-empty-interface": "warn", + "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-inferrable-types": "warn", - "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/switch-exhaustiveness-check": "warn", "import/first": "warn", "import/newline-after-import": "warn", @@ -173,9 +175,8 @@ ], "no-undef": "off", "prefer-const": "warn", - "react/display-name": "off", "react/jsx-boolean-value": "warn", - "unused-imports/no-unused-imports": "error" + "unused-imports/no-unused-imports": "warn" }, "parser": "@typescript-eslint/parser", "parserOptions": { @@ -189,6 +190,7 @@ "react", "unused-imports" ], + "root": true, "settings": { "react": { "version": "detect" diff --git a/src/components/App/App.test.tsx b/src/components/App/App.test.tsx index c53bf846be..488de0d03a 100644 --- a/src/components/App/App.test.tsx +++ b/src/components/App/App.test.tsx @@ -1,6 +1,6 @@ import "jest-canvas-mock"; import { Provider } from "react-redux"; -import renderer from "react-test-renderer"; +import { act, create } from "react-test-renderer"; import configureMockStore from "redux-mock-store"; import thunk from "redux-thunk"; @@ -9,9 +9,13 @@ import "tests/reactI18nextMock"; import { defaultState } from "components/App/DefaultState"; import App from "components/App/component"; -jest.mock("@matt-block/react-recaptcha-v2", () => () => ( -
Recaptcha
-)); +jest.mock( + "@matt-block/react-recaptcha-v2", + () => + function MockRecaptcha() { + return
Recaptcha
; + } +); jest.mock("components/AnnouncementBanner/AnnouncementBanner", () => "div"); const createMockStore = configureMockStore([thunk]); @@ -23,9 +27,9 @@ global.innerHeight = 100; global.analytics = { track: jest.fn() } as any; describe("App", () => { - it("renders without crashing", () => { - renderer.act(() => { - renderer.create( + it("renders without crashing", async () => { + await act(async () => { + create( diff --git a/src/components/AppBar/UserMenu.tsx b/src/components/AppBar/UserMenu.tsx index 1a256d6db5..1eea6f9426 100644 --- a/src/components/AppBar/UserMenu.tsx +++ b/src/components/AppBar/UserMenu.tsx @@ -105,21 +105,12 @@ export default function UserMenu(props: TabProps): ReactElement { open={Boolean(anchorElement)} transformOrigin={{ horizontal: "right", vertical: "top" }} > - + ); } -// automatically applies a ref to its first child for anchoring. The -// following prevents a console warning: "Function components cannot be given refs. -// Attempts to access this ref will fail. Did you mean to use React.forwardRef()?" -const WrappedUserMenuList = React.forwardRef( - (props: React.ComponentProps, ref) => ( - - ) -); - interface UserMenuListProps { isAdmin: boolean; onSelect: () => void; diff --git a/src/components/AppBar/tests/AppBarComponent.test.tsx b/src/components/AppBar/tests/AppBarComponent.test.tsx index a156888354..b58fc64f49 100644 --- a/src/components/AppBar/tests/AppBarComponent.test.tsx +++ b/src/components/AppBar/tests/AppBarComponent.test.tsx @@ -1,6 +1,6 @@ import { Provider } from "react-redux"; import { MemoryRouter } from "react-router-dom"; -import renderer from "react-test-renderer"; +import { act, create } from "react-test-renderer"; import configureMockStore from "redux-mock-store"; import "tests/reactI18nextMock"; @@ -18,8 +18,6 @@ jest.mock("backend", () => ({ const mockStore = configureMockStore()(defaultState); -let testRenderer: renderer.ReactTestRenderer; - function setMockFunctions() { mockGetUser.mockResolvedValue(mockUser); } @@ -30,9 +28,9 @@ beforeAll(() => { }); describe("AppBar", () => { - it("renders", () => { - renderer.act(() => { - testRenderer = renderer.create( + it("renders", async () => { + await act(async () => { + create( diff --git a/src/components/DataEntry/DataEntryTable/EntryCellComponents/VernWithSuggestions.tsx b/src/components/DataEntry/DataEntryTable/EntryCellComponents/VernWithSuggestions.tsx index 9d67cb6e01..499240921d 100644 --- a/src/components/DataEntry/DataEntryTable/EntryCellComponents/VernWithSuggestions.tsx +++ b/src/components/DataEntry/DataEntryTable/EntryCellComponents/VernWithSuggestions.tsx @@ -12,7 +12,7 @@ interface VernWithSuggestionsProps { vernInput?: React.RefObject; updateVernField: (newValue: string, openDialog?: boolean) => void; onBlur: () => void; - onClose?: (e: React.ChangeEvent<{}>, reason: AutocompleteCloseReason) => void; + onClose?: (e: React.SyntheticEvent, reason: AutocompleteCloseReason) => void; suggestedVerns?: string[]; handleEnter: () => void; vernacularLang: WritingSystem; diff --git a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx index 3154bc158a..c10380ebb4 100644 --- a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx +++ b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx @@ -1,6 +1,11 @@ import { ReactElement } from "react"; import { Provider } from "react-redux"; -import renderer from "react-test-renderer"; +import { + ReactTestInstance, + ReactTestRenderer, + act, + create, +} from "react-test-renderer"; import configureMockStore from "redux-mock-store"; import "tests/reactI18nextMock"; @@ -17,7 +22,6 @@ import DataEntryTable, { updateEntryGloss, } from "components/DataEntry/DataEntryTable"; import NewEntry from "components/DataEntry/DataEntryTable/NewEntry"; -import { RecentEntryProps } from "components/DataEntry/DataEntryTable/RecentEntry"; import { newProject } from "types/project"; import { newSemanticDomain, @@ -57,10 +61,10 @@ jest.mock("utilities/utilities"); jest.spyOn(window, "alert").mockImplementation(() => {}); -let testRenderer: renderer.ReactTestRenderer; -let testHandle: renderer.ReactTestInstance; +let testRenderer: ReactTestRenderer; +let testHandle: ReactTestInstance; -function MockRecentEntry(props: RecentEntryProps): ReactElement { +function MockRecentEntry(): ReactElement { return
; } @@ -97,8 +101,8 @@ beforeEach(() => { }); const renderTable = async (): Promise => { - await renderer.act(async () => { - testRenderer = renderer.create( + await act(async () => { + testRenderer = create( => { } mockCreateWord.mockResolvedValueOnce(word); mockGetWord.mockResolvedValueOnce(word); - await renderer.act(async () => { + await act(async () => { await testRenderer.root.findByType(NewEntry).props.addNewEntry(); }); return word.id; @@ -140,7 +144,7 @@ describe("DataEntryTable", () => { it("hides questions", async () => { expect(mockHideQuestions).not.toBeCalled(); testHandle = testRenderer.root.findByProps({ id: exitButtonId }); - await renderer.act(async () => await testHandle.props.onClick()); + await act(async () => await testHandle.props.onClick()); expect(mockHideQuestions).toBeCalled(); }); @@ -149,9 +153,9 @@ describe("DataEntryTable", () => { testHandle = testRenderer.root.findByType(NewEntry); expect(testHandle).not.toBeNull; // Set newVern but not newGloss. - await renderer.act(async () => testHandle.props.setNewVern("hasVern")); + await act(async () => testHandle.props.setNewVern("hasVern")); testHandle = testRenderer.root.findByProps({ id: exitButtonId }); - await renderer.act(async () => await testHandle.props.onClick()); + await act(async () => await testHandle.props.onClick()); expect(mockCreateWord).toBeCalledTimes(1); }); @@ -159,16 +163,16 @@ describe("DataEntryTable", () => { testHandle = testRenderer.root.findByType(NewEntry); expect(testHandle).not.toBeNull; // Set newGloss but not newVern. - await renderer.act(async () => testHandle.props.setNewGloss("hasGloss")); + await act(async () => testHandle.props.setNewGloss("hasGloss")); testHandle = testRenderer.root.findByProps({ id: exitButtonId }); - await renderer.act(async () => await testHandle.props.onClick()); + await act(async () => await testHandle.props.onClick()); expect(mockCreateWord).not.toBeCalled(); }); it("opens the domain tree", async () => { expect(mockOpenTree).not.toBeCalled(); testHandle = testRenderer.root.findByProps({ id: exitButtonId }); - await renderer.act(async () => await testHandle.props.onClick()); + await act(async () => await testHandle.props.onClick()); expect(mockOpenTree).toBeCalledTimes(1); }); }); @@ -293,7 +297,7 @@ describe("DataEntryTable", () => { mockGetFrontierWords.mockResolvedValue([word]); await renderTable(); testHandle = testRenderer.root.findByType(NewEntry); - await renderer.act(async () => { + await act(async () => { await testHandle.props.setNewGloss(firstGlossText(word.senses[0])); await testHandle.props.updateWordWithNewGloss(word.id); }); @@ -312,7 +316,7 @@ describe("DataEntryTable", () => { testHandle = testRenderer.root.findByType(NewEntry); const glossText = firstGlossText(word.senses[senseIndex]); - await renderer.act(async () => { + await act(async () => { await testHandle.props.setNewGloss(glossText); await testHandle.props.updateWordWithNewGloss(word.id); }); @@ -330,7 +334,7 @@ describe("DataEntryTable", () => { it("updates word in backend if gloss doesn't exist", async () => { await renderTable(); testHandle = testRenderer.root.findByType(NewEntry); - await renderer.act(async () => { + await act(async () => { await testHandle.props.setNewGloss("differentGloss"); await testHandle.props.updateWordWithNewGloss(mockMultiWord.id); }); @@ -344,7 +348,7 @@ describe("DataEntryTable", () => { it("checks for duplicate and, if so, updates it", async () => { testHandle = testRenderer.root.findByType(NewEntry); mockGetDuplicateId.mockResolvedValueOnce(true); - await renderer.act(async () => { + await act(async () => { await testHandle.props.addNewEntry(); }); expect(mockUpdateDuplicate).toBeCalledTimes(1); @@ -368,7 +372,7 @@ describe("DataEntryTable", () => { // Verify that the number of recent entries increases by the correct amount expect(testRenderer.root.findAllByType(MockRecentEntry)).toHaveLength(0); - await renderer.act(async () => { + await act(async () => { await testRenderer.root.findByType(NewEntry).props.addNewEntry(); }); expect(testRenderer.root.findAllByType(MockRecentEntry)).toHaveLength( @@ -383,14 +387,14 @@ describe("DataEntryTable", () => { const vern = "vern"; const glossDef = "gloss"; const noteText = "note"; - renderer.act(() => { + act(() => { testHandle.props.setNewVern(vern); testHandle.props.setNewGloss(glossDef); testHandle.props.setNewNote(noteText); }); // Trigger the function to add a new entry - await renderer.act(async () => { + await act(async () => { try { await testHandle.props.addNewEntry(); } catch { @@ -426,7 +430,7 @@ describe("DataEntryTable", () => { it("removes a recent entry", async () => { await addRecentEntry(); const recentEntry = testRenderer.root.findByType(MockRecentEntry); - await renderer.act(async () => { + await act(async () => { await recentEntry.props.removeEntry(); }); expect(testRenderer.root.findAllByType(MockRecentEntry)).toHaveLength(0); @@ -449,7 +453,7 @@ describe("DataEntryTable", () => { // Update the vernacular const newVern = "not the vern generated in addRecentEntry"; - await renderer.act(async () => { + await act(async () => { await recentEntry.props.updateVern(newVern); }); diff --git a/src/components/Login/LoginPage/LoginComponent.tsx b/src/components/Login/LoginPage/LoginComponent.tsx index 393864100b..f9b02ef6be 100644 --- a/src/components/Login/LoginPage/LoginComponent.tsx +++ b/src/components/Login/LoginPage/LoginComponent.tsx @@ -9,7 +9,7 @@ import { TextField, Typography, } from "@mui/material"; -import React from "react"; +import { Component } from "react"; import { withTranslation, WithTranslation } from "react-i18next"; import { BannerType } from "api/models"; @@ -57,7 +57,7 @@ interface LoginError { } /** The login page (also doubles as a logout page) */ -export class Login extends React.Component { +export class Login extends Component { constructor(props: LoginProps) { super(props); this.props.logout(); // Loading this page will reset the app, both store and localStorage diff --git a/src/components/Login/LoginPage/tests/LoginComponent.test.tsx b/src/components/Login/LoginPage/tests/LoginComponent.test.tsx index 872177118f..2fbfd09627 100644 --- a/src/components/Login/LoginPage/tests/LoginComponent.test.tsx +++ b/src/components/Login/LoginPage/tests/LoginComponent.test.tsx @@ -1,28 +1,37 @@ -import renderer from "react-test-renderer"; +import { + ReactTestInstance, + ReactTestRenderer, + act, + create, +} from "react-test-renderer"; import "tests/reactI18nextMock"; import Login from "components/Login/LoginPage/LoginComponent"; -jest.mock("@matt-block/react-recaptcha-v2", () => () => ( -
Recaptcha
-)); +jest.mock( + "@matt-block/react-recaptcha-v2", + () => + function MockRecaptcha() { + return
Recaptcha
; + } +); jest.mock("backend", () => ({ getBannerText: () => Promise.resolve(""), })); jest.mock("browserRouter"); const LOGOUT = jest.fn(); -let loginMaster: renderer.ReactTestRenderer; -let loginHandle: renderer.ReactTestInstance; +let loginMaster: ReactTestRenderer; +let loginHandle: ReactTestInstance; const DATA = "stuff"; const MOCK_EVENT = { preventDefault: jest.fn(), target: { value: DATA } }; describe("Testing login component", () => { - beforeEach(() => { - renderer.act(() => { - loginMaster = renderer.create(); + beforeEach(async () => { + await act(async () => { + loginMaster = create(); }); loginHandle = loginMaster.root.findByType(Login); LOGOUT.mockClear(); diff --git a/src/components/Login/SignUpPage/SignUpComponent.tsx b/src/components/Login/SignUpPage/SignUpComponent.tsx index 7dbc46109a..5a7903f5ab 100644 --- a/src/components/Login/SignUpPage/SignUpComponent.tsx +++ b/src/components/Login/SignUpPage/SignUpComponent.tsx @@ -7,7 +7,7 @@ import { TextField, Typography, } from "@mui/material"; -import React from "react"; +import { Component } from "react"; import { withTranslation, WithTranslation } from "react-i18next"; import router from "browserRouter"; @@ -22,6 +22,7 @@ import { // Chrome silently converts non-ASCII characters in a Textfield of type="email". // Use punycode.toUnicode() to convert them from punycode back to Unicode. +// eslint-disable-next-line @typescript-eslint/no-var-requires const punycode = require("punycode/"); const idAffix = "signUp"; @@ -65,7 +66,7 @@ interface SignUpState { }; } -export class SignUp extends React.Component { +export class SignUp extends Component { constructor(props: SignUpProps) { super(props); this.state = { diff --git a/src/components/Login/SignUpPage/tests/SignUpComponent.test.tsx b/src/components/Login/SignUpPage/tests/SignUpComponent.test.tsx index 6ab05dc941..cb092f7566 100644 --- a/src/components/Login/SignUpPage/tests/SignUpComponent.test.tsx +++ b/src/components/Login/SignUpPage/tests/SignUpComponent.test.tsx @@ -1,27 +1,34 @@ -import renderer from "react-test-renderer"; +import { + ReactTestInstance, + ReactTestRenderer, + act, + create, +} from "react-test-renderer"; import "tests/reactI18nextMock"; import SignUp from "components/Login/SignUpPage/SignUpComponent"; -jest.mock("@matt-block/react-recaptcha-v2", () => () => ( -
Recaptcha
-)); +jest.mock( + "@matt-block/react-recaptcha-v2", + () => + function MockRecaptcha() { + return
Recaptcha
; + } +); jest.mock("browserRouter"); const mockReset = jest.fn(); -let signUpMaster: renderer.ReactTestRenderer; -let signUpHandle: renderer.ReactTestInstance; +let signUpMaster: ReactTestRenderer; +let signUpHandle: ReactTestInstance; const DATA = "stuff"; const MOCK_EVENT = { preventDefault: jest.fn(), target: { value: DATA } }; describe("Testing sign up component", () => { - beforeEach(() => { - renderer.act(() => { - signUpMaster = renderer.create( - - ); + beforeEach(async () => { + await act(async () => { + signUpMaster = create(); }); signUpHandle = signUpMaster.root.findByType(SignUp); mockReset.mockClear(); diff --git a/src/components/ProjectSettings/tests/index.test.tsx b/src/components/ProjectSettings/tests/index.test.tsx index 8b50056682..7147ac151d 100644 --- a/src/components/ProjectSettings/tests/index.test.tsx +++ b/src/components/ProjectSettings/tests/index.test.tsx @@ -1,7 +1,7 @@ import "@testing-library/jest-dom"; import { act, cleanup, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import mediaQuery from "css-mediaquery"; +import { match } from "css-mediaquery"; import { Provider } from "react-redux"; import configureMockStore from "redux-mock-store"; @@ -73,7 +73,7 @@ const createMatchMedia = ( ): ((query: string) => MediaQueryList) => { return (query: string) => ({ - matches: mediaQuery.match(query, { width }), + matches: match(query, { width }), addListener: jest.fn(), removeListener: jest.fn(), }) as any; diff --git a/src/components/Pronunciations/Recorder.ts b/src/components/Pronunciations/Recorder.ts index 79b0064f10..7c324361c4 100644 --- a/src/components/Pronunciations/Recorder.ts +++ b/src/components/Pronunciations/Recorder.ts @@ -4,7 +4,7 @@ export default class Recorder { private toast: (text: string) => void; private recordRTC?: RecordRTC; - static blobType: "audio" = "audio"; + static blobType: RecordRTC.Options["type"] = "audio"; constructor(toast?: (text: string) => void) { this.toast = toast ?? ((text: string) => alert(text)); diff --git a/src/components/Statistics/UserStatistics.tsx b/src/components/Statistics/UserStatistics.tsx index 39eaaf3f65..93a1ec9965 100644 --- a/src/components/Statistics/UserStatistics.tsx +++ b/src/components/Statistics/UserStatistics.tsx @@ -1,6 +1,5 @@ import { Card, Grid, ListItem, List } from "@mui/material"; import { ReactElement, useState, useEffect } from "react"; -import { useTranslation } from "react-i18next"; import { SemanticDomainUserCount } from "api/models"; import { getSemanticDomainUserCount } from "backend"; @@ -17,7 +16,6 @@ export default function UserStatistics( const [domainUserCountList, setDomainUserCountList] = useState< SemanticDomainUserCount[] >([]); - const { t } = useTranslation(); useEffect(() => { const updateSemanticDomainUserCounts = async () => { diff --git a/src/components/TreeView/tests/SemanticDomainMock.ts b/src/components/TreeView/tests/SemanticDomainMock.ts index 052be6725a..e79f5a5657 100644 --- a/src/components/TreeView/tests/SemanticDomainMock.ts +++ b/src/components/TreeView/tests/SemanticDomainMock.ts @@ -4,6 +4,7 @@ import { semDomFromTreeNode, } from "types/semanticDomain"; +/* eslint-disable @typescript-eslint/no-duplicate-enum-values */ export enum mapIds { "head" = "Sem", "parent" = "1", @@ -17,6 +18,7 @@ export enum mapIds { "depth4" = "1.2.1.1", "depth5" = "1.2.1.1.1", } +/* eslint-enable @typescript-eslint/no-duplicate-enum-values */ const nodeMap: TreeNodeMap = {}; diff --git a/src/components/TreeView/tests/TreeSearch.test.tsx b/src/components/TreeView/tests/TreeSearch.test.tsx index a42852237f..73e2edde19 100644 --- a/src/components/TreeView/tests/TreeSearch.test.tsx +++ b/src/components/TreeView/tests/TreeSearch.test.tsx @@ -18,7 +18,7 @@ import domMap, { mapIds } from "components/TreeView/tests/SemanticDomainMock"; import { newSemanticDomainTreeNode } from "types/semanticDomain"; // Handles -const MOCK_ANIMATE = jest.fn((_domain: SemanticDomainTreeNode) => { +const MOCK_ANIMATE = jest.fn(() => { console.log("MockAnimateCalled"); return Promise.resolve(); }); diff --git a/src/components/UserSettings/ClickableAvatar.tsx b/src/components/UserSettings/ClickableAvatar.tsx index 91eed63d06..30c037bdd1 100644 --- a/src/components/UserSettings/ClickableAvatar.tsx +++ b/src/components/UserSettings/ClickableAvatar.tsx @@ -6,7 +6,7 @@ import { ReactElement, useState } from "react"; import { getAvatar } from "backend/localStorage"; import AvatarUpload from "components/UserSettings/AvatarUpload"; -const clickableAvatarClassProps: Styles = { +const clickableAvatarClassProps: Styles = { avatar: { width: 60, height: 60 }, avatarOverlay: { transition: "opacity 0.2s", diff --git a/src/components/UserSettings/UserSettings.tsx b/src/components/UserSettings/UserSettings.tsx index e96461b01b..c27e1631b6 100644 --- a/src/components/UserSettings/UserSettings.tsx +++ b/src/components/UserSettings/UserSettings.tsx @@ -23,6 +23,7 @@ import { uiWritingSystems } from "types/writingSystem"; // Chrome silently converts non-ASCII characters in a Textfield of type="email". // Use punycode.toUnicode() to convert them from punycode back to Unicode. +// eslint-disable-next-line @typescript-eslint/no-var-requires const punycode = require("punycode/"); export enum UserSettingsIds { diff --git a/src/components/UserSettings/tests/UserSettings.test.tsx b/src/components/UserSettings/tests/UserSettings.test.tsx index c9f6c9dad7..849eec1d00 100644 --- a/src/components/UserSettings/tests/UserSettings.test.tsx +++ b/src/components/UserSettings/tests/UserSettings.test.tsx @@ -48,7 +48,7 @@ const setupMocks = (): void => { mockGetAvatar.mockReturnValue(""); mockGetCurrentUser.mockReturnValue(mockUser()); mockIsEmailTaken.mockResolvedValue(false); - mockSetUser.mockImplementation(async (user?: User) => {}); + mockSetUser.mockImplementation(async () => {}); mockUpdateUser.mockImplementation((user: User) => user); }; diff --git a/src/goals/MergeDuplicates/MergeDupsContinueDialog.tsx b/src/goals/MergeDuplicates/MergeDupsContinueDialog.tsx index 3f99617c6d..e0a55d2668 100644 --- a/src/goals/MergeDuplicates/MergeDupsContinueDialog.tsx +++ b/src/goals/MergeDuplicates/MergeDupsContinueDialog.tsx @@ -1,5 +1,5 @@ import { Button, Dialog, DialogActions, DialogTitle } from "@mui/material"; -import React, { ReactElement } from "react"; +import { ReactElement, useState } from "react"; import { useTranslation } from "react-i18next"; export interface MergeDupsContinueDialogProps { @@ -9,7 +9,7 @@ export interface MergeDupsContinueDialogProps { export default function MergeDupsContinueDialog( props: MergeDupsContinueDialogProps ): ReactElement { - const [open, setOpen] = React.useState(true); + const [open, setOpen] = useState(true); const { t } = useTranslation(); const { onSelection } = props; diff --git a/src/goals/MergeDuplicates/Redux/tests/MergeDupsReducer.test.tsx b/src/goals/MergeDuplicates/Redux/tests/MergeDupsReducer.test.tsx index 67ad92c75d..c2cef9ebf8 100644 --- a/src/goals/MergeDuplicates/Redux/tests/MergeDupsReducer.test.tsx +++ b/src/goals/MergeDuplicates/Redux/tests/MergeDupsReducer.test.tsx @@ -20,6 +20,7 @@ import { Hash } from "types/hash"; import { newFlag, testWordList } from "types/word"; jest.mock("uuid"); +// eslint-disable-next-line @typescript-eslint/no-var-requires const mockUuid = require("uuid") as { v4: jest.Mock }; let uuidIndex = 0; diff --git a/src/goals/ReviewEntries/ReviewEntriesComponent/ReviewEntriesTable.tsx b/src/goals/ReviewEntries/ReviewEntriesComponent/ReviewEntriesTable.tsx index a50b197069..9a6908a235 100644 --- a/src/goals/ReviewEntries/ReviewEntriesComponent/ReviewEntriesTable.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesComponent/ReviewEntriesTable.tsx @@ -1,7 +1,7 @@ import MaterialTable from "@material-table/core"; import { Typography } from "@mui/material"; import { enqueueSnackbar } from "notistack"; -import React, { ReactElement, useEffect, useState } from "react"; +import React, { ReactElement, createRef, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; @@ -40,7 +40,7 @@ function getPageState(wordCount: number): PageState { // Constants const ROWS_PER_PAGE = [10, 50, 200]; -const tableRef: React.RefObject = React.createRef(); +const tableRef: React.RefObject = createRef(); export default function ReviewEntriesTable( props: ReviewEntriesTableProps diff --git a/src/goals/ReviewEntries/ReviewEntriesComponent/icons.tsx b/src/goals/ReviewEntries/ReviewEntriesComponent/icons.tsx index fbcfce9a62..4b03847b96 100644 --- a/src/goals/ReviewEntries/ReviewEntriesComponent/icons.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesComponent/icons.tsx @@ -15,6 +15,7 @@ import Search from "@mui/icons-material/Search"; import ViewColumn from "@mui/icons-material/ViewColumn"; import { forwardRef, Ref } from "react"; +/* eslint-disable react/display-name */ const tableIcons = { Add: forwardRef((props: any, ref: Ref) => ( diff --git a/src/index.tsx b/src/index.tsx index 0a42b5b3fd..0d68aa650e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,6 @@ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles"; import { SnackbarProvider } from "notistack"; -import ReactDOM from "react-dom"; +import { render } from "react-dom"; import { Provider } from "react-redux"; import { PersistGate } from "redux-persist/integration/react"; @@ -10,7 +10,7 @@ import { persistor, store } from "store"; import theme from "types/theme"; //Provider connects store to component containers -ReactDOM.render( +render( diff --git a/src/types/goals.ts b/src/types/goals.ts index faf7d30818..0a7e995780 100644 --- a/src/types/goals.ts +++ b/src/types/goals.ts @@ -14,7 +14,8 @@ import { import { newUser } from "types/user"; export type GoalData = CharInvData | MergeDupsData; -export type GoalStep = CharInvStepData | MergeStepData | {}; +// Record is the recommended type for an empty object. +export type GoalStep = CharInvStepData | MergeStepData | Record; export type GoalChanges = CharInvChanges | MergesCompleted; export interface GoalProps { diff --git a/src/types/writingSystem.ts b/src/types/writingSystem.ts index eb42c0897d..31182e6e4b 100644 --- a/src/types/writingSystem.ts +++ b/src/types/writingSystem.ts @@ -3,6 +3,7 @@ import { WritingSystem } from "api/models"; export enum Bcp47Code { Default = "en", Ar = "ar", // Arabic + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values En = "en", // English Es = "es", // Spanish Fr = "fr", // French diff --git a/src/utilities/dictionaryLoader.ts b/src/utilities/dictionaryLoader.ts index 1076a41d9f..fb4c2a89b8 100644 --- a/src/utilities/dictionaryLoader.ts +++ b/src/utilities/dictionaryLoader.ts @@ -35,7 +35,7 @@ export default class DictionaryLoader { .toLocaleLowerCase() .split("") .map((c) => c.charCodeAt(0)); - var key = ""; + let key = ""; while (true) { key = charCodes.join("-"); if (!key || this.keys.includes(key)) { diff --git a/src/utilities/fontCssUtilities.ts b/src/utilities/fontCssUtilities.ts index 83d3100989..7823aed50f 100644 --- a/src/utilities/fontCssUtilities.ts +++ b/src/utilities/fontCssUtilities.ts @@ -31,7 +31,7 @@ export async function fetchCss( source: string, substitute?: string ): Promise { - var cssUrl = ""; + let cssUrl = ""; switch (source) { case "local": cssUrl = `${fontDir}/${font.replace(" ", "")}.css`; diff --git a/src/utilities/testUtilities.tsx b/src/utilities/testUtilities.tsx index 46093b4b0b..dde3275f03 100644 --- a/src/utilities/testUtilities.tsx +++ b/src/utilities/testUtilities.tsx @@ -1,5 +1,5 @@ import { render } from "@testing-library/react"; -import React, { PropsWithChildren } from "react"; +import { PropsWithChildren, ReactElement } from "react"; import { Provider } from "react-redux"; import { PersistGate } from "redux-persist/integration/react"; @@ -22,7 +22,7 @@ interface ExtendedRenderOptions extends Omit { } export function renderWithProviders( - ui: React.ReactElement, + ui: ReactElement, { preloadedState = { ...defaultState, @@ -33,7 +33,7 @@ export function renderWithProviders( ...renderOptions }: ExtendedRenderOptions = {} ) { - function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element { + function Wrapper({ children }: PropsWithChildren): JSX.Element { return ( {children}