From fa43be473918544f69876c8375deb15bf067761c Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 7 Nov 2023 14:52:51 +0500 Subject: [PATCH 01/12] UISACQCOMP-166: configure the plugin via props --- lib/DonorsList/AddDonorButton.js | 26 +++++++-- lib/DonorsList/DonorsContainer.js | 82 +++++++++++++++------------- lib/DonorsList/DonorsForm.js | 56 +++++++++++++++++++ lib/DonorsList/DonorsForm.test.js | 89 +++++++++++++++++++++++++++++++ lib/DonorsList/DonorsList.js | 59 +++++++++++--------- lib/DonorsList/constants.js | 6 +-- lib/DonorsList/index.js | 2 +- 7 files changed, 251 insertions(+), 69 deletions(-) create mode 100644 lib/DonorsList/DonorsForm.js create mode 100644 lib/DonorsList/DonorsForm.test.js diff --git a/lib/DonorsList/AddDonorButton.js b/lib/DonorsList/AddDonorButton.js index f470de74..2a0d6b4d 100644 --- a/lib/DonorsList/AddDonorButton.js +++ b/lib/DonorsList/AddDonorButton.js @@ -13,7 +13,15 @@ import { visibleFilters, } from './constants'; -const AddDonorButton = ({ onAddDonors, fields, stripes, name }) => { +const AddDonorButton = ({ + fields, + name, + onAddDonors, + searchLabel, + showTriggerButton, + stripes, + visibleColumns, +}) => { const addDonors = (donors = []) => { const addedDonorIds = new Set(fields.value); const newDonorsIds = map(donors.filter(({ id }) => !addedDonorIds.has(id)), 'id'); @@ -24,20 +32,24 @@ const AddDonorButton = ({ onAddDonors, fields, stripes, name }) => { } }; + if (!showTriggerButton) { + return null; + } + return ( } + searchLabel={searchLabel} searchButtonStyle="default" disableRecordCreation stripes={stripes} selectVendor={addDonors} modalLabel={modalLabel} resultsPaneTitle={resultsPaneTitle} - visibleColumns={pluginVisibleColumns} + visibleColumns={visibleColumns} initialFilters={initialFilters} searchableIndexes={searchableIndexes} visibleFilters={visibleFilters} @@ -55,6 +67,14 @@ AddDonorButton.propTypes = { fields: PropTypes.object, stripes: PropTypes.object, name: PropTypes.string.isRequired, + showTriggerButton: PropTypes.bool, + searchLabel: PropTypes.node, + visibleColumns: PropTypes.arrayOf(PropTypes.string), }; +AddDonorButton.defaultProps = { + showTriggerButton: true, + searchLabel: , + visibleColumns: pluginVisibleColumns, +}; export default AddDonorButton; diff --git a/lib/DonorsList/DonorsContainer.js b/lib/DonorsList/DonorsContainer.js index 3c914f5e..7812670d 100644 --- a/lib/DonorsList/DonorsContainer.js +++ b/lib/DonorsList/DonorsContainer.js @@ -1,52 +1,62 @@ -import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { FieldArray } from 'react-final-form-arrays'; -import { - Col, - Loading, - Row, -} from '@folio/stripes/components'; +import { useStripes } from '@folio/stripes/core'; +import AddDonorButton from './AddDonorButton'; import DonorsList from './DonorsList'; -import { useFetchDonors } from './hooks'; - -function DonorsContainer({ name, donorOrganizationIds }) { - const [donorIds, setDonorIds] = useState(donorOrganizationIds); - const { donors, isLoading } = useFetchDonors(donorIds); - - const donorsMap = donors.reduce((acc, contact) => { - acc[contact.id] = contact; - - return acc; - }, {}); - - if (isLoading) { - return ; - } +import { defaultVisibleColumns } from './constants'; + +function DonorsContainer({ + donorsMap, + fields, + id, + setDonorIds, + showTriggerButton, + visibleColumns, + ...rest +}) { + const stripes = useStripes(); return ( - - - - - + <> + +
+ { + showTriggerButton && ( + + ) + } + ); } DonorsContainer.propTypes = { - name: PropTypes.string.isRequired, - donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), + columnWidths: PropTypes.object, + donorsMap: PropTypes.object, + fields: PropTypes.object, + formatter: PropTypes.object, + id: PropTypes.string.isRequired, + searchLabel: PropTypes.node, + setDonorIds: PropTypes.func.isRequired, + showTriggerButton: PropTypes.bool, + visibleColumns: PropTypes.arrayOf(PropTypes.string), }; DonorsContainer.defaultProps = { - donorOrganizationIds: [], + showTriggerButton: true, + visibleColumns: defaultVisibleColumns, }; export default DonorsContainer; diff --git a/lib/DonorsList/DonorsForm.js b/lib/DonorsList/DonorsForm.js new file mode 100644 index 00000000..5164ba0e --- /dev/null +++ b/lib/DonorsList/DonorsForm.js @@ -0,0 +1,56 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { FieldArray } from 'react-final-form-arrays'; + +import { + Col, + Loading, + Row, +} from '@folio/stripes/components'; + +import DonorsContainer from './DonorsContainer'; +import { useFetchDonors } from './hooks'; + +function DonorsForm({ name, donorOrganizationIds, ...rest }) { + const [donorIds, setDonorIds] = useState(donorOrganizationIds); + const { donors, isLoading } = useFetchDonors(donorIds); + + const donorsMap = donors.reduce((acc, contact) => { + acc[contact.id] = contact; + + return acc; + }, {}); + + if (isLoading) { + return ; + } + + return ( + + + + + + ); +} + +DonorsForm.propTypes = { + donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), + name: PropTypes.string.isRequired, + searchLabel: PropTypes.node, + showTriggerButton: PropTypes.bool, + visibleColumns: PropTypes.arrayOf(PropTypes.string), +}; + +DonorsForm.defaultProps = { + donorOrganizationIds: [], +}; + +export default DonorsForm; diff --git a/lib/DonorsList/DonorsForm.test.js b/lib/DonorsList/DonorsForm.test.js new file mode 100644 index 00000000..ac196e29 --- /dev/null +++ b/lib/DonorsList/DonorsForm.test.js @@ -0,0 +1,89 @@ +import { MemoryRouter } from 'react-router-dom'; +import { render, screen } from '@testing-library/react'; + +import stripesFinalForm from '@folio/stripes/final-form'; + +import DonorsForm from './DonorsForm'; +import { useFetchDonors } from './hooks'; + +jest.mock('@folio/stripes/components', () => ({ + ...jest.requireActual('@folio/stripes/components'), + Loading: jest.fn(() => 'Loading'), +})); + +jest.mock('./DonorsList', () => jest.fn(({ donorsMap }) => { + if (!Object.values(donorsMap).length) { + return 'stripes-components.tableEmpty'; + } + + return Object.values(donorsMap).map(({ name }) =>
{name}
); +})); + +jest.mock('./hooks', () => ({ + useFetchDonors: jest.fn().mockReturnValue({ + donors: [], + isLoading: false, + }), +})); + +const defaultProps = { + name: 'donors', + donorOrganizationIds: [], +}; + +const renderForm = (props = {}) => ( +
+ + + +); + +const FormCmpt = stripesFinalForm({})(renderForm); + +const renderComponent = (props = {}) => (render( + + { }} {...props} /> + , +)); + +describe('DonorsForm', () => { + beforeEach(() => { + useFetchDonors.mockClear().mockReturnValue({ + donors: [], + isLoading: false, + }); + }); + + it('should render component', () => { + renderComponent(); + + expect(screen.getByText('stripes-components.tableEmpty')).toBeDefined(); + }); + + it('should render Loading component', () => { + useFetchDonors.mockClear().mockReturnValue({ + donors: [], + isLoading: true, + }); + + renderComponent(); + + expect(screen.getByText('Loading')).toBeDefined(); + }); + + it('should call `useFetchDonors` with `donorOrganizationIds`', () => { + const mockData = [{ name: 'Amazon', code: 'AMAZ', id: '1' }]; + + useFetchDonors.mockClear().mockReturnValue({ + donors: mockData, + isLoading: false, + }); + + renderComponent({ donorOrganizationIds: ['1'] }); + + expect(screen.getByText(mockData[0].name)).toBeDefined(); + }); +}); diff --git a/lib/DonorsList/DonorsList.js b/lib/DonorsList/DonorsList.js index c17e54a1..b11a82ef 100644 --- a/lib/DonorsList/DonorsList.js +++ b/lib/DonorsList/DonorsList.js @@ -9,14 +9,12 @@ import { MultiColumnList, TextLink, } from '@folio/stripes/components'; -import { useStripes } from '@folio/stripes/core'; -import AddDonorButton from './AddDonorButton'; import { alignRowProps, - columnMapping, - columnWidths, - visibleColumns, + defaultColumnMapping, + defaultColumnWidths, + defaultVisibleColumns, } from './constants'; const getDonorUrl = (orgId) => { @@ -50,9 +48,17 @@ const getResultsFormatter = ({ ), }); -const DonorsList = ({ setDonorIds, fields, donorsMap, id }) => { +const DonorsList = ({ + columnMapping, + columnWidths, + donorsMap, + fields, + formatter, + id, + stripes, + visibleColumns, +}) => { const intl = useIntl(); - const stripes = useStripes(); const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); const donors = useMemo(() => (fields.value || []) @@ -72,32 +78,33 @@ const DonorsList = ({ setDonorIds, fields, donorsMap, id }) => { }, [canViewOrganizations, fields, intl]); return ( - <> - -
- - + ); }; DonorsList.propTypes = { - setDonorIds: PropTypes.func.isRequired, fields: PropTypes.object, donorsMap: PropTypes.object, id: PropTypes.string.isRequired, + stripes: PropTypes.object, + visibleColumns: PropTypes.arrayOf(PropTypes.string), + formatter: PropTypes.object, + columnWidths: PropTypes.object, + columnMapping: PropTypes.object, +}; + +DonorsList.defaultProps = { + columnMapping: defaultColumnMapping, + columnWidths: defaultColumnWidths, + visibleColumns: defaultVisibleColumns, }; export default DonorsList; diff --git a/lib/DonorsList/constants.js b/lib/DonorsList/constants.js index 2fb6138c..c93a14bc 100644 --- a/lib/DonorsList/constants.js +++ b/lib/DonorsList/constants.js @@ -1,12 +1,12 @@ import { FormattedMessage } from 'react-intl'; -export const columnMapping = { +export const defaultColumnMapping = { name: , code: , unassignDonor: null, }; -export const visibleColumns = [ +export const defaultVisibleColumns = [ 'name', 'code', 'unassignDonor', @@ -14,7 +14,7 @@ export const visibleColumns = [ export const alignRowProps = { alignLastColToEnd: true }; -export const columnWidths = { +export const defaultColumnWidths = { name: '45%', code: '45%', unassignDonor: '10%', diff --git a/lib/DonorsList/index.js b/lib/DonorsList/index.js index 66ae435f..6b255540 100644 --- a/lib/DonorsList/index.js +++ b/lib/DonorsList/index.js @@ -1 +1 @@ -export { default as DonorsList } from './DonorsContainer'; +export { default as DonorsList } from './DonorsForm'; From 9055af65406ad1aadc934253a482cf285c958430 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 7 Nov 2023 19:50:04 +0500 Subject: [PATCH 02/12] refactor: move logic to component container --- lib/DonorsList/DonorsContainer.js | 69 +++++++++++++---- lib/DonorsList/DonorsForm.js | 18 +++-- lib/DonorsList/DonorsList.js | 76 ++----------------- .../{AddDonorButton.js => DonorsLookup.js} | 31 +++----- ...norButton.test.js => DonorsLookup.test.js} | 6 +- lib/DonorsList/utils.js | 36 +++++++++ 6 files changed, 123 insertions(+), 113 deletions(-) rename lib/DonorsList/{AddDonorButton.js => DonorsLookup.js} (68%) rename lib/DonorsList/{AddDonorButton.test.js => DonorsLookup.test.js} (91%) create mode 100644 lib/DonorsList/utils.js diff --git a/lib/DonorsList/DonorsContainer.js b/lib/DonorsList/DonorsContainer.js index 7812670d..dfc36486 100644 --- a/lib/DonorsList/DonorsContainer.js +++ b/lib/DonorsList/DonorsContainer.js @@ -1,40 +1,82 @@ +import { map, sortBy } from 'lodash'; import PropTypes from 'prop-types'; +import { useMemo } from 'react'; +import { useIntl } from 'react-intl'; import { useStripes } from '@folio/stripes/core'; -import AddDonorButton from './AddDonorButton'; -import DonorsList from './DonorsList'; import { defaultVisibleColumns } from './constants'; +import DonorsList from './DonorsList'; +import DonorsLookup from './DonorsLookup'; +import { getResultsFormatter } from './utils'; function DonorsContainer({ - donorsMap, + columnMapping, + columnWidths, + donors, fields, + formatter, id, setDonorIds, + searchLabel, showTriggerButton, visibleColumns, - ...rest }) { const stripes = useStripes(); + const intl = useIntl(); + const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); + + const donorsMap = donors.reduce((acc, contact) => { + acc[contact.id] = contact; + + return acc; + }, {}); + + const listOfDonors = useMemo(() => (fields.value || []) + .map((contactId, _index) => { + const contact = donorsMap?.[contactId]; + + return { + ...(contact || { isDeleted: true }), + _index, + }; + }), [donorsMap, fields.value]); + + const contentData = useMemo(() => sortBy(listOfDonors, [({ lastName }) => lastName?.toLowerCase()]), [listOfDonors]); + + const resultsFormatter = useMemo(() => { + return formatter || getResultsFormatter({ intl, fields, canViewOrganizations }); + }, [canViewOrganizations, fields, formatter, intl]); + + const onAddDonors = (values = []) => { + const addedDonorIds = new Set(fields.value); + const newDonorsIds = map(values.filter(({ id: donorId }) => !addedDonorIds.has(donorId)), 'id'); + + if (newDonorsIds.length) { + setDonorIds([...addedDonorIds, ...newDonorsIds]); + newDonorsIds.forEach(contactId => fields.push(contactId)); + } + }; return ( <>
{ showTriggerButton && ( - ) } @@ -44,7 +86,8 @@ function DonorsContainer({ DonorsContainer.propTypes = { columnWidths: PropTypes.object, - donorsMap: PropTypes.object, + columnMapping: PropTypes.object, + donors: PropTypes.arrayOf(PropTypes.object), fields: PropTypes.object, formatter: PropTypes.object, id: PropTypes.string.isRequired, diff --git a/lib/DonorsList/DonorsForm.js b/lib/DonorsList/DonorsForm.js index 5164ba0e..374ad839 100644 --- a/lib/DonorsList/DonorsForm.js +++ b/lib/DonorsList/DonorsForm.js @@ -1,6 +1,6 @@ -import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { FieldArray } from 'react-final-form-arrays'; +import { useState } from 'react'; import { Col, @@ -8,6 +8,10 @@ import { Row, } from '@folio/stripes/components'; +import { + defaultColumnMapping, + defaultColumnWidths, +} from './constants'; import DonorsContainer from './DonorsContainer'; import { useFetchDonors } from './hooks'; @@ -15,12 +19,6 @@ function DonorsForm({ name, donorOrganizationIds, ...rest }) { const [donorIds, setDonorIds] = useState(donorOrganizationIds); const { donors, isLoading } = useFetchDonors(donorIds); - const donorsMap = donors.reduce((acc, contact) => { - acc[contact.id] = contact; - - return acc; - }, {}); - if (isLoading) { return ; } @@ -33,7 +31,7 @@ function DonorsForm({ name, donorOrganizationIds, ...rest }) { id={name} component={DonorsContainer} setDonorIds={setDonorIds} - donorsMap={donorsMap} + donors={donors} {...rest} /> @@ -42,6 +40,8 @@ function DonorsForm({ name, donorOrganizationIds, ...rest }) { } DonorsForm.propTypes = { + columnMapping: PropTypes.object, + columnWidths: PropTypes.object, donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), name: PropTypes.string.isRequired, searchLabel: PropTypes.node, @@ -51,6 +51,8 @@ DonorsForm.propTypes = { DonorsForm.defaultProps = { donorOrganizationIds: [], + columnMapping: defaultColumnMapping, + columnWidths: defaultColumnWidths, }; export default DonorsForm; diff --git a/lib/DonorsList/DonorsList.js b/lib/DonorsList/DonorsList.js index b11a82ef..6406d2b4 100644 --- a/lib/DonorsList/DonorsList.js +++ b/lib/DonorsList/DonorsList.js @@ -1,14 +1,6 @@ -import React, { useMemo } from 'react'; -import { sortBy } from 'lodash'; import PropTypes from 'prop-types'; -import { useIntl } from 'react-intl'; -import { - Button, - Icon, - MultiColumnList, - TextLink, -} from '@folio/stripes/components'; +import { MultiColumnList } from '@folio/stripes/components'; import { alignRowProps, @@ -17,72 +9,20 @@ import { defaultVisibleColumns, } from './constants'; -const getDonorUrl = (orgId) => { - if (orgId) { - return `/organizations/view/${orgId}`; - } - - return undefined; -}; - -const getResultsFormatter = ({ - canViewOrganizations, - fields, - intl, -}) => ({ - name: donor => {donor.name}, - code: donor => donor.code, - unassignDonor: donor => ( - - ), -}); - const DonorsList = ({ columnMapping, columnWidths, - donorsMap, - fields, + contentData, formatter, id, - stripes, visibleColumns, }) => { - const intl = useIntl(); - const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); - - const donors = useMemo(() => (fields.value || []) - .map((contactId, _index) => { - const contact = donorsMap?.[contactId]; - - return { - ...(contact || { isDeleted: true }), - _index, - }; - }), [donorsMap, fields.value]); - - const contentData = useMemo(() => sortBy(donors, [({ lastName }) => lastName?.toLowerCase()]), [donors]); - - const resultsFormatter = useMemo(() => { - return getResultsFormatter({ intl, fields, canViewOrganizations }); - }, [canViewOrganizations, fields, intl]); - return ( { - const addDonors = (donors = []) => { - const addedDonorIds = new Set(fields.value); - const newDonorsIds = map(donors.filter(({ id }) => !addedDonorIds.has(id)), 'id'); - - if (newDonorsIds.length) { - onAddDonors([...addedDonorIds, ...newDonorsIds]); - newDonorsIds.forEach(contactId => fields.push(contactId)); - } - }; + const stripes = useStripes(); if (!showTriggerButton) { return null; @@ -46,7 +38,7 @@ const AddDonorButton = ({ searchButtonStyle="default" disableRecordCreation stripes={stripes} - selectVendor={addDonors} + selectVendor={onAddDonors} modalLabel={modalLabel} resultsPaneTitle={resultsPaneTitle} visibleColumns={visibleColumns} @@ -62,19 +54,18 @@ const AddDonorButton = ({ ); }; -AddDonorButton.propTypes = { +DonorsLookup.propTypes = { onAddDonors: PropTypes.func.isRequired, - fields: PropTypes.object, - stripes: PropTypes.object, name: PropTypes.string.isRequired, showTriggerButton: PropTypes.bool, searchLabel: PropTypes.node, visibleColumns: PropTypes.arrayOf(PropTypes.string), }; -AddDonorButton.defaultProps = { +DonorsLookup.defaultProps = { showTriggerButton: true, searchLabel: , visibleColumns: pluginVisibleColumns, }; -export default AddDonorButton; + +export default DonorsLookup; diff --git a/lib/DonorsList/AddDonorButton.test.js b/lib/DonorsList/DonorsLookup.test.js similarity index 91% rename from lib/DonorsList/AddDonorButton.test.js rename to lib/DonorsList/DonorsLookup.test.js index 86465a20..4b3a3c36 100644 --- a/lib/DonorsList/AddDonorButton.test.js +++ b/lib/DonorsList/DonorsLookup.test.js @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; import user from '@testing-library/user-event'; -import AddDonorButton from './AddDonorButton'; +import DonorsLookup from './DonorsLookup'; const mockVendorData = { id: '1', name: 'Amazon' }; @@ -34,10 +34,10 @@ const defaultProps = { }; const renderComponent = (props = defaultProps) => (render( - , + , )); -describe('AddDonorButton', () => { +describe('DonorsLookup', () => { it('should render component', async () => { renderComponent({ fields: { diff --git a/lib/DonorsList/utils.js b/lib/DonorsList/utils.js new file mode 100644 index 00000000..97faee99 --- /dev/null +++ b/lib/DonorsList/utils.js @@ -0,0 +1,36 @@ +import { + Button, + Icon, + TextLink, +} from '@folio/stripes/components'; + +const getDonorUrl = (orgId) => { + if (orgId) { + return `/organizations/view/${orgId}`; + } + + return undefined; +}; + +export const getResultsFormatter = ({ + canViewOrganizations, + fields, + intl, +}) => ({ + name: donor => {donor.name}, + code: donor => donor.code, + unassignDonor: donor => ( + + ), +}); From 07773553f52ad3dbae77bfd19a6c5348020aaddf Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 00:00:48 +0500 Subject: [PATCH 03/12] test: fix failing tests --- lib/DonorsList/DonorsContainer.test.js | 52 ++++++++++++-------------- lib/DonorsList/DonorsForm.test.js | 21 ----------- lib/DonorsList/DonorsList.js | 2 +- lib/DonorsList/DonorsList.test.js | 20 +++------- lib/DonorsList/DonorsLookup.test.js | 20 ++++------ lib/DonorsList/utils.test.js | 13 +++++++ 6 files changed, 51 insertions(+), 77 deletions(-) create mode 100644 lib/DonorsList/utils.test.js diff --git a/lib/DonorsList/DonorsContainer.test.js b/lib/DonorsList/DonorsContainer.test.js index e1433fad..9b8f1d54 100644 --- a/lib/DonorsList/DonorsContainer.test.js +++ b/lib/DonorsList/DonorsContainer.test.js @@ -6,17 +6,12 @@ import stripesFinalForm from '@folio/stripes/final-form'; import DonorsContainer from './DonorsContainer'; import { useFetchDonors } from './hooks'; -jest.mock('@folio/stripes/components', () => ({ - ...jest.requireActual('@folio/stripes/components'), - Loading: jest.fn(() => 'Loading'), -})); - -jest.mock('./DonorsList', () => jest.fn(({ donorsMap }) => { - if (!Object.values(donorsMap).length) { +jest.mock('./DonorsList', () => jest.fn(({ contentData }) => { + if (!contentData.length) { return 'stripes-components.tableEmpty'; } - return Object.values(donorsMap).map(({ name }) =>
{name}
); + return contentData.map(({ name }) =>
{name}
); })); jest.mock('./hooks', () => ({ @@ -27,8 +22,25 @@ jest.mock('./hooks', () => ({ })); const defaultProps = { - name: 'donors', - donorOrganizationIds: [], + columnMapping: {}, + columnWidths: {}, + donors: [], + fields: { + value: [ + '1', + '2', + ], + }, + donorsMap: { + 1: { id: '1', name: 'Amazon' }, + 2: { id: '2', name: 'Google' }, + }, + formatter: jest.fn(), + id: 'donors', + setDonorIds: jest.fn(), + searchLabel: 'Search', + showTriggerButton: true, + visibleColumns: ['name'], }; const renderForm = (props = {}) => ( @@ -60,29 +72,13 @@ describe('DonorsContainer', () => { it('should render component', () => { renderComponent(); - expect(screen.getByText('stripes-components.tableEmpty')).toBeDefined(); - }); - - it('should render Loading component', () => { - useFetchDonors.mockClear().mockReturnValue({ - donors: [], - isLoading: true, - }); - - renderComponent(); - - expect(screen.getByText('Loading')).toBeDefined(); + expect(screen.getByText('stripes-acq-components.donors.noFindOrganizationPlugin')).toBeDefined(); }); it('should call `useFetchDonors` with `donorOrganizationIds`', () => { const mockData = [{ name: 'Amazon', code: 'AMAZ', id: '1' }]; - useFetchDonors.mockClear().mockReturnValue({ - donors: mockData, - isLoading: false, - }); - - renderComponent({ donorOrganizationIds: ['1'] }); + renderComponent({ donors: mockData }); expect(screen.getByText(mockData[0].name)).toBeDefined(); }); diff --git a/lib/DonorsList/DonorsForm.test.js b/lib/DonorsList/DonorsForm.test.js index ac196e29..2b59f405 100644 --- a/lib/DonorsList/DonorsForm.test.js +++ b/lib/DonorsList/DonorsForm.test.js @@ -11,14 +11,6 @@ jest.mock('@folio/stripes/components', () => ({ Loading: jest.fn(() => 'Loading'), })); -jest.mock('./DonorsList', () => jest.fn(({ donorsMap }) => { - if (!Object.values(donorsMap).length) { - return 'stripes-components.tableEmpty'; - } - - return Object.values(donorsMap).map(({ name }) =>
{name}
); -})); - jest.mock('./hooks', () => ({ useFetchDonors: jest.fn().mockReturnValue({ donors: [], @@ -73,17 +65,4 @@ describe('DonorsForm', () => { expect(screen.getByText('Loading')).toBeDefined(); }); - - it('should call `useFetchDonors` with `donorOrganizationIds`', () => { - const mockData = [{ name: 'Amazon', code: 'AMAZ', id: '1' }]; - - useFetchDonors.mockClear().mockReturnValue({ - donors: mockData, - isLoading: false, - }); - - renderComponent({ donorOrganizationIds: ['1'] }); - - expect(screen.getByText(mockData[0].name)).toBeDefined(); - }); }); diff --git a/lib/DonorsList/DonorsList.js b/lib/DonorsList/DonorsList.js index 6406d2b4..cd4e3751 100644 --- a/lib/DonorsList/DonorsList.js +++ b/lib/DonorsList/DonorsList.js @@ -33,7 +33,7 @@ const DonorsList = ({ DonorsList.propTypes = { columnMapping: PropTypes.object, columnWidths: PropTypes.object, - contentData: PropTypes.arrayOf(PropTypes.object), + contentData: PropTypes.arrayOf(PropTypes.object).isRequired, formatter: PropTypes.object, id: PropTypes.string.isRequired, visibleColumns: PropTypes.arrayOf(PropTypes.string), diff --git a/lib/DonorsList/DonorsList.test.js b/lib/DonorsList/DonorsList.test.js index 2dd4647c..45723de2 100644 --- a/lib/DonorsList/DonorsList.test.js +++ b/lib/DonorsList/DonorsList.test.js @@ -3,12 +3,8 @@ import { render, screen } from '@testing-library/react'; import DonorsList from './DonorsList'; -const mockSetDonorIds = jest.fn(); - const defaultProps = { - setDonorIds: mockSetDonorIds, - fields: {}, - donorsMap: {}, + contentData: [], id: 'donors', }; @@ -35,16 +31,10 @@ describe('DonorsList', () => { it('should render the list of donor organizations', () => { renderComponent({ - fields: { - value: [ - '1', - '2', - ], - }, - donorsMap: { - 1: { id: '1', name: 'Amazon' }, - 2: { id: '2', name: 'Google' }, - }, + contentData: [ + { id: '1', name: 'Amazon' }, + { id: '2', name: 'Google' }, + ], }); expect(screen.getByText('Amazon')).toBeDefined(); diff --git a/lib/DonorsList/DonorsLookup.test.js b/lib/DonorsList/DonorsLookup.test.js index 4b3a3c36..7e29e5a4 100644 --- a/lib/DonorsList/DonorsLookup.test.js +++ b/lib/DonorsList/DonorsLookup.test.js @@ -27,9 +27,6 @@ const mockOnAddDonors = jest.fn(); const defaultProps = { onAddDonors: mockOnAddDonors, - fields: { - name: 'donors', - }, name: 'donors', }; @@ -39,14 +36,7 @@ const renderComponent = (props = defaultProps) => (render( describe('DonorsLookup', () => { it('should render component', async () => { - renderComponent({ - fields: { - name: 'donors', - push: jest.fn(), - }, - name: 'donors', - onAddDonors: mockOnAddDonors, - }); + renderComponent(); const addDonorsButton = screen.getByText('Add donor'); @@ -54,6 +44,12 @@ describe('DonorsLookup', () => { await user.click(addDonorsButton); - expect(mockOnAddDonors).toHaveBeenCalledWith([mockVendorData.id]); + expect(mockOnAddDonors).toHaveBeenCalledWith([mockVendorData]); + }); + + it('should not render component', () => { + renderComponent({ showTriggerButton: false }); + + expect(screen.queryByText('Add donor')).toBeNull(); }); }); diff --git a/lib/DonorsList/utils.test.js b/lib/DonorsList/utils.test.js new file mode 100644 index 00000000..a070429f --- /dev/null +++ b/lib/DonorsList/utils.test.js @@ -0,0 +1,13 @@ +import { getResultsFormatter } from './utils'; + +describe('getResultsFormatter', () => { + it('should return object with name, code and unassignDonor functions', () => { + const result = getResultsFormatter({}); + + expect(result).toEqual(expect.objectContaining({ + name: expect.any(Function), + code: expect.any(Function), + unassignDonor: expect.any(Function), + })); + }); +}); From c2ede79d8df45302ba071fcf729fa7925872ce44 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 11:56:44 +0500 Subject: [PATCH 04/12] test: add test cases --- lib/DonorsList/DonorsContainer.test.js | 46 +++++++++++++++++++++----- lib/DonorsList/utils.test.js | 12 ++++++- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/DonorsList/DonorsContainer.test.js b/lib/DonorsList/DonorsContainer.test.js index 9b8f1d54..89409656 100644 --- a/lib/DonorsList/DonorsContainer.test.js +++ b/lib/DonorsList/DonorsContainer.test.js @@ -1,11 +1,14 @@ import { MemoryRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; +import user from '@testing-library/user-event'; import stripesFinalForm from '@folio/stripes/final-form'; import DonorsContainer from './DonorsContainer'; import { useFetchDonors } from './hooks'; +const mockVendor = { id: '1', name: 'Amazon' }; + jest.mock('./DonorsList', () => jest.fn(({ contentData }) => { if (!contentData.length) { return 'stripes-components.tableEmpty'; @@ -14,6 +17,20 @@ jest.mock('./DonorsList', () => jest.fn(({ contentData }) => { return contentData.map(({ name }) =>
{name}
); })); +jest.mock('./DonorsLookup', () => jest.fn(({ onAddDonors, name }) => { + return ( + + ); +})); + +const setDonorIds = jest.fn(); + jest.mock('./hooks', () => ({ useFetchDonors: jest.fn().mockReturnValue({ donors: [], @@ -31,13 +48,9 @@ const defaultProps = { '2', ], }, - donorsMap: { - 1: { id: '1', name: 'Amazon' }, - 2: { id: '2', name: 'Google' }, - }, - formatter: jest.fn(), + formatter: {}, id: 'donors', - setDonorIds: jest.fn(), + setDonorIds, searchLabel: 'Search', showTriggerButton: true, visibleColumns: ['name'], @@ -72,14 +85,31 @@ describe('DonorsContainer', () => { it('should render component', () => { renderComponent(); - expect(screen.getByText('stripes-acq-components.donors.noFindOrganizationPlugin')).toBeDefined(); + expect(screen.getByText('Add donor')).toBeDefined(); }); it('should call `useFetchDonors` with `donorOrganizationIds`', () => { const mockData = [{ name: 'Amazon', code: 'AMAZ', id: '1' }]; renderComponent({ donors: mockData }); - expect(screen.getByText(mockData[0].name)).toBeDefined(); }); + + it('should call `setDonorIds` when `onAddDonors` is called', async () => { + renderComponent({ + donors: [mockVendor], + fields: { + value: [], + push: jest.fn(), + }, + }); + + const addDonorsButton = screen.getByText('Add donor'); + + expect(addDonorsButton).toBeDefined(); + + await user.click(addDonorsButton); + + expect(setDonorIds).toHaveBeenCalled(); + }); }); diff --git a/lib/DonorsList/utils.test.js b/lib/DonorsList/utils.test.js index a070429f..ad912718 100644 --- a/lib/DonorsList/utils.test.js +++ b/lib/DonorsList/utils.test.js @@ -1,8 +1,18 @@ import { getResultsFormatter } from './utils'; +const defaultProps = { + canViewOrganizations: true, + fields: { + remove: jest.fn(), + }, + intl: { + formatMessage: jest.fn((id) => id), + }, +}; + describe('getResultsFormatter', () => { it('should return object with name, code and unassignDonor functions', () => { - const result = getResultsFormatter({}); + const result = getResultsFormatter(defaultProps); expect(result).toEqual(expect.objectContaining({ name: expect.any(Function), From 601a880dbbeecbe3e027aee717d6b2d0028056c2 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 12:16:56 +0500 Subject: [PATCH 05/12] refactor: change component name --- lib/{DonorsList/DonorsForm.js => Donors/Donors.js} | 14 ++++---------- .../DonorsForm.test.js => Donors/Donors.test.js} | 6 +++--- lib/{DonorsList => Donors}/DonorsContainer.js | 0 lib/{DonorsList => Donors}/DonorsContainer.test.js | 0 lib/{DonorsList => Donors}/DonorsList.js | 2 -- lib/{DonorsList => Donors}/DonorsList.test.js | 0 lib/{DonorsList => Donors}/DonorsLookup.js | 0 lib/{DonorsList => Donors}/DonorsLookup.test.js | 0 lib/{DonorsList => Donors}/constants.js | 7 ------- lib/{DonorsList => Donors}/hooks/index.js | 0 .../hooks/useFetchDonors/constants.js | 0 .../hooks/useFetchDonors/index.js | 0 .../hooks/useFetchDonors/useFetchDonors.js | 0 .../hooks/useFetchDonors/useFetchDonors.test.js | 0 lib/Donors/index.js | 1 + lib/{DonorsList => Donors}/utils.js | 0 lib/{DonorsList => Donors}/utils.test.js | 0 lib/DonorsList/index.js | 1 - lib/index.js | 2 +- 19 files changed, 9 insertions(+), 24 deletions(-) rename lib/{DonorsList/DonorsForm.js => Donors/Donors.js} (80%) rename lib/{DonorsList/DonorsForm.test.js => Donors/Donors.test.js} (94%) rename lib/{DonorsList => Donors}/DonorsContainer.js (100%) rename lib/{DonorsList => Donors}/DonorsContainer.test.js (100%) rename lib/{DonorsList => Donors}/DonorsList.js (94%) rename lib/{DonorsList => Donors}/DonorsList.test.js (100%) rename lib/{DonorsList => Donors}/DonorsLookup.js (100%) rename lib/{DonorsList => Donors}/DonorsLookup.test.js (100%) rename lib/{DonorsList => Donors}/constants.js (91%) rename lib/{DonorsList => Donors}/hooks/index.js (100%) rename lib/{DonorsList => Donors}/hooks/useFetchDonors/constants.js (100%) rename lib/{DonorsList => Donors}/hooks/useFetchDonors/index.js (100%) rename lib/{DonorsList => Donors}/hooks/useFetchDonors/useFetchDonors.js (100%) rename lib/{DonorsList => Donors}/hooks/useFetchDonors/useFetchDonors.test.js (100%) create mode 100644 lib/Donors/index.js rename lib/{DonorsList => Donors}/utils.js (100%) rename lib/{DonorsList => Donors}/utils.test.js (100%) delete mode 100644 lib/DonorsList/index.js diff --git a/lib/DonorsList/DonorsForm.js b/lib/Donors/Donors.js similarity index 80% rename from lib/DonorsList/DonorsForm.js rename to lib/Donors/Donors.js index 374ad839..3b7390a0 100644 --- a/lib/DonorsList/DonorsForm.js +++ b/lib/Donors/Donors.js @@ -8,14 +8,11 @@ import { Row, } from '@folio/stripes/components'; -import { - defaultColumnMapping, - defaultColumnWidths, -} from './constants'; +import { defaultColumnMapping } from './constants'; import DonorsContainer from './DonorsContainer'; import { useFetchDonors } from './hooks'; -function DonorsForm({ name, donorOrganizationIds, ...rest }) { +export function Donors({ name, donorOrganizationIds, ...rest }) { const [donorIds, setDonorIds] = useState(donorOrganizationIds); const { donors, isLoading } = useFetchDonors(donorIds); @@ -39,7 +36,7 @@ function DonorsForm({ name, donorOrganizationIds, ...rest }) { ); } -DonorsForm.propTypes = { +Donors.propTypes = { columnMapping: PropTypes.object, columnWidths: PropTypes.object, donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), @@ -49,10 +46,7 @@ DonorsForm.propTypes = { visibleColumns: PropTypes.arrayOf(PropTypes.string), }; -DonorsForm.defaultProps = { +Donors.defaultProps = { donorOrganizationIds: [], columnMapping: defaultColumnMapping, - columnWidths: defaultColumnWidths, }; - -export default DonorsForm; diff --git a/lib/DonorsList/DonorsForm.test.js b/lib/Donors/Donors.test.js similarity index 94% rename from lib/DonorsList/DonorsForm.test.js rename to lib/Donors/Donors.test.js index 2b59f405..8980c0a1 100644 --- a/lib/DonorsList/DonorsForm.test.js +++ b/lib/Donors/Donors.test.js @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'; import stripesFinalForm from '@folio/stripes/final-form'; -import DonorsForm from './DonorsForm'; +import { Donors } from './Donors'; import { useFetchDonors } from './hooks'; jest.mock('@folio/stripes/components', () => ({ @@ -25,7 +25,7 @@ const defaultProps = { const renderForm = (props = {}) => (
- @@ -41,7 +41,7 @@ const renderComponent = (props = {}) => (render( , )); -describe('DonorsForm', () => { +describe('Donors', () => { beforeEach(() => { useFetchDonors.mockClear().mockReturnValue({ donors: [], diff --git a/lib/DonorsList/DonorsContainer.js b/lib/Donors/DonorsContainer.js similarity index 100% rename from lib/DonorsList/DonorsContainer.js rename to lib/Donors/DonorsContainer.js diff --git a/lib/DonorsList/DonorsContainer.test.js b/lib/Donors/DonorsContainer.test.js similarity index 100% rename from lib/DonorsList/DonorsContainer.test.js rename to lib/Donors/DonorsContainer.test.js diff --git a/lib/DonorsList/DonorsList.js b/lib/Donors/DonorsList.js similarity index 94% rename from lib/DonorsList/DonorsList.js rename to lib/Donors/DonorsList.js index cd4e3751..6165cf4f 100644 --- a/lib/DonorsList/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -5,7 +5,6 @@ import { MultiColumnList } from '@folio/stripes/components'; import { alignRowProps, defaultColumnMapping, - defaultColumnWidths, defaultVisibleColumns, } from './constants'; @@ -41,7 +40,6 @@ DonorsList.propTypes = { DonorsList.defaultProps = { columnMapping: defaultColumnMapping, - columnWidths: defaultColumnWidths, visibleColumns: defaultVisibleColumns, }; diff --git a/lib/DonorsList/DonorsList.test.js b/lib/Donors/DonorsList.test.js similarity index 100% rename from lib/DonorsList/DonorsList.test.js rename to lib/Donors/DonorsList.test.js diff --git a/lib/DonorsList/DonorsLookup.js b/lib/Donors/DonorsLookup.js similarity index 100% rename from lib/DonorsList/DonorsLookup.js rename to lib/Donors/DonorsLookup.js diff --git a/lib/DonorsList/DonorsLookup.test.js b/lib/Donors/DonorsLookup.test.js similarity index 100% rename from lib/DonorsList/DonorsLookup.test.js rename to lib/Donors/DonorsLookup.test.js diff --git a/lib/DonorsList/constants.js b/lib/Donors/constants.js similarity index 91% rename from lib/DonorsList/constants.js rename to lib/Donors/constants.js index c93a14bc..f145e44a 100644 --- a/lib/DonorsList/constants.js +++ b/lib/Donors/constants.js @@ -9,17 +9,10 @@ export const defaultColumnMapping = { export const defaultVisibleColumns = [ 'name', 'code', - 'unassignDonor', ]; export const alignRowProps = { alignLastColToEnd: true }; -export const defaultColumnWidths = { - name: '45%', - code: '45%', - unassignDonor: '10%', -}; - export const modalLabel = ; export const resultsPaneTitle = ; diff --git a/lib/DonorsList/hooks/index.js b/lib/Donors/hooks/index.js similarity index 100% rename from lib/DonorsList/hooks/index.js rename to lib/Donors/hooks/index.js diff --git a/lib/DonorsList/hooks/useFetchDonors/constants.js b/lib/Donors/hooks/useFetchDonors/constants.js similarity index 100% rename from lib/DonorsList/hooks/useFetchDonors/constants.js rename to lib/Donors/hooks/useFetchDonors/constants.js diff --git a/lib/DonorsList/hooks/useFetchDonors/index.js b/lib/Donors/hooks/useFetchDonors/index.js similarity index 100% rename from lib/DonorsList/hooks/useFetchDonors/index.js rename to lib/Donors/hooks/useFetchDonors/index.js diff --git a/lib/DonorsList/hooks/useFetchDonors/useFetchDonors.js b/lib/Donors/hooks/useFetchDonors/useFetchDonors.js similarity index 100% rename from lib/DonorsList/hooks/useFetchDonors/useFetchDonors.js rename to lib/Donors/hooks/useFetchDonors/useFetchDonors.js diff --git a/lib/DonorsList/hooks/useFetchDonors/useFetchDonors.test.js b/lib/Donors/hooks/useFetchDonors/useFetchDonors.test.js similarity index 100% rename from lib/DonorsList/hooks/useFetchDonors/useFetchDonors.test.js rename to lib/Donors/hooks/useFetchDonors/useFetchDonors.test.js diff --git a/lib/Donors/index.js b/lib/Donors/index.js new file mode 100644 index 00000000..75c97d82 --- /dev/null +++ b/lib/Donors/index.js @@ -0,0 +1 @@ +export { Donors } from './Donors'; diff --git a/lib/DonorsList/utils.js b/lib/Donors/utils.js similarity index 100% rename from lib/DonorsList/utils.js rename to lib/Donors/utils.js diff --git a/lib/DonorsList/utils.test.js b/lib/Donors/utils.test.js similarity index 100% rename from lib/DonorsList/utils.test.js rename to lib/Donors/utils.test.js diff --git a/lib/DonorsList/index.js b/lib/DonorsList/index.js deleted file mode 100644 index 6b255540..00000000 --- a/lib/DonorsList/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as DonorsList } from './DonorsForm'; diff --git a/lib/index.js b/lib/index.js index 7c0140a3..0760f77a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,7 +16,7 @@ export * from './Currency'; export * from './CurrencyExchangeRateFields'; export * from './CurrencySymbol'; export * from './DeleteHoldingsModal'; -export * from './DonorsList'; +export * from './Donors'; export * from './DragDropMCL'; export * from './DynamicSelection'; export * from './DynamicSelectionFilter'; From bffeaf6f081ca5f409858894d9e9b8403e917ace Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 12:19:11 +0500 Subject: [PATCH 06/12] remove isRequired attribute from name prop --- lib/Donors/Donors.js | 3 ++- lib/Donors/DonorsContainer.js | 2 +- lib/Donors/DonorsList.js | 2 +- lib/Donors/DonorsLookup.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Donors/Donors.js b/lib/Donors/Donors.js index 3b7390a0..7754b0f6 100644 --- a/lib/Donors/Donors.js +++ b/lib/Donors/Donors.js @@ -40,7 +40,7 @@ Donors.propTypes = { columnMapping: PropTypes.object, columnWidths: PropTypes.object, donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), - name: PropTypes.string.isRequired, + name: PropTypes.string, searchLabel: PropTypes.node, showTriggerButton: PropTypes.bool, visibleColumns: PropTypes.arrayOf(PropTypes.string), @@ -48,5 +48,6 @@ Donors.propTypes = { Donors.defaultProps = { donorOrganizationIds: [], + name: 'donorOrganizationIds', columnMapping: defaultColumnMapping, }; diff --git a/lib/Donors/DonorsContainer.js b/lib/Donors/DonorsContainer.js index dfc36486..64e4b7b4 100644 --- a/lib/Donors/DonorsContainer.js +++ b/lib/Donors/DonorsContainer.js @@ -90,7 +90,7 @@ DonorsContainer.propTypes = { donors: PropTypes.arrayOf(PropTypes.object), fields: PropTypes.object, formatter: PropTypes.object, - id: PropTypes.string.isRequired, + id: PropTypes.string, searchLabel: PropTypes.node, setDonorIds: PropTypes.func.isRequired, showTriggerButton: PropTypes.bool, diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index 6165cf4f..feb75162 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -34,7 +34,7 @@ DonorsList.propTypes = { columnWidths: PropTypes.object, contentData: PropTypes.arrayOf(PropTypes.object).isRequired, formatter: PropTypes.object, - id: PropTypes.string.isRequired, + id: PropTypes.string, visibleColumns: PropTypes.arrayOf(PropTypes.string), }; diff --git a/lib/Donors/DonorsLookup.js b/lib/Donors/DonorsLookup.js index c66e5aef..a070aa3d 100644 --- a/lib/Donors/DonorsLookup.js +++ b/lib/Donors/DonorsLookup.js @@ -56,7 +56,7 @@ const DonorsLookup = ({ DonorsLookup.propTypes = { onAddDonors: PropTypes.func.isRequired, - name: PropTypes.string.isRequired, + name: PropTypes.string, showTriggerButton: PropTypes.bool, searchLabel: PropTypes.node, visibleColumns: PropTypes.arrayOf(PropTypes.string), From 8bbe386d888f9dbafb1a23c2f43582213aada983 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 12:52:57 +0500 Subject: [PATCH 07/12] refactor: default props --- lib/Donors/DonorsContainer.js | 15 ++++++++------- lib/Donors/DonorsContainer.test.js | 8 ++++++-- lib/Donors/DonorsList.js | 2 ++ lib/Donors/DonorsLookup.js | 7 ------- lib/Donors/DonorsLookup.test.js | 8 -------- lib/Donors/constants.js | 6 ++++++ lib/Donors/utils.js | 2 +- lib/Donors/utils.test.js | 6 +++--- 8 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/Donors/DonorsContainer.js b/lib/Donors/DonorsContainer.js index 64e4b7b4..c09621ff 100644 --- a/lib/Donors/DonorsContainer.js +++ b/lib/Donors/DonorsContainer.js @@ -8,7 +8,7 @@ import { useStripes } from '@folio/stripes/core'; import { defaultVisibleColumns } from './constants'; import DonorsList from './DonorsList'; import DonorsLookup from './DonorsLookup'; -import { getResultsFormatter } from './utils'; +import { getDonorsListFormatter } from './utils'; function DonorsContainer({ columnMapping, @@ -26,11 +26,13 @@ function DonorsContainer({ const intl = useIntl(); const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); - const donorsMap = donors.reduce((acc, contact) => { - acc[contact.id] = contact; + const donorsMap = useMemo(() => { + return donors.reduce((acc, contact) => { + acc[contact.id] = contact; - return acc; - }, {}); + return acc; + }, {}); + }, [donors]); const listOfDonors = useMemo(() => (fields.value || []) .map((contactId, _index) => { @@ -45,7 +47,7 @@ function DonorsContainer({ const contentData = useMemo(() => sortBy(listOfDonors, [({ lastName }) => lastName?.toLowerCase()]), [listOfDonors]); const resultsFormatter = useMemo(() => { - return formatter || getResultsFormatter({ intl, fields, canViewOrganizations }); + return formatter || getDonorsListFormatter({ intl, fields, canViewOrganizations }); }, [canViewOrganizations, fields, formatter, intl]); const onAddDonors = (values = []) => { @@ -75,7 +77,6 @@ function DonorsContainer({ onAddDonors={onAddDonors} name={id} searchLabel={searchLabel} - showTriggerButton={showTriggerButton} visibleColumns={visibleColumns} /> ) diff --git a/lib/Donors/DonorsContainer.test.js b/lib/Donors/DonorsContainer.test.js index 89409656..bcd0a617 100644 --- a/lib/Donors/DonorsContainer.test.js +++ b/lib/Donors/DonorsContainer.test.js @@ -107,9 +107,13 @@ describe('DonorsContainer', () => { const addDonorsButton = screen.getByText('Add donor'); expect(addDonorsButton).toBeDefined(); - await user.click(addDonorsButton); - expect(setDonorIds).toHaveBeenCalled(); }); + + it('should not render `DonorsLookup` when `showTriggerButton` is false', () => { + renderComponent({ showTriggerButton: false }); + + expect(screen.queryByText('Add donor')).toBeNull(); + }); }); diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index feb75162..5e9b3767 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -5,6 +5,7 @@ import { MultiColumnList } from '@folio/stripes/components'; import { alignRowProps, defaultColumnMapping, + defaultFormatter, defaultVisibleColumns, } from './constants'; @@ -40,6 +41,7 @@ DonorsList.propTypes = { DonorsList.defaultProps = { columnMapping: defaultColumnMapping, + formatter: defaultFormatter, visibleColumns: defaultVisibleColumns, }; diff --git a/lib/Donors/DonorsLookup.js b/lib/Donors/DonorsLookup.js index a070aa3d..c4688e57 100644 --- a/lib/Donors/DonorsLookup.js +++ b/lib/Donors/DonorsLookup.js @@ -19,15 +19,10 @@ const DonorsLookup = ({ name, onAddDonors, searchLabel, - showTriggerButton, visibleColumns, }) => { const stripes = useStripes(); - if (!showTriggerButton) { - return null; - } - return ( , visibleColumns: pluginVisibleColumns, }; diff --git a/lib/Donors/DonorsLookup.test.js b/lib/Donors/DonorsLookup.test.js index 7e29e5a4..67bbbf47 100644 --- a/lib/Donors/DonorsLookup.test.js +++ b/lib/Donors/DonorsLookup.test.js @@ -41,15 +41,7 @@ describe('DonorsLookup', () => { const addDonorsButton = screen.getByText('Add donor'); expect(addDonorsButton).toBeDefined(); - await user.click(addDonorsButton); - expect(mockOnAddDonors).toHaveBeenCalledWith([mockVendorData]); }); - - it('should not render component', () => { - renderComponent({ showTriggerButton: false }); - - expect(screen.queryByText('Add donor')).toBeNull(); - }); }); diff --git a/lib/Donors/constants.js b/lib/Donors/constants.js index f145e44a..87ba62be 100644 --- a/lib/Donors/constants.js +++ b/lib/Donors/constants.js @@ -1,3 +1,4 @@ +import { TextLink } from '@folio/stripes/components'; import { FormattedMessage } from 'react-intl'; export const defaultColumnMapping = { @@ -11,6 +12,11 @@ export const defaultVisibleColumns = [ 'code', ]; +export const defaultFormatter = { + name: donor => {donor.name}, + code: donor => donor.code, +}; + export const alignRowProps = { alignLastColToEnd: true }; export const modalLabel = ; diff --git a/lib/Donors/utils.js b/lib/Donors/utils.js index 97faee99..54beadd7 100644 --- a/lib/Donors/utils.js +++ b/lib/Donors/utils.js @@ -12,7 +12,7 @@ const getDonorUrl = (orgId) => { return undefined; }; -export const getResultsFormatter = ({ +export const getDonorsListFormatter = ({ canViewOrganizations, fields, intl, diff --git a/lib/Donors/utils.test.js b/lib/Donors/utils.test.js index ad912718..6b4834ea 100644 --- a/lib/Donors/utils.test.js +++ b/lib/Donors/utils.test.js @@ -1,4 +1,4 @@ -import { getResultsFormatter } from './utils'; +import { getDonorsListFormatter } from './utils'; const defaultProps = { canViewOrganizations: true, @@ -10,9 +10,9 @@ const defaultProps = { }, }; -describe('getResultsFormatter', () => { +describe('getDonorsListFormatter', () => { it('should return object with name, code and unassignDonor functions', () => { - const result = getResultsFormatter(defaultProps); + const result = getDonorsListFormatter(defaultProps); expect(result).toEqual(expect.objectContaining({ name: expect.any(Function), From ebbf18955edc1d217b718dde9af69963157be507 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 14:11:20 +0500 Subject: [PATCH 08/12] refactor: add default formatter --- lib/Donors/DonorsContainer.js | 18 +++++++++++++++--- lib/Donors/utils.js | 9 ++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/Donors/DonorsContainer.js b/lib/Donors/DonorsContainer.js index c09621ff..c8e2ab73 100644 --- a/lib/Donors/DonorsContainer.js +++ b/lib/Donors/DonorsContainer.js @@ -8,7 +8,10 @@ import { useStripes } from '@folio/stripes/core'; import { defaultVisibleColumns } from './constants'; import DonorsList from './DonorsList'; import DonorsLookup from './DonorsLookup'; -import { getDonorsListFormatter } from './utils'; +import { + getDonorsListFormatter, + getUnAssignDonorFormatter, +} from './utils'; function DonorsContainer({ columnMapping, @@ -47,8 +50,17 @@ function DonorsContainer({ const contentData = useMemo(() => sortBy(listOfDonors, [({ lastName }) => lastName?.toLowerCase()]), [listOfDonors]); const resultsFormatter = useMemo(() => { - return formatter || getDonorsListFormatter({ intl, fields, canViewOrganizations }); - }, [canViewOrganizations, fields, formatter, intl]); + const defaultFormatter = formatter || getDonorsListFormatter({ intl, fields, canViewOrganizations }); + + if (visibleColumns.includes('unassignDonor')) { + return { + ...getUnAssignDonorFormatter({ intl, fields }), + ...defaultFormatter, + }; + } + + return defaultFormatter; + }, [canViewOrganizations, fields, formatter, intl, visibleColumns]); const onAddDonors = (values = []) => { const addedDonorIds = new Set(fields.value); diff --git a/lib/Donors/utils.js b/lib/Donors/utils.js index 54beadd7..a31a8569 100644 --- a/lib/Donors/utils.js +++ b/lib/Donors/utils.js @@ -12,13 +12,12 @@ const getDonorUrl = (orgId) => { return undefined; }; -export const getDonorsListFormatter = ({ - canViewOrganizations, - fields, - intl, -}) => ({ +export const getDonorsListFormatter = ({ canViewOrganizations }) => ({ name: donor => {donor.name}, code: donor => donor.code, +}); + +export const getUnAssignDonorFormatter = ({ fields, intl }) => ({ unassignDonor: donor => ( - ); +jest.mock('./DonorsLookup', () => ({ + DonorsLookup: jest.fn(({ onAddDonors }) => { + return ( +
+ +
+ ); + }), })); const setDonorIds = jest.fn(); diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index 5e9b3767..5d9316fd 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -9,7 +9,7 @@ import { defaultVisibleColumns, } from './constants'; -const DonorsList = ({ +export const DonorsList = ({ columnMapping, columnWidths, contentData, @@ -44,5 +44,3 @@ DonorsList.defaultProps = { formatter: defaultFormatter, visibleColumns: defaultVisibleColumns, }; - -export default DonorsList; diff --git a/lib/Donors/DonorsList.test.js b/lib/Donors/DonorsList.test.js index 45723de2..4b3ead67 100644 --- a/lib/Donors/DonorsList.test.js +++ b/lib/Donors/DonorsList.test.js @@ -1,7 +1,7 @@ import { MemoryRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; -import DonorsList from './DonorsList'; +import { DonorsList } from './DonorsList'; const defaultProps = { contentData: [], diff --git a/lib/Donors/DonorsLookup.js b/lib/Donors/DonorsLookup.js index c4688e57..0fed2002 100644 --- a/lib/Donors/DonorsLookup.js +++ b/lib/Donors/DonorsLookup.js @@ -15,7 +15,7 @@ import { visibleFilters, } from './constants'; -const DonorsLookup = ({ +export const DonorsLookup = ({ name, onAddDonors, searchLabel, @@ -60,5 +60,3 @@ DonorsLookup.defaultProps = { searchLabel: , visibleColumns: pluginVisibleColumns, }; - -export default DonorsLookup; diff --git a/lib/Donors/DonorsLookup.test.js b/lib/Donors/DonorsLookup.test.js index 67bbbf47..05ec4a8c 100644 --- a/lib/Donors/DonorsLookup.test.js +++ b/lib/Donors/DonorsLookup.test.js @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; import user from '@testing-library/user-event'; -import DonorsLookup from './DonorsLookup'; +import { DonorsLookup } from './DonorsLookup'; const mockVendorData = { id: '1', name: 'Amazon' }; diff --git a/lib/Donors/utils.test.js b/lib/Donors/utils.test.js index 6b4834ea..1388756a 100644 --- a/lib/Donors/utils.test.js +++ b/lib/Donors/utils.test.js @@ -1,4 +1,7 @@ -import { getDonorsListFormatter } from './utils'; +import { + getDonorsListFormatter, + getUnAssignDonorFormatter, +} from './utils'; const defaultProps = { canViewOrganizations: true, @@ -17,6 +20,15 @@ describe('getDonorsListFormatter', () => { expect(result).toEqual(expect.objectContaining({ name: expect.any(Function), code: expect.any(Function), + })); + }); +}); + +describe('getUnAssignDonorFormatter', () => { + it('should return object with name, code and unassignDonor functions', () => { + const result = getUnAssignDonorFormatter(defaultProps); + + expect(result).toEqual(expect.objectContaining({ unassignDonor: expect.any(Function), })); }); From 2ef422e24378e937fe2d0bbefbccea5caa8c8b67 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 15:51:37 +0500 Subject: [PATCH 10/12] remove default formatter --- lib/Donors/DonorsList.js | 2 -- lib/Donors/constants.js | 6 ------ 2 files changed, 8 deletions(-) diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index 5d9316fd..49a3e44a 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -5,7 +5,6 @@ import { MultiColumnList } from '@folio/stripes/components'; import { alignRowProps, defaultColumnMapping, - defaultFormatter, defaultVisibleColumns, } from './constants'; @@ -41,6 +40,5 @@ DonorsList.propTypes = { DonorsList.defaultProps = { columnMapping: defaultColumnMapping, - formatter: defaultFormatter, visibleColumns: defaultVisibleColumns, }; diff --git a/lib/Donors/constants.js b/lib/Donors/constants.js index 87ba62be..f145e44a 100644 --- a/lib/Donors/constants.js +++ b/lib/Donors/constants.js @@ -1,4 +1,3 @@ -import { TextLink } from '@folio/stripes/components'; import { FormattedMessage } from 'react-intl'; export const defaultColumnMapping = { @@ -12,11 +11,6 @@ export const defaultVisibleColumns = [ 'code', ]; -export const defaultFormatter = { - name: donor => {donor.name}, - code: donor => donor.code, -}; - export const alignRowProps = { alignLastColToEnd: true }; export const modalLabel = ; From 3385e506f34a337e9df80637d166a73b014443f7 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 8 Nov 2023 16:51:40 +0500 Subject: [PATCH 11/12] update DonorsList with providing default formatter --- lib/Donors/DonorsList.js | 10 +++++++++- lib/Donors/index.js | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index 49a3e44a..af46a299 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -1,6 +1,8 @@ +import { isEmpty } from 'lodash'; import PropTypes from 'prop-types'; import { MultiColumnList } from '@folio/stripes/components'; +import { useStripes } from '@folio/stripes/core'; import { alignRowProps, @@ -8,6 +10,8 @@ import { defaultVisibleColumns, } from './constants'; +import { getDonorsListFormatter } from './utils'; + export const DonorsList = ({ columnMapping, columnWidths, @@ -16,12 +20,16 @@ export const DonorsList = ({ id, visibleColumns, }) => { + const stripes = useStripes(); + const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); + const defaultFormatter = isEmpty(formatter) || getDonorsListFormatter({ canViewOrganizations }); + return ( Date: Wed, 8 Nov 2023 17:34:41 +0500 Subject: [PATCH 12/12] fix formatter --- lib/Donors/DonorsList.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Donors/DonorsList.js b/lib/Donors/DonorsList.js index af46a299..75dcb284 100644 --- a/lib/Donors/DonorsList.js +++ b/lib/Donors/DonorsList.js @@ -1,5 +1,5 @@ -import { isEmpty } from 'lodash'; import PropTypes from 'prop-types'; +import { useMemo } from 'react'; import { MultiColumnList } from '@folio/stripes/components'; import { useStripes } from '@folio/stripes/core'; @@ -9,27 +9,28 @@ import { defaultColumnMapping, defaultVisibleColumns, } from './constants'; - import { getDonorsListFormatter } from './utils'; export const DonorsList = ({ columnMapping, columnWidths, contentData, - formatter, + formatter: formatterProp, id, visibleColumns, }) => { const stripes = useStripes(); const canViewOrganizations = stripes.hasPerm('ui-organizations.view'); - const defaultFormatter = isEmpty(formatter) || getDonorsListFormatter({ canViewOrganizations }); + const formatter = useMemo(() => { + return formatterProp || getDonorsListFormatter({ canViewOrganizations }); + }, [canViewOrganizations, formatterProp]); return (