diff --git a/lib/DonorsList/DonorsContainer.js b/lib/Donors/Donors.js
similarity index 51%
rename from lib/DonorsList/DonorsContainer.js
rename to lib/Donors/Donors.js
index 3c914f5e..6c314c6b 100644
--- a/lib/DonorsList/DonorsContainer.js
+++ b/lib/Donors/Donors.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,19 +8,14 @@ import {
Row,
} from '@folio/stripes/components';
-import DonorsList from './DonorsList';
+import { defaultColumnMapping } from './constants';
+import { DonorsContainer } from './DonorsContainer';
import { useFetchDonors } from './hooks';
-function DonorsContainer({ name, donorOrganizationIds }) {
+export function Donors({ 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 ;
}
@@ -31,22 +26,28 @@ function DonorsContainer({ name, donorOrganizationIds }) {
);
}
-DonorsContainer.propTypes = {
- name: PropTypes.string.isRequired,
+Donors.propTypes = {
+ columnMapping: PropTypes.object,
+ columnWidths: PropTypes.object,
donorOrganizationIds: PropTypes.arrayOf(PropTypes.string),
+ name: PropTypes.string,
+ searchLabel: PropTypes.node,
+ showTriggerButton: PropTypes.bool,
+ visibleColumns: PropTypes.arrayOf(PropTypes.string),
};
-DonorsContainer.defaultProps = {
+Donors.defaultProps = {
donorOrganizationIds: [],
+ name: 'donorOrganizationIds',
+ columnMapping: defaultColumnMapping,
};
-
-export default DonorsContainer;
diff --git a/lib/DonorsList/DonorsContainer.test.js b/lib/Donors/Donors.test.js
similarity index 66%
rename from lib/DonorsList/DonorsContainer.test.js
rename to lib/Donors/Donors.test.js
index e1433fad..8980c0a1 100644
--- a/lib/DonorsList/DonorsContainer.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 DonorsContainer from './DonorsContainer';
+import { Donors } from './Donors';
import { useFetchDonors } from './hooks';
jest.mock('@folio/stripes/components', () => ({
@@ -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: [],
@@ -33,7 +25,7 @@ const defaultProps = {
const renderForm = (props = {}) => (
+);
+
+const FormCmpt = stripesFinalForm({})(renderForm);
+
+const renderComponent = (props = {}) => (render(
+
+ { }} {...props} />
+ ,
+));
+
+describe('DonorsContainer', () => {
+ beforeEach(() => {
+ useFetchDonors.mockClear().mockReturnValue({
+ donors: [],
+ isLoading: false,
+ });
+ });
+
+ it('should render component', () => {
+ renderComponent();
+
+ 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();
+ });
+
+ 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
new file mode 100644
index 00000000..75dcb284
--- /dev/null
+++ b/lib/Donors/DonorsList.js
@@ -0,0 +1,53 @@
+import PropTypes from 'prop-types';
+import { useMemo } from 'react';
+
+import { MultiColumnList } from '@folio/stripes/components';
+import { useStripes } from '@folio/stripes/core';
+
+import {
+ alignRowProps,
+ defaultColumnMapping,
+ defaultVisibleColumns,
+} from './constants';
+import { getDonorsListFormatter } from './utils';
+
+export const DonorsList = ({
+ columnMapping,
+ columnWidths,
+ contentData,
+ formatter: formatterProp,
+ id,
+ visibleColumns,
+}) => {
+ const stripes = useStripes();
+ const canViewOrganizations = stripes.hasPerm('ui-organizations.view');
+ const formatter = useMemo(() => {
+ return formatterProp || getDonorsListFormatter({ canViewOrganizations });
+ }, [canViewOrganizations, formatterProp]);
+
+ return (
+
+ );
+};
+
+DonorsList.propTypes = {
+ columnMapping: PropTypes.object,
+ columnWidths: PropTypes.object,
+ contentData: PropTypes.arrayOf(PropTypes.object).isRequired,
+ formatter: PropTypes.object,
+ id: PropTypes.string,
+ visibleColumns: PropTypes.arrayOf(PropTypes.string),
+};
+
+DonorsList.defaultProps = {
+ columnMapping: defaultColumnMapping,
+ visibleColumns: defaultVisibleColumns,
+};
diff --git a/lib/DonorsList/DonorsList.test.js b/lib/Donors/DonorsList.test.js
similarity index 69%
rename from lib/DonorsList/DonorsList.test.js
rename to lib/Donors/DonorsList.test.js
index 2dd4647c..4b3ead67 100644
--- a/lib/DonorsList/DonorsList.test.js
+++ b/lib/Donors/DonorsList.test.js
@@ -1,14 +1,10 @@
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
-import DonorsList from './DonorsList';
-
-const mockSetDonorIds = jest.fn();
+import { DonorsList } from './DonorsList';
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/AddDonorButton.js b/lib/Donors/DonorsLookup.js
similarity index 52%
rename from lib/DonorsList/AddDonorButton.js
rename to lib/Donors/DonorsLookup.js
index f470de74..0fed2002 100644
--- a/lib/DonorsList/AddDonorButton.js
+++ b/lib/Donors/DonorsLookup.js
@@ -1,8 +1,10 @@
-import { map } from 'lodash';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
-import { Pluggable } from '@folio/stripes/core';
+import {
+ Pluggable,
+ useStripes,
+} from '@folio/stripes/core';
import {
initialFilters,
@@ -13,16 +15,13 @@ import {
visibleFilters,
} from './constants';
-const AddDonorButton = ({ onAddDonors, fields, stripes, name }) => {
- 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));
- }
- };
+export const DonorsLookup = ({
+ name,
+ onAddDonors,
+ searchLabel,
+ visibleColumns,
+}) => {
+ const stripes = useStripes();
return (
{
aria-haspopup="true"
type="find-organization"
dataKey="organization"
- searchLabel={}
+ searchLabel={searchLabel}
searchButtonStyle="default"
disableRecordCreation
stripes={stripes}
- selectVendor={addDonors}
+ selectVendor={onAddDonors}
modalLabel={modalLabel}
resultsPaneTitle={resultsPaneTitle}
- visibleColumns={pluginVisibleColumns}
+ visibleColumns={visibleColumns}
initialFilters={initialFilters}
searchableIndexes={searchableIndexes}
visibleFilters={visibleFilters}
@@ -50,11 +49,14 @@ const AddDonorButton = ({ onAddDonors, fields, stripes, name }) => {
);
};
-AddDonorButton.propTypes = {
+DonorsLookup.propTypes = {
onAddDonors: PropTypes.func.isRequired,
- fields: PropTypes.object,
- stripes: PropTypes.object,
- name: PropTypes.string.isRequired,
+ name: PropTypes.string,
+ searchLabel: PropTypes.node,
+ visibleColumns: PropTypes.arrayOf(PropTypes.string),
};
-export default AddDonorButton;
+DonorsLookup.defaultProps = {
+ searchLabel: ,
+ visibleColumns: pluginVisibleColumns,
+};
diff --git a/lib/DonorsList/AddDonorButton.test.js b/lib/Donors/DonorsLookup.test.js
similarity index 75%
rename from lib/DonorsList/AddDonorButton.test.js
rename to lib/Donors/DonorsLookup.test.js
index 86465a20..05ec4a8c 100644
--- a/lib/DonorsList/AddDonorButton.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 AddDonorButton from './AddDonorButton';
+import { DonorsLookup } from './DonorsLookup';
const mockVendorData = { id: '1', name: 'Amazon' };
@@ -27,33 +27,21 @@ const mockOnAddDonors = jest.fn();
const defaultProps = {
onAddDonors: mockOnAddDonors,
- fields: {
- name: 'donors',
- },
name: 'donors',
};
const renderComponent = (props = defaultProps) => (render(
- ,
+ ,
));
-describe('AddDonorButton', () => {
+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');
expect(addDonorsButton).toBeDefined();
-
await user.click(addDonorsButton);
-
- expect(mockOnAddDonors).toHaveBeenCalledWith([mockVendorData.id]);
+ expect(mockOnAddDonors).toHaveBeenCalledWith([mockVendorData]);
});
});
diff --git a/lib/DonorsList/constants.js b/lib/Donors/constants.js
similarity index 87%
rename from lib/DonorsList/constants.js
rename to lib/Donors/constants.js
index 2fb6138c..f145e44a 100644
--- a/lib/DonorsList/constants.js
+++ b/lib/Donors/constants.js
@@ -1,25 +1,18 @@
import { FormattedMessage } from 'react-intl';
-export const columnMapping = {
+export const defaultColumnMapping = {
name: ,
code: ,
unassignDonor: null,
};
-export const visibleColumns = [
+export const defaultVisibleColumns = [
'name',
'code',
- 'unassignDonor',
];
export const alignRowProps = { alignLastColToEnd: true };
-export const columnWidths = {
- 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..bfdd00b0
--- /dev/null
+++ b/lib/Donors/index.js
@@ -0,0 +1,3 @@
+export { Donors } from './Donors';
+export { DonorsList } from './DonorsList';
+export { useFetchDonors } from './hooks/useFetchDonors';
diff --git a/lib/Donors/utils.js b/lib/Donors/utils.js
new file mode 100644
index 00000000..a31a8569
--- /dev/null
+++ b/lib/Donors/utils.js
@@ -0,0 +1,35 @@
+import {
+ Button,
+ Icon,
+ TextLink,
+} from '@folio/stripes/components';
+
+const getDonorUrl = (orgId) => {
+ if (orgId) {
+ return `/organizations/view/${orgId}`;
+ }
+
+ return undefined;
+};
+
+export const getDonorsListFormatter = ({ canViewOrganizations }) => ({
+ name: donor => {donor.name},
+ code: donor => donor.code,
+});
+
+export const getUnAssignDonorFormatter = ({ fields, intl }) => ({
+ unassignDonor: donor => (
+
+ ),
+});
diff --git a/lib/Donors/utils.test.js b/lib/Donors/utils.test.js
new file mode 100644
index 00000000..1388756a
--- /dev/null
+++ b/lib/Donors/utils.test.js
@@ -0,0 +1,35 @@
+import {
+ getDonorsListFormatter,
+ getUnAssignDonorFormatter,
+} from './utils';
+
+const defaultProps = {
+ canViewOrganizations: true,
+ fields: {
+ remove: jest.fn(),
+ },
+ intl: {
+ formatMessage: jest.fn((id) => id),
+ },
+};
+
+describe('getDonorsListFormatter', () => {
+ it('should return object with name, code and unassignDonor functions', () => {
+ const result = getDonorsListFormatter(defaultProps);
+
+ 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),
+ }));
+ });
+});
diff --git a/lib/DonorsList/DonorsList.js b/lib/DonorsList/DonorsList.js
deleted file mode 100644
index c17e54a1..00000000
--- a/lib/DonorsList/DonorsList.js
+++ /dev/null
@@ -1,103 +0,0 @@
-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 { useStripes } from '@folio/stripes/core';
-
-import AddDonorButton from './AddDonorButton';
-import {
- alignRowProps,
- columnMapping,
- columnWidths,
- visibleColumns,
-} 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 = ({ setDonorIds, fields, donorsMap, id }) => {
- const intl = useIntl();
- const stripes = useStripes();
- 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 (
- <>
-
-
-
- >
- );
-};
-
-DonorsList.propTypes = {
- setDonorIds: PropTypes.func.isRequired,
- fields: PropTypes.object,
- donorsMap: PropTypes.object,
- id: PropTypes.string.isRequired,
-};
-
-export default DonorsList;
diff --git a/lib/DonorsList/index.js b/lib/DonorsList/index.js
deleted file mode 100644
index 66ae435f..00000000
--- a/lib/DonorsList/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as DonorsList } from './DonorsContainer';
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';