diff --git a/package.json b/package.json index 8c499c1df9..0fa9db28c1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "flexboxgrid": "^6.3.1", "flexboxgrid-helpers": "^1.1.3", "js-slang": "^1.0.23", - "konva": "^8.3.14", + "konva": "^9.2.0", "lodash": "^4.17.21", "lz-string": "^1.4.4", "moment": "^2.29.4", @@ -56,14 +56,14 @@ "phaser": "^3.55.2", "query-string": "^7.1.1", "re-resizable": "^6.9.9", - "react": "^17.0.2", + "react": "^18.2.0", "react-ace": "^10.1.0", "react-copy-to-clipboard": "^5.1.0", - "react-dom": "^17.0.2", + "react-dom": "^18.2.0", "react-draggable": "^4.4.5", "react-dropzone": "^14.2.3", "react-hotkeys": "^2.0.0", - "react-konva": "^17.0.2-5", + "react-konva": "^18.2.10", "react-latex-next": "^2.1.0", "react-mde": "^11.5.0", "react-papaparse": "^4.0.2", @@ -89,8 +89,8 @@ "@craco/craco": "^7.1.0", "@svgr/webpack": "^6.3.1", "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "12.1.2", - "@testing-library/user-event": "^14.3.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", "@types/acorn": "^6.0.0", "@types/gapi": "^0.0.44", "@types/gapi.auth2": "^0.0.57", @@ -100,15 +100,15 @@ "@types/jest": "^28.1.6", "@types/lodash": "^4.14.195", "@types/lz-string": "^1.3.34", - "@types/react": "^17.0.38", + "@types/react": "^18.2.13", "@types/react-copy-to-clipboard": "^5.0.4", - "@types/react-dom": "^17.0.20", + "@types/react-dom": "^18.2.6", "@types/react-redux": "^7.1.24", "@types/react-responsive": "^8.0.5", "@types/react-router": "^5.1.20", "@types/react-router-dom": "^5.3.3", "@types/react-syntax-highlighter": "^15.5.7", - "@types/react-test-renderer": "^17.0.2", + "@types/react-test-renderer": "^18.0.0", "@types/react-textarea-autosize": "^8.0.0", "@types/redux-mock-store": "^1.0.3", "@types/showdown": "^2.0.1", @@ -130,7 +130,7 @@ "process": "^0.11.10", "react-error-overlay": "^6.0.11", "react-scripts": "^5.0.1", - "react-test-renderer": "^17.0.2", + "react-test-renderer": "^18.2.0", "redux-saga-test-plan": "^4.0.6", "resize-observer-polyfill": "^1.5.1", "sass": "^1.62.1", @@ -141,10 +141,6 @@ "url": "^0.11.0", "webpack-bundle-analyzer": "^4.9.0" }, - "resolutions": { - "//": "React 18's types are much stricter and upgrading to it (even if indirectly via a dependency) results in many type errors. FIXME: Remove this after upgrading fully to React 18 and fixing all of the type errors.", - "@types/react": "^17.0.38" - }, "browserslist": { "production": [ "Firefox ESR", diff --git a/src/commons/ContentDisplay.tsx b/src/commons/ContentDisplay.tsx index fbdee7be0d..30f77584f7 100644 --- a/src/commons/ContentDisplay.tsx +++ b/src/commons/ContentDisplay.tsx @@ -8,8 +8,10 @@ export type ContentDisplayProps = { }; const ContentDisplay: React.FC = props => { - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(() => props.loadContentDispatch?.(), []); + useEffect(() => { + props.loadContentDispatch?.(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return (
diff --git a/src/commons/achievement/AchievementCommentCard.tsx b/src/commons/achievement/AchievementCommentCard.tsx index 2bac0a8f06..7ad0975e38 100644 --- a/src/commons/achievement/AchievementCommentCard.tsx +++ b/src/commons/achievement/AchievementCommentCard.tsx @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router'; import { Assessment } from '../assessment/AssessmentTypes'; import { useTypedSelector } from '../utils/Hooks'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { assessmentTypeLink } from '../utils/ParamParseHelper'; const AchievementCommentCard = ({ diff --git a/src/commons/achievement/AchievementManualEditor.tsx b/src/commons/achievement/AchievementManualEditor.tsx index 19413f8c12..bde34b4ead 100644 --- a/src/commons/achievement/AchievementManualEditor.tsx +++ b/src/commons/achievement/AchievementManualEditor.tsx @@ -8,7 +8,7 @@ import { GoalProgress } from 'src/features/achievement/AchievementTypes'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; type AchievementManualEditorProps = { hiddenState: [boolean, any]; diff --git a/src/commons/achievement/control/achievementEditor/AchievementUuidCopier.tsx b/src/commons/achievement/control/achievementEditor/AchievementUuidCopier.tsx index 97f5321b6a..3b636a4ee0 100644 --- a/src/commons/achievement/control/achievementEditor/AchievementUuidCopier.tsx +++ b/src/commons/achievement/control/achievementEditor/AchievementUuidCopier.tsx @@ -1,7 +1,7 @@ import { Button } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Tooltip2 } from '@blueprintjs/popover2'; -import { showSuccessMessage } from 'src/commons/utils/NotificationsHelper'; +import { showSuccessMessage } from 'src/commons/utils/notifications/NotificationsHelper'; type AchievementUuidCopierProps = { uuid: string; diff --git a/src/commons/achievement/control/common/ItemSaver.tsx b/src/commons/achievement/control/common/ItemSaver.tsx index 2a11ef9967..3505f7ee1d 100644 --- a/src/commons/achievement/control/common/ItemSaver.tsx +++ b/src/commons/achievement/control/common/ItemSaver.tsx @@ -1,7 +1,10 @@ import { Button } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { Tooltip2 } from '@blueprintjs/popover2'; -import { showSuccessMessage, showWarningMessage } from 'src/commons/utils/NotificationsHelper'; +import { + showSuccessMessage, + showWarningMessage +} from 'src/commons/utils/notifications/NotificationsHelper'; type ItemSaverProps = { discardChanges: () => void; diff --git a/src/commons/achievement/utils/AchievementInferencer.ts b/src/commons/achievement/utils/AchievementInferencer.ts index 7e1b70867c..f83478ac7d 100644 --- a/src/commons/achievement/utils/AchievementInferencer.ts +++ b/src/commons/achievement/utils/AchievementInferencer.ts @@ -10,7 +10,7 @@ import { GoalProgress, GoalType } from '../../../features/achievement/AchievementTypes'; -import { showDangerMessage } from '../../utils/NotificationsHelper'; +import { showDangerMessage } from '../../utils/notifications/NotificationsHelper'; import { isExpired, isReleased } from './DateHelper'; /** diff --git a/src/commons/achievement/utils/EventHandler.ts b/src/commons/achievement/utils/EventHandler.ts index 0269f93066..b2673650f1 100644 --- a/src/commons/achievement/utils/EventHandler.ts +++ b/src/commons/achievement/utils/EventHandler.ts @@ -6,7 +6,7 @@ import { GoalProgress, GoalType } from '../../../features/achievement/AchievementTypes'; -import { showSuccessMessage } from '../../utils/NotificationsHelper'; +import { showSuccessMessage } from '../../utils/notifications/NotificationsHelper'; import AchievementInferencer from './AchievementInferencer'; import { isExpired, isReleased, isWithinTimeRange } from './DateHelper'; diff --git a/src/commons/assessment/__tests__/__snapshots__/Assessment.tsx.snap b/src/commons/assessment/__tests__/__snapshots__/Assessment.tsx.snap index 999cc77446..86e6ef8772 100644 --- a/src/commons/assessment/__tests__/__snapshots__/Assessment.tsx.snap +++ b/src/commons/assessment/__tests__/__snapshots__/Assessment.tsx.snap @@ -1091,9 +1091,7 @@ exports[`Assessment page with multiple loaded missions renders correctly 1`] = ` - - + /> diff --git a/src/commons/controlBar/ControlBarSessionButton.tsx b/src/commons/controlBar/ControlBarSessionButton.tsx index 9043a0f9c3..46d487110c 100644 --- a/src/commons/controlBar/ControlBarSessionButton.tsx +++ b/src/commons/controlBar/ControlBarSessionButton.tsx @@ -6,7 +6,7 @@ import * as CopyToClipboard from 'react-copy-to-clipboard'; import { checkSessionIdExists, createNewSession } from '../collabEditing/CollabEditingHelper'; import ControlButton from '../ControlButton'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; type ControlBarSessionButtonsProps = DispatchProps & StateProps; diff --git a/src/commons/controlBar/github/ControlBarTaskAddButton.tsx b/src/commons/controlBar/github/ControlBarTaskAddButton.tsx index 3c211c89a6..edb49a79a6 100644 --- a/src/commons/controlBar/github/ControlBarTaskAddButton.tsx +++ b/src/commons/controlBar/github/ControlBarTaskAddButton.tsx @@ -2,7 +2,7 @@ import { IconNames } from '@blueprintjs/icons'; import ControlButton from '../../ControlButton'; import { maximumTasksPerMission } from '../../githubAssessments/GitHubMissionDataUtils'; -import { showWarningMessage } from '../../utils/NotificationsHelper'; +import { showWarningMessage } from '../../utils/notifications/NotificationsHelper'; export type ControlBarTaskAddButtonProps = { addNewQuestion: () => void; diff --git a/src/commons/controlBar/github/ControlBarTaskDeleteButton.tsx b/src/commons/controlBar/github/ControlBarTaskDeleteButton.tsx index d4fffa5847..e385314a70 100644 --- a/src/commons/controlBar/github/ControlBarTaskDeleteButton.tsx +++ b/src/commons/controlBar/github/ControlBarTaskDeleteButton.tsx @@ -2,7 +2,7 @@ import { IconNames } from '@blueprintjs/icons'; import ControlButton from '../../ControlButton'; import { showSimpleConfirmDialog } from '../../utils/DialogHelper'; -import { showWarningMessage } from '../../utils/NotificationsHelper'; +import { showWarningMessage } from '../../utils/notifications/NotificationsHelper'; export type ControlBarTaskDeleteButtonProps = { deleteCurrentQuestion: () => void; diff --git a/src/commons/dropdown/DropdownAbout.tsx b/src/commons/dropdown/DropdownAbout.tsx index b38fde67d3..6163db2b04 100644 --- a/src/commons/dropdown/DropdownAbout.tsx +++ b/src/commons/dropdown/DropdownAbout.tsx @@ -9,7 +9,7 @@ type DialogProps = { onClose: () => void; }; -const DropdownAbout: React.SFC = props => ( +const DropdownAbout: React.FC = props => ( void; }; -const DropdownHelp: React.SFC = props => { +const DropdownHelp: React.FC = props => { const moduleHelpText = useTypedSelector(store => store.session.moduleHelpText); return ( diff --git a/src/commons/editor/UseHighlighting.tsx b/src/commons/editor/UseHighlighting.tsx index e3f0cc3e67..9cc327194d 100644 --- a/src/commons/editor/UseHighlighting.tsx +++ b/src/commons/editor/UseHighlighting.tsx @@ -78,16 +78,16 @@ const useHighlighting: EditorHook = (inProps, outProps, keyBindings, reactAceRef const { onChange: prevOnChange, onCursorChange: prevOnCursorChange } = outProps; outProps.onChange = React.useCallback( - (v, e) => { + (value: string, event?: any) => { handleVariableHighlighting(); - prevOnChange && prevOnChange(v, e); + prevOnChange && prevOnChange(value, event); }, [handleVariableHighlighting, prevOnChange] ); outProps.onCursorChange = React.useCallback( - (v, e) => { + (value: any, event?: any) => { handleVariableHighlighting(); - prevOnCursorChange && prevOnCursorChange(v, e); + prevOnCursorChange && prevOnCursorChange(value, event); }, [handleVariableHighlighting, prevOnCursorChange] ); diff --git a/src/commons/gitHubOverlay/RepositoryDialog.tsx b/src/commons/gitHubOverlay/RepositoryDialog.tsx index e9c3a993c1..6139059d8a 100644 --- a/src/commons/gitHubOverlay/RepositoryDialog.tsx +++ b/src/commons/gitHubOverlay/RepositoryDialog.tsx @@ -10,7 +10,7 @@ import { import classNames from 'classnames'; import React, { useState } from 'react'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; export type RepositoryDialogProps = { userRepos: any[]; diff --git a/src/commons/gitHubOverlay/__tests__/FileExplorerDialog.tsx b/src/commons/gitHubOverlay/__tests__/FileExplorerDialog.tsx index 145a179967..0819a42318 100644 --- a/src/commons/gitHubOverlay/__tests__/FileExplorerDialog.tsx +++ b/src/commons/gitHubOverlay/__tests__/FileExplorerDialog.tsx @@ -57,8 +57,9 @@ test('Opening folder for first time causes child files to be loaded', async () = await screen.findByText('Select a File'); + const dropdownCaret = await screen.findByText('Expand group'); act(() => { - fireEvent.click(screen.getByText('Expand group')); + fireEvent.click(dropdownCaret); }); await waitFor(() => expect(screen.getAllByText('TestFolder').length).toBe(2)); @@ -87,7 +88,7 @@ test('Closing folder hides child files', async () => { await screen.findByText('Select a File'); - const dropdownCaret = screen.getByText('Expand group'); + const dropdownCaret = await screen.findByText('Expand group'); // Open the folder for the first time, now there should be 2 TestFolders act(() => { @@ -129,7 +130,7 @@ test('Opening folder for second time does not cause child files to be loaded', a await screen.findByText('Select a File'); - const dropdownCaret = screen.getByText('Expand group'); + const dropdownCaret = await screen.findByText('Expand group'); // Open the folder, there should be 2 TestFolders in the render act(() => { diff --git a/src/commons/gitHubOverlay/__tests__/RepositoryDialog.tsx b/src/commons/gitHubOverlay/__tests__/RepositoryDialog.tsx index 832c5d3bbb..48c7fce7af 100644 --- a/src/commons/gitHubOverlay/__tests__/RepositoryDialog.tsx +++ b/src/commons/gitHubOverlay/__tests__/RepositoryDialog.tsx @@ -1,8 +1,10 @@ import { act, fireEvent, render, screen } from '@testing-library/react'; +import * as NotificationHelper from '../../utils/notifications/NotificationsHelper'; import RepositoryDialog from '../RepositoryDialog'; test('Submitting without selecting causes error message to be displayed', async () => { + const notificationMock = jest.spyOn(NotificationHelper, 'showWarningMessage'); function onSubmit(inputValue: string) {} const userRepos = [ @@ -17,7 +19,7 @@ test('Submitting without selecting causes error message to be displayed', async fireEvent.click(screen.getByText('Select')); - await screen.findByText('No repository selected!'); + expect(notificationMock).toBeCalledTimes(1); }); test('Selection sets repoName for submission', () => { diff --git a/src/commons/githubAssessments/GitHubMissionCreateDialog.tsx b/src/commons/githubAssessments/GitHubMissionCreateDialog.tsx index e96c846101..9f81e9038c 100644 --- a/src/commons/githubAssessments/GitHubMissionCreateDialog.tsx +++ b/src/commons/githubAssessments/GitHubMissionCreateDialog.tsx @@ -2,7 +2,7 @@ import { AnchorButton, Button, Classes, Dialog, InputGroup, Intent } from '@blue import classNames from 'classnames'; import React, { useState } from 'react'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; export type GitHubMissionCreateDialogResolution = { confirmSave: boolean; diff --git a/src/commons/githubAssessments/GitHubMissionDataUtils.ts b/src/commons/githubAssessments/GitHubMissionDataUtils.ts index eecea36e14..c6285e68bf 100644 --- a/src/commons/githubAssessments/GitHubMissionDataUtils.ts +++ b/src/commons/githubAssessments/GitHubMissionDataUtils.ts @@ -7,7 +7,7 @@ import { Chapter } from 'js-slang/dist/types'; import { isEqual } from 'lodash'; import { IMCQQuestion, Testcase } from '../assessment/AssessmentTypes'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { MissionData, MissionMetadata, MissionRepoData, TaskData } from './GitHubMissionTypes'; export const maximumTasksPerMission = 20; diff --git a/src/commons/mcqChooser/McqChooser.tsx b/src/commons/mcqChooser/McqChooser.tsx index 4c5cf40867..3e2d543f3b 100644 --- a/src/commons/mcqChooser/McqChooser.tsx +++ b/src/commons/mcqChooser/McqChooser.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { IMCQQuestion } from '../assessment/AssessmentTypes'; import Markdown from '../Markdown'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; export type McqChooserProps = { mcq: IMCQQuestion; diff --git a/src/commons/mocks/BackendMocks.ts b/src/commons/mocks/BackendMocks.ts index 6534b11858..0d027e84cb 100644 --- a/src/commons/mocks/BackendMocks.ts +++ b/src/commons/mocks/BackendMocks.ts @@ -43,7 +43,7 @@ import { } from '../notificationBadge/NotificationBadgeTypes'; import { routerNavigate } from '../sagas/BackendSaga'; import { actions } from '../utils/ActionsHelper'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { WorkspaceLocation } from '../workspace/WorkspaceTypes'; import { mockAssessmentConfigurations, diff --git a/src/commons/navigationBar/NavigationBar.tsx b/src/commons/navigationBar/NavigationBar.tsx index 8cbe58cc93..31457d87ea 100644 --- a/src/commons/navigationBar/NavigationBar.tsx +++ b/src/commons/navigationBar/NavigationBar.tsx @@ -40,7 +40,7 @@ export type NavbarEntryInfo = { hiddenInBreakpoints?: ('xs' | 'sm' | 'md' | 'lg')[]; // hide text in Blueprint breakpoints }; -type CreateNavlinkFunction = (navbarEntry: NavbarEntryInfo) => React.ReactFragment; +type CreateNavlinkFunction = (navbarEntry: NavbarEntryInfo) => React.ReactElement; const NavigationBar: React.FC = () => { const [mobileSideMenuOpen, setMobileSideMenuOpen] = useState(false); @@ -96,7 +96,7 @@ const NavigationBar: React.FC = () => { [setMobileSideMenuOpen] ); - const wrapWithMobileHamburger = (navlinks: (React.ReactFragment | null)[]) => { + const wrapWithMobileHamburger = (navlinks: (React.ReactElement | null)[]) => { // Don't render drawer when there are 0 navlinks in it const nonNullNavlinks = navlinks.filter(e => e !== null); const renderDrawer = nonNullNavlinks.length > 0; @@ -352,7 +352,7 @@ const playgroundOnlyNavbarLeftInfo: NavbarEntryInfo[] = [ export const renderNavlinksFromInfo = ( navbarEntries: NavbarEntryInfo[], createNavlink: CreateNavlinkFunction -): (React.ReactFragment | null)[] => +): (React.ReactElement | null)[] => navbarEntries.map(entry => { if (entry.disabled) { return null; diff --git a/src/commons/notificationBadge/NotificationBadge.tsx b/src/commons/notificationBadge/NotificationBadge.tsx index 57770cc538..764ec1c601 100644 --- a/src/commons/notificationBadge/NotificationBadge.tsx +++ b/src/commons/notificationBadge/NotificationBadge.tsx @@ -17,7 +17,7 @@ type OwnProps = { notificationFilter?: (notifications: Notification[]) => Notification[]; }; -const NotificationBadge: React.SFC = props => { +const NotificationBadge: React.FC = props => { const dispatch = useDispatch(); const initialNotifications = useTypedSelector(state => state.session.notifications); diff --git a/src/commons/sagas/BackendSaga.ts b/src/commons/sagas/BackendSaga.ts index c7bc781261..a2f0a97235 100644 --- a/src/commons/sagas/BackendSaga.ts +++ b/src/commons/sagas/BackendSaga.ts @@ -82,7 +82,7 @@ import { } from '../notificationBadge/NotificationBadgeTypes'; import { actions } from '../utils/ActionsHelper'; import { computeRedirectUri, getClientId, getDefaultProvider } from '../utils/AuthHelper'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { CHANGE_SUBLANGUAGE, WorkspaceLocation } from '../workspace/WorkspaceTypes'; import { deleteAssessment, diff --git a/src/commons/sagas/GitHubPersistenceSaga.ts b/src/commons/sagas/GitHubPersistenceSaga.ts index 1c693ca8ba..84b96f1173 100644 --- a/src/commons/sagas/GitHubPersistenceSaga.ts +++ b/src/commons/sagas/GitHubPersistenceSaga.ts @@ -20,7 +20,7 @@ import RepositoryDialog, { RepositoryDialogProps } from '../gitHubOverlay/Reposi import { actions } from '../utils/ActionsHelper'; import Constants from '../utils/Constants'; import { promisifyDialog } from '../utils/DialogHelper'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { EditorTabState } from '../workspace/WorkspaceTypes'; export function* GitHubPersistenceSaga(): SagaIterator { diff --git a/src/commons/sagas/LoginSaga.ts b/src/commons/sagas/LoginSaga.ts index 04d82db1f6..aa68a1a174 100644 --- a/src/commons/sagas/LoginSaga.ts +++ b/src/commons/sagas/LoginSaga.ts @@ -6,7 +6,7 @@ import { LOG_OUT } from '../application/types/CommonsTypes'; import { LOGIN, SET_USER } from '../application/types/SessionTypes'; import { actions } from '../utils/ActionsHelper'; import { computeEndpointUrl } from '../utils/AuthHelper'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { safeTakeEvery as takeEvery } from './SafeEffects'; export default function* LoginSaga(): SagaIterator { diff --git a/src/commons/sagas/PersistenceSaga.tsx b/src/commons/sagas/PersistenceSaga.tsx index 2054dfd9bd..e87e2f923d 100644 --- a/src/commons/sagas/PersistenceSaga.tsx +++ b/src/commons/sagas/PersistenceSaga.tsx @@ -22,7 +22,7 @@ import { showMessage, showSuccessMessage, showWarningMessage -} from '../utils/NotificationsHelper'; +} from '../utils/notifications/NotificationsHelper'; import { AsyncReturnType } from '../utils/TypeHelper'; import { safeTakeEvery as takeEvery, safeTakeLatest as takeLatest } from './SafeEffects'; diff --git a/src/commons/sagas/PlaygroundSaga.ts b/src/commons/sagas/PlaygroundSaga.ts index 4552939fef..1f21129a0c 100644 --- a/src/commons/sagas/PlaygroundSaga.ts +++ b/src/commons/sagas/PlaygroundSaga.ts @@ -15,7 +15,7 @@ import { OverallState } from '../application/ApplicationTypes'; import { ExternalLibraryName } from '../application/types/ExternalTypes'; import { retrieveFilesInWorkspaceAsRecord } from '../fileSystem/utils'; import Constants from '../utils/Constants'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { EditorTabState } from '../workspace/WorkspaceTypes'; import { safeTakeEvery as takeEvery } from './SafeEffects'; diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts index 029c2fc7a0..dd115f1445 100644 --- a/src/commons/sagas/RequestsSaga.ts +++ b/src/commons/sagas/RequestsSaga.ts @@ -50,7 +50,7 @@ import { Notification } from '../notificationBadge/NotificationBadgeTypes'; import { actions } from '../utils/ActionsHelper'; import { castLibrary } from '../utils/CastBackend'; import Constants from '../utils/Constants'; -import { showWarningMessage } from '../utils/NotificationsHelper'; +import { showWarningMessage } from '../utils/notifications/NotificationsHelper'; /** * @property accessToken - backend access token diff --git a/src/commons/sagas/WorkspaceSaga.ts b/src/commons/sagas/WorkspaceSaga.ts index 54d2a176f0..90a5576476 100644 --- a/src/commons/sagas/WorkspaceSaga.ts +++ b/src/commons/sagas/WorkspaceSaga.ts @@ -59,7 +59,7 @@ import { makeElevatedContext, visualizeEnv } from '../utils/JsSlangHelper'; -import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper'; +import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper'; import { makeExternalBuiltins as makeSourcerorExternalBuiltins } from '../utils/SourcerorHelper'; import { showFullJSDisclaimer, showFullTSDisclaimer } from '../utils/WarningDialogHelper'; import { notifyProgramEvaluated } from '../workspace/WorkspaceActions'; diff --git a/src/commons/sagas/__tests__/BackendSaga.ts b/src/commons/sagas/__tests__/BackendSaga.ts index b09e5f7173..2e10e6c601 100644 --- a/src/commons/sagas/__tests__/BackendSaga.ts +++ b/src/commons/sagas/__tests__/BackendSaga.ts @@ -80,7 +80,10 @@ import { mockNotifications } from '../../mocks/UserMocks'; import { Notification } from '../../notificationBadge/NotificationBadgeTypes'; import { computeRedirectUri } from '../../utils/AuthHelper'; import Constants from '../../utils/Constants'; -import { showSuccessMessage, showWarningMessage } from '../../utils/NotificationsHelper'; +import { + showSuccessMessage, + showWarningMessage +} from '../../utils/notifications/NotificationsHelper'; import { updateHasUnsavedChanges } from '../../workspace/WorkspaceActions'; import { CHANGE_SUBLANGUAGE, diff --git a/src/commons/sagas/__tests__/PlaygroundSaga.ts b/src/commons/sagas/__tests__/PlaygroundSaga.ts index aeaef5c963..bde0b82eac 100644 --- a/src/commons/sagas/__tests__/PlaygroundSaga.ts +++ b/src/commons/sagas/__tests__/PlaygroundSaga.ts @@ -15,7 +15,10 @@ import { } from '../../application/ApplicationTypes'; import { ExternalLibraryName } from '../../application/types/ExternalTypes'; import Constants from '../../utils/Constants'; -import { showSuccessMessage, showWarningMessage } from '../../utils/NotificationsHelper'; +import { + showSuccessMessage, + showWarningMessage +} from '../../utils/notifications/NotificationsHelper'; import PlaygroundSaga, { shortenURLRequest } from '../PlaygroundSaga'; describe('Playground saga tests', () => { diff --git a/src/commons/sagas/__tests__/WorkspaceSaga.ts b/src/commons/sagas/__tests__/WorkspaceSaga.ts index 059717fd88..a3a95c02d2 100644 --- a/src/commons/sagas/__tests__/WorkspaceSaga.ts +++ b/src/commons/sagas/__tests__/WorkspaceSaga.ts @@ -33,7 +33,10 @@ import { import { Library, Testcase, TestcaseType, TestcaseTypes } from '../../assessment/AssessmentTypes'; import { mockRuntimeContext } from '../../mocks/ContextMocks'; import { mockTestcases } from '../../mocks/GradingMocks'; -import { showSuccessMessage, showWarningMessage } from '../../utils/NotificationsHelper'; +import { + showSuccessMessage, + showWarningMessage +} from '../../utils/notifications/NotificationsHelper'; import { beginClearContext, changeExternalLibrary, diff --git a/src/commons/sideContent/SideContentContestVotingContainer.tsx b/src/commons/sideContent/SideContentContestVotingContainer.tsx index f7681ef027..90712f83e7 100644 --- a/src/commons/sideContent/SideContentContestVotingContainer.tsx +++ b/src/commons/sideContent/SideContentContestVotingContainer.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { showWarningMessage } from 'src/commons/utils/NotificationsHelper'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import { ContestEntry } from '../assessment/AssessmentTypes'; import SideContentContestVoting from './SideContentContestVoting'; diff --git a/src/commons/sideContent/__tests__/SideContentEnvVisualizer.tsx b/src/commons/sideContent/__tests__/SideContentEnvVisualizer.tsx index 8b0f3a6fa1..b83715dbd1 100644 --- a/src/commons/sideContent/__tests__/SideContentEnvVisualizer.tsx +++ b/src/commons/sideContent/__tests__/SideContentEnvVisualizer.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import { runInContext } from 'js-slang/dist/'; import { Provider } from 'react-redux'; import { mockInitialStore } from 'src/commons/mocks/StoreMocks'; @@ -26,8 +26,8 @@ test('EnvVisualizer sets visualization state and renders', async () => { expect(screen.queryAllByTestId('sa-env-visualizer')).toHaveLength(0); const context = mockContext(); - await runInContext('const hello="world"; debugger;', context); - visualizeEnv({ context }); + runInContext('const hello="world"; debugger;', context); + act(() => visualizeEnv({ context })); expect(screen.queryAllByTestId('env-visualizer-default-text')).toHaveLength(0); expect(screen.queryAllByTestId('sa-env-visualizer')).toHaveLength(1); diff --git a/src/commons/sideContent/githubAssessments/SideContentTestcaseEditor.tsx b/src/commons/sideContent/githubAssessments/SideContentTestcaseEditor.tsx index 0597f8195a..34d3f93c28 100644 --- a/src/commons/sideContent/githubAssessments/SideContentTestcaseEditor.tsx +++ b/src/commons/sideContent/githubAssessments/SideContentTestcaseEditor.tsx @@ -99,7 +99,7 @@ const SideContentTestcaseEditor: React.FunctionComponent< ); const convertToTestcaseCard = React.useCallback( - (testcase, index) => { + (testcase: Testcase, index: number) => { if (allowEdits) { return ( { dialogOnClose: null }; - public static create(): DialogHelper { + /** + * Blueprintjs v4 OverlayToaster still uses ReactDOM.render, which is deprecated in React v18. + * Temporary custom workaround as follows. + * + * https://github.com/palantir/blueprint/issues/5212 + */ + public static create() { const containerElement = document.createElement('div'); document.body.appendChild(containerElement); - const dialogHelper = ReactDOM.render<{}>(, containerElement) as DialogHelper; - if (dialogHelper == null) { - throw new Error('Could not create DialogHelper - are you in a React lifecycle method?'); - } - return dialogHelper; + const root = createRoot(containerElement); + + const dialogRef = React.createRef(); + root.render(); + return dialogRef; } public show(dialog: DialogHelperState['dialog'], onClose?: () => void) { @@ -48,12 +54,13 @@ class DialogHelper extends React.PureComponent<{}, DialogHelperState> { const singleton = DialogHelper.create(); +// singleton should be rendered by the time React lifecycle completes export function showDialog(dialog: DialogHelperState['dialog'], dialogOnClose?: () => void) { - singleton.show(dialog, dialogOnClose); + singleton.current!.show(dialog, dialogOnClose); } export function closeDialog() { - singleton.close(); + singleton.current!.close(); } export function promisifyDialog

, R>( diff --git a/src/commons/utils/SortableList.tsx b/src/commons/utils/SortableList.tsx index c134a83249..4f0b06819d 100644 --- a/src/commons/utils/SortableList.tsx +++ b/src/commons/utils/SortableList.tsx @@ -1,18 +1,31 @@ import { Button } from '@blueprintjs/core'; import { arrayMoveImmutable } from 'array-move'; import React from 'react'; -import { SortableContainer, SortableElement } from 'react-sortable-hoc'; +import { + SortableContainer, + SortableContainerProps, + SortableElement, + SortableElementProps +} from 'react-sortable-hoc'; + +type SortableItemProps = SortableElementProps & { + value: any; +}; const SortableItem = React.memo( - SortableElement(({ value }: any) => ( + SortableElement(({ value }: any) => (

)) ); +type SortableListProps = SortableContainerProps & { + items: string[]; +}; + export const SortableList = React.memo( - SortableContainer(({ items }: any) => { + SortableContainer(({ items }: any) => { return (
{items && diff --git a/src/commons/utils/WarningDialogHelper.tsx b/src/commons/utils/WarningDialogHelper.tsx index d162bfbbeb..4f4a7620fb 100644 --- a/src/commons/utils/WarningDialogHelper.tsx +++ b/src/commons/utils/WarningDialogHelper.tsx @@ -1,5 +1,5 @@ import { showSimpleConfirmDialog, SimpleConfirmDialogProps } from './DialogHelper'; -import { showWarningMessage } from './NotificationsHelper'; +import { showWarningMessage } from './notifications/NotificationsHelper'; // Full JS const FULL_JS_DISCLAIMER_DIALOG_PROPS: SimpleConfirmDialogProps = { diff --git a/src/commons/utils/NotificationsHelper.ts b/src/commons/utils/notifications/NotificationsHelper.ts similarity index 75% rename from src/commons/utils/NotificationsHelper.ts rename to src/commons/utils/notifications/NotificationsHelper.ts index 973c897d80..1f6f43226c 100644 --- a/src/commons/utils/NotificationsHelper.ts +++ b/src/commons/utils/notifications/NotificationsHelper.ts @@ -1,8 +1,6 @@ -import { Intent, IToastProps, Position, Toaster } from '@blueprintjs/core'; +import { Intent, ToastProps } from '@blueprintjs/core'; -const notification = Toaster.create({ - position: Position.TOP -}); +import { notification } from './createNotification'; export const showSuccessMessage = ( message: string | JSX.Element, @@ -46,6 +44,6 @@ export const showDangerMessage = ( key ); -export const showMessage = (props: IToastProps, key?: string) => notification.show(props, key); +export const showMessage = (props: ToastProps, key?: string) => notification.show(props, key); export const dismiss = (key: string) => notification.dismiss(key); diff --git a/src/commons/utils/notifications/createNotification.tsx b/src/commons/utils/notifications/createNotification.tsx new file mode 100644 index 0000000000..34c0af4cf2 --- /dev/null +++ b/src/commons/utils/notifications/createNotification.tsx @@ -0,0 +1,38 @@ +import { OverlayToaster, OverlayToasterProps, Position } from '@blueprintjs/core'; +import { createRoot } from 'react-dom/client'; + +/** + * Blueprintjs v4 OverlayToaster still uses ReactDOM.render, which is deprecated in React v18. + * They plan to migrate out of ReactDOM.render in v6. Temporary custom workaround `Toaster.create` as follows. + * + * https://github.com/palantir/blueprint/issues/5212 + * + * SEPARATE NOTE: `notification` is intentionally exported from this separate file in order for us to mock it in `NotificationsHelper.tsx` + * - `ReactDOM.render` was deprecated in react v18, which previously returned the instance of the class component `OverlayToaster` from Blueprint v4 + * - the new React `createRoot.render` returns `void` + * - we are able to circumvent this by accessing the `OverlayToaster` instance from the `ref` prop based on the linked GitHub suggestion above + * - HOWEVER, React Testing Library (@testing-library/react) does not support refs and accessing component instances based on their [Guiding Principles](https://testing-library.com/docs/guiding-principles) and [FAQ](https://testing-library.com/docs/dom-testing-library/faq/) + * - thus we have to mock `notification` in our tests from this file (see `src/setupTests.ts`) + * - this also means we can't test UI changes related to notification toasters since we are mocking the toaster (e.g. checking that the toast appears in our test UI) + */ +export let notification: OverlayToaster; + +const createToaster = (props?: OverlayToasterProps, container = document.body) => { + const containerElement = document.createElement('div'); + container.appendChild(containerElement); + const root = createRoot(containerElement); + + root.render( + { + notification = instance!; + }} + /> + ); +}; + +createToaster({ + position: Position.TOP +}); diff --git a/src/features/assessments/AssessmentUtils.ts b/src/features/assessments/AssessmentUtils.ts index 9ad637e1bc..029d2d80d0 100644 --- a/src/features/assessments/AssessmentUtils.ts +++ b/src/features/assessments/AssessmentUtils.ts @@ -6,7 +6,7 @@ import { QuestionTypes, Testcase } from '../../commons/assessment/AssessmentTypes'; -import { showWarningMessage } from '../../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../../commons/utils/notifications/NotificationsHelper'; /** * Returns a nullary function that defers the navigation of the browser window, until the diff --git a/src/features/github/GitHubUtils.tsx b/src/features/github/GitHubUtils.tsx index c6d160a14a..21866465b4 100644 --- a/src/features/github/GitHubUtils.tsx +++ b/src/features/github/GitHubUtils.tsx @@ -6,7 +6,10 @@ import { import { actions } from '../../commons/utils/ActionsHelper'; import { showSimpleConfirmDialog } from '../../commons/utils/DialogHelper'; -import { showSuccessMessage, showWarningMessage } from '../../commons/utils/NotificationsHelper'; +import { + showSuccessMessage, + showWarningMessage +} from '../../commons/utils/notifications/NotificationsHelper'; import { store } from '../../pages/createStore'; /** diff --git a/src/index.tsx b/src/index.tsx index 4262c2b621..36a303b9df 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,10 +2,10 @@ import 'src/styles/index.scss'; import * as Sentry from '@sentry/browser'; import { setModulesStaticURL } from 'js-slang/dist/modules/moduleLoader'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { Provider } from 'react-redux'; import Constants, { Links } from 'src/commons/utils/Constants'; -import { showWarningMessage } from 'src/commons/utils/NotificationsHelper'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import { register as registerServiceWorker } from 'src/commons/utils/RegisterServiceWorker'; import { triggerSyncLogs } from 'src/features/eventLogging/client'; import { store } from 'src/pages/createStore'; @@ -24,6 +24,7 @@ if (Constants.sentryDsn) { } const rootContainer = document.getElementById('root') as HTMLElement; +const root = createRoot(rootContainer); (window as any).__REDUX_STORE__ = store; // need this for slang's display console.log( `%cSource Academy ${Constants.sourceAcademyEnvironment}-${Constants.sourceAcademyVersion}; ` + @@ -38,11 +39,10 @@ console.log(`Using module backend: ${Constants.moduleBackendUrl}`); createInBrowserFileSystem(store) .catch(err => console.error(err)) .finally(() => { - render( + root.render( - , - rootContainer + ); }); diff --git a/src/pages/academy/adminPanel/subcomponents/assessmentConfigPanel/AssessmentConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/assessmentConfigPanel/AssessmentConfigPanel.tsx index c4f6f44efb..f7ae3efde6 100644 --- a/src/pages/academy/adminPanel/subcomponents/assessmentConfigPanel/AssessmentConfigPanel.tsx +++ b/src/pages/academy/adminPanel/subcomponents/assessmentConfigPanel/AssessmentConfigPanel.tsx @@ -3,7 +3,7 @@ import { CellValueChangedEvent, GridApi, GridReadyEvent, RowDragEvent } from 'ag import { AgGridReact } from 'ag-grid-react'; import { isEqual } from 'lodash'; import React from 'react'; -import { showWarningMessage } from 'src/commons/utils/NotificationsHelper'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; import { AssessmentConfiguration } from '../../../../../commons/assessment/AssessmentTypes'; import BooleanCell from './BooleanCell'; diff --git a/src/pages/academy/adminPanel/subcomponents/userConfigPanel/UserActionsCell.tsx b/src/pages/academy/adminPanel/subcomponents/userConfigPanel/UserActionsCell.tsx index 18afa927e1..60ad048816 100644 --- a/src/pages/academy/adminPanel/subcomponents/userConfigPanel/UserActionsCell.tsx +++ b/src/pages/academy/adminPanel/subcomponents/userConfigPanel/UserActionsCell.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Role } from 'src/commons/application/ApplicationTypes'; import { AdminPanelCourseRegistration } from 'src/commons/application/types/SessionTypes'; import ControlButton from 'src/commons/ControlButton'; -import { showWarningMessage } from 'src/commons/utils/NotificationsHelper'; +import { showWarningMessage } from 'src/commons/utils/notifications/NotificationsHelper'; type DeleteUserCellProps = OwnProps; diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx index c56b3c64d1..bd00000e57 100644 --- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx +++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx @@ -21,7 +21,7 @@ import { showSimpleConfirmDialog } from '../../../../commons/utils/DialogHelper' import { showSuccessMessage, showWarningMessage -} from '../../../../commons/utils/NotificationsHelper'; +} from '../../../../commons/utils/notifications/NotificationsHelper'; import { convertParamToInt } from '../../../../commons/utils/ParamParseHelper'; type GradingEditorProps = DispatchProps & OwnProps; diff --git a/src/pages/academy/groundControl/subcomponents/GroundControlDropzone.tsx b/src/pages/academy/groundControl/subcomponents/GroundControlDropzone.tsx index e10818f5b8..95ed442d43 100644 --- a/src/pages/academy/groundControl/subcomponents/GroundControlDropzone.tsx +++ b/src/pages/academy/groundControl/subcomponents/GroundControlDropzone.tsx @@ -6,7 +6,7 @@ import { FileRejection, useDropzone } from 'react-dropzone'; import { AssessmentConfiguration } from 'src/commons/assessment/AssessmentTypes'; import ControlButton from '../../../../commons/ControlButton'; -import { showWarningMessage } from '../../../../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../../../../commons/utils/notifications/NotificationsHelper'; export type DropzoneProps = DispatchProps & StateProps; diff --git a/src/pages/academy/groundControl/subcomponents/GroundControlEditCell.tsx b/src/pages/academy/groundControl/subcomponents/GroundControlEditCell.tsx index d6d1254994..ba9c58083e 100644 --- a/src/pages/academy/groundControl/subcomponents/GroundControlEditCell.tsx +++ b/src/pages/academy/groundControl/subcomponents/GroundControlEditCell.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { AssessmentOverview } from '../../../../commons/assessment/AssessmentTypes'; import ControlButton from '../../../../commons/ControlButton'; -import { showWarningMessage } from '../../../../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../../../../commons/utils/notifications/NotificationsHelper'; export type EditCellProps = DispatchProps & StateProps; diff --git a/src/pages/githubAssessments/GitHubAssessmentWorkspace.tsx b/src/pages/githubAssessments/GitHubAssessmentWorkspace.tsx index e730d53f48..18db93619c 100644 --- a/src/pages/githubAssessments/GitHubAssessmentWorkspace.tsx +++ b/src/pages/githubAssessments/GitHubAssessmentWorkspace.tsx @@ -87,7 +87,7 @@ import { SideContentProps } from '../../commons/sideContent/SideContent'; import { SideContentTab, SideContentType } from '../../commons/sideContent/SideContentTypes'; import Constants from '../../commons/utils/Constants'; import { promisifyDialog, showSimpleConfirmDialog } from '../../commons/utils/DialogHelper'; -import { showWarningMessage } from '../../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../../commons/utils/notifications/NotificationsHelper'; import Workspace, { WorkspaceProps } from '../../commons/workspace/Workspace'; import { WorkspaceLocation, WorkspaceState } from '../../commons/workspace/WorkspaceTypes'; import { @@ -729,7 +729,7 @@ const GitHubAssessmentWorkspace: React.FC = () => { }, [isMobileBreakpoint, selectedTab]); const onEditorValueChange = useCallback( - val => { + (editorTabIndex: number, val: string) => { // TODO: Hardcoded to make use of the first editor tab. Refactoring is needed for this workspace to enable Folder mode. handleEditorValueChange(0, val); editCode(currentTaskNumber, val); diff --git a/src/pages/githubAssessments/GitHubClassroom.tsx b/src/pages/githubAssessments/GitHubClassroom.tsx index 27dbd658c8..ca6448c799 100644 --- a/src/pages/githubAssessments/GitHubClassroom.tsx +++ b/src/pages/githubAssessments/GitHubClassroom.tsx @@ -12,7 +12,7 @@ import { useTypedSelector } from 'src/commons/utils/Hooks'; import ContentDisplay from '../../commons/ContentDisplay'; import { MissionRepoData } from '../../commons/githubAssessments/GitHubMissionTypes'; import GitHubAssessmentsNavigationBar from '../../commons/navigationBar/subcomponents/GitHubAssessmentsNavigationBar'; -import { showWarningMessage } from '../../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../../commons/utils/notifications/NotificationsHelper'; import { assessmentTypeLink } from '../../commons/utils/ParamParseHelper'; import GitHubAssessmentListing from './GitHubAssessmentListing'; import GitHubAssessmentWorkspace from './GitHubAssessmentWorkspace'; diff --git a/src/pages/localStorage.ts b/src/pages/localStorage.ts index e23f818b18..0c5da166be 100644 --- a/src/pages/localStorage.ts +++ b/src/pages/localStorage.ts @@ -4,7 +4,7 @@ import { compressToUTF16, decompressFromUTF16 } from 'lz-string'; import { OverallState, SALanguage } from '../commons/application/ApplicationTypes'; import { ExternalLibraryName } from '../commons/application/types/ExternalTypes'; import { SessionState } from '../commons/application/types/SessionTypes'; -import { showWarningMessage } from '../commons/utils/NotificationsHelper'; +import { showWarningMessage } from '../commons/utils/notifications/NotificationsHelper'; import { EditorTabState } from '../commons/workspace/WorkspaceTypes'; import { AchievementItem } from '../features/achievement/AchievementTypes'; diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx index aeac327287..4783167dd7 100644 --- a/src/pages/login/Login.tsx +++ b/src/pages/login/Login.tsx @@ -29,7 +29,10 @@ const Login: React.FunctionComponent<{}> = () => { const location = useLocation(); const { code, provider: providerId } = parseQuery(location.search); - const handleLogin = React.useCallback(providerId => dispatch(login(providerId)), [dispatch]); + const handleLogin = React.useCallback( + (providerId: string) => dispatch(login(providerId)), + [dispatch] + ); React.useEffect(() => { if (code) { diff --git a/src/pages/notFound/NotFound.tsx b/src/pages/notFound/NotFound.tsx index c052f33d64..3ff63750aa 100644 --- a/src/pages/notFound/NotFound.tsx +++ b/src/pages/notFound/NotFound.tsx @@ -3,7 +3,7 @@ import { IconNames } from '@blueprintjs/icons'; import classNames from 'classnames'; import * as React from 'react'; -const NotFound: React.SFC<{}> = () => ( +const NotFound: React.FC = () => (
= props => { [isGreen] ); - const onEditorValueChange = useCallback( - (editorTabIndex, newEditorValue) => { + const onEditorValueChange = React.useCallback( + (editorTabIndex: number, newEditorValue: string) => { setLastEdit(new Date()); handleEditorValueChange(editorTabIndex, newEditorValue); }, diff --git a/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap b/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap index 5d55167963..8ecffb2227 100644 --- a/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap +++ b/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap @@ -27,7 +27,6 @@ exports[`Playground tests Playground renders correctly 1`] = ` -
-
({ + notification: { + show: jest.fn() + } +})); diff --git a/yarn.lock b/yarn.lock index 0ec549442f..e0ca7dbbcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2359,10 +2359,10 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.9.2.tgz#0402364a2a9692edf04e05ee6f385dfa27271da5" integrity sha512-ajc0OF+karBAdaSz7OK09rCoAHB1XI1+wEhu+tDNMPc+XcO+dTlXXN/Vc0a8vym4kElvEjXEDd9c8Zfgt4bekA== -"@testing-library/dom@^8.0.0": - version "8.20.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" - integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== +"@testing-library/dom@^9.0.0": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" + integrity sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -2388,15 +2388,16 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@12.1.2": - version "12.1.2" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.2.tgz#f1bc9a45943461fa2a598bb4597df1ae044cfc76" - integrity sha512-ihQiEOklNyHIpo2Y8FREkyD1QAea054U0MVbwH1m8N9TxeFz+KoJ9LkqoKqJlzx2JDm56DVwaJ1r36JYxZM05g== +"@testing-library/react@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c" + integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" -"@testing-library/user-event@^14.3.0": +"@testing-library/user-event@^14.4.3": version "14.4.3" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591" integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q== @@ -2840,17 +2841,17 @@ dependencies: "@types/react" "*" -"@types/react-dom@^17.0.20": - version "17.0.20" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.20.tgz#e0c8901469d732b36d8473b40b679ad899da1b53" - integrity sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA== +"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.6": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.6.tgz#ad621fa71a8db29af7c31b41b2ea3d8a6f4144d1" + integrity sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A== dependencies: - "@types/react" "^17" + "@types/react" "*" -"@types/react-reconciler@~0.26.2": - version "0.26.7" - resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36" - integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ== +"@types/react-reconciler@^0.28.0", "@types/react-reconciler@^0.28.2": + version "0.28.2" + resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.2.tgz#f16b0e8cc4748af70ca975eaaace0d79582c71fa" + integrity sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw== dependencies: "@types/react" "*" @@ -2895,12 +2896,12 @@ dependencies: "@types/react" "*" -"@types/react-test-renderer@^17.0.2": - version "17.0.2" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.2.tgz#5f800a39b12ac8d2a2149e7e1885215bcf4edbbf" - integrity sha512-+F1KONQTBHDBBhbHuT2GNydeMpPuviduXIVJRB7Y4nma4NR5DrTJfMMZ+jbhEHbpwL+Uqhs1WXh4KHiyrtYTPg== +"@types/react-test-renderer@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243" + integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ== dependencies: - "@types/react" "^17" + "@types/react" "*" "@types/react-textarea-autosize@^8.0.0": version "8.0.0" @@ -2909,10 +2910,10 @@ dependencies: react-textarea-autosize "*" -"@types/react@*", "@types/react@^17", "@types/react@^17.0.38": - version "17.0.53" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" - integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== +"@types/react@*", "@types/react@^18.2.13": + version "18.2.13" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.13.tgz#a98c09bde8b18f80021935b11d2d29ef5f4dcb2f" + integrity sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -7509,6 +7510,13 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +its-fine@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-1.1.1.tgz#e74b93fddd487441f978a50f64f0f5af4d2fc38e" + integrity sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw== + dependencies: + "@types/react-reconciler" "^0.28.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -8293,10 +8301,10 @@ klona@^2.0.4, klona@^2.0.5: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== -konva@^8.3.14: - version "8.4.2" - resolved "https://registry.yarnpkg.com/konva/-/konva-8.4.2.tgz#6de2b9d54f2b56b8c7c76eba66955f7255dd9afb" - integrity sha512-4VQcrgj/PI8ydJjtLcTuinHBE8o0WGX0YoRwbiN5mpYQiC52aOzJ0XbpKNDJdRvORQphK5LP+jeM0hQJEYIuUA== +konva@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/konva/-/konva-9.2.0.tgz#3739e539724b0e6b76d697a322efdaa01baa1508" + integrity sha512-+woI76Sk+VFVl9z7zPkuTnN2zFpEYg27YWz8BCdQXpt5IS3pdnSPAPQVPPMidcbDi9/G5b/IOIp35/KqMGiYPA== language-subtag-registry@~0.3.2: version "0.3.22" @@ -10370,14 +10378,13 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-draggable@^4.4.5: version "4.4.5" @@ -10418,24 +10425,25 @@ react-is@^16.10.2, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@^17.0.1, react-is@^17.0.2: +react-is@^17.0.1: version "17.0.2" resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-konva@^17.0.2-5: - version "17.0.2-6" - resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-17.0.2-6.tgz#80b3fdebfd915878eafe554c191bec282cdda0de" - integrity sha512-cfRsBKxqAQ6gTBmyrKptKRWhHD+C068dvyqcZ4h8qzKbAacTXQtfLMSpOI+d+ptxSMvayIbbCdbsBmk3bWiClQ== +react-konva@^18.2.10: + version "18.2.10" + resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-18.2.10.tgz#5b5edc5e9ed452755d21babc353747828868decc" + integrity sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g== dependencies: - "@types/react-reconciler" "~0.26.2" - react-reconciler "~0.26.2" - scheduler "^0.20.2" + "@types/react-reconciler" "^0.28.2" + its-fine "^1.1.1" + react-reconciler "~0.29.0" + scheduler "^0.23.0" react-latex-next@^2.1.0: version "2.2.0" @@ -10492,14 +10500,13 @@ react-qr-reader@^3.0.0-beta-1: "@zxing/library" "^0.18.3" rollup "^2.67.2" -react-reconciler@~0.26.2: - version "0.26.2" - resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91" - integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q== +react-reconciler@~0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.29.0.tgz#ee769bd362915076753f3845822f2d1046603de7" + integrity sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-redux@^8.0.7: version "8.0.7" @@ -10605,7 +10612,7 @@ react-scripts@^5.0.1: optionalDependencies: fsevents "^2.3.2" -react-shallow-renderer@^16.13.1: +react-shallow-renderer@^16.15.0: version "16.15.0" resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== @@ -10646,15 +10653,14 @@ react-syntax-highlighter@^15.5.0: prismjs "^1.27.0" refractor "^3.6.0" -react-test-renderer@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" - integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== +react-test-renderer@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" + integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA== dependencies: - object-assign "^4.1.1" - react-is "^17.0.2" - react-shallow-renderer "^16.13.1" - scheduler "^0.20.2" + react-is "^18.2.0" + react-shallow-renderer "^16.15.0" + scheduler "^0.23.0" react-textarea-autosize@*: version "8.4.0" @@ -10699,13 +10705,12 @@ react-transition-state@^1.1.5: resolved "https://registry.yarnpkg.com/react-transition-state/-/react-transition-state-1.1.5.tgz#22accee21d0011b1d0245be24b6262ae67f494c3" integrity sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ== -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-cache@^1.0.0: version "1.0.0" @@ -11159,13 +11164,12 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@2.7.0: version "2.7.0"