diff --git a/cypress/integration/edit.js b/cypress/integration/edit.js
index 069fd31607d..c16a2fac369 100644
--- a/cypress/integration/edit.js
+++ b/cypress/integration/edit.js
@@ -1,12 +1,16 @@
import createPageFactory from '../support/CreatePage';
import editPageFactory from '../support/EditPage';
import listPageFactory from '../support/ListPage';
+import loginPageFactory from '../support/LoginPage';
describe('Edit Page', () => {
const EditPostPage = editPageFactory('/#/posts/5');
+ const ListPagePosts = listPageFactory('/#/posts');
const CreatePostPage = createPageFactory('/#/posts/create');
const EditCommentPage = editPageFactory('/#/comments/5');
- const ListPagePosts = listPageFactory('/#/posts');
+ const LoginPage = loginPageFactory('/#/login');
+ const EditUserPage = editPageFactory('/#/users/3');
+ const CreateUserPage = createPageFactory('/#/users/create');
describe('Title', () => {
it('should show the correct title in the appBar', () => {
@@ -165,6 +169,26 @@ describe('Edit Page', () => {
});
});
+ it('should not lose the cloned values when switching tabs', () => {
+ EditPostPage.navigate();
+ EditPostPage.logout();
+ LoginPage.navigate();
+ LoginPage.login('admin', 'password');
+ EditUserPage.navigate();
+ cy.get(EditUserPage.elements.input('name')).should(el =>
+ expect(el).to.have.value('Annamarie Mayer')
+ );
+ EditUserPage.clone();
+ cy.get(CreateUserPage.elements.input('name')).then(el => {
+ expect(el).to.have.value('Annamarie Mayer');
+ });
+ CreateUserPage.gotoTab(2);
+ CreateUserPage.gotoTab(1);
+ cy.get(CreateUserPage.elements.input('name')).then(el => {
+ expect(el).to.have.value('Annamarie Mayer');
+ });
+ });
+
it('should persit emptied inputs', () => {
EditPostPage.navigate();
EditPostPage.gotoTab(3);
diff --git a/cypress/support/EditPage.js b/cypress/support/EditPage.js
index 16324ed6514..61c896d106c 100644
--- a/cypress/support/EditPage.js
+++ b/cypress/support/EditPage.js
@@ -15,6 +15,8 @@ export default url => ({
cloneButton: '.button-clone',
tab: index => `.form-tab:nth-of-type(${index})`,
title: '#react-admin-title',
+ userMenu: 'button[title="Profile"]',
+ logout: '.logout',
},
navigate() {
@@ -57,4 +59,9 @@ export default url => ({
clone() {
cy.get(this.elements.cloneButton).click();
},
+
+ logout() {
+ cy.get(this.elements.userMenu).click();
+ cy.get(this.elements.logout).click();
+ },
});
diff --git a/examples/simple/src/users/UserEdit.js b/examples/simple/src/users/UserEdit.js
index 09e8a22048e..a5754a124cd 100644
--- a/examples/simple/src/users/UserEdit.js
+++ b/examples/simple/src/users/UserEdit.js
@@ -2,15 +2,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
+ CloneButton,
DeleteWithConfirmButton,
Edit,
FormTab,
+ required,
SaveButton,
SelectInput,
+ ShowButton,
TabbedForm,
TextInput,
Toolbar,
- required,
+ TopToolbar,
} from 'react-admin';
import { makeStyles } from '@material-ui/core/styles';
@@ -39,8 +42,24 @@ const UserEditToolbar = props => {
);
};
+const EditActions = ({ basePath, data, hasShow }) => (
+
+
+
+
+);
+
const UserEdit = ({ permissions, ...props }) => (
- } aside={} {...props}>
+ }
+ aside={}
+ actions={}
+ {...props}
+ >
}
diff --git a/packages/ra-core/src/sideEffect/useRedirect.ts b/packages/ra-core/src/sideEffect/useRedirect.ts
index e245c70301f..b8f57369fe7 100644
--- a/packages/ra-core/src/sideEffect/useRedirect.ts
+++ b/packages/ra-core/src/sideEffect/useRedirect.ts
@@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux';
import { Identifier, Record } from '../types';
import resolveRedirectTo from '../util/resolveRedirectTo';
import { refreshView } from '../actions/uiActions';
-import { useHistory } from 'react-router-dom';
+import { useHistory, useLocation } from 'react-router-dom';
type RedirectToFunction = (
basePath?: string,
@@ -32,6 +32,7 @@ export type RedirectionSideEffect = string | boolean | RedirectToFunction;
const useRedirect = () => {
const dispatch = useDispatch();
const history = useHistory();
+ const location = useLocation();
return useCallback(
(
redirectTo: RedirectionSideEffect,
@@ -40,13 +41,21 @@ const useRedirect = () => {
data?: Partial
) => {
if (!redirectTo) {
- dispatch(refreshView());
+ if (location.state || location.search) {
+ history.replace({
+ ...location,
+ state: {},
+ search: undefined,
+ });
+ } else {
+ dispatch(refreshView());
+ }
return;
}
history.push(resolveRedirectTo(redirectTo, basePath, id, data));
},
- [dispatch, history]
+ [dispatch, history, location]
);
};
diff --git a/packages/ra-ui-materialui/src/form/FormTab.js b/packages/ra-ui-materialui/src/form/FormTab.js
index b7335e30816..d98729afea4 100644
--- a/packages/ra-ui-materialui/src/form/FormTab.js
+++ b/packages/ra-ui-materialui/src/form/FormTab.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Link } from 'react-router-dom';
+import { Link, useLocation } from 'react-router-dom';
import MuiTab from '@material-ui/core/Tab';
import classnames from 'classnames';
import { useTranslate } from 'ra-core';
@@ -35,6 +35,7 @@ const FormTab = ({
...rest
}) => {
const translate = useTranslate();
+ const location = useLocation();
const renderHeader = () => (
);
diff --git a/packages/ra-ui-materialui/src/form/TabbedForm.js b/packages/ra-ui-materialui/src/form/TabbedForm.js
index 5c6389cb01e..73d856b2cc1 100644
--- a/packages/ra-ui-materialui/src/form/TabbedForm.js
+++ b/packages/ra-ui-materialui/src/form/TabbedForm.js
@@ -1,4 +1,4 @@
-import React, { Children, isValidElement } from 'react';
+import React, { Children, useEffect, isValidElement } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Route, useRouteMatch, useLocation } from 'react-router-dom';
@@ -141,6 +141,10 @@ export const TabbedFormView = ({
const classes = useStyles({ classes: classesOverride });
const match = useRouteMatch();
const location = useLocation();
+ useEffect(() => {
+ console.log('form mount');
+ return () => console.log('form unmount');
+ }, []);
const url = match ? match.url : location.pathname;
return (