Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cleanup plugin error boundary [UX-136] #856

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions adapter/i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-04-24T13:58:13.591Z\n"
"PO-Revision-Date: 2024-04-24T13:58:13.591Z\n"
"POT-Creation-Date: 2024-06-21T08:27:55.991Z\n"
"PO-Revision-Date: 2024-06-21T08:27:55.991Z\n"

msgid "Save your data"
msgstr "Save your data"
Expand Down Expand Up @@ -39,6 +39,12 @@ msgstr "An error occurred in the DHIS2 application."
msgid "Technical details copied to clipboard"
msgstr "Technical details copied to clipboard"

msgid "There was a problem loading this plugin"
msgstr "There was a problem loading this plugin"

msgid "Copy debug info to clipboard"
msgstr "Copy debug info to clipboard"

msgid "Try again"
msgstr "Try again"

Expand All @@ -48,9 +54,6 @@ msgstr "Something went wrong"
msgid "Redirect to safe login mode"
msgstr "Redirect to safe login mode"

msgid "Redirect to safe login mode"
msgstr "Redirect to safe login mode"

msgid "Hide technical details"
msgstr "Hide technical details"

Expand Down
50 changes: 45 additions & 5 deletions adapter/src/components/ErrorBoundary.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,35 @@ import styles from './styles/ErrorBoundary.style.js'
// In order to avoid using @dhis2/ui components in the error boundary - as anything
// that breaks within it will not be caught properly - we define a component
// with the same styles as Button
const UIButton = ({ children, onClick }) => (
const UIButton = ({ children, onClick, plugin }) => (
<>
<style jsx>{buttonStyles}</style>
<button onClick={onClick}>{children}</button>
<button className={plugin ? 'pluginButton' : null} onClick={onClick}>
{children}
</button>
</>
)

UIButton.propTypes = {
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired,
plugin: PropTypes.bool,
}

const InfoIcon24 = () => (
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 2c5.5228475 0 10 4.4771525 10 10s-4.4771525 10-10 10-10-4.4771525-10-10 4.4771525-10 10-10zm0 2c-4.418278 0-8 3.581722-8 8s3.581722 8 8 8 8-3.581722 8-8-3.581722-8-8-8zm1 7v6h-2v-6zm-1-4c.5522847 0 1 .44771525 1 1s-.4477153 1-1 1-1-.44771525-1-1 .4477153-1 1-1z"
fill="#A0ADBA"
></path>
</svg>
)

const translatedErrorHeading = i18n.t(
'An error occurred in the DHIS2 application.'
)
Expand Down Expand Up @@ -61,6 +78,13 @@ export class ErrorBoundary extends Component {
})
}

handleCopyErrorDetailsPlugin = ({ error, errorInfo }) => {
const errorDetails = `${error}\n${error?.stack}\n${errorInfo?.componentStack}`
navigator.clipboard.writeText(errorDetails).then(() => {
alert(i18n.t('Technical details copied to clipboard'))
})
}

handleSafeLoginRedirect = () => {
window.location.href =
this.props.baseURL +
Expand All @@ -77,10 +101,26 @@ export class ErrorBoundary extends Component {
<>
<style jsx>{styles}</style>
<div className="pluginBoundary">
<span>I am the default plugin boundary</span>
<InfoIcon24 />
<div className="pluginErrorMessage">
{i18n.t(
'There was a problem loading this plugin'
)}
</div>
<div
className="pluginErrorCopy"
onClick={() => {
this.handleCopyErrorDetailsPlugin({
error: this.state.error,
errorInfo: this.state.errorInfo,
})
}}
>
{i18n.t('Copy debug info to clipboard')}
</div>
{onRetry && (
<div className="retry">
<UIButton onClick={onRetry}>
<div className="pluginRetry">
<UIButton onClick={onRetry} plugin>
{i18n.t('Try again')}
</UIButton>
</div>
Expand Down
14 changes: 13 additions & 1 deletion adapter/src/components/styles/Button.style.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import css from 'styled-jsx/css'
const grey900 = '#21934',
grey500 = '#a0adba',
grey200 = '#f3f5f7',
primary600 = '#147cd7'
primary600 = '#147cd7',
grey600 = '#6C7787'

export default css`
button {
Expand Down Expand Up @@ -87,4 +88,15 @@ export default css`
button:focus::after {
border-color: ${primary600};
}

.pluginButton {
/*small*/
height: 28px;
padding: 0 6px;
font-size: 14px;
line-height: 16px;

/*text color for plugin error*/
color: ${grey600};
}
`
27 changes: 23 additions & 4 deletions adapter/src/components/styles/ErrorBoundary.style.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const bgColor = '#F4F6F8',
secondaryTextColor = '#494949',
errorColor = '#D32F2F',
grey050 = '#FBFCFD',
red200 = '#ffcdd2'
grey100 = '#F8F9FA',
grey600 = '#6C7787'

export default css`
.mask {
Expand Down Expand Up @@ -103,10 +104,28 @@ export default css`
}

.pluginBoundary {
background-color: ${red200};
background-color: ${grey100};
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
padding-block-start: 16px;
}

.pluginErrorMessage {
margin-block-start: 8px;
color: ${grey600};
}

.pluginErrorCopy {
margin-block-start: 8px;
color: ${grey600};
text-decoration: underline;
font-size: 14px;
}

.pluginBoundary span {
display: inline-block;
.pluginRetry {
margin-block-start: 16px;
}
`
46 changes: 45 additions & 1 deletion shell/src/PluginOuterErrorBoundary.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'

const grey100 = '#F8F9FA',
grey600 = '#6C7787'

const InfoIcon24 = () => (
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 2c5.5228475 0 10 4.4771525 10 10s-4.4771525 10-10 10-10-4.4771525-10-10 4.4771525-10 10-10zm0 2c-4.418278 0-8 3.581722-8 8s3.581722 8 8 8 8-3.581722 8-8-3.581722-8-8-8zm1 7v6h-2v-6zm-1-4c.5522847 0 1 .44771525 1 1s-.4477153 1-1 1-1-.44771525-1-1 .4477153-1 1-1z"
fill="#A0ADBA"
></path>
</svg>
)

export class PluginOuterErrorBoundary extends Component {
constructor(props) {
super(props)
Expand All @@ -20,7 +37,34 @@ export class PluginOuterErrorBoundary extends Component {
render() {
const { children } = this.props
if (this.state.error) {
return <p>Plugin outermost error boundary</p>
return (
<>
<div className="pluginBoundary">
<InfoIcon24 />
<div className="pluginErrorMessage">
There was a problem loading this plugin
</div>
</div>
<style jsx>
{`
.pluginBoundary {
background-color: ${grey100};
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
padding-block-start: 16px;
}

.pluginErrorMessage {
margin-block-start: 8px;
color: ${grey600};
}
`}
</style>
</>
)
}

return children
Expand Down
Loading