Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
UladzislauKutarkin committed Oct 24, 2023
2 parents bf186ee + 60b331e commit 178270f
Show file tree
Hide file tree
Showing 47 changed files with 635 additions and 448 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## In progress

* [UIBULKED-355](https://issues.folio.org/browse/UIBULKED-355) Remove reference to Query tab on the landing page.
* [UIBULKED-315](https://issues.folio.org/browse/UIBULKED-315) Group holdings record properties using optgroup component

## [4.0.0](https://github.com/folio-org/ui-bulk-edit/tree/v4.0.0) (2023-10-12)

* [UIBULKED-255](https://issues.folio.org/browse/UIBULKED-255) Incorrect message on the logs landing page.
* [UIBULKED-252](https://issues.folio.org/browse/UIBULKED-252) Selected filters are not reset on "Logs" tab
* [UIBULKED-267](https://issues.folio.org/browse/UIBULKED-267) User's names in "Run by" column of Bulk edit logs.
Expand Down Expand Up @@ -48,6 +53,8 @@
* [UIBULKED-341](https://issues.folio.org/browse/UIBULKED-341) Separate Item notes in different columns based on the note type
* [UIBULKED-351](https://issues.folio.org/browse/UIBULKED-351) Hide Query tab
* [UIBULKED-276](https://issues.folio.org/browse/UIBULKED-276) Rename "Instance" column for holdings records preview
* [UIBULKED-352](https://issues.folio.org/browse/UIBULKED-352) Localize numbers displayed in bulk edit


## [3.0.5](https://github.com/folio-org/ui-bulk-edit/tree/v3.0.5) (2023-03-22)

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@
"subPermissions": [
"ui-bulk-edit.app-view",
"ui-bulk-edit.edit.base",
"inventory-storage.item-note-types.collection.get"
"inventory-storage.item-note-types.collection.get",
"inventory-storage.holdings-note-types.collection.get"
],
"visible": true
},
Expand Down
13 changes: 8 additions & 5 deletions src/components/BulkEdit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { QueryClientProvider } from 'react-query';
import { IntlProvider } from 'react-intl';

import { useOkapiKy } from '@folio/stripes/core';
import { runAxeTest } from '@folio/stripes-testing';
Expand All @@ -22,11 +23,13 @@ const history = createMemoryHistory();

const renderBulkEdit = (type = 'USERS') => {
render(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[`/bulk-edit?capabilities=${type}&identifier=BARCODE&criteria=identifier`]}>
<BulkEdit />
</MemoryRouter>,
</QueryClientProvider>,
<IntlProvider locale="en">
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[`/bulk-edit?capabilities=${type}&identifier=BARCODE&criteria=identifier`]}>
<BulkEdit />
</MemoryRouter>,
</QueryClientProvider>,
</IntlProvider>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
} from '../../../hooks/api';
import { useBulkPermissions, useLocationFilters } from '../../../hooks';
import { LogsFilters } from './LogsFilters/LogsFilters';
import { getCapabilityOptions, isCapabilityDisabled } from '../../../utils/filters';
import { getCapabilityOptions, isCapabilityDisabled } from '../../../utils/helpers';
import FilterTabs from './FilterTabs/FilterTabs';
import Capabilities from './Capabilities/Capabilities';
import { getIsDisabledByPerm } from './utils/getIsDisabledByPerm';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { BulkEditInAppTitle } from './BulkEditInAppTitle/BulkEditInAppTitle';
import { ContentUpdatesForm } from './ContentUpdatesForm/ContentUpdatesForm';
import { CAPABILITIES, getHoldingsOptions, getItemsOptions, getUserOptions } from '../../../../constants';
import { useItemNotes } from '../../../../hooks/api/useItemNotes';
import { useHoldingsNotes } from '../../../../hooks/api/useHoldingsNotes';

export const BulkEditInApp = ({
onContentUpdatesChanged,
Expand All @@ -24,17 +25,19 @@ export const BulkEditInApp = ({

const fileUploadedName = search.get('fileName');
const isItemCapability = capabilities === CAPABILITIES.ITEM;
const isHoldingsCapability = capabilities === CAPABILITIES.HOLDING;

const { itemNotes, isItemNotesLoading } = useItemNotes({ enabled: isItemCapability });
const { holdingsNotes, isHoldingsNotesLoading } = useHoldingsNotes({ enabled: isHoldingsCapability });

const optionsMap = {
[CAPABILITIES.ITEM]: getItemsOptions(intl.formatMessage, itemNotes),
[CAPABILITIES.USER]: getUserOptions(intl.formatMessage),
[CAPABILITIES.HOLDING]: getHoldingsOptions(intl.formatMessage),
[CAPABILITIES.HOLDING]: getHoldingsOptions(intl.formatMessage, holdingsNotes),
};

const options = optionsMap[capabilities];
const showContentUpdatesForm = options && !isItemNotesLoading;
const showContentUpdatesForm = options && !isItemNotesLoading && !isHoldingsNotesLoading;

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MemoryRouter } from 'react-router-dom';
import { QueryClientProvider } from 'react-query';
import { act, render, screen } from '@testing-library/react';
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { useOkapiKy } from '@folio/stripes/core';
Expand Down Expand Up @@ -256,11 +256,16 @@ describe('BulkEditInApp', () => {
expect(optionPatronGroup.selected).toBeTruthy();
});

it('should display holdings permanent location', () => {
it('should display holdings permanent location', async () => {
renderBulkEditInApp({ capability: CAPABILITIES.HOLDING });

const selectOption = screen.getByTestId('select-option-0');
const optionStatus = screen.getByRole('option', { name: /layer.options.holdings.permanentLocation/ });
let selectOption;
let optionStatus;

await waitFor(() => {
selectOption = screen.getByTestId('select-option-0');
optionStatus = screen.getByRole('option', { name: /layer.options.holdings.permanentLocation/ });
});

act(() => userEvent.selectOptions(selectOption, optionStatus));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
getFilteredFields,
getExtraActions,
} from './helpers';
import { groupByCategory } from '../../../../../utils/filters';
import { groupByCategory } from '../../../../../utils/helpers';

export const ContentUpdatesForm = ({
onContentUpdatesChanged,
Expand Down
9 changes: 6 additions & 3 deletions src/components/BulkEditLogs/BulkEditLogs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';

import '../../../test/jest/__mock__';
import { IntlProvider } from 'react-intl';
import { bulkEditLogsData } from '../../../test/jest/__mock__/fakeData';

import { LOGS_COLUMNS } from '../../constants';
Expand All @@ -20,9 +21,11 @@ jest.mock('../../hooks/api', () => ({

const renderBulkEditLogs = () => {
render(
<MemoryRouter initialEntries={['/bulk-edit?criteria=logs']}>
<BulkEditLogs />
</MemoryRouter>,
<IntlProvider locale="en">
<MemoryRouter initialEntries={['/bulk-edit?criteria=logs']}>
<BulkEditLogs />
</MemoryRouter>,
</IntlProvider>
);
};

Expand Down
20 changes: 16 additions & 4 deletions src/constants/selectOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ export const OPTIONS = {
TEMPORARY_HOLDINGS_LOCATION: 'TEMPORARY_HOLDINGS_LOCATION',
PERMANENT_HOLDINGS_LOCATION: 'PERMANENT_HOLDINGS_LOCATION',
ITEM_NOTE: 'ITEM_NOTE',
HOLDINGS_NOTE: 'HOLDINGS_NOTE',
ADMINISTRATIVE_NOTE: 'ADMINISTRATIVE_NOTE',
CHECK_IN_NOTE: 'CHECK_IN_NOTE',
CHECK_OUT_NOTE: 'CHECK_OUT_NOTE',
};

export const PARAMETERS_KEYS = {
ITEM_NOTE_TYPE_ID_KEY: 'ITEM_NOTE_TYPE_ID_KEY',
HOLDINGS_NOTE_TYPE_ID_KEY: 'HOLDINGS_NOTE_TYPE_ID_KEY',
};

export const OPTIONS_WITH_ADDITIONAL_PARAMETERS = [
Expand Down Expand Up @@ -168,20 +170,30 @@ export const getUserOptions = (formatMessage) => [
},
];

export const getHoldingsOptions = (formatMessage) => [
export const getHoldingsOptions = (formatMessage, holdingsNotes = []) => [
{
value: '',
label: formatMessage({ id: 'ui-bulk-edit.options.placeholder' }),
disabled: true,
},
{ value: OPTIONS.TEMPORARY_HOLDINGS_LOCATION,
label: formatMessage({ id: 'ui-bulk-edit.layer.options.holdings.temporaryLocation' }),
disabled: false },
{
value: OPTIONS.ADMINISTRATIVE_NOTE,
label: formatMessage({ id: 'ui-bulk-edit.layer.options.administrativeNote' }),
disabled: false,
},
{
value: OPTIONS.PERMANENT_HOLDINGS_LOCATION,
label: formatMessage({ id: 'ui-bulk-edit.layer.options.holdings.permanentLocation' }),
disabled: false,
categoryName: formatMessage({ id: 'ui-bulk-edit.category.holdingsLocation' }),
},
{
value: OPTIONS.TEMPORARY_HOLDINGS_LOCATION,
label: formatMessage({ id: 'ui-bulk-edit.layer.options.holdings.temporaryLocation' }),
disabled: false,
categoryName: formatMessage({ id: 'ui-bulk-edit.category.holdingsLocation' }),
},
...holdingsNotes,
{
value: OPTIONS.SUPPRESS_FROM_DISCOVERY,
label: formatMessage({ id: 'ui-bulk-edit.layer.options.holdings.suppress' }),
Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions src/hooks/api/useHoldingsNotes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useOkapiKy } from '@folio/stripes/core';
import { useQuery } from 'react-query';
import { useIntl } from 'react-intl';
import { OPTIONS, PARAMETERS_KEYS } from '../../constants';
import { getMappedAndSortedNotes } from '../../utils/helpers';

export const useHoldingsNotes = (options = {}) => {
const ky = useOkapiKy();
const { formatMessage } = useIntl();

const { data, isLoading: isHoldingsNotesLoading } = useQuery(
{
queryKey: 'holdingsNotes',
cacheTime: Infinity,
staleTime: Infinity,
queryFn: () => ky.get('holdings-note-types').json(),
...options,
},
);

const holdingsNotes = getMappedAndSortedNotes({
notes: data?.holdingsNoteTypes,
categoryName: formatMessage({ id: 'ui-bulk-edit.category.holdingsNotes' }),
type: OPTIONS.HOLDINGS_NOTE,
key: PARAMETERS_KEYS.HOLDINGS_NOTE_TYPE_ID_KEY,
});

return {
holdingsNotes,
isHoldingsNotesLoading,
};
};
18 changes: 6 additions & 12 deletions src/hooks/api/useItemNotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useOkapiKy } from '@folio/stripes/core';
import { useQuery } from 'react-query';
import { useIntl } from 'react-intl';
import { OPTIONS, PARAMETERS_KEYS } from '../../constants';
import { getMappedAndSortedNotes } from '../../utils/helpers';

export const useItemNotes = (options = {}) => {
const ky = useOkapiKy();
Expand All @@ -17,19 +18,12 @@ export const useItemNotes = (options = {}) => {
},
);

const mappedNotes = data?.itemNoteTypes?.map(type => ({
label: type.name,
value: type.id,
type: OPTIONS.ITEM_NOTE,
parameters: [{
key: PARAMETERS_KEYS.ITEM_NOTE_TYPE_ID_KEY,
value: type.id,
}],
disabled: false,
const itemNotes = getMappedAndSortedNotes({
notes: data?.itemNoteTypes,
categoryName: formatMessage({ id: 'ui-bulk-edit.category.itemNotes' }),
})) || [];

const itemNotes = mappedNotes.sort((a, b) => a.label.localeCompare(b.label));
type: OPTIONS.ITEM_NOTE,
key: PARAMETERS_KEYS.ITEM_NOTE_TYPE_ID_KEY,
});

return {
itemNotes,
Expand Down
6 changes: 3 additions & 3 deletions src/utils/formatters.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, FormattedNumber } from 'react-intl';

import { NoValue } from '@folio/stripes/components';
import { FolioFormattedTime } from '@folio/stripes-acq-components';
Expand All @@ -17,8 +17,8 @@ export const getLogsResultsFormatter = () => ({
userId: item => item.runBy,
startTime: item => <FolioFormattedTime dateString={item.startTime} />,
endTime: item => <FolioFormattedTime dateString={item.endTime} />,
totalNumOfRecords: item => item.totalNumOfRecords,
processedNumOfRecords: item => item.processedNumOfRecords,
totalNumOfRecords: item => <FormattedNumber value={item.totalNumOfRecords} />,
processedNumOfRecords: item => <FormattedNumber value={item.processedNumOfRecords} />,
editing: item => (
item.approach
? <FormattedMessage id={`ui-bulk-edit.logs.approach.${item.approach}`} />
Expand Down
21 changes: 21 additions & 0 deletions src/utils/filters.js → src/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,25 @@ export const groupByCategory = (array) => {
}, {});
};

export const getMappedAndSortedNotes = ({
notes,
categoryName,
key,
type
}) => {
const mappedNotes = notes?.map(note => ({
label: note.name,
value: note.id,
type,
parameters: [{
key,
value: note.id,
}],
disabled: false,
categoryName,
})) || [];

return mappedNotes.sort((a, b) => a.label.localeCompare(b.label));
};


1 change: 1 addition & 0 deletions test/jest/__mock__/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './stripesComponents.mock';
import './stripesCore.mock';
import './stripesSmartComponents.mock';
import './reactIntl.mock';
43 changes: 43 additions & 0 deletions test/jest/__mock__/reactIntl.mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

jest.mock('react-intl', () => {
const intl = {
formatDate: value => (value ? value.substring(0, 10) : value),
formatTime: value => (value ? value.substring(0, 10) : value),
formatDisplayName: value => value,
formatMessage: ({ id }) => id,
formatNumber: value => value,
};

const sharedMockFn = jest.fn(({
value, children,
}) => {
if (children) {
return children([value]);
}

return value;
});

return {
...jest.requireActual('react-intl'),
FormattedMessage: jest.fn(({
id, children,
}) => {
if (children) {
return children([id]);
}

return id;
}),
FormattedTime: sharedMockFn,
FormattedNumber: sharedMockFn,
useIntl: () => intl,
injectIntl: Component => props => (
<Component
{...props}
intl={intl}
/>
),
};
});
Loading

0 comments on commit 178270f

Please sign in to comment.