Add error boundary to subscriber import/export app

[MAILPOET-4706]
This commit is contained in:
Sam Najian
2022-12-06 14:02:06 +01:00
committed by Aschepikov
parent 7fa694314b
commit 7fd11d4fb5
16 changed files with 110 additions and 71 deletions

View File

@@ -1,5 +1,6 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { withBoundary } from './error_boundary';
function ScrollToTopComponent({ children, location: { pathname } }) { function ScrollToTopComponent({ children, location: { pathname } }) {
useEffect(() => { useEffect(() => {
@@ -9,4 +10,5 @@ function ScrollToTopComponent({ children, location: { pathname } }) {
return children || null; return children || null;
} }
export const ScrollToTop = withRouter(ScrollToTopComponent); ScrollToTopComponent.displayName = 'ScrollToTopComponent';
export const ScrollToTop = withRouter(withBoundary(ScrollToTopComponent));

View File

@@ -1,7 +1,7 @@
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { FilterType, List, Logs } from './list';
import { ErrorBoundary } from 'common'; import { ErrorBoundary } from 'common';
import { FilterType, List, Logs } from './list';
interface LogsWindow extends Window { interface LogsWindow extends Window {
mailpoet_logs: Logs; mailpoet_logs: Logs;

View File

@@ -1,14 +1,15 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { GlobalContext } from 'context/index.jsx'; import { GlobalContext } from 'context/index.jsx';
import { withBoundary } from 'common';
import { Notice } from './notice.tsx'; import { Notice } from './notice.tsx';
const Notices = () => { const NoticesComponent = () => {
const { notices } = useContext(GlobalContext); const { notices } = useContext(GlobalContext);
return notices.items.map(({ id, ...props }) => ( return notices.items.map(({ id, ...props }) => (
<Notice key={id} {...props} /> <Notice key={id} {...props} />
)); ));
}; };
Notices.displayName = 'Notices'; NoticesComponent.displayName = 'Notices';
const Notices = withBoundary(NoticesComponent);
export { Notices }; export { Notices };

View File

@@ -5,6 +5,7 @@ import { ScrollToTop } from 'common/scroll_to_top.jsx';
import { GlobalContext, useGlobalContextValue } from 'context/index.jsx'; import { GlobalContext, useGlobalContextValue } from 'context/index.jsx';
import { Notices } from 'notices/notices.jsx'; import { Notices } from 'notices/notices.jsx';
import { withBoundary } from 'common';
import { StepMethodSelection } from './import/step_method_selection.jsx'; import { StepMethodSelection } from './import/step_method_selection.jsx';
import { StepInputValidation } from './import/step_input_validation.jsx'; import { StepInputValidation } from './import/step_input_validation.jsx';
import { StepDataManipulation } from './import/step_data_manipulation.jsx'; import { StepDataManipulation } from './import/step_data_manipulation.jsx';
@@ -28,7 +29,7 @@ function ImportSubscribers() {
<Switch> <Switch>
<Route <Route
path="/step_clean_list" path="/step_clean_list"
render={(props) => <StepCleanList {...props} />} render={withBoundary(StepCleanList)}
/> />
<Route <Route
path="/step_method_selection" path="/step_method_selection"

View File

@@ -5,7 +5,7 @@ type Props = {
onProceed?: () => void; onProceed?: () => void;
}; };
export function CleanList({ onProceed }: Props): JSX.Element { function CleanList({ onProceed }: Props): JSX.Element {
return ( return (
<div className="mailpoet-clean-list-step-container"> <div className="mailpoet-clean-list-step-container">
<p>{MailPoet.I18n.t('cleanListText1')}</p> <p>{MailPoet.I18n.t('cleanListText1')}</p>
@@ -26,3 +26,6 @@ export function CleanList({ onProceed }: Props): JSX.Element {
</div> </div>
); );
} }
CleanList.displayName = 'CleanList';
export { CleanList };

View File

@@ -47,5 +47,5 @@ PreviousNextStepButtons.defaultProps = {
onPreviousAction: () => {}, onPreviousAction: () => {},
onNextAction: () => {}, onNextAction: () => {},
}; };
PreviousNextStepButtons.displayName = 'PreviousNextStepButtons';
export { PreviousNextStepButtons }; export { PreviousNextStepButtons };

View File

@@ -1,8 +1,11 @@
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { CleanList } from './clean_list'; import { CleanList } from './clean_list';
export function StepCleanList({ history }: RouteComponentProps): JSX.Element { function StepCleanList({ history }: RouteComponentProps): JSX.Element {
return ( return (
<CleanList onProceed={(): void => history.push('step_method_selection')} /> <CleanList onProceed={(): void => history.push('step_method_selection')} />
); );
} }
StepCleanList.displayName = 'StepCleanList';
export { StepCleanList };

View File

@@ -1,8 +1,9 @@
import { useEffect, useState, useCallback } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CleanList } from 'subscribers/importExport/import/clean_list'; import { CleanList } from 'subscribers/importExport/import/clean_list';
import { ErrorBoundary } from 'common';
import { InitialQuestion } from './step_input_validation/initial_question.jsx'; import { InitialQuestion } from './step_input_validation/initial_question.jsx';
import { WrongSourceBlock } from './step_input_validation/wrong_source_block.jsx'; import { WrongSourceBlock } from './step_input_validation/wrong_source_block.jsx';
import { LastSentQuestion } from './step_input_validation/last_sent_question.jsx'; import { LastSentQuestion } from './step_input_validation/last_sent_question.jsx';
@@ -30,17 +31,23 @@ function StepInputValidationComponent({ stepMethodSelectionData, history }) {
return ( return (
<> <>
{importSource === undefined && ( {importSource === undefined && (
<ErrorBoundary>
<InitialQuestion onSubmit={setImportSource} history={history} /> <InitialQuestion onSubmit={setImportSource} history={history} />
</ErrorBoundary>
)} )}
{importSource === 'address-book' && <WrongSourceBlock />} {importSource === 'address-book' && <WrongSourceBlock />}
{importSource === 'existing-list' && lastSent === undefined && ( {importSource === 'existing-list' && lastSent === undefined && (
<ErrorBoundary>
<LastSentQuestion onSubmit={lastSentSubmit} /> <LastSentQuestion onSubmit={lastSentSubmit} />
</ErrorBoundary>
)} )}
{importSource === 'existing-list' && lastSent === 'notRecently' && ( {importSource === 'existing-list' && lastSent === 'notRecently' && (
<ErrorBoundary>
<CleanList /> <CleanList />
</ErrorBoundary>
)} )}
</> </>
); );

View File

@@ -57,5 +57,5 @@ InitialQuestion.propTypes = {
}).isRequired, }).isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
}; };
InitialQuestion.displayName = 'InitialQuestion';
export { InitialQuestion }; export { InitialQuestion };

View File

@@ -1,4 +1,4 @@
import { useState, useCallback } from 'react'; import { useCallback, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MailPoet } from 'mailpoet'; import { MailPoet } from 'mailpoet';
import { Button } from 'common/button/button'; import { Button } from 'common/button/button';
@@ -63,5 +63,5 @@ function LastSentQuestion({ onSubmit }) {
LastSentQuestion.propTypes = { LastSentQuestion.propTypes = {
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
}; };
LastSentQuestion.displayName = 'LastSentQuestion';
export { LastSentQuestion }; export { LastSentQuestion };

View File

@@ -2,6 +2,7 @@ import { useState } from 'react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MailPoet } from 'mailpoet'; import { MailPoet } from 'mailpoet';
import { ErrorBoundary } from 'common';
import { SelectImportMethod } from './step_method_selection/select_import_method.jsx'; import { SelectImportMethod } from './step_method_selection/select_import_method.jsx';
import { MethodPaste } from './step_method_selection/method_paste.jsx'; import { MethodPaste } from './step_method_selection/method_paste.jsx';
import { MethodUpload } from './step_method_selection/method_upload.jsx'; import { MethodUpload } from './step_method_selection/method_upload.jsx';
@@ -57,8 +58,11 @@ function StepMethodSelectionComponent({
return ( return (
<div className="mailpoet-settings-grid"> <div className="mailpoet-settings-grid">
<ErrorBoundary>
<SelectImportMethod activeMethod={method} onMethodChange={setMethod} /> <SelectImportMethod activeMethod={method} onMethodChange={setMethod} />
</ErrorBoundary>
{method === 'paste-method' && ( {method === 'paste-method' && (
<ErrorBoundary>
<MethodPaste <MethodPaste
onPrevious={previousStep} onPrevious={previousStep}
onValueChange={setPastedCsvData} onValueChange={setPastedCsvData}
@@ -66,8 +70,10 @@ function StepMethodSelectionComponent({
canFinish={!!pastedCsvData.trim()} canFinish={!!pastedCsvData.trim()}
data={pastedCsvData} data={pastedCsvData}
/> />
</ErrorBoundary>
)} )}
{method === 'file-method' && ( {method === 'file-method' && (
<ErrorBoundary>
<MethodUpload <MethodUpload
onPrevious={previousStep} onPrevious={previousStep}
onValueChange={setFile} onValueChange={setFile}
@@ -75,8 +81,10 @@ function StepMethodSelectionComponent({
canFinish={!!file} canFinish={!!file}
data={file} data={file}
/> />
</ErrorBoundary>
)} )}
{method === 'mailchimp-method' && ( {method === 'mailchimp-method' && (
<ErrorBoundary>
<MethodMailChimp <MethodMailChimp
onPrevious={previousStep} onPrevious={previousStep}
onFinish={(data) => { onFinish={(data) => {
@@ -86,12 +94,15 @@ function StepMethodSelectionComponent({
finish(data); finish(data);
}} }}
/> />
</ErrorBoundary>
)} )}
{method === undefined && ( {method === undefined && (
<ErrorBoundary>
<PreviousNextStepButtons <PreviousNextStepButtons
canGoNext={false} canGoNext={false}
onPreviousAction={previousStep} onPreviousAction={previousStep}
/> />
</ErrorBoundary>
)} )}
</div> </div>
); );
@@ -104,5 +115,5 @@ StepMethodSelectionComponent.propTypes = {
setStepMethodSelectionData: PropTypes.func.isRequired, setStepMethodSelectionData: PropTypes.func.isRequired,
subscribersLimitForValidation: PropTypes.number.isRequired, subscribersLimitForValidation: PropTypes.number.isRequired,
}; };
StepMethodSelectionComponent.diplayName = 'StepMethodSelection';
export const StepMethodSelection = withRouter(StepMethodSelectionComponent); export const StepMethodSelection = withRouter(StepMethodSelectionComponent);

View File

@@ -160,4 +160,6 @@ MethodMailChimp.defaultProps = {
onPrevious: () => {}, onPrevious: () => {},
}; };
MethodMailChimp.displayName = 'MethodMailChimp';
export { MethodMailChimp }; export { MethodMailChimp };

View File

@@ -70,5 +70,5 @@ MethodPaste.defaultProps = {
onPrevious: () => {}, onPrevious: () => {},
data: '', data: '',
}; };
MethodPaste.displayName = 'MethodPaste';
export { MethodPaste }; export { MethodPaste };

View File

@@ -71,5 +71,5 @@ MethodUpload.defaultProps = {
onFinish: () => {}, onFinish: () => {},
onPrevious: () => {}, onPrevious: () => {},
}; };
MethodUpload.displayName = 'MethodUpload';
export { MethodUpload }; export { MethodUpload };

View File

@@ -78,5 +78,5 @@ SelectImportMethod.propTypes = {
SelectImportMethod.defaultProps = { SelectImportMethod.defaultProps = {
activeMethod: undefined, activeMethod: undefined,
}; };
SelectImportMethod.displayName = 'SelectImportMethod';
export { SelectImportMethod }; export { SelectImportMethod };

View File

@@ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom';
import ReactStringReplace from 'react-string-replace'; import ReactStringReplace from 'react-string-replace';
import { Button } from 'common/button/button'; import { Button } from 'common/button/button';
import { ErrorBoundary } from 'common';
function ResultMessage({ subscribersCount, segments, initialMessage }) { function ResultMessage({ subscribersCount, segments, initialMessage }) {
if (subscribersCount) { if (subscribersCount) {
@@ -33,6 +34,7 @@ ResultMessage.defaultProps = {
subscribersCount: 0, subscribersCount: 0,
initialMessage: '', initialMessage: '',
}; };
ResultMessage.displayName = 'ResultMessage';
function NoAction({ createdSubscribers, updatedSubscribers }) { function NoAction({ createdSubscribers, updatedSubscribers }) {
if (!createdSubscribers && !updatedSubscribers) { if (!createdSubscribers && !updatedSubscribers) {
@@ -50,6 +52,7 @@ NoAction.defaultProps = {
createdSubscribers: 0, createdSubscribers: 0,
updatedSubscribers: 0, updatedSubscribers: 0,
}; };
NoAction.displayName = 'NoAction';
function SuppressionListReminder({ createdSubscribers, updatedSubscribers }) { function SuppressionListReminder({ createdSubscribers, updatedSubscribers }) {
if (createdSubscribers || updatedSubscribers) { if (createdSubscribers || updatedSubscribers) {
@@ -91,6 +94,7 @@ SuppressionListReminder.defaultProps = {
createdSubscribers: 0, createdSubscribers: 0,
updatedSubscribers: 0, updatedSubscribers: 0,
}; };
SuppressionListReminder.displayName = 'SuppressionListReminder';
function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification }) { function NoWelcomeEmail({ addedToSegmentWithWelcomeNotification }) {
if (addedToSegmentWithWelcomeNotification) { if (addedToSegmentWithWelcomeNotification) {
@@ -106,6 +110,7 @@ NoWelcomeEmail.propTypes = {
NoWelcomeEmail.defaultProps = { NoWelcomeEmail.defaultProps = {
addedToSegmentWithWelcomeNotification: false, addedToSegmentWithWelcomeNotification: false,
}; };
NoWelcomeEmail.diplayName = 'NoWelcomeEmail';
function StepResultsComponent({ function StepResultsComponent({
errors, errors,
@@ -136,6 +141,7 @@ function StepResultsComponent({
} }
return ( return (
<> <>
<ErrorBoundary>
<div className="updated"> <div className="updated">
<ResultMessage <ResultMessage
subscribersCount={createdSubscribers} subscribersCount={createdSubscribers}
@@ -157,10 +163,13 @@ function StepResultsComponent({
} }
/> />
</div> </div>
</ErrorBoundary>
<ErrorBoundary>
<SuppressionListReminder <SuppressionListReminder
createdSubscribers={createdSubscribers} createdSubscribers={createdSubscribers}
updatedSubscribers={updatedSubscribers} updatedSubscribers={updatedSubscribers}
/> />
</ErrorBoundary>
<div className="mailpoet-settings-grid"> <div className="mailpoet-settings-grid">
<div className="mailpoet-settings-save"> <div className="mailpoet-settings-save">
<Button <Button
@@ -203,5 +212,5 @@ StepResultsComponent.defaultProps = {
updatedSubscribers: undefined, updatedSubscribers: undefined,
addedToSegmentWithWelcomeNotification: undefined, addedToSegmentWithWelcomeNotification: undefined,
}; };
StepResultsComponent.displayName = 'StepResultsComponent';
export const StepResults = withRouter(StepResultsComponent); export const StepResults = withRouter(StepResultsComponent);