diff --git a/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.js b/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.js
index 6c2f8ac4..966c2ec6 100644
--- a/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.js
+++ b/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.js
@@ -52,6 +52,7 @@ const FundDistributionFieldsFinal = ({
}) => {
const intl = useIntl();
const [remainingAmount, setRemainingAmount] = useState(0);
+ const [hasValidationError, setValidationError] = useState(false);
const fundsForSelect = useMemo(() => {
const activeFunds = funds.filter(({ fundStatus }) => fundStatus === 'Active');
@@ -109,12 +110,16 @@ const FundDistributionFieldsFinal = ({
validateFundDistributionUniqueFunds(records),
].filter(Boolean);
- return required
+ const error = required
? errors[0]
: undefined;
+
+ setValidationError(Boolean(error));
+
+ return error;
}
- return undefined;
+ return setValidationError(false);
}, [
currency,
fundDistribution,
@@ -187,6 +192,7 @@ const FundDistributionFieldsFinal = ({
}
@@ -237,20 +243,32 @@ const FundDistributionFieldsFinal = ({
]);
return (
- }
- component={RepeatableFieldWithValidation}
- id={name}
- legend={remainingAmountNode}
- name={name}
- onAdd={onAdd}
- onRemove={onRemove}
- canAdd={!disabled}
- canRemove={!disabled}
- renderField={renderSubForm}
- validate={debouncedValidate}
- />
+ <>
+ {
+ hasValidationError && (
+ true}
+ validateFields={[]}
+ render={() => <>>}
+ />
+ )
+ }
+ }
+ component={RepeatableFieldWithValidation}
+ id={name}
+ legend={remainingAmountNode}
+ name={name}
+ onAdd={onAdd}
+ onRemove={onRemove}
+ canAdd={!disabled}
+ canRemove={!disabled}
+ renderField={renderSubForm}
+ validate={debouncedValidate}
+ />
+ >
);
};
diff --git a/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.test.js b/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.test.js
index 077a3093..a96f3072 100644
--- a/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.test.js
+++ b/lib/FundDistribution/FundDistributionFields/FundDistributionFieldsFinal.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { act, render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import user from '@testing-library/user-event';
@@ -7,8 +7,24 @@ import stripesFinalForm from '@folio/stripes/final-form';
import { FUND_DISTR_TYPE } from '../../constants';
import FundDistributionFieldsFinal from './FundDistributionFieldsFinal';
-
-const FUNDS = [{ id: '1', code: 'AFRICAHIST', name: 'african', fundStatus: 'Active' }, { id: '2', code: 'TEST', name: 'test', fundStatus: 'Active' }];
+import { handleValidationErrorResponse } from './handleValidationErrorResponse';
+import { validateFundDistributionTotal as validateFundDistributionTotalDefault } from './validateFundDistributionFinal';
+
+jest.useFakeTimers('modern');
+jest.mock('./handleValidationErrorResponse', () => ({
+ handleValidationErrorResponse: jest.fn(() => 'error message'),
+}));
+jest.mock('./validateFundDistributionFinal', () => ({
+ ...jest.requireActual('./validateFundDistributionFinal'),
+ validateFundDistributionTotal: jest.fn(() => Promise.resolve()),
+}));
+
+const DELAY = 500;
+const validateFundDistributionTotal = jest.fn(() => Promise.resolve());
+const FUNDS = [
+ { id: '1', code: 'AFRICAHIST', name: 'african', fundStatus: 'Active' },
+ { id: '2', code: 'TEST', name: 'test', fundStatus: 'Active' },
+];
const inactiveFund = {
name: 'TestName',
code: 'TestCode',
@@ -18,7 +34,13 @@ const inactiveFund = {
};
// eslint-disable-next-line react/prop-types
-const renderForm = ({ fundDistribution, onSelectFund = () => { }, totalAmount = 0, funds = FUNDS }) => (
+const renderForm = ({
+ fundDistribution,
+ onSelectFund = () => { },
+ totalAmount = 0,
+ funds = FUNDS,
+ ...rest
+}) => (
);
@@ -42,6 +65,12 @@ const renderComponent = (props = {}) => (render(
));
describe('FundDistributionFieldsFinal', () => {
+ beforeEach(() => {
+ handleValidationErrorResponse.mockClear();
+ validateFundDistributionTotal.mockClear();
+ validateFundDistributionTotalDefault.mockClear();
+ });
+
it('should add new record and call select fund', () => {
const onSelectFund = jest.fn();
@@ -53,9 +82,10 @@ describe('FundDistributionFieldsFinal', () => {
});
it('should display fund distribution and handle clicks', () => {
+ const expenseClassesByFundId = { [FUNDS[1].id]: [{ id: 'expClassId', name: 'expClassName' }] };
const fundDistribution = [{ code: 'TEST', fundId: '2', value: 100, distributionType: FUND_DISTR_TYPE.percent }];
- renderComponent({ fundDistribution, totalAmount: 5 });
+ renderComponent({ fundDistribution, totalAmount: 5, expenseClassesByFundId });
user.click(screen.getByText('stripes-acq-components.fundDistribution.addBtn'));
expect(screen.getByText('$5.00')).toBeDefined();
});
@@ -75,4 +105,54 @@ describe('FundDistributionFieldsFinal', () => {
expect(screen.getAllByText('TestName (TestCode) - stripes-acq-components.fundDistribution.fundStatus.Inactive')).toBeDefined();
});
+
+ it('should call fund distribution validator from props', () => {
+ const fundDistribution = [inactiveFund];
+
+ renderComponent({ fundDistribution, funds: fundDistribution, validateFundDistributionTotal });
+
+ const valueInput = screen.getByTestId('fundDistribution-value');
+
+ user.type(valueInput, '100');
+ user.tab();
+
+ jest.advanceTimersByTime(DELAY * 1.5);
+
+ expect(validateFundDistributionTotal).toHaveBeenCalled();
+ });
+
+ it('should call default fund distribution validator', () => {
+ const fundDistribution = [inactiveFund];
+
+ renderComponent({ fundDistribution, funds: fundDistribution, required: false });
+
+ const valueInput = screen.getByTestId('fundDistribution-value');
+
+ user.type(valueInput, '100');
+ user.tab();
+
+ jest.advanceTimersByTime(DELAY * 1.5);
+
+ expect(validateFundDistributionTotalDefault).toHaveBeenCalled();
+ });
+
+ it('should call \'handleValidationErrorResponse\' when validator rejected with an error', async () => {
+ const fundDistribution = [inactiveFund];
+
+ // eslint-disable-next-line prefer-promise-reject-errors
+ validateFundDistributionTotal.mockClear().mockReturnValue(Promise.reject('error message'));
+
+ renderComponent({ fundDistribution, funds: fundDistribution, validateFundDistributionTotal });
+
+ const valueInput = screen.getByTestId('fundDistribution-value');
+
+ await act(async () => {
+ user.type(valueInput, '100');
+ user.tab();
+
+ jest.advanceTimersByTime(DELAY * 1.5);
+ });
+
+ expect(handleValidationErrorResponse).toHaveBeenCalled();
+ });
});