Skip to content

Commit

Permalink
Alert user before closing the createItemModal form
Browse files Browse the repository at this point in the history
The form data inside the `createItemModal` was getting lost when user
hit the `cancel` button. This could lead to a bad UX.

This fix will identifies if form data has changed from its initial
values before closing the modal. If yes, then it alerts the user for
confirmation. Otherwise, will close the modal.

Closes: #2822

Add cypress test for confirmation modal
  • Loading branch information
singhArmani committed Aug 3, 2020
1 parent 7650ecd commit b655338
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/purple-knives-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystonejs/app-admin-ui': patch
---

Alerted the user before canceling the createItemModal form.
59 changes: 56 additions & 3 deletions packages/app-admin-ui/client/components/CreateItemModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useToasts } from 'react-toast-notifications';

import { Button, LoadingButton } from '@arch-ui/button';
import Drawer from '@arch-ui/drawer';
import Confirm from '@arch-ui/confirm';
import {
arrayToObject,
captureSuspensePromises,
Expand Down Expand Up @@ -50,6 +51,7 @@ const CreateItemModal = ({ prefillData = {}, onClose, onCreate, viewOnSave }) =>
const { list, closeCreateItemModal, isCreateItemModalOpen } = useList();

const [item, setItem] = useState(list.getInitialItemData({ prefill: prefillData }));
const [isConfirmOpen, setConfirmOpen] = useState(false);
const [validationErrors, setValidationErrors] = useState({});
const [validationWarnings, setValidationWarnings] = useState({});

Expand Down Expand Up @@ -129,16 +131,41 @@ const CreateItemModal = ({ prefillData = {}, onClose, onCreate, viewOnSave }) =>
}
});

const _onClose = () => {
if (loading) return;
// Identifies if the user has changed the initial data in the form.
const hasFormDataChanged = () => {
const data = arrayToObject(creatable, 'path');
let hasChanged = false;
const initialData = list.getInitialItemData({ prefill: prefillData });
const initialValues = getValues(data, initialData);
const currentValues = getValues(data, item);
for (const path of Object.keys(currentValues)) {
if (data[path].hasChanged(initialValues, currentValues)) {
hasChanged = true;
break;
}
}
return hasChanged;
};

const _createItemModalClose = () => {
closeCreateItemModal();
setItem(list.getInitialItemData({}));
const data = arrayToObject(creatable, 'path', field => field.serialize(item));
if (onClose) {
const data = arrayToObject(creatable, 'path', field => field.serialize(item));
onClose(data);
}
};

const _onClose = () => {
if (loading) return;
if (hasFormDataChanged()) {
// Ask for user confirmation before canceling.
setConfirmOpen(true);
return;
}
_createItemModalClose();
};

const _onKeyDown = event => {
if (event.defaultPrevented) return;
switch (event.key) {
Expand Down Expand Up @@ -244,10 +271,36 @@ const CreateItemModal = ({ prefillData = {}, onClose, onCreate, viewOnSave }) =>
));
}}
</Render>
<ConfirmModal
onConfirm={isConfirmOpen}
onCancel={() => {
setConfirmOpen(false);
_createItemModalClose();
}}
onNo={() => setConfirmOpen(false)}
/>
</Suspense>
</div>
</Drawer>
);
};

const ConfirmModal = ({ isOpen, onConfirm, onCancel }) => {
return (
<Confirm isOpen={isOpen}>
<p style={{ marginTop: 0 }}>
All of your form data will be lost. Are you sure you want to cancel?
</p>
<footer>
<Button appearance="danger" variant="ghost" onClick={onConfirm}>
Ok
</Button>
<Button variant="subtle" onClick={onCancel}>
Cancel
</Button>
</footer>
</Confirm>
);
};

export default CreateItemModal;
17 changes: 17 additions & 0 deletions test-projects/basic/cypress/integration/create-buttons_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,21 @@ describe('Home page', () => {
cy.contains('div', `Create ${text} Dialog`).should('not.exist');
});
});

it('Ensure Create Modal triggers a confirmation dialog when form data is filled, and user hits cancel button', () => {
cy.visit('/admin/users');

cy.get('#list-page-create-button').click({ force: true });
cy.get('#ks-input-name').type('Aman', {
force: true,
});
cy.contains('div', `Create User Dialog`)
.contains('button', 'Cancel')
.click({ force: true });

cy.get('div[role="alertdialog"]')
.contains('button', 'Cancel')
.click({ force: true });
cy.contains('div', `Create User Dialog`).should('exist');
});
});

0 comments on commit b655338

Please sign in to comment.