diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a01447d..4d3862fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Add `inputType` prop to ``. Refs UISACQCOMP-165. * View the list of donors. Refs UISACQCOMP-166. * Added `indexRef` and `inputRef` props to ``. Refs UISACQCOMP-167. +* Extend Donors component functionality. Refs UISACQCOMP-168. ## [5.0.0](https://github.com/folio-org/stripes-acq-components/tree/v5.0.0) (2023-10-12) [Full Changelog](https://github.com/folio-org/stripes-acq-components/compare/v4.0.2...v5.0.0) diff --git a/lib/Donors/Donors.js b/lib/Donors/Donors.js index 6c314c6b..69365a09 100644 --- a/lib/Donors/Donors.js +++ b/lib/Donors/Donors.js @@ -1,10 +1,10 @@ +import { noop } from 'lodash'; import PropTypes from 'prop-types'; import { FieldArray } from 'react-final-form-arrays'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Col, - Loading, Row, } from '@folio/stripes/components'; @@ -12,13 +12,18 @@ import { defaultColumnMapping } from './constants'; import { DonorsContainer } from './DonorsContainer'; import { useFetchDonors } from './hooks'; -export function Donors({ name, donorOrganizationIds, ...rest }) { +export function Donors({ name, donorOrganizationIds, onChange, ...rest }) { const [donorIds, setDonorIds] = useState(donorOrganizationIds); - const { donors, isLoading } = useFetchDonors(donorIds); + const { donors, isLoading } = useFetchDonors(donorIds, { keepPreviousData: true }); - if (isLoading) { - return ; - } + useEffect(() => { + setDonorIds(donorOrganizationIds); + }, [donorOrganizationIds]); + + const onSetDonorIds = (values = []) => { + setDonorIds(values); + onChange(values); + }; return ( @@ -27,8 +32,9 @@ export function Donors({ name, donorOrganizationIds, ...rest }) { name={name} id={name} component={DonorsContainer} - setDonorIds={setDonorIds} + setDonorIds={onSetDonorIds} donors={donors} + loading={isLoading} {...rest} /> @@ -41,13 +47,17 @@ Donors.propTypes = { columnWidths: PropTypes.object, donorOrganizationIds: PropTypes.arrayOf(PropTypes.string), name: PropTypes.string, + onChange: PropTypes.func, + onRemove: PropTypes.func, searchLabel: PropTypes.node, showTriggerButton: PropTypes.bool, visibleColumns: PropTypes.arrayOf(PropTypes.string), }; Donors.defaultProps = { + columnMapping: defaultColumnMapping, donorOrganizationIds: [], name: 'donorOrganizationIds', - columnMapping: defaultColumnMapping, + onChange: noop, + onRemove: noop, }; diff --git a/lib/Donors/Donors.test.js b/lib/Donors/Donors.test.js index 8980c0a1..504e7d88 100644 --- a/lib/Donors/Donors.test.js +++ b/lib/Donors/Donors.test.js @@ -1,14 +1,21 @@ 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 { Donors } from './Donors'; import { useFetchDonors } from './hooks'; -jest.mock('@folio/stripes/components', () => ({ - ...jest.requireActual('@folio/stripes/components'), - Loading: jest.fn(() => 'Loading'), +jest.mock('./DonorsLookup', () => ({ + DonorsLookup: jest.fn(({ onAddDonors }) => ( + + )), })); jest.mock('./hooks', () => ({ @@ -21,6 +28,7 @@ jest.mock('./hooks', () => ({ const defaultProps = { name: 'donors', donorOrganizationIds: [], + onChange: jest.fn(), }; const renderForm = (props = {}) => ( @@ -55,14 +63,15 @@ describe('Donors', () => { expect(screen.getByText('stripes-components.tableEmpty')).toBeDefined(); }); - it('should render Loading component', () => { - useFetchDonors.mockClear().mockReturnValue({ - donors: [], - isLoading: true, - }); + it('should call onChange when donorOrganizationIds changed', () => { + const onChange = jest.fn(); - renderComponent(); + renderComponent({ onChange }); + + const addDonorButton = screen.getByText('Add donor'); + + user.click(addDonorButton); - expect(screen.getByText('Loading')).toBeDefined(); + expect(onChange).toHaveBeenCalled(); }); }); diff --git a/lib/Donors/DonorsContainer.js b/lib/Donors/DonorsContainer.js index 60add8db..25286c6a 100644 --- a/lib/Donors/DonorsContainer.js +++ b/lib/Donors/DonorsContainer.js @@ -1,4 +1,4 @@ -import { map, sortBy } from 'lodash'; +import { map, noop, sortBy } from 'lodash'; import PropTypes from 'prop-types'; import { useMemo } from 'react'; import { useIntl } from 'react-intl'; @@ -17,6 +17,7 @@ export function DonorsContainer({ fields, formatter, id, + onRemove, setDonorIds, searchLabel, showTriggerButton, @@ -45,11 +46,11 @@ export function DonorsContainer({ }; }), [donorsMap, fields.value]); - const contentData = useMemo(() => sortBy(listOfDonors, [({ lastName }) => lastName?.toLowerCase()]), [listOfDonors]); + const contentData = useMemo(() => sortBy(listOfDonors, [({ name }) => name?.toLowerCase()]), [listOfDonors]); const resultsFormatter = useMemo(() => { - return formatter || getDonorsFormatter({ intl, fields, canViewOrganizations }); - }, [canViewOrganizations, fields, formatter, intl]); + return formatter || getDonorsFormatter({ intl, fields, canViewOrganizations, onRemove }); + }, [canViewOrganizations, fields, formatter, intl, onRemove]); const onAddDonors = (values = []) => { const addedDonorIds = new Set(fields.value); @@ -79,7 +80,6 @@ export function DonorsContainer({ onAddDonors={onAddDonors} name={id} searchLabel={searchLabel} - visibleColumns={visibleColumns} /> ) } @@ -94,6 +94,7 @@ DonorsContainer.propTypes = { fields: PropTypes.object, formatter: PropTypes.object, id: PropTypes.string, + onRemove: PropTypes.func, searchLabel: PropTypes.node, setDonorIds: PropTypes.func.isRequired, showTriggerButton: PropTypes.bool, @@ -101,6 +102,7 @@ DonorsContainer.propTypes = { }; DonorsContainer.defaultProps = { + onRemove: noop, showTriggerButton: true, visibleColumns: defaultContainerVisibleColumns, }; diff --git a/lib/Donors/DonorsContainer.test.js b/lib/Donors/DonorsContainer.test.js index 70c141bc..4d199d3a 100644 --- a/lib/Donors/DonorsContainer.test.js +++ b/lib/Donors/DonorsContainer.test.js @@ -102,7 +102,7 @@ describe('DonorsContainer', () => { expect(screen.getByText(mockData[0].name)).toBeDefined(); }); - it('should call `setDonorIds` when `onAddDonors` is called', async () => { + it('should call `setDonorIds` when `onAddDonors` is called', () => { renderComponent({ donors: [mockVendor], fields: { @@ -114,7 +114,7 @@ describe('DonorsContainer', () => { const addDonorsButton = screen.getByText('Add donor'); expect(addDonorsButton).toBeDefined(); - await user.click(addDonorsButton); + user.click(addDonorsButton); expect(setDonorIds).toHaveBeenCalled(); }); diff --git a/lib/Donors/DonorsLookup.js b/lib/Donors/DonorsLookup.js index 0fed2002..51783418 100644 --- a/lib/Donors/DonorsLookup.js +++ b/lib/Donors/DonorsLookup.js @@ -12,6 +12,7 @@ import { pluginVisibleColumns, resultsPaneTitle, searchableIndexes, + sortableColumns, visibleFilters, } from './constants'; @@ -41,6 +42,7 @@ export const DonorsLookup = ({ searchableIndexes={searchableIndexes} visibleFilters={visibleFilters} isMultiSelect + sortableColumns={sortableColumns} > diff --git a/lib/Donors/constants.js b/lib/Donors/constants.js index f0b0d217..68ba2fdf 100644 --- a/lib/Donors/constants.js +++ b/lib/Donors/constants.js @@ -11,6 +11,8 @@ export const defaultVisibleColumns = [ 'code', ]; +export const sortableColumns = ['name', 'code']; + export const defaultContainerVisibleColumns = [ ...defaultVisibleColumns, 'unassignDonor', diff --git a/lib/Donors/hooks/useFetchDonors/useFetchDonors.js b/lib/Donors/hooks/useFetchDonors/useFetchDonors.js index ae7c10e1..6f9e5dd1 100644 --- a/lib/Donors/hooks/useFetchDonors/useFetchDonors.js +++ b/lib/Donors/hooks/useFetchDonors/useFetchDonors.js @@ -9,7 +9,7 @@ import { VENDORS_API } from '../../../constants'; import { batchRequest } from '../../../utils'; import { DEFAULT_DATA } from './constants'; -export const useFetchDonors = (donorOrganizationIds = DEFAULT_DATA) => { +export const useFetchDonors = (donorOrganizationIds = DEFAULT_DATA, options = {}) => { const ky = useOkapiKy(); const namespace = useNamespace({ key: 'fetch-donors-list' }); @@ -24,7 +24,10 @@ export const useFetchDonors = (donorOrganizationIds = DEFAULT_DATA) => { donorOrganizationIds, ); }, - { enabled: Boolean(donorOrganizationIds.length) }, + { + enabled: Boolean(donorOrganizationIds.length), + ...options, + }, ); return ({ diff --git a/lib/Donors/index.js b/lib/Donors/index.js index f625423c..0a0e73f6 100644 --- a/lib/Donors/index.js +++ b/lib/Donors/index.js @@ -1,4 +1,5 @@ export { Donors } from './Donors'; export { DonorsList } from './DonorsList'; export { DonorsListContainer } from './DonorsListContainer'; +export { DonorsLookup } from './DonorsLookup'; export { useFetchDonors } from './hooks/useFetchDonors'; diff --git a/lib/Donors/utils.js b/lib/Donors/utils.js index 8381f8f2..946fa2ce 100644 --- a/lib/Donors/utils.js +++ b/lib/Donors/utils.js @@ -17,7 +17,12 @@ export const getDonorsListFormatter = ({ canViewOrganizations }) => ({ code: donor => donor.code, }); -export const getDonorsFormatter = ({ canViewOrganizations, fields, intl }) => ({ +export const getDonorsFormatter = ({ + canViewOrganizations, + fields, + intl, + onRemove, +}) => ({ ...getDonorsListFormatter({ canViewOrganizations }), unassignDonor: donor => (