From 7229406cfbdbf79624b6a64dbdb6d93fb53cdfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pasteau?= <4895034+ClementPasteau@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:04:01 +0100 Subject: [PATCH] Add errorBoundaries on all main components to prevent crashing the whole app (#5889) Do not show in changelog --- .../ExtensionStore/ExtensionsSearchDialog.js | 19 +- newIDE/app/src/AssetStore/NewObjectDialog.js | 15 +- .../InstructionEditorDialog.js | 19 +- newIDE/app/src/EventsSheet/index.js | 57 ++-- .../src/ExportAndShare/ShareDialog/index.js | 13 +- .../FullSizeInstancesEditorWithScrollbars.js | 71 +++-- .../InstancePropertiesEditor/index.js | 4 +- .../InstancesEditor/InstancesList/index.js | 53 +++- newIDE/app/src/LayersList/index.js | 19 +- newIDE/app/src/MainFrame/AboutDialog.js | 17 +- .../HomePage/BuildSection/index.js | 12 +- .../HomePage/CommunitySection.js | 12 +- .../HomePage/GetStartedSection/index.js | 12 +- .../HomePage/LearnSection/index.js | 12 +- .../EditorContainers/HomePage/PlaySection.js | 12 +- .../HomePage/StoreSection/index.js | 12 +- .../HomePage/TeamSection/index.js | 12 +- .../MainFrame/Preferences/LanguageDialog.js | 6 +- .../Preferences/PreferencesDialog.js | 19 +- newIDE/app/src/MainFrame/Providers.js | 3 +- newIDE/app/src/MainFrame/index.js | 17 +- .../src/ObjectEditor/ObjectEditorDialog.js | 16 +- newIDE/app/src/ObjectGroupsList/index.js | 17 +- newIDE/app/src/ObjectsList/index.js | 17 +- .../PlatformSpecificAssetsDialog.js | 19 +- newIDE/app/src/Profile/ProfileDialog.js | 13 +- .../ProjectManager/ProjectPropertiesDialog.js | 14 +- newIDE/app/src/ProjectManager/index.js | 14 +- .../SceneEditor/MosaicEditorsDisplay/index.js | 6 +- .../SwipeableDrawerEditorsDisplay/index.js | 70 +++-- newIDE/app/src/UI/ErrorBoundary.js | 274 +++++++++++++----- newIDE/app/src/UI/PlaceholderMessage.js | 5 +- newIDE/app/src/UI/Search/BoxSearchResults.js | 2 +- newIDE/app/src/UI/Search/ListSearchResults.js | 2 +- newIDE/app/src/VariablesList/VariablesList.js | 2 +- .../ErrorBoundaries.stories.js | 91 ++++++ .../UI/PlaceholderMessage.stories.js | 22 ++ .../src/stories/everything-else.stories.js | 27 -- 38 files changed, 769 insertions(+), 258 deletions(-) create mode 100644 newIDE/app/src/stories/componentStories/ErrorBoundaries.stories.js create mode 100644 newIDE/app/src/stories/componentStories/UI/PlaceholderMessage.stories.js diff --git a/newIDE/app/src/AssetStore/ExtensionStore/ExtensionsSearchDialog.js b/newIDE/app/src/AssetStore/ExtensionStore/ExtensionsSearchDialog.js index f034ffeaa96d..ae5b0b306806 100644 --- a/newIDE/app/src/AssetStore/ExtensionStore/ExtensionsSearchDialog.js +++ b/newIDE/app/src/AssetStore/ExtensionStore/ExtensionsSearchDialog.js @@ -19,6 +19,7 @@ import { import { useResponsiveWindowWidth } from '../../UI/Reponsive/ResponsiveWindowMeasurer'; import Download from '../../UI/CustomSvgIcons/Download'; import Add from '../../UI/CustomSvgIcons/Add'; +import ErrorBoundary from '../../UI/ErrorBoundary'; type Props = {| project: gdProject, @@ -31,13 +32,13 @@ type Props = {| /** * Allows to browse and install events based extensions. */ -export default function ExtensionsSearchDialog({ +const ExtensionsSearchDialog = ({ project, onClose, onInstallExtension, onExtensionInstalled, onCreateNew, -}: Props) { +}: Props) => { const windowWidth = useResponsiveWindowWidth(); const isMobileScreen = windowWidth === 'small'; const [isInstalling, setIsInstalling] = React.useState(false); @@ -171,4 +172,16 @@ export default function ExtensionsSearchDialog({ )} ); -} +}; + +const ExtensionsSearchDialogWithErrorBoundary = (props: Props) => ( + Extensions search} + scope="extensions-search-dialog" + onClose={props.onClose} + > + + +); + +export default ExtensionsSearchDialogWithErrorBoundary; diff --git a/newIDE/app/src/AssetStore/NewObjectDialog.js b/newIDE/app/src/AssetStore/NewObjectDialog.js index bbbaeb494469..9c0c0e1018bf 100644 --- a/newIDE/app/src/AssetStore/NewObjectDialog.js +++ b/newIDE/app/src/AssetStore/NewObjectDialog.js @@ -36,6 +36,7 @@ import { enumerateAssetStoreIds } from './EnumerateAssetStoreIds'; import PromisePool from '@supercharge/promise-pool'; import NewObjectFromScratch from './NewObjectFromScratch'; import { getAssetShortHeadersToDisplay } from './AssetsList'; +import ErrorBoundary from '../UI/ErrorBoundary'; const isDev = Window.isDev(); @@ -104,7 +105,7 @@ type Props = {| canInstallPrivateAsset: () => boolean, |}; -export default function NewObjectDialog({ +function NewObjectDialog({ project, layout, objectsContainer, @@ -501,3 +502,15 @@ export default function NewObjectDialog({ ); } + +const NewObjectDialogWithErrorBoundary = (props: Props) => ( + New Object dialog} + scope="new-object-dialog" + onClose={props.onClose} + > + + +); + +export default NewObjectDialogWithErrorBoundary; diff --git a/newIDE/app/src/EventsSheet/InstructionEditor/InstructionEditorDialog.js b/newIDE/app/src/EventsSheet/InstructionEditor/InstructionEditorDialog.js index 7bf11cce60a1..0e2a641dfd32 100644 --- a/newIDE/app/src/EventsSheet/InstructionEditor/InstructionEditorDialog.js +++ b/newIDE/app/src/EventsSheet/InstructionEditor/InstructionEditorDialog.js @@ -35,6 +35,7 @@ import { import ExtensionsSearchDialog from '../../AssetStore/ExtensionStore/ExtensionsSearchDialog'; import { sendBehaviorAdded } from '../../Utils/Analytics/EventSender'; import { useShouldAutofocusInput } from '../../UI/Reponsive/ScreenTypeMeasurer'; +import ErrorBoundary from '../../UI/ErrorBoundary'; const styles = { fullHeightSelector: { @@ -88,7 +89,7 @@ const getInitialTab = ( * A responsive instruction editor in a dialog, showing InstructionParametersEditor * at the end. */ -export default function InstructionEditorDialog({ +const InstructionEditorDialog = ({ project, globalObjectsContainer, objectsContainer, @@ -101,7 +102,7 @@ export default function InstructionEditorDialog({ onSubmit, resourceManagementProps, openInstructionOrExpression, -}: Props) { +}: Props) => { const forceUpdate = useForceUpdate(); const [ instructionEditorState, @@ -438,4 +439,16 @@ export default function InstructionEditorDialog({ )} ); -} +}; + +const InstructionEditorDialogWithErrorBoundary = (props: Props) => ( + Instruction editor} + scope="scene-events-instruction-editor" + onClose={props.onCancel} + > + + +); + +export default InstructionEditorDialogWithErrorBoundary; diff --git a/newIDE/app/src/EventsSheet/index.js b/newIDE/app/src/EventsSheet/index.js index f1d8ab0a725e..9ae12ca234b9 100644 --- a/newIDE/app/src/EventsSheet/index.js +++ b/newIDE/app/src/EventsSheet/index.js @@ -106,6 +106,7 @@ import { TutorialContext } from '../Tutorial/TutorialContext'; import { type Tutorial } from '../Utils/GDevelopServices/Tutorial'; import AlertMessage from '../UI/AlertMessage'; import { Column, Line } from '../UI/Grid'; +import ErrorBoundary from '../UI/ErrorBoundary'; const gd: libGDevelop = global.gd; @@ -1866,31 +1867,37 @@ export class EventsSheetComponentWithoutHandle extends React.Component< tutorials={tutorials} /> {this.state.showSearchPanel && ( - (this._searchPanel = searchPanel)} - onSearchInEvents={inputs => - this._searchInEvents(searchInEvents, inputs) - } - onReplaceInEvents={inputs => { - this._replaceInEvents(replaceInEvents, inputs); - }} - resultsCount={ - eventsSearchResultEvents - ? eventsSearchResultEvents.length - : null - } - hasEventSelected={hasEventSelected(this.state.selection)} - onGoToPreviousSearchResult={() => - this._ensureEventUnfolded(goToPreviousSearchResult) - } - onCloseSearchPanel={() => { - this._closeSearchPanel(); - }} - onGoToNextSearchResult={() => - this._ensureEventUnfolded(goToNextSearchResult) - } - searchFocusOffset={searchFocusOffset} - /> + Search panel} + scope="scene-events-search" + onClose={() => this._closeSearchPanel()} + > + (this._searchPanel = searchPanel)} + onSearchInEvents={inputs => + this._searchInEvents(searchInEvents, inputs) + } + onReplaceInEvents={inputs => { + this._replaceInEvents(replaceInEvents, inputs); + }} + resultsCount={ + eventsSearchResultEvents + ? eventsSearchResultEvents.length + : null + } + hasEventSelected={hasEventSelected(this.state.selection)} + onGoToPreviousSearchResult={() => + this._ensureEventUnfolded(goToPreviousSearchResult) + } + onCloseSearchPanel={() => { + this._closeSearchPanel(); + }} + onGoToNextSearchResult={() => + this._ensureEventUnfolded(goToNextSearchResult) + } + searchFocusOffset={searchFocusOffset} + /> + )} ( + Share dialog} + scope="export-and-share" + onClose={props.onClose} + > + + +); + +export default ShareDialogWithErrorBoundary; diff --git a/newIDE/app/src/InstancesEditor/FullSizeInstancesEditorWithScrollbars.js b/newIDE/app/src/InstancesEditor/FullSizeInstancesEditorWithScrollbars.js index e42528167dca..411ffe74d3cc 100644 --- a/newIDE/app/src/InstancesEditor/FullSizeInstancesEditorWithScrollbars.js +++ b/newIDE/app/src/InstancesEditor/FullSizeInstancesEditorWithScrollbars.js @@ -12,6 +12,8 @@ import { useDebounce } from '../Utils/UseDebounce'; import Rectangle from '../Utils/Rectangle'; import PreferencesContext from '../MainFrame/Preferences/PreferencesContext'; import { useIsMounted } from '../Utils/UseIsMounted'; +import ErrorBoundary from '../UI/ErrorBoundary'; +import { Trans } from '@lingui/macro'; const SCROLLBAR_DETECTION_WIDTH = 50; // Those scrollbar dimensions should be the same as in the CSS file Scrollbar.css @@ -378,38 +380,45 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => { {({ width, height }) => (
{width !== undefined && height !== undefined && ( - { - wrappedEditorRef && wrappedEditorRef(editor); - editorRef.current = editor; - }} - width={width} - height={height} - screenType={screenType} - onMouseMove={onMouseMoveOverInstanceEditor} - onMouseLeave={(event: MouseEvent) => { - const { relatedTarget } = event; - if (!isDragging.current && relatedTarget) { - if ( - // Flow says className is not present in ElementTarget but this piece - // of code cannot break. - // $FlowFixMe - relatedTarget.className && - typeof relatedTarget.className === 'string' && - // Hide only if the mouse is not leaving to go on one of the scrollbars' thumb. - // $FlowFixMe - !relatedTarget.className.includes('canvas-scrollbar-thumb') - ) { - hideScrollbarsAfterDelay(); - } + Instances editor} + scope="scene-editor-canvas" + > + + ref={(editor: ?InstancesEditor) => { + wrappedEditorRef && wrappedEditorRef(editor); + editorRef.current = editor; + }} + width={width} + height={height} + screenType={screenType} + onMouseMove={onMouseMoveOverInstanceEditor} + onMouseLeave={(event: MouseEvent) => { + const { relatedTarget } = event; + if (!isDragging.current && relatedTarget) { + if ( + // Flow says className is not present in ElementTarget but this piece + // of code cannot break. + // $FlowFixMe + relatedTarget.className && + typeof relatedTarget.className === 'string' && + // Hide only if the mouse is not leaving to go on one of the scrollbars' thumb. + // $FlowFixMe + !relatedTarget.className.includes( + 'canvas-scrollbar-thumb' + ) + ) { + hideScrollbarsAfterDelay(); + } + } + }} + showObjectInstancesIn3D={values.use3DEditor} + {...otherProps} + /> + )} {screenType !== 'touch' && (
Instance properties} + scope="scene-editor-instance-properties" > , - onSelectInstances: (Array, boolean) => void, -|}; - type RenderedRowInfo = { instance: gdInitialInstance, name: string, @@ -72,7 +62,23 @@ const compareStrings = (x: string, y: string, direction: number): number => { return 0; }; -export default class InstancesList extends Component { +export type InstancesListInterface = {| + forceUpdate: () => void, +|}; + +type State = {| + searchText: string, + sortBy: string, + sortDirection: SortDirection, +|}; + +type Props = {| + instances: gdInitialInstancesContainer, + selectedInstances: Array, + onSelectInstances: (Array, boolean) => void, +|}; + +class InstancesList extends Component { state = { searchText: '', sortBy: '', @@ -344,3 +350,24 @@ export default class InstancesList extends Component { ); } } + +const InstancesListWithErrorBoundary = React.forwardRef< + Props, + InstancesListInterface +>((props, ref) => { + const forceUpdate = useForceUpdate(); + React.useImperativeHandle(ref, () => ({ + forceUpdate, + })); + + return ( + Instances list} + scope="scene-editor-instances-list" + > + + + ); +}); + +export default InstancesListWithErrorBoundary; diff --git a/newIDE/app/src/LayersList/index.js b/newIDE/app/src/LayersList/index.js index 00806e8b337a..b7e180bcfd3e 100644 --- a/newIDE/app/src/LayersList/index.js +++ b/newIDE/app/src/LayersList/index.js @@ -18,6 +18,7 @@ import GDevelopThemeContext from '../UI/Theme/GDevelopThemeContext'; import Add from '../UI/CustomSvgIcons/Add'; import { addDefaultLightToLayer } from '../ProjectCreation/CreateProject'; import { getEffects2DCount, getEffects3DCount } from '../EffectsList'; +import ErrorBoundary from '../UI/ErrorBoundary'; const gd: libGDevelop = global.gd; @@ -215,9 +216,9 @@ type Props = {| hotReloadPreviewButtonProps: HotReloadPreviewButtonProps, |}; -export type LayersListInterface = { +export type LayersListInterface = {| forceUpdate: () => void, -}; +|}; const hasLightingLayer = (layout: gdLayout) => { const layersCount = layout.getLayersCount(); @@ -320,4 +321,16 @@ const LayersList = React.forwardRef( } ); -export default LayersList; +const LayersListWithErrorBoundary = React.forwardRef< + Props, + LayersListInterface +>((props, ref) => ( + Layers list} + scope="scene-editor-layers-list" + > + + +)); + +export default LayersListWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/AboutDialog.js b/newIDE/app/src/MainFrame/AboutDialog.js index 277e147a5c49..e95ea3bd26d6 100644 --- a/newIDE/app/src/MainFrame/AboutDialog.js +++ b/newIDE/app/src/MainFrame/AboutDialog.js @@ -26,6 +26,7 @@ import { } from '../Version'; import { ColumnStackLayout } from '../UI/Layout'; import optionalRequire from '../Utils/OptionalRequire'; +import ErrorBoundary from '../UI/ErrorBoundary'; const electron = optionalRequire('electron'); type Props = {| @@ -248,7 +249,7 @@ const contributors = [ }, ]; -export default function AboutDialog({ onClose, updateStatus }: Props) { +const AboutDialog = ({ onClose, updateStatus }: Props) => { const openContributePage = React.useCallback(() => { Window.openExternalURL('https://gdevelop.io/page/contribute/'); }, []); @@ -416,4 +417,16 @@ export default function AboutDialog({ onClose, updateStatus }: Props) { )} ); -} +}; + +const AboutDialogWithErrorBoundary = (props: Props) => ( + About dialog} + scope="about" + onClose={props.onClose} + > + + +); + +export default AboutDialogWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/index.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/index.js index 2d21ee2cc08a..31673070f5de 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/index.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/BuildSection/index.js @@ -49,6 +49,7 @@ import { getProjectLineHeight, transformCloudProjectsIntoFileMetadataWithStorageProviderName, } from './utils'; +import ErrorBoundary from '../../../../UI/ErrorBoundary'; const styles = { listItem: { @@ -398,4 +399,13 @@ const BuildSection = React.forwardRef( } ); -export default BuildSection; +const BuildSectionWithErrorBoundary = (props: Props) => ( + Build section} + scope="start-page-build" + > + + +); + +export default BuildSectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/CommunitySection.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/CommunitySection.js index 21076233a258..2856541dddd6 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/CommunitySection.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/CommunitySection.js @@ -15,6 +15,7 @@ import SectionContainer, { SectionRow } from './SectionContainer'; import { ListItem } from '../../../UI/List'; import List from '@material-ui/core/List'; import { AnnouncementsFeed } from '../../../AnnouncementsFeed'; +import ErrorBoundary from '../../../UI/ErrorBoundary'; const styles = { list: { @@ -94,4 +95,13 @@ const CommunitySection = () => { ); }; -export default CommunitySection; +const CommunitySectionWithErrorBoundary = () => ( + Community section} + scope="start-page-community" + > + + +); + +export default CommunitySectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/GetStartedSection/index.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/GetStartedSection/index.js index eec128597612..95d72fcd61a3 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/GetStartedSection/index.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/GetStartedSection/index.js @@ -30,6 +30,7 @@ import AuthenticatedUserContext from '../../../../Profile/AuthenticatedUserConte import PreferencesContext from '../../../Preferences/PreferencesContext'; import PlaceholderError from '../../../../UI/PlaceholderError'; import { FLING_GAME_IN_APP_TUTORIAL_ID } from '../../../../Utils/GDevelopServices/InAppTutorial'; +import ErrorBoundary from '../../../../UI/ErrorBoundary'; const getColumnsFromWidth = (width: WidthType) => { switch (width) { @@ -411,4 +412,13 @@ const GetStartedSection = ({ ); }; -export default GetStartedSection; +const GetStartedSectionWithErrorBoundary = (props: Props) => ( + Get started section} + scope="start-page-get-started" + > + + +); + +export default GetStartedSectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/LearnSection/index.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/LearnSection/index.js index fb4750ef7fde..81d0f4badc4a 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/LearnSection/index.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/LearnSection/index.js @@ -18,6 +18,7 @@ import { secondsToMinutesAndSeconds } from '../../../../Utils/DateDisplay'; import { type ImageTileComponent } from '../../../../UI/ImageTileGrid'; import Paper from '../../../../UI/Paper'; import { selectMessageByLocale } from '../../../../Utils/i18n/MessageByLocale'; +import ErrorBoundary from '../../../../UI/ErrorBoundary'; export const TUTORIAL_CATEGORY_TEXTS = { 'full-game': { @@ -142,4 +143,13 @@ const LearnSection = ({ ); }; -export default LearnSection; +const LearnSectionWithErrorBoundary = (props: Props) => ( + Learn section} + scope="start-page-learn" + > + + +); + +export default LearnSectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/PlaySection.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/PlaySection.js index f9ee62afb61e..8cc61ec47ae8 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/PlaySection.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/PlaySection.js @@ -4,6 +4,7 @@ import { Trans } from '@lingui/macro'; import SectionContainer, { SectionRow } from './SectionContainer'; import GDevelopThemeContext from '../../../UI/Theme/GDevelopThemeContext'; import PlaceHolderLoader from '../../../UI/PlaceholderLoader'; +import ErrorBoundary from '../../../UI/ErrorBoundary'; const styles = { iframe: { @@ -44,4 +45,13 @@ const PlaySection = () => { ); }; -export default PlaySection; +const PlaySectionWithErrorBoundary = () => ( + Play section} + scope="start-page-play" + > + + +); + +export default PlaySectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/StoreSection/index.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/StoreSection/index.js index 7a5a3dc9d4af..88b768b76ca5 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/StoreSection/index.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/StoreSection/index.js @@ -10,6 +10,7 @@ import { AssetStoreContext } from '../../../../AssetStore/AssetStoreContext'; import AssetPackInstallDialog from '../../../../AssetStore/AssetPackInstallDialog'; import { enumerateAssetStoreIds } from '../../../../AssetStore/EnumerateAssetStoreIds'; import { type PrivateGameTemplateListingData } from '../../../../Utils/GDevelopServices/Shop'; +import ErrorBoundary from '../../../../UI/ErrorBoundary'; type Props = {| project: ?gdProject, @@ -123,4 +124,13 @@ const StoreSection = ({ ); }; -export default StoreSection; +const StoreSectionWithErrorBoundary = (props: Props) => ( + Shop section} + scope="start-page-shop" + > + + +); + +export default StoreSectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/index.js b/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/index.js index 0177593bcde0..9a13a690f354 100644 --- a/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/index.js +++ b/newIDE/app/src/MainFrame/EditorContainers/HomePage/TeamSection/index.js @@ -35,6 +35,7 @@ import Paper from '../../../../UI/Paper'; import { useResponsiveWindowWidth } from '../../../../UI/Reponsive/ResponsiveWindowMeasurer'; import RaisedButton from '../../../../UI/RaisedButton'; import { groupMembersByGroupId } from './utils'; +import ErrorBoundary from '../../../../UI/ErrorBoundary'; const PADDING = 16; @@ -377,4 +378,13 @@ const TeamSection = React.forwardRef( } ); -export default TeamSection; +const TeamSectionWithErrorBoundary = (props: Props) => ( + Team section} + scope="start-page-team" + > + + +); + +export default TeamSectionWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/Preferences/LanguageDialog.js b/newIDE/app/src/MainFrame/Preferences/LanguageDialog.js index 267101e87eef..e5392a71c9c0 100644 --- a/newIDE/app/src/MainFrame/Preferences/LanguageDialog.js +++ b/newIDE/app/src/MainFrame/Preferences/LanguageDialog.js @@ -11,7 +11,7 @@ import LanguageSelector from './LanguageSelector'; type Props = {| open: boolean, - onClose: (languageDidChange: boolean) => void, + onClose: (options: {| languageDidChange: boolean |}) => void, |}; const LanguageDialog = ({ open, onClose }: Props) => { @@ -43,7 +43,7 @@ const LanguageDialog = ({ open, onClose }: Props) => { } primary={false} onClick={() => { - onClose(languageDidChange); + onClose({ languageDidChange }); }} disabled={isLoadingLanguage} key="close" @@ -62,7 +62,7 @@ const LanguageDialog = ({ open, onClose }: Props) => { />, ]} cannotBeDismissed={isLoadingLanguage} - onRequestClose={() => onClose(languageDidChange)} + onRequestClose={() => onClose({ languageDidChange })} open={open} maxWidth="sm" > diff --git a/newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js b/newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js index 48af3575713f..d0c80498dd2d 100644 --- a/newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js +++ b/newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js @@ -27,11 +27,12 @@ import { adaptAcceleratorString } from '../../UI/AcceleratorString'; import { getElectronAccelerator } from '../../KeyboardShortcuts'; import defaultShortcuts from '../../KeyboardShortcuts/DefaultShortcuts'; import AlertMessage from '../../UI/AlertMessage'; +import ErrorBoundary from '../../UI/ErrorBoundary'; const electron = optionalRequire('electron'); type Props = {| i18n: I18n, - onClose: (languageDidChange: boolean) => void, + onClose: (options: {| languageDidChange: boolean |}) => void, |}; const PreferencesDialog = ({ i18n, onClose }: Props) => { @@ -82,10 +83,10 @@ const PreferencesDialog = ({ i18n, onClose }: Props) => { key="close" label={Close} primary={false} - onClick={() => onClose(languageDidChange)} + onClick={() => onClose({ languageDidChange })} />, ]} - onRequestClose={() => onClose(languageDidChange)} + onRequestClose={() => onClose({ languageDidChange })} open maxWidth="sm" fixedContent={ @@ -462,4 +463,14 @@ const PreferencesDialog = ({ i18n, onClose }: Props) => { ); }; -export default PreferencesDialog; +const PreferencesDialogWithErrorBoundary = (props: Props) => ( + Preferences} + scope="preferences" + onClose={() => props.onClose({ languageDidChange: false })} + > + + +); + +export default PreferencesDialogWithErrorBoundary; diff --git a/newIDE/app/src/MainFrame/Providers.js b/newIDE/app/src/MainFrame/Providers.js index d10d7e06ba08..9d4032f00d76 100644 --- a/newIDE/app/src/MainFrame/Providers.js +++ b/newIDE/app/src/MainFrame/Providers.js @@ -36,6 +36,7 @@ import { RouterContextProvider } from './RouterContext'; import ErrorBoundary from '../UI/ErrorBoundary'; import { FullThemeProvider } from '../UI/Theme/FullThemeProvider'; import { useShopNavigation } from '../AssetStore/AssetStoreNavigator'; +import { Trans } from '@lingui/macro'; type Props = {| authentication: Authentication, @@ -73,7 +74,7 @@ const Providers = ({ GDevelop app} scope="app" > diff --git a/newIDE/app/src/MainFrame/index.js b/newIDE/app/src/MainFrame/index.js index cfe57d5d869e..73aa0bf33e2d 100644 --- a/newIDE/app/src/MainFrame/index.js +++ b/newIDE/app/src/MainFrame/index.js @@ -51,7 +51,9 @@ import { renderEventsFunctionsExtensionEditorContainer } from './EditorContainer import { renderHomePageContainer } from './EditorContainers/HomePage'; import { renderResourcesEditorContainer } from './EditorContainers/ResourcesEditorContainer'; import { type RenderEditorContainerPropsWithRef } from './EditorContainers/BaseEditor'; -import ErrorBoundary from '../UI/ErrorBoundary'; +import ErrorBoundary, { + getEditorErrorBoundaryProps, +} from '../UI/ErrorBoundary'; import ResourcesLoader from '../ResourcesLoader/index'; import { type PreviewLauncherInterface, @@ -3112,13 +3114,14 @@ const MainFrame = (props: Props) => { > {getEditors(state.editorTabs).map((editorTab, id) => { const isCurrentTab = getCurrentTabIndex(state.editorTabs) === id; + const errorBoundaryProps = getEditorErrorBoundaryProps(editorTab.key); return ( {editorTab.renderEditorContainer({ isActive: isCurrentTab, @@ -3357,18 +3360,18 @@ const MainFrame = (props: Props) => { {preferencesDialogOpen && ( { + onClose={options => { openPreferencesDialog(false); - if (languageChanged) _languageDidChange(); + if (options.languageDidChange) _languageDidChange(); }} /> )} {languageDialogOpen && ( { + onClose={options => { openLanguageDialog(false); - if (languageChanged) _languageDidChange(); + if (options.languageDidChange) _languageDidChange(); }} /> )} diff --git a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js index a80203e3140f..ab0f8221890c 100644 --- a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js +++ b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js @@ -23,6 +23,7 @@ import VariablesList from '../VariablesList/VariablesList'; import { sendBehaviorsEditorShown } from '../Utils/Analytics/EventSender'; import useDismissableTutorialMessage from '../Hints/useDismissableTutorialMessage'; import useAlertDialog from '../UI/Alert/useAlertDialog'; +import ErrorBoundary from '../UI/ErrorBoundary'; const gd: libGDevelop = global.gd; @@ -344,7 +345,7 @@ type State = {| objectName: string, |}; -export default class ObjectEditorDialog extends React.Component { +class ObjectEditorDialog extends React.Component { state = { editorComponent: null, castToObjectType: null, @@ -408,3 +409,16 @@ export default class ObjectEditorDialog extends React.Component { ); } } + +const ObjectEditorWithErrorBoundary = (props: Props) => ( + Object editor} + scope="object-details" + onClose={props.onCancel} + showOnTop + > + + +); + +export default ObjectEditorWithErrorBoundary; diff --git a/newIDE/app/src/ObjectGroupsList/index.js b/newIDE/app/src/ObjectGroupsList/index.js index 239881fb6195..632e180f8c5e 100644 --- a/newIDE/app/src/ObjectGroupsList/index.js +++ b/newIDE/app/src/ObjectGroupsList/index.js @@ -25,6 +25,7 @@ import { type EmptyPlaceholder } from '../ObjectsList'; import TreeView, { type TreeViewInterface } from '../UI/TreeView'; import useForceUpdate from '../Utils/UseForceUpdate'; import useAlertDialog from '../UI/Alert/useAlertDialog'; +import ErrorBoundary from '../UI/ErrorBoundary'; export const groupWithContextReactDndType = 'GD_GROUP_WITH_CONTEXT'; @@ -623,7 +624,21 @@ const arePropsEqual = (prevProps: Props, nextProps: Props): boolean => prevProps.globalObjectGroups === nextProps.globalObjectGroups || prevProps.objectGroups === nextProps.objectGroups; -export default React.memo( +const MemoizedObjectGroupsList = React.memo( ObjectGroupsList, arePropsEqual ); + +const ObjectGroupsListWithErrorBoundary = React.forwardRef< + Props, + ObjectGroupsListInterface +>((props, ref) => ( + Object groups list} + scope="scene-editor-object-groups-list" + > + + +)); + +export default ObjectGroupsListWithErrorBoundary; diff --git a/newIDE/app/src/ObjectsList/index.js b/newIDE/app/src/ObjectsList/index.js index ca52485f8358..53eface5c218 100644 --- a/newIDE/app/src/ObjectsList/index.js +++ b/newIDE/app/src/ObjectsList/index.js @@ -50,6 +50,7 @@ import Link from '../UI/Link'; import { getHelpLink } from '../Utils/HelpLink'; import useAlertDialog from '../UI/Alert/useAlertDialog'; import { useResponsiveWindowWidth } from '../UI/Reponsive/ResponsiveWindowMeasurer'; +import ErrorBoundary from '../UI/ErrorBoundary'; const gd: libGDevelop = global.gd; const sceneObjectsRootFolderId = 'scene-objects'; @@ -1591,7 +1592,21 @@ const arePropsEqual = (prevProps: Props, nextProps: Props): boolean => prevProps.project === nextProps.project && prevProps.objectsContainer === nextProps.objectsContainer; -export default React.memo( +const MemoizedObjectsList = React.memo( ObjectsList, arePropsEqual ); + +const ObjectsListWithErrorBoundary = React.forwardRef< + Props, + ObjectsListInterface +>((props, ref) => ( + Objects list} + scope="scene-editor-objects-list" + > + + +)); + +export default ObjectsListWithErrorBoundary; diff --git a/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js b/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js index 2303cdf8dd0b..3ae82984d8d1 100644 --- a/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js +++ b/newIDE/app/src/PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog.js @@ -17,6 +17,7 @@ import optionalRequire from '../Utils/OptionalRequire'; import Text from '../UI/Text'; import { ColumnStackLayout } from '../UI/Layout'; import AlertMessage from '../UI/AlertMessage'; +import ErrorBoundary from '../UI/ErrorBoundary'; const path = optionalRequire('path'); const gd: libGDevelop = global.gd; @@ -68,10 +69,7 @@ const iosSizes = [ 20, ]; -export default class PlatformSpecificAssetsDialog extends React.Component< - Props, - State -> { +class PlatformSpecificAssetsDialog extends React.Component { constructor(props: Props) { super(props); this.state = this._loadFrom(props.project); @@ -448,3 +446,16 @@ export default class PlatformSpecificAssetsDialog extends React.Component< ); } } + +const PlatformSpecificAssetsDialogWithErrorBoundary = (props: Props) => ( + Project icons} + scope="project-icons" + onClose={props.onClose} + showOnTop + > + + +); + +export default PlatformSpecificAssetsDialogWithErrorBoundary; diff --git a/newIDE/app/src/Profile/ProfileDialog.js b/newIDE/app/src/Profile/ProfileDialog.js index 4eead7b30b28..f5405576ecf9 100644 --- a/newIDE/app/src/Profile/ProfileDialog.js +++ b/newIDE/app/src/Profile/ProfileDialog.js @@ -21,6 +21,7 @@ import PlaceholderLoader from '../UI/PlaceholderLoader'; import RouterContext from '../MainFrame/RouterContext'; import useIsElementVisibleInScroll from '../Utils/UseIsElementVisibleInScroll'; import { markBadgesAsSeen as doMarkBadgesAsSeen } from '../Utils/GDevelopServices/Badge'; +import ErrorBoundary from '../UI/ErrorBoundary'; export type ProfileTab = 'profile' | 'games-dashboard'; @@ -249,4 +250,14 @@ const ProfileDialog = ({ currentProject, open, onClose }: Props) => { ); }; -export default ProfileDialog; +const ProfileDialogWithErrorBoundary = (props: Props) => ( + Profile} + scope="profile" + onClose={props.onClose} + > + + +); + +export default ProfileDialogWithErrorBoundary; diff --git a/newIDE/app/src/ProjectManager/ProjectPropertiesDialog.js b/newIDE/app/src/ProjectManager/ProjectPropertiesDialog.js index 66525f8e71d7..cede6b65c1bf 100644 --- a/newIDE/app/src/ProjectManager/ProjectPropertiesDialog.js +++ b/newIDE/app/src/ProjectManager/ProjectPropertiesDialog.js @@ -32,6 +32,7 @@ import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewB import PublicGameProperties from '../GameDashboard/PublicGameProperties'; import PreviewIcon from '../UI/CustomSvgIcons/Preview'; import { useResponsiveWindowWidth } from '../UI/Reponsive/ResponsiveWindowMeasurer'; +import ErrorBoundary from '../UI/ErrorBoundary'; type Props = {| project: gdProject, @@ -759,4 +760,15 @@ const ProjectPropertiesDialog = (props: Props) => { ); }; -export default ProjectPropertiesDialog; +const ProjectPropertiesDialogWithErrorBoundary = (props: Props) => ( + Project properties} + scope="project-properties" + onClose={props.onClose} + showOnTop + > + + +); + +export default ProjectPropertiesDialogWithErrorBoundary; diff --git a/newIDE/app/src/ProjectManager/index.js b/newIDE/app/src/ProjectManager/index.js index d18d2fcf89bb..29ba4b7ea288 100644 --- a/newIDE/app/src/ProjectManager/index.js +++ b/newIDE/app/src/ProjectManager/index.js @@ -54,6 +54,7 @@ import Paper from '../UI/Paper'; import { makeDragSourceAndDropTarget } from '../UI/DragAndDrop/DragSourceAndDropTarget'; import { useScreenType } from '../UI/Reponsive/ScreenTypeMeasurer'; import { addDefaultLightToAllLayers } from '../ProjectCreation/CreateProject'; +import ErrorBoundary from '../UI/ErrorBoundary'; const LAYOUT_CLIPBOARD_KIND = 'Layout'; const EXTERNAL_LAYOUT_CLIPBOARD_KIND = 'External layout'; @@ -147,7 +148,7 @@ type State = {| layoutVariablesDialogOpen: boolean, |}; -export default class ProjectManager extends React.Component { +class ProjectManager extends React.Component { _searchBar: ?SearchBarInterface; _draggedLayoutIndex: number | null = null; _draggedExternalLayoutIndex: number | null = null; @@ -1276,3 +1277,14 @@ export default class ProjectManager extends React.Component { ); } } + +const ProjectManagerWithErrorBoundary = (props: Props) => ( + Project manager} + scope="project-manager" + > + + +); + +export default ProjectManagerWithErrorBoundary; diff --git a/newIDE/app/src/SceneEditor/MosaicEditorsDisplay/index.js b/newIDE/app/src/SceneEditor/MosaicEditorsDisplay/index.js index 2a8c33da1866..41b43c624551 100644 --- a/newIDE/app/src/SceneEditor/MosaicEditorsDisplay/index.js +++ b/newIDE/app/src/SceneEditor/MosaicEditorsDisplay/index.js @@ -18,7 +18,9 @@ import ObjectsList, { type ObjectsListInterface } from '../../ObjectsList'; import ObjectGroupsList, { type ObjectGroupsListInterface, } from '../../ObjectGroupsList'; -import InstancesList from '../../InstancesEditor/InstancesList'; +import InstancesList, { + type InstancesListInterface, +} from '../../InstancesEditor/InstancesList'; import ObjectsRenderingService from '../../ObjectsRendering/ObjectsRenderingService'; import Rectangle from '../../Utils/Rectangle'; @@ -94,7 +96,7 @@ const MosaicEditorsDisplay = React.forwardRef< null ); const layersListRef = React.useRef(null); - const instancesListRef = React.useRef(null); + const instancesListRef = React.useRef(null); const editorRef = React.useRef(null); const objectsListRef = React.useRef(null); const editorMosaicRef = React.useRef(null); diff --git a/newIDE/app/src/SceneEditor/SwipeableDrawerEditorsDisplay/index.js b/newIDE/app/src/SceneEditor/SwipeableDrawerEditorsDisplay/index.js index 0c3f46833d54..fd7a6dd1ce79 100644 --- a/newIDE/app/src/SceneEditor/SwipeableDrawerEditorsDisplay/index.js +++ b/newIDE/app/src/SceneEditor/SwipeableDrawerEditorsDisplay/index.js @@ -13,7 +13,9 @@ import ObjectsList, { type ObjectsListInterface } from '../../ObjectsList'; import ObjectGroupsList, { type ObjectGroupsListInterface, } from '../../ObjectGroupsList'; -import InstancesList from '../../InstancesEditor/InstancesList'; +import InstancesList, { + type InstancesListInterface, +} from '../../InstancesEditor/InstancesList'; import ObjectsRenderingService from '../../ObjectsRendering/ObjectsRenderingService'; import Rectangle from '../../Utils/Rectangle'; @@ -28,6 +30,7 @@ import { type SceneEditorsDisplayInterface, type SceneEditorsDisplayProps, } from '../EditorsDisplay.flow'; +import ErrorBoundary from '../../UI/ErrorBoundary'; export const swipeableDrawerContainerId = 'swipeable-drawer-container'; @@ -67,7 +70,7 @@ const SwipeableDrawerEditorsDisplay = React.forwardRef< null ); const layersListRef = React.useRef(null); - const instancesListRef = React.useRef(null); + const instancesListRef = React.useRef(null); const editorRef = React.useRef(null); const objectsListRef = React.useRef(null); const objectGroupsListRef = React.useRef(null); @@ -225,35 +228,40 @@ const SwipeableDrawerEditorsDisplay = React.forwardRef< {({ width, height }) => (
- + Instances editor.} + scope="scene-editor-canvas" + > + +
{ + if (editorKey.startsWith('debugger')) { + return { + componentTitle: Debugger, + scope: 'debugger', + }; + } + if (editorKey.startsWith('start page')) { + return { + componentTitle: Home page, + scope: 'start-page', + }; + } + if (editorKey.startsWith('resources')) { + return { + componentTitle: Resources, + scope: 'resources', + }; + } + if (editorKey.startsWith('events functions extension')) { + return { + componentTitle: Events functions extension, + scope: 'extension-editor', + }; + } + if (editorKey.startsWith('layout events')) { + return { + componentTitle: Events, + scope: 'scene-events', + }; + } + if (editorKey.startsWith('layout')) { + return { + componentTitle: Scene, + scope: 'scene-editor', + }; + } + if (editorKey.startsWith('external layout')) { + return { + componentTitle: External layout, + scope: 'external-layout-editor', + }; + } + if (editorKey.startsWith('external events')) { + return { + componentTitle: External events, + scope: 'external-events-editor', + }; + } + + return { + componentTitle: Editor, + scope: 'editor', + }; +}; const errorHandler = ( error: Error, @@ -75,94 +167,118 @@ const errorHandler = ( export const ErrorFallbackComponent = ({ componentStack, error, - title, + componentTitle, uniqueErrorId, + onClose, + showOnTop, }: {| componentStack: string, error: Error, - title: string, + componentTitle: React.Node, uniqueErrorId: string, -|}) => ( - - - - - - {title} - - + onClose?: () => void, + showOnTop?: boolean, +|}) => { + const isCriticalError = error.stack && error.stack.includes('.wasm'); + return ( + - + + + + + + {isCriticalError ? ( + + A critical error occurred in the {componentTitle}. + + ) : ( + An error occurred in the {componentTitle}. + )} + + + {onClose && ( + + + + )} + + + + + + + Please backup your game file and save your game to ensure + that you don't lose anything. + + + - Please backup your game file and save your game to ensure - that you don't lose anything. + To help us fix this issue, you can create a{' '} + Window.openExternalURL('https://github.com')} + > + GitHub account + {' '} + then report the issue with the button below. (ID: {uniqueErrorId}) - - - - To help us fix this issue, you can create a{' '} - Window.openExternalURL('https://github.com')} - > - GitHub account - {' '} - then report the issue with the button below. (ID: {uniqueErrorId}) - - - {error && error.stack && ( - - {error.stack.slice(0, 200)}... - - )} - {componentStack && ( - - {componentStack.slice(0, 200)}... - - )} - - - Report the issue on GitHub} - primary - onClick={() => { - const templateFile = '--automatic-crash.yml'; - const title = 'Crash while using an editor'; - const errorStack = - error && error.stack - ? `${error.stack.slice(0, 600)}...` - : 'No error found'; - const gdevelopVersion = getIDEVersionWithHash(); - const platformInfo = `System Version: ${getSystemVersion()}, Arch: ${getArch()}, User Agent: ${getUserAgent()}, Platform: ${getPlatformName()}`; - const additionalContext = componentStack - ? `${componentStack.slice(0, 600)}...` - : 'No component stack found'; + {error && error.stack && ( + + {error.stack.slice(0, 200)}... + + )} + {componentStack && ( + + {componentStack.slice(0, 200)}... + + )} + + + Report the issue on GitHub} + primary + onClick={() => { + const templateFile = '--automatic-crash.yml'; + const title = 'Crash while using an editor'; + const errorStack = + error && error.stack + ? `${error.stack.slice(0, 600)}...` + : 'No error found'; + const gdevelopVersion = getIDEVersionWithHash(); + const platformInfo = `System Version: ${getSystemVersion()}, Arch: ${getArch()}, User Agent: ${getUserAgent()}, Platform: ${getPlatformName()}`; + const additionalContext = componentStack + ? `${componentStack.slice(0, 600)}...` + : 'No component stack found'; - const baseUrl = new URL( - 'https://github.com/4ian/GDevelop/issues/new' - ); - baseUrl.searchParams.set('template', templateFile); - baseUrl.searchParams.set('title', title); - baseUrl.searchParams.set('labels', '💥crash'); - baseUrl.searchParams.set('gdevelop_version', gdevelopVersion); - baseUrl.searchParams.set('platform_info', platformInfo); - baseUrl.searchParams.set('error_stack', errorStack); - baseUrl.searchParams.set('component_stack', additionalContext); + const baseUrl = new URL( + 'https://github.com/4ian/GDevelop/issues/new' + ); + baseUrl.searchParams.set('template', templateFile); + baseUrl.searchParams.set('title', title); + baseUrl.searchParams.set('labels', '💥crash'); + baseUrl.searchParams.set('gdevelop_version', gdevelopVersion); + baseUrl.searchParams.set('platform_info', platformInfo); + baseUrl.searchParams.set('error_stack', errorStack); + baseUrl.searchParams.set('component_stack', additionalContext); - Window.openExternalURL(baseUrl.href); - }} - /> - - - -); + Window.openExternalURL(baseUrl.href); + }} + /> + + + + ); +}; type Props = {| children: React.Node, - title: string, + componentTitle: React.Node, scope: ErrorBoundaryScope, + onClose?: () => void, + showOnTop?: boolean, |}; const ErrorBoundary = (props: Props) => { @@ -172,8 +288,10 @@ const ErrorBoundary = (props: Props) => { FallbackComponent={fallbackComponentProps => ( )} onError={(error, componentStack) => { diff --git a/newIDE/app/src/UI/PlaceholderMessage.js b/newIDE/app/src/UI/PlaceholderMessage.js index e813fd9fbe84..acaa22d5a547 100644 --- a/newIDE/app/src/UI/PlaceholderMessage.js +++ b/newIDE/app/src/UI/PlaceholderMessage.js @@ -4,6 +4,7 @@ import Paper from './Paper'; type Props = {| children: React.Node, + showOnTop?: boolean, |}; const PlaceholderMessage = (props: Props) => { @@ -19,13 +20,15 @@ const PlaceholderMessage = (props: Props) => { display: 'flex', alignItems: 'center', justifyContent: 'center', + zIndex: props.showOnTop ? 9999 : undefined, // Ensure it's above most things }} > diff --git a/newIDE/app/src/UI/Search/BoxSearchResults.js b/newIDE/app/src/UI/Search/BoxSearchResults.js index 5e850099289c..7dd1f51f2655 100644 --- a/newIDE/app/src/UI/Search/BoxSearchResults.js +++ b/newIDE/app/src/UI/Search/BoxSearchResults.js @@ -100,7 +100,7 @@ export const BoxSearchResults = React.forwardRef< return ( Search results} scope="box-search-result" >
diff --git a/newIDE/app/src/UI/Search/ListSearchResults.js b/newIDE/app/src/UI/Search/ListSearchResults.js index 2a9bf963388a..c3a702a759a6 100644 --- a/newIDE/app/src/UI/Search/ListSearchResults.js +++ b/newIDE/app/src/UI/Search/ListSearchResults.js @@ -114,7 +114,7 @@ export const ListSearchResults = ({ return ( Search results} scope="list-search-result" >
{ return ( Variables list} scope="variables-list" > diff --git a/newIDE/app/src/stories/componentStories/ErrorBoundaries.stories.js b/newIDE/app/src/stories/componentStories/ErrorBoundaries.stories.js new file mode 100644 index 000000000000..deda0e1cc29e --- /dev/null +++ b/newIDE/app/src/stories/componentStories/ErrorBoundaries.stories.js @@ -0,0 +1,91 @@ +// @flow +import * as React from 'react'; + +import muiDecorator from '../ThemeDecorator'; +import paperDecorator from '../PaperDecorator'; +import { ErrorFallbackComponent } from '../../UI/ErrorBoundary'; +import { generateUUID } from 'three/src/math/MathUtils'; + +export default { + title: 'ErrorBoundary/ErrorFallbackComponent', + component: ErrorFallbackComponent, + decorators: [paperDecorator, muiDecorator], +}; + +const fakeError = new Error('Fake error for storybook'); +const fakeComponentStack = ` +in e +in div +in s +in f +in e +in div +in s +in div +in s +in div +in ForwardRef +in ck +in div +in ForwardRef +in ForwardRef +in Unknown +in Ff +in ForwardRef +in t +in div +in div +in t +in DragSource(t) +in DropTarget(DragSource(t)) +in n +in y +in t +in div +in div +in t +in div +in t +in n +in ForwardRef +in div +in l +in n +in n +in t +in Unknown +in aI +in div +in n +in Na +in div +in xL +in Unknown +in mt +in f +in u +in v +in m`; +const fakeErrorWithCriticalStack = new Error('Fake error for storybook'); +const fakeCriticalErrorStack = ` +TypeError: Cannot read properties of undefined (reading 'toString') at a.getProperties (https://editor.gdevelop.io/static/js/1859.6eb1cd77.chunk.js:2:680094) at 4317 (https://editor.gdevelop.io/libGD.js?cache-buster=5.3.180-8dbf9c99ce5a0fe0417c0cb09302abcbc974d172:9:21098) at _emscripten_asm_const_iii (https://editor.gdevelop.io/libGD.js?cache-buster=5.3.180-8dbf9c99ce5a0fe0417c0cb09302abcbc974d172:9:28304) at https://editor.gdevelop.io/libGD.wasm?cache-buster=5.3.180-8dbf9c99ce5a0fe0417c0cb09302abcbc974d172:wasm-function[52]:0x26260 at https://editor.gdevelop.io/libGD.wasm?cache-buster=5.3.180-8dbf9c99ce5a0fe0417c0cb09302abcbc974d172:wasm-function[126]:0x2813d at ObjectConfiguration.GetProperties.ObjectConfiguration.GetProperties [as getProperties] (https://editor.gdevelop.io/libGD.js?cache-buster=5.3.180-8dbf9c99ce5a0fe0417c0cb09302abcbc974d172:9:1000775) at d.update (https://editor.gdevelop.io/static/js/1859.6eb1cd77.chunk.js:2:689155) at new d (https://editor.gdevelop.io/static/js/1859.6eb1cd77.chunk.js:2:688949) at Object.createNewInstanceRenderer (https://editor.gdevelop.io/static/js/5496.fae99fda.chunk.js:1:1978219) at e.getRendererOfInstance (https://editor.gdevelop.io/static/js/5496.fae99fda.chunk.js:1:1340592) +`; +fakeErrorWithCriticalStack.stack = fakeCriticalErrorStack; + +export const Default = () => ( + +); + +export const Critical = () => ( + +); diff --git a/newIDE/app/src/stories/componentStories/UI/PlaceholderMessage.stories.js b/newIDE/app/src/stories/componentStories/UI/PlaceholderMessage.stories.js new file mode 100644 index 000000000000..eb1dff35e01f --- /dev/null +++ b/newIDE/app/src/stories/componentStories/UI/PlaceholderMessage.stories.js @@ -0,0 +1,22 @@ +// @flow +import * as React from 'react'; + +import muiDecorator from '../../ThemeDecorator'; +import paperDecorator from '../../PaperDecorator'; + +import PlaceholderMessage from '../../../UI/PlaceholderMessage'; + +export default { + title: 'PlaceholderMessage', + component: PlaceholderMessage, + decorators: [paperDecorator, muiDecorator], +}; + +export const Default = () => ( + +

+ Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, + consectetur, adipisci velit +

+
+); diff --git a/newIDE/app/src/stories/everything-else.stories.js b/newIDE/app/src/stories/everything-else.stories.js index f9003fd0e93a..0ede8a481b3c 100644 --- a/newIDE/app/src/stories/everything-else.stories.js +++ b/newIDE/app/src/stories/everything-else.stories.js @@ -37,7 +37,6 @@ import ValueStateHolder from './ValueStateHolder'; import DragAndDropContextProvider from '../UI/DragAndDrop/DragAndDropContextProvider'; import InstructionSelector from '../EventsSheet/InstructionEditor/InstructionOrExpressionSelector/InstructionSelector'; import ParameterRenderingService from '../EventsSheet/ParameterRenderingService'; -import { ErrorFallbackComponent } from '../UI/ErrorBoundary'; import CreateProfile from '../Profile/CreateProfile'; import AuthenticatedUserProfileDetails from '../Profile/AuthenticatedUserProfileDetails'; import CurrentUsageDisplayer from '../Profile/CurrentUsageDisplayer'; @@ -65,7 +64,6 @@ import BuildStepsProgress from '../ExportAndShare/Builds/BuildStepsProgress'; import MeasuresTable from '../Debugger/Profiler/MeasuresTable'; import Profiler from '../Debugger/Profiler'; import SearchPanel from '../EventsSheet/SearchPanel'; -import PlaceholderMessage from '../UI/PlaceholderMessage'; import PlaceholderLoader from '../UI/PlaceholderLoader'; import ColorField from '../UI/ColorField'; import EmptyMessage from '../UI/EmptyMessage'; @@ -980,18 +978,6 @@ storiesOf('UI Building Blocks/Accordion', module) )); -storiesOf('UI Building Blocks/PlaceholderMessage', module) - .addDecorator(paperDecorator) - .addDecorator(muiDecorator) - .add('default', () => ( - -

- Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, - consectetur, adipisci velit -

-
- )); - storiesOf('UI Building Blocks/PlaceholderLoader', module) .addDecorator(paperDecorator) .addDecorator(muiDecorator) @@ -2463,19 +2449,6 @@ storiesOf('ObjectSelector', module) /> )); -const fakeError = new Error('Fake error for storybook'); -storiesOf('ErrorBoundary', module) - .addDecorator(paperDecorator) - .addDecorator(muiDecorator) - .add('default', () => ( - - )); - storiesOf('Changelog', module) .addDecorator(paperDecorator) .addDecorator(muiDecorator)