mirror of
https://github.com/strapi/strapi.git
synced 2025-08-04 06:49:16 +00:00
Merge pull request #9942 from strapi/chore/fix-error-plugins
Improve CM error management
This commit is contained in:
commit
54b51d9d51
@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* ErrorBoundary
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class ErrorBoundary extends React.Component {
|
|
||||||
// eslint-disable-line react/prefer-stateless-function
|
|
||||||
state = { error: null, errorInfo: null };
|
|
||||||
|
|
||||||
componentDidCatch(error, errorInfo) {
|
|
||||||
this.setState({
|
|
||||||
error,
|
|
||||||
errorInfo,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { error, errorInfo } = this.state;
|
|
||||||
|
|
||||||
if (errorInfo) {
|
|
||||||
return (
|
|
||||||
<div style={{ background: '#ffff' }}>
|
|
||||||
<h2>Something went wrong.</h2>
|
|
||||||
<details style={{ whiteSpace: 'pre-wrap' }}>
|
|
||||||
{error && error.toString()}
|
|
||||||
<br />
|
|
||||||
{errorInfo.componentStack}
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.props.children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorBoundary.defaultProps = {
|
|
||||||
children: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
ErrorBoundary.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ErrorBoundary;
|
|
@ -1,22 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
|
|
||||||
import ErrorBoundary from '../index';
|
|
||||||
|
|
||||||
describe('<ErrorBoundary />', () => {
|
|
||||||
it('should not crash', () => {
|
|
||||||
shallow(<ErrorBoundary />);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render its child', () => {
|
|
||||||
const Child = () => <div>test</div>;
|
|
||||||
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ErrorBoundary>
|
|
||||||
<Child />
|
|
||||||
</ErrorBoundary>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.find(Child)).toHaveLength(1);
|
|
||||||
});
|
|
||||||
});
|
|
@ -8,12 +8,10 @@ import React, { memo } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { BlockerComponent } from 'strapi-helper-plugin';
|
import { BlockerComponent, ErrorFallback } from 'strapi-helper-plugin';
|
||||||
import PageTitle from '../../components/PageTitle';
|
import PageTitle from '../../components/PageTitle';
|
||||||
|
|
||||||
import { LOGIN_LOGO } from '../../config';
|
import { LOGIN_LOGO } from '../../config';
|
||||||
import ErrorBoundary from '../ErrorBoundary';
|
|
||||||
|
|
||||||
export function PluginDispatcher(props) {
|
export function PluginDispatcher(props) {
|
||||||
const {
|
const {
|
||||||
@ -46,7 +44,7 @@ export function PluginDispatcher(props) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageTitle title={`Strapi - ${name}`} />
|
<PageTitle title={`Strapi - ${name}`} />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
<PluginEntryComponent
|
<PluginEntryComponent
|
||||||
{...props}
|
{...props}
|
||||||
{...blockerComponentProps}
|
{...blockerComponentProps}
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
"react-dnd": "^10.0.2",
|
"react-dnd": "^10.0.2",
|
||||||
"react-dnd-html5-backend": "^10.0.2",
|
"react-dnd-html5-backend": "^10.0.2",
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
|
"react-error-boundary": "3.1.1",
|
||||||
"react-fast-compare": "^3.2.0",
|
"react-fast-compare": "^3.2.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-intl": "4.5.0",
|
"react-intl": "4.5.0",
|
||||||
|
@ -27,6 +27,7 @@ const alias = [
|
|||||||
'react-dnd',
|
'react-dnd',
|
||||||
'react-dnd-html5-backend',
|
'react-dnd-html5-backend',
|
||||||
'react-dom',
|
'react-dom',
|
||||||
|
'react-error-boundary',
|
||||||
'react-fast-compare',
|
'react-fast-compare',
|
||||||
'react-helmet',
|
'react-helmet',
|
||||||
'react-is',
|
'react-is',
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
// https://github.com/bvaughn/react-error-boundary#usage
|
||||||
|
|
||||||
|
function ErrorFallback({ error }) {
|
||||||
|
return (
|
||||||
|
<div style={{ background: '#ffff' }}>
|
||||||
|
<h2>Something went wrong.</h2>
|
||||||
|
<details style={{ whiteSpace: 'pre-wrap' }}>
|
||||||
|
{error.message}
|
||||||
|
<br />
|
||||||
|
{error.stack}
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorFallback.defaultProps = {
|
||||||
|
error: { message: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorFallback.propTypes = {
|
||||||
|
error: PropTypes.shape({ message: PropTypes.string }),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorFallback;
|
@ -17,6 +17,7 @@ export { default as CircleButton } from './components/CircleButton';
|
|||||||
export { default as ContainerFluid } from './components/ContainerFluid';
|
export { default as ContainerFluid } from './components/ContainerFluid';
|
||||||
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
||||||
export { default as ExtendComponent } from './components/ExtendComponent';
|
export { default as ExtendComponent } from './components/ExtendComponent';
|
||||||
|
export { default as ErrorFallback } from './components/ErrorFallback';
|
||||||
export { default as FilterButton } from './components/FilterButton';
|
export { default as FilterButton } from './components/FilterButton';
|
||||||
export { default as GlobalPagination } from './components/GlobalPagination';
|
export { default as GlobalPagination } from './components/GlobalPagination';
|
||||||
export { default as HeaderNav } from './components/HeaderNav';
|
export { default as HeaderNav } from './components/HeaderNav';
|
||||||
|
@ -13,6 +13,7 @@ import PropTypes from 'prop-types';
|
|||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { createDefaultForm, getTrad, removePasswordFieldsFromData } from '../../utils';
|
import { createDefaultForm, getTrad, removePasswordFieldsFromData } from '../../utils';
|
||||||
import pluginId from '../../pluginId';
|
import pluginId from '../../pluginId';
|
||||||
|
import { useFindRedirectionLink } from '../../hooks';
|
||||||
import {
|
import {
|
||||||
getData,
|
getData,
|
||||||
getDataSucceeded,
|
getDataSucceeded,
|
||||||
@ -23,8 +24,8 @@ import {
|
|||||||
submitSucceeded,
|
submitSucceeded,
|
||||||
} from '../../sharedReducers/crudReducer/actions';
|
} from '../../sharedReducers/crudReducer/actions';
|
||||||
import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
|
import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
|
||||||
import { getRedirectionLink, getRequestUrl } from './utils';
|
import { getRequestUrl } from './utils';
|
||||||
import selectMenuLinks from './selectors';
|
|
||||||
// This container is used to handle the CRUD
|
// This container is used to handle the CRUD
|
||||||
const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }) => {
|
const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }) => {
|
||||||
const { emitEvent } = useGlobalContext();
|
const { emitEvent } = useGlobalContext();
|
||||||
@ -38,8 +39,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|||||||
isLoading,
|
isLoading,
|
||||||
status,
|
status,
|
||||||
} = useSelector(selectCrudReducer);
|
} = useSelector(selectCrudReducer);
|
||||||
const collectionTypesMenuLinks = useSelector(selectMenuLinks);
|
const redirectionLink = useFindRedirectionLink(slug);
|
||||||
const redirectionLink = getRedirectionLink(collectionTypesMenuLinks, slug, rawQuery);
|
|
||||||
|
|
||||||
const isMounted = useRef(true);
|
const isMounted = useRef(true);
|
||||||
const emitEventRef = useRef(emitEvent);
|
const emitEventRef = useRef(emitEvent);
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export { default as getRedirectionLink } from './getRedirectionLink';
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export { default as getRequestUrl } from './getRequestUrl';
|
export { default as getRequestUrl } from './getRequestUrl';
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
import { ErrorFallback, LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
||||||
import pluginPermissions from '../../permissions';
|
import pluginPermissions from '../../permissions';
|
||||||
import { ContentTypeLayoutContext } from '../../contexts';
|
import { ContentTypeLayoutContext } from '../../contexts';
|
||||||
import { useFetchContentTypeLayout } from '../../hooks';
|
import { useFetchContentTypeLayout } from '../../hooks';
|
||||||
@ -82,6 +83,7 @@ const CollectionTypeRecursivePath = ({
|
|||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||||
<ContentTypeLayoutContext.Provider value={layout}>
|
<ContentTypeLayoutContext.Provider value={layout}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={`${url}/configurations/list`}>
|
<Route path={`${url}/configurations/list`}>
|
||||||
@ -107,6 +109,7 @@ const CollectionTypeRecursivePath = ({
|
|||||||
{routes}
|
{routes}
|
||||||
</Switch>
|
</Switch>
|
||||||
</ContentTypeLayoutContext.Provider>
|
</ContentTypeLayoutContext.Provider>
|
||||||
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,14 +10,14 @@ import { Flex, Padded } from '@buffetjs/core';
|
|||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { stringify } from 'qs';
|
import { stringify } from 'qs';
|
||||||
import {
|
import {
|
||||||
PopUpWarning,
|
|
||||||
request,
|
|
||||||
CheckPermissions,
|
CheckPermissions,
|
||||||
useGlobalContext,
|
|
||||||
InjectionZone,
|
InjectionZone,
|
||||||
InjectionZoneList,
|
InjectionZoneList,
|
||||||
|
PopUpWarning,
|
||||||
|
useGlobalContext,
|
||||||
useQueryParams,
|
useQueryParams,
|
||||||
useUser,
|
useUser,
|
||||||
|
request,
|
||||||
} from 'strapi-helper-plugin';
|
} from 'strapi-helper-plugin';
|
||||||
import pluginId from '../../pluginId';
|
import pluginId from '../../pluginId';
|
||||||
import pluginPermissions from '../../permissions';
|
import pluginPermissions from '../../permissions';
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useQueryParams } from 'strapi-helper-plugin';
|
import { useQueryParams } from 'strapi-helper-plugin';
|
||||||
|
import { useFindRedirectionLink } from '../../hooks';
|
||||||
import { resetProps, setLayout } from '../ListView/actions';
|
import { resetProps, setLayout } from '../ListView/actions';
|
||||||
import useSyncRbac from '../RBACManager/useSyncRbac';
|
import useSyncRbac from '../RBACManager/useSyncRbac';
|
||||||
import Permissions from './Permissions';
|
import Permissions from './Permissions';
|
||||||
|
|
||||||
const ListViewLayout = ({ layout, ...props }) => {
|
const ListViewLayout = ({ layout, ...props }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [{ query }] = useQueryParams();
|
const { replace } = useHistory();
|
||||||
|
const [{ query, rawQuery }] = useQueryParams();
|
||||||
const permissions = useSyncRbac(query, props.slug, 'listView');
|
const permissions = useSyncRbac(query, props.slug, 'listView');
|
||||||
|
const redirectionLink = useFindRedirectionLink(props.slug);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!rawQuery) {
|
||||||
|
replace(redirectionLink);
|
||||||
|
}
|
||||||
|
}, [rawQuery, replace, redirectionLink]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setLayout(layout.contentType));
|
dispatch(setLayout(layout.contentType));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export { default as useContentTypeLayout } from './useContentTypeLayout';
|
export { default as useContentTypeLayout } from './useContentTypeLayout';
|
||||||
export { default as useFetchContentTypeLayout } from './useFetchContentTypeLayout';
|
export { default as useFetchContentTypeLayout } from './useFetchContentTypeLayout';
|
||||||
|
export { default as useFindRedirectionLink } from './useFindRedirectionLink';
|
||||||
export { default as useLayoutDnd } from './useLayoutDnd';
|
export { default as useLayoutDnd } from './useLayoutDnd';
|
||||||
export { default as useListView } from './useListView';
|
export { default as useListView } from './useListView';
|
||||||
export { default as useWysiwyg } from './useWysiwyg';
|
export { default as useWysiwyg } from './useWysiwyg';
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useQueryParams } from 'strapi-helper-plugin';
|
||||||
|
import selectMenuLinks from './selectors';
|
||||||
|
import getRedirectionLink from './utils/getRedirectionLink';
|
||||||
|
|
||||||
|
const useFindRedirectionLink = slug => {
|
||||||
|
const [{ rawQuery }] = useQueryParams();
|
||||||
|
const collectionTypesMenuLinks = useSelector(selectMenuLinks);
|
||||||
|
const redirectionLink = getRedirectionLink(collectionTypesMenuLinks, slug, rawQuery);
|
||||||
|
|
||||||
|
return redirectionLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFindRedirectionLink;
|
@ -15,7 +15,7 @@ const VALID_REST_OPERATORS = [
|
|||||||
'null',
|
'null',
|
||||||
];
|
];
|
||||||
|
|
||||||
// from strapi-utims/convert-rest-query-params
|
// from strapi-utils/convert-rest-query-params
|
||||||
const findAppliedFilter = whereClause => {
|
const findAppliedFilter = whereClause => {
|
||||||
// Useful to remove the mainField of relation fields.
|
// Useful to remove the mainField of relation fields.
|
||||||
const formattedWhereClause = whereClause.split('.')[0];
|
const formattedWhereClause = whereClause.split('.')[0];
|
||||||
|
@ -16285,6 +16285,13 @@ react-dom@^16.9.0:
|
|||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.19.1"
|
scheduler "^0.19.1"
|
||||||
|
|
||||||
|
react-error-boundary@3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.1.tgz#932c5ca5cbab8ec4fe37fd7b415aa5c3a47597e7"
|
||||||
|
integrity sha512-W3xCd9zXnanqrTUeViceufD3mIW8Ut29BUD+S2f0eO2XCOU8b6UrJfY46RDGe5lxCJzfe4j0yvIfh0RbTZhKJw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
react-fast-compare@^2.0.1:
|
react-fast-compare@^2.0.1:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user