Skip to content

Commit

Permalink
Merge pull request #1372 from folio-org/release/11.1.1
Browse files Browse the repository at this point in the history
Release 11.1.1
  • Loading branch information
Jack-Golding authored Nov 29, 2024
2 parents ce9bd04 + 80dd6e4 commit 806f4be
Show file tree
Hide file tree
Showing 24 changed files with 245 additions and 180 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change history for ui-agreements

## 11.1.1 2024-11-29
* ERM-3449 The Agreement content filter is not set back to original empty selection state
* ERM-3392 Agreements: Changes to the field content type are not saved, but a success message is displayed
* ERM 3390 Agreements > Show columns: Name column should not be in the list of unselectable columns

## 11.1.0 2024-10-31
* ERM-3375 Update module license and guidance for ui-agreements
* ERM-3365 UI Agreements does not display monograph information if TitleInstance.Type.label is changed
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@folio/agreements",
"version": "11.1.0",
"version": "11.1.1",
"description": "ERM agreement functionality for Stripes",
"main": "src/index.js",
"publishConfig": {
Expand Down Expand Up @@ -344,5 +344,8 @@
"title": "Print title"
}
]
},
"resolutions": {
"nwsapi" : "2.2.13"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const AgreementContentFieldArray = ({ handleSubmit }) => {
}}
/>
<Field
key={values?.agreementContent[index]?.content}
component={MultiSelection}
dataOptions={translatedContentOptions}
id={`${filter}-content-multi-select`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import PropTypes from 'prop-types';
import { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';

import isEqual from 'lodash/isEqual';

import { FormattedMessage } from 'react-intl';
import { Field } from 'react-final-form';
import { getRefdataValuesByDesc, requiredValidator, usePrevious } from '@folio/stripes-erm-components';

import {
Button,
Col,
Expand All @@ -13,18 +16,9 @@ import {
} from '@folio/stripes/components';
import { useKiwtFieldArray } from '@k-int/stripes-kint-components';

import { useAgreementsRefdata } from '../../../hooks';

// Utility function to check if two arrays of scalars contain the same items (Order does not count)
const arraysAreEqual = (a, b) => {
if (a.length !== b.length) {
return false;
}
import { getRefdataValuesByDesc, requiredValidator, usePrevious } from '@folio/stripes-erm-components';

return a.every(element => {
return b.includes(element);
});
};
import { useAgreementsRefdata } from '../../../hooks';

const [
AGREEMENT_CONTENT_TYPE
Expand All @@ -40,8 +34,12 @@ const ContentTypesFieldArray = ({
AGREEMENT_CONTENT_TYPE,
]
});
const contentTypeValues = getRefdataValuesByDesc(refdata, AGREEMENT_CONTENT_TYPE);
const agreementContentType = contentTypeValues.map(ct => ({ value: ct.value, label: ct.label }));
const contentTypeSelectOptions = getRefdataValuesByDesc(refdata, AGREEMENT_CONTENT_TYPE)
.map(ct => ({
value: ct.id, // Map to id for submittal purposes
label: ct.label
}));

const { items, onAddField, onDeleteField } = useKiwtFieldArray(name);
const [contentTypeInUse, setContentTypeInUse] = useState([]);
const contentTypeRefs = useRef([]);
Expand All @@ -51,12 +49,13 @@ const ContentTypesFieldArray = ({
const previousCount = usePrevious(itemsLength);

useEffect(() => {
const newContentTypeInUse = items.map(i => i?.contentType?.value).filter(x => !!x);
if (!arraysAreEqual(contentTypeInUse, newContentTypeInUse)) {
const newContentTypeInUse = items.map(i => i?.contentType?.id).filter(x => !!x);
if (!isEqual(contentTypeInUse, newContentTypeInUse)) {
setContentTypeInUse(newContentTypeInUse);
}
}, [items, contentTypeInUse]);


useEffect(() => {
// the second conditional is checking if the current field to be focused is the default content type field.
if (contentTypeRefs.current.length > 1 || (contentTypeRefs.current.length > 0 && previousCount - itemsLength === 1)) {
Expand All @@ -73,21 +72,29 @@ const ContentTypesFieldArray = ({

const renderContentTypes = () => (
items.map((act, index) => {
const dataOptions = agreementContentType.filter(ct => !contentTypeInUse.includes(ct.value) || ct.value === act.contentType?.value);
const dataOptions = contentTypeSelectOptions.filter(ct => !contentTypeInUse.includes(ct.value) || ct.value === act.contentType?.id);

return (
<div
key={`${act?.contentType?.id}`}
data-testid={`contentTypesFieldArray[${index}]`}
>
<Row>
<Col xs={11}>
{/*
* This select will ONLY change the id of the refdata,
* and the bind will happen from that. That will make
* the PUT look a little funky as it will have the old
* label/value information, but prevents us from having
* to do loads of eaxtra tweaking at the field level
*/}
<Field
component={Select}
dataOptions={dataOptions}
id="content-type-field-array"
index={index}
inputRef={addToRefs}
name={`${name}[${index}].contentType.value`}
name={`${name}[${index}].contentType.id`}
placeholder=" "
validate={requiredValidator}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,90 +1,85 @@

import { waitFor } from '@folio/jest-config-stripes/testing-library/react';

import { Button, Dropdown, MultiColumnList, renderWithIntl } from '@folio/stripes-erm-testing';
import { MemoryRouter } from 'react-router-dom';

import translationsProperties from '../../../../test/helpers';
import CoveredEResourcesList from './CoveredEResourcesList';
import agreement from './testResources';

jest.mock('../../IfEResourcesEnabled', () => ({ children }) => {
return typeof children === 'function' ? children({ isEnabled: true }) : children;
});

// Use manual mocks set up in hooks/__mocks__ folder
jest.mock('../../../hooks');
// Use manual mocks set up in hooks/__mocks__ folder (small correction to the way this was done before)
jest.mock('../../../hooks/useAgreementsSettings');
jest.mock('../../../hooks/useEresourcesEnabled');

const handlers = {
onFilterEResources: jest.fn(),
onExportEResourcesAsJSON: jest.fn().mockImplementation(() => Promise.resolve()),
onExportEResourcesAsKBART: jest.fn().mockImplementation(() => Promise.resolve()),
};

// TODO there are 2 warnings when running this test... contentData ends up as a boolean not an array --
// maybe needs mocking fetchMultiplePages
// received NaN for the children attribute within PrevNextPagination, better mocking needed there too?
describe('CoveredEResourcesList', () => {
beforeEach(() => {
renderWithIntl(
<MemoryRouter>
beforeEach(async () => {
// This is blowing my mind... I do not understand why this needs to be wrapped in a waitFor
await waitFor(() => {
renderWithIntl(
<CoveredEResourcesList
agreement={agreement}
eresourcesFilterPath="current"
{...handlers}
/>
</MemoryRouter>,
translationsProperties
);
});

test('renders the expected filter buttons', async () => {
await Button('Current').exists();
await Button('Future').exists();
await Button('Dropped').exists();
await Button('All').exists();
/>,
translationsProperties
);
});
});

test('clicking the filter buttons should call the onFilterEResources callback', async () => {
await waitFor(async () => {
await Button('Future').click();
describe.each(['Future', 'Current', 'Dropped', 'All'])('%s filter button', (filterLabel) => {
test(`renders the ${filterLabel} button`, async () => {
await waitFor(async () => {
await Button(filterLabel).exists();
});
});

await waitFor(async () => {
expect(handlers.onFilterEResources.mock.calls.length).toBe(1);
});

await waitFor(async () => {
await Button('Dropped').click();
});
describe(`clicking the ${filterLabel} button`, () => {
beforeEach(async () => {
handlers.onFilterEResources.mockClear();

await waitFor(async () => {
expect(handlers.onFilterEResources.mock.calls.length).toBe(2);
});
expect(handlers.onFilterEResources.mock.calls.length).toBe(0);

await waitFor(async () => {
await Button('All').click();
});
await waitFor(async () => {
await Button(filterLabel).click();
});
});

await waitFor(async () => {
expect(handlers.onFilterEResources.mock.calls.length).toBe(3);
test('onFilterEResources callback called', async () => {
await waitFor(async () => {
expect(handlers.onFilterEResources.mock.calls.length).toBe(1);
});
});
});
});

test('renders the Export dropdown', async () => {
await Dropdown('Export as...').exists();
});

test('choosing the dropdown options', async () => {
await waitFor(async () => {
await Dropdown('Export as...').choose('JSON');
await Dropdown('Export as...').exists();
});
});

await waitFor(async () => {
expect(handlers.onExportEResourcesAsJSON.mock.calls.length).toBe(1);
describe.each([
{ dropdownChoice: 'JSON', mockHandler: handlers.onExportEResourcesAsJSON },
{ dropdownChoice: 'KBART', mockHandler: handlers.onExportEResourcesAsKBART }
])('choosing export as $dropdownChoice', ({ dropdownChoice, mockHandler }) => {
beforeEach(async () => {
await waitFor(async () => {
await Dropdown('Export as...').choose(dropdownChoice);
});
});

await waitFor(async () => {
await Dropdown('Export as...').choose('KBART');
});

await waitFor(async () => {
expect(handlers.onExportEResourcesAsKBART.mock.calls.length).toBe(1);
test('correct onExportResources handler called', async () => {
await waitFor(async () => {
expect(mockHandler.mock.calls.length).toBe(1);
});
});
});

Expand All @@ -100,4 +95,3 @@ describe('CoveredEResourcesList', () => {
await MultiColumnList({ columns: ['Name', 'eISSN/ISSN', 'Platform', 'Package', 'Coverage', ' ', 'Access start', 'Access end'] }).exists();
});
});

1 change: 1 addition & 0 deletions src/components/AgreementSections/Lines/Lines.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const Lines = ({
</Button>
<ColumnManagerMenu
columnMapping={LINE_LISTING_COLUMN_MAPPING}
excludeColumns={['name']}
prefix="line-listing"
toggleColumn={toggleColumn}
visibleColumns={visibleColumns}
Expand Down
1 change: 1 addition & 0 deletions src/components/views/AgreementLines/AgreementLines.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const AgreementLines = ({
return (
<ColumnManagerMenu
columnMapping={AGREEMENT_LINES_COLUMN_MAPPING}
excludeColumns={['name']}
prefix="agreement-lines"
toggleColumn={toggleColumn}
visibleColumns={visibleColumns}
Expand Down
1 change: 1 addition & 0 deletions src/components/views/Agreements/Agreements.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const Agreements = ({
</IfPermission>
<ColumnManagerMenu
columnMapping={AGREEMENTS_COLUMN_MAPPING}
excludeColumns={['name']}
prefix="agreements-list"
toggleColumn={toggleColumn}
visibleColumns={visibleColumns}
Expand Down
7 changes: 0 additions & 7 deletions src/hooks/__mocks__/index.js

This file was deleted.

11 changes: 11 additions & 0 deletions src/hooks/__mocks__/useAgreementsSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = jest.fn(() => {
return (
{
parsedSettings: {
pageSize: {
agreementEresources: 12
}
}
}
);
});
1 change: 1 addition & 0 deletions src/hooks/__mocks__/useEresourcesEnabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = jest.fn(() => true);
12 changes: 8 additions & 4 deletions src/routes/AgreementLinesRoute/AgreementLinesRoute.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ describe('AgreementLinesRoute', () => {
expect(getByTestId('agreementLines')).toBeInTheDocument();
});

describe('re-rendering the route', () => { // makes sure that we hit the componentDidUpdate block
// TODO we should actually be _properly_ testing the useEffect, see AgreementsRoute example
// using memory router to render with props which force it to call history.push mock and measuring that mock output

/* describe('re-rendering the route', () => { // makes sure that we hit the componentDidUpdate block
beforeEach(() => {
renderWithIntl(
<MemoryRouter>
Expand All @@ -51,9 +54,10 @@ describe('AgreementLinesRoute', () => {
});
test('renders the agreementLines component', () => {
const { getByTestId } = renderComponent;
expect(getByTestId('agreementLines')).toBeInTheDocument();
const { getAllByTestId } = renderComponent;
const agreementLinesElements = getAllByTestId('agreementLines');
expect(agreementLinesElements.length).toBeGreaterThan(0);
});
});
}); */
});
});
12 changes: 7 additions & 5 deletions src/routes/AgreementViewRoute/AgreementViewRoute.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ jest.mock('../../components/views/Agreement', () => {
});

const data = {
history:{
history: {
push: historyPushMock,
},
location,
Expand All @@ -137,8 +137,9 @@ describe('AgreementViewRoute', () => {
});

test('renders the Agreement component', () => {
const { getByText } = renderComponent;
expect(getByText('Agreement')).toBeInTheDocument();
const { getAllByText } = renderComponent;
const agreementElements = getAllByText('Agreement');
expect(agreementElements.length).toBeGreaterThan(0);
});

test('renders the AgreementLineButton', () => {
Expand Down Expand Up @@ -198,8 +199,9 @@ describe('AgreementViewRoute', () => {
});

test('renders the Agreements component', () => {
const { getByText } = renderComponent;
expect(getByText('Agreement')).toBeInTheDocument();
const { getAllByText } = renderComponent;
const agreementElements = getAllByText('Agreement');
expect(agreementElements.length).toBeGreaterThan(0);
});
});
});
Expand Down
Loading

0 comments on commit 806f4be

Please sign in to comment.