From 42163e3901fc7386a7905daedbbd5d2f7df4b477 Mon Sep 17 00:00:00 2001 From: Roy Christo Date: Thu, 21 Apr 2022 01:42:07 +0400 Subject: [PATCH] feat(app): implement a global error boundary component --- src/assets/img/error_boundary_ui.svg | 1 + src/components/App/App.jsx | 19 ++--- .../ErrorBoundary/ErrorBoundary.css | 56 +++++++++++++++ .../ErrorBoundary/ErrorBoundary.jsx | 69 +++++++++++++++++++ .../components/PluginItem/PluginItem.jsx | 40 ++++++----- .../PluginsCategories/PluginsCategories.jsx | 42 ++++++----- 6 files changed, 179 insertions(+), 48 deletions(-) create mode 100644 src/assets/img/error_boundary_ui.svg create mode 100644 src/components/ErrorBoundary/ErrorBoundary.css create mode 100644 src/components/ErrorBoundary/ErrorBoundary.jsx diff --git a/src/assets/img/error_boundary_ui.svg b/src/assets/img/error_boundary_ui.svg new file mode 100644 index 00000000..ca37dc52 --- /dev/null +++ b/src/assets/img/error_boundary_ui.svg @@ -0,0 +1 @@ +server down \ No newline at end of file diff --git a/src/components/App/App.jsx b/src/components/App/App.jsx index 41fcfc85..84cfa3db 100644 --- a/src/components/App/App.jsx +++ b/src/components/App/App.jsx @@ -3,6 +3,7 @@ import { BrowserRouter, Switch, Route } from 'react-router-dom'; import ConnectedSignIn from '../SignIn/SignIn'; import Router from '../Router/Router'; import ChrisStore from '../../store/ChrisStore'; +import ErrorBoundary from '../ErrorBoundary/ErrorBoundary'; import './App.css'; // import the patternfly CSS globally @@ -18,14 +19,16 @@ import '../../../node_modules/@patternfly/patternfly/patternfly-no-reset.css'; const App = () => ( -
- - - - - - -
+ +
+ + + + + + +
+
); diff --git a/src/components/ErrorBoundary/ErrorBoundary.css b/src/components/ErrorBoundary/ErrorBoundary.css new file mode 100644 index 00000000..90a2b321 --- /dev/null +++ b/src/components/ErrorBoundary/ErrorBoundary.css @@ -0,0 +1,56 @@ +.container { + height: 100vh; + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.illustration-container { + position: 'relative'; + background: rgba(134, 101, 197, 0.2); + width: 100%; + height: 100%; + margin-top: 24px; + padding: 0px 3%; + display: flex; + flex-direction: column; + align-items: center; +} + +.illustration { + position: relative; + top: -15%; + width: 50vh; +} + +.error-message-container { + height: 100vh; + padding: 0px 2%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.error-message-container h1 { + font-size: 4em; + font-weight: bolder; + color: rgba(0, 0, 0, 0.7); +} + +.error-message-container h2 { + text-align: center; + font-size: 2em; + margin-bottom: 12px; + font-weight: bold; + color: rgba(0, 0, 0, 0.55); + margin-top: 12px; +} + +.error-message-container p { + text-align: center; + color: rgba(0, 0, 0, 0.55); + margin-bottom: 2rem; +} diff --git a/src/components/ErrorBoundary/ErrorBoundary.jsx b/src/components/ErrorBoundary/ErrorBoundary.jsx new file mode 100644 index 00000000..6edf60b4 --- /dev/null +++ b/src/components/ErrorBoundary/ErrorBoundary.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import ErrorNotification from '../Notification'; +import Button from '../Button'; +import Illustration from '../../assets/img/error_boundary_ui.svg'; +import './ErrorBoundary.css'; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false, error: null, errorInfo: null }; + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + componentDidCatch(error, errorInfo) { + // You can also log the error to an error reporting service + //notifySlack(error, errorInfo) + this.setState({ error, errorInfo }); + } + + showNotifications = (error) => { + this.setState((prev) => ({ + errors: [...prev.errors, error], + })); + }; + + reload = () => { + window.location.href = '/'; + }; + + render() { + if (this.state.hasError) { + return ( + <> + {}} + /> +
+
+

400

+

{this.state.error?.message}

+

We are having an issue, please click on the button bellow to reload the page !

+ + +
+
+ Error illustration +
+
+ + ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/src/components/Plugins/components/PluginItem/PluginItem.jsx b/src/components/Plugins/components/PluginItem/PluginItem.jsx index 6b7d44e8..a9017417 100644 --- a/src/components/Plugins/components/PluginItem/PluginItem.jsx +++ b/src/components/Plugins/components/PluginItem/PluginItem.jsx @@ -1,8 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - Badge, Card, CardBody, Split, SplitItem, -} from '@patternfly/react-core'; +import { Badge, Card, CardBody, Split, SplitItem } from '@patternfly/react-core'; import { StarIcon } from '@patternfly/react-icons'; import { Link } from 'react-router-dom'; import RelativeDate from '../../../RelativeDate/RelativeDate'; @@ -11,7 +9,15 @@ import './PluginItem.css'; const PluginItem = ({ // Need to do this because the property "creation_date" comes from CUBE // eslint-disable-next-line camelcase - name, authors, title, creation_date, modification_date, category, isFavorite, isLoggedIn, onStarClicked, + name, + authors, + title, + creation_date, + modification_date, + category, + isFavorite, + isLoggedIn, + onStarClicked, }) => { function renderStarButton() { let className = 'plugin-star'; @@ -29,15 +35,16 @@ const PluginItem = ({
-

{name}

- {category} + +

{name}

+
+ + {category} +
- - { title } + + {title} {renderStarButton()}
@@ -46,15 +53,12 @@ const PluginItem = ({ to={`/author/${authors}`} className="plugin-item-author" > - { authors.join(', ') } + {authors.join(', ')}

- { - RelativeDate.isValid(modification_date) ? - `Updated ${new RelativeDate(modification_date).format()}` - : - `Created ${new RelativeDate(creation_date).format()}` - } + {RelativeDate.isValid(modification_date) + ? `Updated ${new RelativeDate(modification_date).format()}` + : `Created ${new RelativeDate(creation_date).format()}`}

diff --git a/src/components/Plugins/components/PluginsCategories/PluginsCategories.jsx b/src/components/Plugins/components/PluginsCategories/PluginsCategories.jsx index ea041005..94e05795 100644 --- a/src/components/Plugins/components/PluginsCategories/PluginsCategories.jsx +++ b/src/components/Plugins/components/PluginsCategories/PluginsCategories.jsx @@ -7,36 +7,34 @@ const PluginsCategories = ({ categories, selected, onSelect }) => (
Categories
- { - categories.map(({ name, length }) => ( -
onSelect(name)} - onKeyPress={() => {}} - > -
{name}
-
{length}
-
- )) - } + {categories.map(({ name, length }) => ( +
onSelect(name)} + onKeyPress={() => {}} + > +
{name}
+
{length}
+
+ ))}
onSelect(null)} onKeyPress={() => {}}> -
- Clear -
+
Clear
); PluginsCategories.propTypes = { - categories: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - length: PropTypes.number.isRequired, - })).isRequired, + categories: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + length: PropTypes.number.isRequired, + }) + ).isRequired, selected: PropTypes.string, onSelect: PropTypes.func, };