diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc9bdfe4b..d8eefcbe1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -87,6 +87,7 @@ UIIN-3437.
* When moving item within one holding manually, recalculate other item orders based on their position in the list. Fixes UIIN-3539.
* Handle audit-marc dependency: hide audit button. Refs UIIN-3576.
* Include additional call numbers in Version History for Inventory Item. Refs UIIN-3558.
+* Include additional call numbers in Version History for Inventory Holdings. Refs UIIN-3540.
## [13.0.10](https://github.com/folio-org/ui-inventory/tree/v13.0.10) (2025-09-01)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v13.0.9...v13.0.10)
diff --git a/package.json b/package.json
index b2aa173f1..117787a0a 100644
--- a/package.json
+++ b/package.json
@@ -1071,7 +1071,7 @@
"@folio/eslint-config-stripes": "^8.0.0",
"@folio/jest-config-stripes": "^3.0.0",
"@folio/service-interaction": "^4.0.0",
- "@folio/stripes": "^10.0.0",
+ "@folio/stripes": "^10.1.0",
"@folio/stripes-cli": "^4.0.0",
"@folio/stripes-components": "^13.0.0",
"@folio/stripes-connect": "^10.0.0",
@@ -1124,7 +1124,7 @@
},
"peerDependencies": {
"@folio/service-interaction": "^4.0.0",
- "@folio/stripes": "^10.0.0",
+ "@folio/stripes": "^10.1.0",
"@folio/stripes-erm-components": "^10.0.0",
"@folio/stripes-inventory-components": "^2.0.0",
"@folio/stripes-marc-components": "^2.0.0",
diff --git a/src/Holding/HoldingVersionHistory/HoldingVersionHistory.js b/src/Holding/HoldingVersionHistory/HoldingVersionHistory.js
index 9dfaeab74..0fd02f25e 100644
--- a/src/Holding/HoldingVersionHistory/HoldingVersionHistory.js
+++ b/src/Holding/HoldingVersionHistory/HoldingVersionHistory.js
@@ -30,6 +30,31 @@ export const getFieldFormatter = referenceData => ({
publicDisplay: value => value.toString(),
});
+export const getItemFormatter = (fieldLabelsMap, fieldFormatter) => (element, i) => {
+ if (!element) return null;
+
+ const { name: fieldName, value, collectionName } = element;
+ const compositeKey = collectionName && fieldName
+ ? `${collectionName}.${fieldName}`
+ : null;
+
+ const label = (compositeKey && fieldLabelsMap?.[compositeKey])
+ || fieldLabelsMap?.[fieldName]
+ || fieldLabelsMap?.[collectionName];
+
+ const formattedValue = (compositeKey && fieldFormatter?.[compositeKey]?.(value))
+ || fieldFormatter?.[fieldName]?.(value)
+ || fieldFormatter?.[collectionName]?.(value)
+ || value;
+
+ return (
+
+ {fieldName && {label}: }
+ {formattedValue}
+
+ );
+};
+
const HoldingVersionHistory = ({ onClose, holdingId }) => {
const { formatMessage } = useIntl();
const referenceData = useContext(DataContext);
@@ -81,9 +106,35 @@ const HoldingVersionHistory = ({ onClose, holdingId }) => {
acquisitionMethod: formatMessage({ id: 'ui-inventory.acquisitionMethod' }),
receiptStatus: formatMessage({ id: 'ui-inventory.receiptStatus' }),
entries: formatMessage({ id: 'ui-inventory.receivingHistory' }),
+ additionalCallNumbers: formatMessage({ id: 'ui-inventory.additionalCallNumbers' }),
+ 'additionalCallNumbers.prefix': formatMessage({ id: 'ui-inventory.additionalCallNumberPrefix' }),
+ 'additionalCallNumbers.suffix': formatMessage({ id: 'ui-inventory.additionalCallNumberSuffix' }),
+ 'additionalCallNumbers.typeId': formatMessage({ id: 'ui-inventory.additionalCallNumberType' }),
+ 'additionalCallNumbers.callNumber': formatMessage({ id: 'ui-inventory.additionalCallNumber' }),
+ 'holdingsStatements.statement': formatMessage({ id: 'ui-inventory.holdingsStatement' }),
+ 'holdingsStatements.note': formatMessage({ id: 'ui-inventory.holdingsStatementPublicNote' }),
+ 'holdingsStatements.staffNote': formatMessage({ id: 'ui-inventory.holdingsStatementStaffNote' }),
+ 'holdingsStatementsForSupplements.statement': formatMessage({ id: 'ui-inventory.holdingsStatementForSupplements' }),
+ 'holdingsStatementsForSupplements.note': formatMessage({ id: 'ui-inventory.holdingsStatementForSupplementsPublicNote' }),
+ 'holdingsStatementsForSupplements.staffNote': formatMessage({ id: 'ui-inventory.holdingsStatementForSupplementsStaffNote' }),
+ 'holdingsStatementsForIndexes.statement': formatMessage({ id: 'ui-inventory.holdingsStatementForIndexes' }),
+ 'holdingsStatementsForIndexes.note': formatMessage({ id: 'ui-inventory.holdingsStatementForIndexesPublicNote' }),
+ 'holdingsStatementsForIndexes.staffNote': formatMessage({ id: 'ui-inventory.holdingsStatementForIndexesStaffNote' }),
+ 'notes.holdingsNoteTypeId': formatMessage({ id: 'ui-inventory.noteType' }),
+ 'notes.note': formatMessage({ id: 'ui-inventory.note' }),
+ 'notes.staffOnly': formatMessage({ id: 'ui-inventory.staffOnly' }),
+ 'electronicAccess.urlRelationship': formatMessage({ id: 'ui-inventory.urlRelationship' }),
+ 'electronicAccess.uri': formatMessage({ id: 'ui-inventory.uri' }),
+ 'electronicAccess.linkText': formatMessage({ id: 'ui-inventory.linkText' }),
+ 'electronicAccess.materialsSpecification': formatMessage({ id: 'ui-inventory.materialsSpecification' }),
+ 'electronicAccess.urlPublicNote': formatMessage({ id: 'ui-inventory.urlPublicNote' }),
+ 'entries.publicDisplay': formatMessage({ id: 'ui-inventory.publicDisplay' }),
+ 'entries.enumeration': formatMessage({ id: 'ui-inventory.enumeration' }),
+ 'entries.chronology': formatMessage({ id: 'ui-inventory.chronology' }),
};
const fieldFormatter = getFieldFormatter(referenceData);
+ const itemFormatter = getItemFormatter(fieldLabelsMap, fieldFormatter);
return (
{
isInitialLoading={isLoading}
fieldLabelsMap={fieldLabelsMap}
fieldFormatter={fieldFormatter}
+ itemFormatter={itemFormatter}
actionsMap={actionsMap}
totalVersions={totalVersions}
/>
diff --git a/src/Holding/HoldingVersionHistory/HoldingVersionHistory.test.js b/src/Holding/HoldingVersionHistory/HoldingVersionHistory.test.js
index 63e1a7a89..e643e12d2 100644
--- a/src/Holding/HoldingVersionHistory/HoldingVersionHistory.test.js
+++ b/src/Holding/HoldingVersionHistory/HoldingVersionHistory.test.js
@@ -12,7 +12,7 @@ import {
} from '../../../test/jest/helpers';
import { DataContext } from '../../contexts';
-import HoldingVersionHistory, { getFieldFormatter } from './HoldingVersionHistory';
+import HoldingVersionHistory, { getFieldFormatter, getItemFormatter } from './HoldingVersionHistory';
jest.mock('@folio/stripes/components', () => ({
...jest.requireActual('@folio/stripes/components'),
@@ -115,3 +115,86 @@ describe('field formatter', () => {
expect(fieldFormatter.publicDisplay(false)).toBe('false');
});
});
+
+describe('getItemFormatter', () => {
+ const fieldLabelsMap = {
+ formerIds: 'Former ID',
+ additionalCallNumbers: 'Additional call numbers',
+ notes: 'Notes',
+ 'additionalCallNumbers.prefix': 'Additional call number prefix',
+ 'additionalCallNumbers.suffix': 'Additional call number suffix',
+ 'additionalCallNumbers.typeId': 'Additional call number type',
+ 'additionalCallNumbers.callNumber': 'Additional call number',
+ 'notes.holdingsNoteTypeId': 'Note type',
+ 'notes.note': 'Note',
+ 'notes.staffOnly': 'Staff only',
+ };
+
+ const fieldFormatter = getFieldFormatter(mockReferenceData);
+ const itemFormatter = getItemFormatter(fieldLabelsMap, fieldFormatter);
+
+ it('should return null for null element', () => {
+ expect(itemFormatter(null, 0)).toBeNull();
+ });
+
+ it('should return null for undefined element', () => {
+ expect(itemFormatter(undefined, 0)).toBeNull();
+ });
+
+ it('should format field with collectionName using composite key', () => {
+ const element = {
+ name: 'staffOnly',
+ value: false,
+ collectionName: 'notes',
+ };
+
+ const result = itemFormatter(element, 0);
+ const { container } = renderWithIntl(result, translationsProperties);
+
+ expect(container.querySelector('strong')).toHaveTextContent('Staff only:');
+ expect(container.querySelector('li')).toHaveTextContent('Staff only: false');
+ });
+
+ it('should fallback to fieldName label when composite key not found', () => {
+ const element = {
+ name: 'formerIds',
+ value: '1123',
+ collectionName: 'unknownCollection',
+ };
+
+ const result = itemFormatter(element, 0);
+ const { container } = renderWithIntl(result, translationsProperties);
+
+ expect(container.querySelector('strong')).toHaveTextContent('Former ID:');
+ expect(container.querySelector('li')).toHaveTextContent('Former ID: 1123');
+ });
+
+ it('should fallback to collectionName label when fieldName not found', () => {
+ const element = {
+ name: 'unknownField',
+ value: 'test value',
+ collectionName: 'notes',
+ };
+
+ const result = itemFormatter(element, 0);
+ const { container } = renderWithIntl(result, translationsProperties);
+
+ expect(container.querySelector('strong')).toHaveTextContent('Notes:');
+ expect(container.querySelector('li')).toHaveTextContent('Notes: test value');
+ });
+
+ it('should render additionalCallNumbers.prefix with label and value', () => {
+ const element = {
+ name: 'prefix',
+ value: 'ABC',
+ collectionName: 'additionalCallNumbers',
+ };
+
+ const result = itemFormatter(element, 0);
+ const { container } = renderWithIntl(result, translationsProperties);
+
+ expect(container.querySelector('li'))
+ .toHaveTextContent('Additional call number prefix: ABC');
+ });
+});
+