Skip to content

Commit

Permalink
STSMACOM-796 Show successful toast notifications for Create and Edit …
Browse files Browse the repository at this point in the history
…actions in `<ControlledVocab>`
  • Loading branch information
BogdanDenis committed Jan 8, 2024
1 parent d3dc0f4 commit 3c235a1
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* `<EditCustomFieldsSettings>` now passes the `entityType` when making PUT requests to `/custom-fields`. Refs FCFIELDS-44.
* Added `tenant` prop to `<ControlledVocab>`. Refs STSMACOM-794.
* Use the default match and search option in Advanced search when they are not entered. Refs STSMACOM-793.
* Show successful toast notifications for Create and Edit actions in `<ControlledVocab>`. Refs STSMACOM-796.

## [9.0.0](https://github.com/folio-org/stripes-smart-components/tree/v9.0.0) (2023-10-11)
[Full Changelog](https://github.com/folio-org/stripes-smart-components/compare/v8.0.0...v9.0.0)
Expand Down
85 changes: 57 additions & 28 deletions lib/ControlledVocab/ControlledVocab.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import EditableList from '../EditableList';
import css from './ControlledVocab.css';
import makeRefdataActuatorsBoundTo from './actuators-refdata';

const ACTIONS = {
CREATE: 'termCreated',
EDIT: 'termUpdated',
DELETE: 'termDeleted',
};

const getTenantFromRESTResource = (queryParams, pathComponents, resourceValues, logger, props) => {
const {
tenant,
Expand Down Expand Up @@ -114,9 +120,6 @@ class ControlledVocab extends React.Component {
GET: PropTypes.func,
reset: PropTypes.func,
}),
tenant: PropTypes.shape({
replace: PropTypes.func,
}),
values: PropTypes.shape({
DELETE: PropTypes.func,
GET: PropTypes.func,
Expand Down Expand Up @@ -159,7 +162,9 @@ class ControlledVocab extends React.Component {
cannotDeleteTermHeader: "Cannot delete patron group",
cannotDeleteTermMessage: "This patron group cannot be deleted, as it is in use by one or more records.",
deleteEntry: "Delete patron group",
termCreated: "The patron group <b>{term}</b> was successfully <b>created</b>",
termDeleted: "The patron group <b>{term}</b> was successfully <b>deleted</b>",
termUpdated: "The patron group <b>{term}</b> was successfully <b>updated</b>",
termWillBeDeleted: "The patron group <b>{term}</b> will be <b>deleted.</b>"
}
*/
Expand All @@ -169,6 +174,8 @@ class ControlledVocab extends React.Component {
cannotDeleteTermMessage: PropTypes.string,
deleteEntry: PropTypes.string,
termDeleted: PropTypes.string,
termCreated: PropTypes.string,
termUpdated: PropTypes.string,
termWillBeDeleted: PropTypes.string,
}),
/*
Expand Down Expand Up @@ -211,7 +218,8 @@ class ControlledVocab extends React.Component {
// !{limitParam:-limit}
// in the manifest above.
actuatorType: 'rest',
canCreate: true
canCreate: true,
translations: {},
};

constructor(props) {
Expand Down Expand Up @@ -274,7 +282,10 @@ class ControlledVocab extends React.Component {
}

onCreateItem(item) {
return this.props.mutator.values.POST(this.props.preCreateHook(item));
return this.props.mutator.values.POST(this.props.preCreateHook(item))
.then(() => {
this.showSuccessCallout(item, ACTIONS.CREATE);
});
}

onDeleteItem() {
Expand All @@ -284,7 +295,7 @@ class ControlledVocab extends React.Component {

return this.props.mutator.values.DELETE({ id: selectedItem.id })
.then(() => {
this.showDeletionSuccessCallout(selectedItem);
this.showSuccessCallout(selectedItem, ACTIONS.DELETE);
this.deleteItemResolve();
})
.catch(() => {
Expand All @@ -296,7 +307,10 @@ class ControlledVocab extends React.Component {

onUpdateItem(item) {
this.props.mutator.activeRecord.update({ id: item.id });
return this.props.mutator.values.PUT(this.props.preUpdateHook(item));
return this.props.mutator.values.PUT(this.props.preUpdateHook(item))
.then(() => {
this.showSuccessCallout(item, ACTIONS.EDIT);
});
}

filteredRows(rows) {
Expand Down Expand Up @@ -343,28 +357,43 @@ class ControlledVocab extends React.Component {
});
}

showDeletionSuccessCallout(item) {
if (this.callout) {
const { termDeleted } = this.props.translations || {};
const message = (
termDeleted ?
<FormattedMessage
id={termDeleted}
values={{
term: item[this.state.primaryField],
}}
/> :
<FormattedMessage
id="stripes-smart-components.cv.termDeleted"
values={{
type: this.props.labelSingular,
term: item[this.state.primaryField],
}}
/>
);

this.callout.sendCallout({ message });
showSuccessCallout(item, action) {
if (!this.callout) {
return;
}

const {
termCreated,
termDeleted,
termUpdated,
} = this.props.translations;

const translationByAction = {
[ACTIONS.CREATE]: termCreated,
[ACTIONS.DELETE]: termDeleted,
[ACTIONS.EDIT]: termUpdated,
};

const translation = translationByAction[action];

const message = (
translation ?
<FormattedMessage
id={translation}
values={{
term: item[this.state.primaryField],
}}
/> :
<FormattedMessage
id={`stripes-smart-components.cv.${action}`}
values={{
type: this.props.labelSingular,
term: item[this.state.primaryField],
}}
/>
);

this.callout.sendCallout({ message });
}

handlePaneFocus({ paneTitleRef }) {
Expand Down
42 changes: 39 additions & 3 deletions lib/ControlledVocab/tests/ControlledVocab-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import {
including,
MultiColumnList,
MultiColumnListCell,
Callout,
} from '@folio/stripes-testing';

import mountComponent from './mountComponent';

import { setupApplication } from '../../../tests/helpers';

describe('ControlledVocab', () => {
describe.only('ControlledVocab', () => {

Check failure on line 25 in lib/ControlledVocab/tests/ControlledVocab-test.js

View workflow job for this annotation

GitHub Actions / build-npm

describe.only not permitted

Check failure on line 25 in lib/ControlledVocab/tests/ControlledVocab-test.js

View workflow job for this annotation

GitHub Actions / build-npm

describe.only not permitted
const cv = EditableList();
const firstRow = EditableListRow();
const cm = ConfirmationModal();
Expand Down Expand Up @@ -59,7 +60,9 @@ describe('ControlledVocab', () => {
it('should display Delete button', () => cm.find(Button('Delete')).exists());

describe('click delete on confirmation modal', () => {
beforeEach(async () => {
beforeEach(async function () {
this.server.delete('location-units/institutions/:id', {}, 500);

await Button('Delete').click();
});

Expand All @@ -79,7 +82,9 @@ describe('ControlledVocab', () => {
it('should have row count 5', () => cv.has({ rowCount: 5 }));

describe('clicking Delete icon on first row', () => {
beforeEach(async () => {
beforeEach(async function () {
this.server.delete('location-units/institutions/:id', {}, 500);

await firstRow.delete();
});

Expand All @@ -99,6 +104,15 @@ describe('ControlledVocab', () => {
it('cannot delete modal title', () => mo.has({ title: 'Cannot delete Institution' }));
});
});

describe('when deleting is successful', () => {
beforeEach(async () => {
await firstRow.delete();
await Button('Delete').click();
});

it('should display successful callout message', () => Callout('The Institution Bowdoin College was successfully deleted').exists());
});
});

describe('User can edit EditableListForm', () => {
Expand Down Expand Up @@ -167,6 +181,14 @@ describe('ControlledVocab', () => {
it('should not display the error message', () => firstRow.find(TextField(including('name'))).is({ valid: true }));

it('should enable Save button', () => firstRow.has({ saveDisabled: false }));

describe('when creating is successful', () => {
beforeEach(async () => {
await firstRow.save();
});

it('should display successful callout message', () => Callout('The Institution test was successfully created').exists());
});
});
});

Expand Down Expand Up @@ -200,6 +222,20 @@ describe('ControlledVocab', () => {

it('should enable Delete button', () => cv.has({ deleteDisabled: false }));
});

describe('when editing an item', () => {
beforeEach(async () => {
await firstRow.find(TextField(including('name'))).fillIn('Bowdoin College edit');
});

describe('when creating is successful', () => {
beforeEach(async () => {
await firstRow.save();
});

it('should display successful callout message', () => Callout('The Institution Bowdoin College edit was successfully updated').exists());
});
});
});
});

Expand Down
124 changes: 79 additions & 45 deletions lib/ControlledVocab/tests/mountComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,88 @@ export default async function (editable, server, props) {
let valuesLoaded = false;
let usersLoaded = false;

const institutions = [{
'id' : '40ee00ca-a518-4b49-be01-0638d0a4ac57',
'name' : 'Bowdoin College',
'code' : 'BC'
}, {
'id' : '786bccb0-1795-4896-9452-a6e5bd5f28ac',
'name' : 'Harvard University',
'code' : 'hu',
'metadata' : {
'createdDate' : '2019-04-18T16:09:26.816+0000',
'createdByUserId' : 'user-1',
'updatedDate' : '2019-04-18T16:09:26.816+0000',
'updatedByUserId' : 'user-1'
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'Dartmouth College',
'code' : 'dc',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-1',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-1',
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'University of Maryland, Baltimore',
'code' : 'umb',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-2',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-2',
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'Cornell University',
'code' : 'cu',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-2',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-2'
}
}];

server.get('location-units/institutions', (schema, request) => {
valuesLoaded = true;

// stripes-connect requires `X-Request-URL` header for `response.url`
return new Response(200, { 'X-Request-URL': request.url }, {
'locinsts' : [{
'id' : '40ee00ca-a518-4b49-be01-0638d0a4ac57',
'name' : 'Bowdoin College',
'code' : 'BC'
}, {
'id' : '786bccb0-1795-4896-9452-a6e5bd5f28ac',
'name' : 'Harvard University',
'code' : 'hu',
'metadata' : {
'createdDate' : '2019-04-18T16:09:26.816+0000',
'createdByUserId' : 'user-1',
'updatedDate' : '2019-04-18T16:09:26.816+0000',
'updatedByUserId' : 'user-1'
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'Dartmouth College',
'code' : 'dc',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-1',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-1',
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'University of Maryland, Baltimore',
'code' : 'umb',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-2',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-2',
}
}, {
'id' : '16e4d83d-a076-4175-a810-90190eb2954c',
'name' : 'Cornell University',
'code' : 'cu',
'metadata' : {
'createdDate' : '2019-04-18T16:41:36.806+0000',
'createdByUserId' : 'user-2',
'updatedDate' : '2019-04-18T16:41:36.806+0000',
'updatedByUserId' : 'user-2'
}
}],
'locinsts': institutions,
'totalRecords' : 5
});
});

server.delete('location-units/institutions/:id', (schema, request) => {
valuesLoaded = true;

// stripes-connect requires `X-Request-URL` header for `response.url`
return new Response(201, { 'X-Request-URL': request.url }, {
'locinsts': institutions,
'totalRecords' : 5
});
});

server.post('location-units/institutions/', (schema, request) => {
valuesLoaded = true;

// stripes-connect requires `X-Request-URL` header for `response.url`
return new Response(201, { 'X-Request-URL': request.url }, {
'locinsts': institutions,
'totalRecords' : 5
});
});

server.put('location-units/institutions/:id', (schema, request) => {
valuesLoaded = true;

// stripes-connect requires `X-Request-URL` header for `response.url`
return new Response(201, { 'X-Request-URL': request.url }, {
'locinsts': institutions,
'totalRecords' : 5
});
});
Expand Down Expand Up @@ -96,6 +128,8 @@ export default async function (editable, server, props) {
});
});



const onEditHandler = sinon.spy();
const onDeleteHandler = sinon.spy();

Expand Down
2 changes: 2 additions & 0 deletions translations/stripes-smart-components/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"cv.numberOfObjects": "# of {objects}",
"cv.termWillBeDeleted": "The {type} <b>{term}</b> will be <b>deleted.</b>",
"cv.termDeleted": "The {type} <b>{term}</b> was successfully <b>deleted</b>",
"cv.termCreated": "The {type} <b>{term}</b> was successfully <b>created</b>",
"cv.termUpdated": "The {type} <b>{term}</b> was successfully <b>updated</b>",
"ll.locationLookup": "Location look-up",
"ll.selectLocationHeader": "Select {type} location",
"ll.institution": "Institution",
Expand Down

0 comments on commit 3c235a1

Please sign in to comment.