Skip to content

Commit

Permalink
6261/fix delete alert (#6296)
Browse files Browse the repository at this point in the history
* refactor confirm procedure to only add success toast on success

* new deletion logic in Listview

* add crud-notifications test project

* update deletion solution to be more pragmatic at scale

* update bug fix to be more verbose

* update schema.graphql

* minor updates

* fix yarn lint:examples to not break when running more than one test-project

* minor updates to copy

* remove log

* changeset

Co-authored-by: Tim Leslie <[email protected]>
  • Loading branch information
gwyneplaine and timleslie authored Aug 17, 2021
1 parent f3014a6 commit e3fefaf
Show file tree
Hide file tree
Showing 10 changed files with 532 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-spiders-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/keystone': patch
---

Fixed delete success notifications in the Admin UI appearing on failed deletes in List view and Item view.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"update": "manypkg upgrade",
"no-cypress-install": "cross-env CYPRESS_INSTALL_BINARY=0 yarn",
"postinstall-examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; yarn keystone-next postinstall --fix; cd ../..; done",
"lint:examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done",
"lint:examples": "for d in `find examples -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find examples-staging -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../..; done; for d in `find tests/test-projects -type d -maxdepth 1 -mindepth 1`; do cd $d; echo $d; SKIP_PROMPTS=1 yarn keystone-next postinstall; if [ $? -ne 0 ]; then exit 1; fi; cd ../../..; done",
"generate-filters": "cd prisma-utils && yarn generate",
"lint:filters": "cd prisma-utils && yarn verify"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,19 @@ function DeleteButton({
confirm: {
label: 'Delete',
action: async () => {
await deleteItem().catch(err => {
toasts.addToast({
title: 'Failed to delete item',
try {
await deleteItem();
} catch (err) {
return toasts.addToast({
title: `Failed to delete ${list.singular} item: ${itemLabel}`,
message: err.message,
tone: 'negative',
});
});
}
router.push(`/${list.path}`);
toasts.addToast({
return toasts.addToast({
title: itemLabel,
message: 'Deleted successfully',
message: `Deleted ${list.singular} item successfully`,
tone: 'positive',
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,18 @@ function DeleteManyButton({
useMemo(
() =>
gql`
mutation($where: [${list.gqlNames.whereUniqueInputName}!]!) {
${list.gqlNames.deleteManyMutationName}(where: $where) {
id
}
}
mutation($where: [${list.gqlNames.whereUniqueInputName}!]!) {
${list.gqlNames.deleteManyMutationName}(where: $where) {
id
${list.labelField}
}
}
`,
[list]
)
),
{
errorPolicy: 'all',
}
);
const [isOpen, setIsOpen] = useState(false);
const toasts = useToasts();
Expand All @@ -466,20 +470,75 @@ function DeleteManyButton({
confirm: {
label: 'Delete',
action: async () => {
await deleteItems({
const { data, errors } = await deleteItems({
variables: { where: [...selectedItems].map(id => ({ id })) },
}).catch(err => {
});
/*
Data returns an array where successful deletions are item objects
and unsuccessful deletions are null values.
Run a reduce to count success and failure as well as
to generate the success message to be passed to the success toast
*/
const { successfulItems, unsuccessfulItems, successMessage } = data[
list.gqlNames.deleteManyMutationName
].reduce(
(
acc: {
successfulItems: number;
unsuccessfulItems: number;
successMessage: string;
},
curr: any
) => {
if (curr) {
acc.successfulItems++;
acc.successMessage =
acc.successMessage === ''
? (acc.successMessage += curr.label)
: (acc.successMessage += `, ${curr.label}`);
} else {
acc.unsuccessfulItems++;
}
return acc;
},
{ successfulItems: 0, unsuccessfulItems: 0, successMessage: '' } as {
successfulItems: number;
unsuccessfulItems: number;
successMessage: string;
}
);

// If there are errors
if (errors?.length) {
// Find out how many items failed to delete.
// Reduce error messages down to unique instances, and append to the toast as a message.
toasts.addToast({
title: 'Failed to delete items',
message: err.message,
tone: 'negative',
title: `Failed to delete ${unsuccessfulItems} of ${
data[list.gqlNames.deleteManyMutationName].length
} ${list.plural}`,
message: errors
.reduce((acc, error) => {
if (acc.indexOf(error.message) < 0) {
acc.push(error.message);
}
return acc;
}, [] as string[])
.join('\n'),
});
});
toasts.addToast({
title: 'Deleted items successfully',
tone: 'positive',
});
refetch();
}

if (successfulItems) {
toasts.addToast({
tone: 'positive',
title: `Deleted ${successfulItems} of ${
data[list.gqlNames.deleteManyMutationName].length
} ${list.plural} successfully`,
message: successMessage,
});
}

return refetch();
},
},
cancel: {
Expand Down
4 changes: 4 additions & 0 deletions tests/test-projects/crud-notifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## THIS IS A TEST PROJECT

The sole purpose of this project is to act as a fixture through which we run our admin-ui integration tests.
For useful and applicable examples of how to use keystone, please visit the [examples directory](https://github.com/keystonejs/keystone/tree/master/examples/) or visit our [docs](https://next.keystonejs.com).
23 changes: 23 additions & 0 deletions tests/test-projects/crud-notifications/keystone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { config } from '@keystone-next/keystone/schema';
import { lists } from './schema';

export default config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./test.db',
async onConnect(context) {
await context.lists.Task.createMany({
data: [...Array.from(Array(50).keys())].map(key => {
return { label: `do not delete ${key}` };
}),
});

await context.lists.Task.createMany({
data: [...Array.from(Array(25).keys())].map(key => {
return { label: `deletable ${key}` };
}),
});
},
},
lists,
});
22 changes: 22 additions & 0 deletions tests/test-projects/crud-notifications/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@keystone-next/test-projects-crud-notifications",
"version": "0.0.2",
"private": true,
"license": "MIT",
"scripts": {
"dev": "keystone-next dev",
"start": "keystone-next start",
"build": "keystone-next build"
},
"dependencies": {
"@keystone-next/fields": "^13.0.0",
"@keystone-next/keystone": "^23.0.0"
},
"devDependencies": {
"typescript": "^4.3.5"
},
"engines": {
"node": "^12.20 || >= 14.13"
},
"repository": "https://github.com/keystonejs/keystone/tree/master/tests/test-projects/crud-notifications"
}
Loading

0 comments on commit e3fefaf

Please sign in to comment.