Add general error message for wardrobe crashes

Boom, pulled some stuff out into util! Now we also have error boundaries in both panels of the wardrobe page.

You can test this one by visiting `/outfits/new?send-test-error-for-sentry`, just like on the home page! Util component for fake errors owo
This commit is contained in:
Emi Matchu 2021-05-05 00:22:28 -07:00
parent 6dc6ad2578
commit 65af7ef64c
6 changed files with 139 additions and 106 deletions

View file

@ -29,6 +29,7 @@ import {
ErrorMessage,
Heading1,
Heading2,
TestErrorSender,
useCommonStyles,
useLocalStorage,
usePageTitle,
@ -51,12 +52,6 @@ function HomePage() {
const [previewState, setPreviewState] = React.useState(null);
React.useEffect(() => {
if (window.location.href.includes("send-test-error-for-sentry")) {
throw new Error("Test error for Sentry");
}
});
return (
<Flex direction="column" align="center" textAlign="center" marginTop="8">
<Box
@ -103,6 +98,7 @@ function HomePage() {
<WIPCallout details="We started building this last year, but, well… what a year 😅 Anyway, this will eventually become the main site, at impress.openneo.net!">
Maybe we'll rename it to Impress 2021 or maybe not! 🤔
</WIPCallout>
<TestErrorSender />
</Flex>
);
}

View file

@ -1,11 +1,9 @@
import React from "react";
import { Box, Flex, Grid, Link } from "@chakra-ui/react";
import { Box } from "@chakra-ui/react";
import { loadable } from "./util";
import * as Sentry from "@sentry/react";
import { WarningIcon } from "@chakra-ui/icons";
import ErrorGrundoImg from "./images/error-grundo.png";
import ErrorGrundoImg2x from "./images/error-grundo@2x.png";
import { MajorErrorMessage } from "./util";
const GlobalHeader = loadable(() => import("./GlobalHeader"));
const GlobalFooter = loadable(() => import("./GlobalFooter"));
@ -41,49 +39,4 @@ function PageLayout({ children }) {
);
}
function MajorErrorMessage({ error }) {
return (
<Flex justify="center" marginTop="8">
<Grid
templateAreas='"icon title" "icon description" "icon details"'
templateColumns="auto 1fr"
maxWidth="500px"
columnGap="4"
>
<Box gridArea="icon" marginTop="2">
<Box
as="img"
src={ErrorGrundoImg}
srcSet={`${ErrorGrundoImg} 1x, ${ErrorGrundoImg2x} 2x`}
borderRadius="full"
boxShadow="md"
width="100px"
height="100px"
alt=""
/>
</Box>
<Box gridArea="title" fontSize="lg" marginBottom="1">
Ah dang, I broke it 😖
</Box>
<Box gridArea="description" marginBottom="2">
There was an error displaying this page. I'll get info about it
automatically, but you can tell me more at{" "}
<Link href="mailto:matchu@openneo.net" color="green.400">
matchu@openneo.net
</Link>
!
</Box>
<Box gridArea="details" fontSize="xs" opacity="0.8">
<WarningIcon
marginRight="1.5"
marginTop="-2px"
aria-label="Error message"
/>
"{error.message}"
</Box>
</Grid>
</Flex>
);
}
export default PageLayout;

View file

@ -1,5 +1,6 @@
import React from "react";
import { Box, Flex } from "@chakra-ui/react";
import * as Sentry from "@sentry/react";
import ItemsPanel from "./ItemsPanel";
import SearchToolbar, {
@ -7,6 +8,7 @@ import SearchToolbar, {
searchQueryIsEmpty,
} from "./SearchToolbar";
import SearchPanel from "./SearchPanel";
import { MajorErrorMessage, TestErrorSender } from "../util";
/**
* ItemsAndSearchPanels manages the shared layout and state for:
@ -21,60 +23,68 @@ import SearchPanel from "./SearchPanel";
* performing some wiring to help them interact with each other via simple
* state and refs.
*/
function ItemsAndSearchPanels({ loading, outfitState, outfitSaving, dispatchToOutfit }) {
function ItemsAndSearchPanels({
loading,
outfitState,
outfitSaving,
dispatchToOutfit,
}) {
const [searchQuery, setSearchQuery] = React.useState(emptySearchQuery);
const scrollContainerRef = React.useRef();
const searchQueryRef = React.useRef();
const firstSearchResultRef = React.useRef();
return (
<Flex direction="column" height="100%">
<Box px="5" py="3" boxShadow="sm">
<SearchToolbar
query={searchQuery}
searchQueryRef={searchQueryRef}
firstSearchResultRef={firstSearchResultRef}
onChange={setSearchQuery}
/>
</Box>
{!searchQueryIsEmpty(searchQuery) ? (
<Box
key="search-panel"
gridArea="items"
position="relative"
overflow="auto"
ref={scrollContainerRef}
data-test-id="search-panel-scroll-container"
>
<Box px="4" py="2">
<SearchPanel
query={searchQuery}
outfitState={outfitState}
dispatchToOutfit={dispatchToOutfit}
scrollContainerRef={scrollContainerRef}
searchQueryRef={searchQueryRef}
firstSearchResultRef={firstSearchResultRef}
/>
</Box>
<Sentry.ErrorBoundary fallback={MajorErrorMessage}>
<TestErrorSender />
<Flex direction="column" height="100%">
<Box px="5" py="3" boxShadow="sm">
<SearchToolbar
query={searchQuery}
searchQueryRef={searchQueryRef}
firstSearchResultRef={firstSearchResultRef}
onChange={setSearchQuery}
/>
</Box>
) : (
<Box
gridArea="items"
position="relative"
overflow="auto"
key="items-panel"
>
<Box px="4" py="2">
<ItemsPanel
loading={loading}
outfitState={outfitState}
outfitSaving={outfitSaving}
dispatchToOutfit={dispatchToOutfit}
/>
{!searchQueryIsEmpty(searchQuery) ? (
<Box
key="search-panel"
gridArea="items"
position="relative"
overflow="auto"
ref={scrollContainerRef}
data-test-id="search-panel-scroll-container"
>
<Box px="4" py="2">
<SearchPanel
query={searchQuery}
outfitState={outfitState}
dispatchToOutfit={dispatchToOutfit}
scrollContainerRef={scrollContainerRef}
searchQueryRef={searchQueryRef}
firstSearchResultRef={firstSearchResultRef}
/>
</Box>
</Box>
</Box>
)}
</Flex>
) : (
<Box
gridArea="items"
position="relative"
overflow="auto"
key="items-panel"
>
<Box px="4" py="2">
<ItemsPanel
loading={loading}
outfitState={outfitState}
outfitSaving={outfitSaving}
dispatchToOutfit={dispatchToOutfit}
/>
</Box>
</Box>
)}
</Flex>
</Sentry.ErrorBoundary>
);
}

View file

@ -32,7 +32,12 @@ function WardrobePageLayout({ previewAndControls, itemsAndSearch }) {
height="100%"
width="100%"
>
<Box gridArea="previewAndControls" bg="gray.900" position="relative">
<Box
gridArea="previewAndControls"
bg="gray.900"
color="gray.50"
position="relative"
>
{previewAndControls}
</Box>
<Box gridArea="itemsAndSearch" bg={itemsAndSearchBackground}>

View file

@ -2,13 +2,14 @@ import React from "react";
import { Box, DarkMode } from "@chakra-ui/react";
import gql from "graphql-tag";
import { useQuery } from "@apollo/client";
import * as Sentry from "@sentry/react";
import OutfitThumbnail, {
outfitThumbnailFragment,
getOutfitThumbnailRenderSize,
} from "../components/OutfitThumbnail";
import { useOutfitPreview } from "../components/OutfitPreview";
import { loadable } from "../util";
import { loadable, MajorErrorMessage, TestErrorSender } from "../util";
const OutfitControls = loadable(() => import("./OutfitControls"));
@ -34,7 +35,8 @@ function WardrobePreviewAndControls({
});
return (
<>
<Sentry.ErrorBoundary fallback={MajorErrorMessage}>
<TestErrorSender />
<Box position="absolute" top="0" bottom="0" left="0" right="0">
<DarkMode>{preview}</DarkMode>
</Box>
@ -46,7 +48,7 @@ function WardrobePreviewAndControls({
appearance={appearance}
/>
</Box>
</>
</Sentry.ErrorBoundary>
);
}

View file

@ -1,8 +1,19 @@
import React from "react";
import { Box, Heading, useColorModeValue } from "@chakra-ui/react";
import {
Box,
Flex,
Grid,
Heading,
Link,
useColorModeValue,
} from "@chakra-ui/react";
import loadableLibrary from "@loadable/component";
import * as Sentry from "@sentry/react";
import ErrorGrundoImg from "./images/error-grundo.png";
import ErrorGrundoImg2x from "./images/error-grundo@2x.png";
import { WarningIcon } from "@chakra-ui/icons";
/**
* Delay hides its content at first, then shows it after the given delay.
*
@ -375,3 +386,59 @@ export function logAndCapture(e) {
console.error(e);
Sentry.captureException(e);
}
export function MajorErrorMessage({ error }) {
return (
<Flex justify="center" marginTop="8">
<Grid
templateAreas='"icon title" "icon description" "icon details"'
templateColumns="auto 1fr"
maxWidth="500px"
marginX="8"
columnGap="4"
>
<Box gridArea="icon" marginTop="2">
<Box
as="img"
src={ErrorGrundoImg}
srcSet={`${ErrorGrundoImg} 1x, ${ErrorGrundoImg2x} 2x`}
borderRadius="full"
boxShadow="md"
width="100px"
height="100px"
alt=""
/>
</Box>
<Box gridArea="title" fontSize="lg" marginBottom="1">
Ah dang, I broke it 😖
</Box>
<Box gridArea="description" marginBottom="2">
There was an error displaying this page. I'll get info about it
automatically, but you can tell me more at{" "}
<Link href="mailto:matchu@openneo.net" color="green.400">
matchu@openneo.net
</Link>
!
</Box>
<Box gridArea="details" fontSize="xs" opacity="0.8">
<WarningIcon
marginRight="1.5"
marginTop="-2px"
aria-label="Error message"
/>
"{error.message}"
</Box>
</Grid>
</Flex>
);
}
export function TestErrorSender() {
React.useEffect(() => {
if (window.location.href.includes("send-test-error-for-sentry")) {
throw new Error("Test error for Sentry");
}
});
return null;
}