diff --git a/lib/AuditLog/AuditLogCard.js b/lib/AuditLog/AuditLogCard.js index 707919509..095dd1802 100644 --- a/lib/AuditLog/AuditLogCard.js +++ b/lib/AuditLog/AuditLogCard.js @@ -18,6 +18,7 @@ const AuditLogCard = ({ fieldLabelsMap, isOriginal, isCurrentVersion, + itemFormatter, showSharedLabel, source, userName, @@ -89,6 +90,7 @@ const AuditLogCard = ({ onClose={() => setIsModalOpen(false)} fieldLabelsMap={fieldLabelsMap} fieldFormatter={fieldFormatter} + itemFormatter={itemFormatter} actionsMap={actionsMap} columnWidths={columnWidths} /> @@ -105,8 +107,9 @@ AuditLogCard.propTypes = { fieldLabelsMap: PropTypes.object, isCurrentVersion: PropTypes.bool, isOriginal: PropTypes.bool, - showSharedLabel: PropTypes.bool, + itemFormatter: PropTypes.func, modalFieldChanges: PropTypes.arrayOf(PropTypes.object), + showSharedLabel: PropTypes.bool, source: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, userName: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, }; diff --git a/lib/AuditLog/AuditLogModal.js b/lib/AuditLog/AuditLogModal.js index ead99e827..2c389c580 100644 --- a/lib/AuditLog/AuditLogModal.js +++ b/lib/AuditLog/AuditLogModal.js @@ -12,6 +12,7 @@ const AuditLogModal = ({ columnWidths, contentData, fieldFormatter, + itemFormatter, fieldLabelsMap, label, onClose, @@ -25,26 +26,27 @@ const AuditLogModal = ({ changedTo: , }; - const itemFormatter = (field, i) => { + const defaultItemFormatter = (field, i) => { return (
  • {fieldFormatter?.[field.name]?.(field.value) || field.value}
  • ); }; + const formatter = { action: item => actionsMap?.[item.changeType] || item.changeType, field: item => fieldLabelsMap?.[item.fieldName] || item.fieldName, changedFrom: item => changedFieldsFormatter({ fieldValue: item.oldValue, fieldName: item.fieldName, - listItemFormatter: itemFormatter, + listItemFormatter: itemFormatter || defaultItemFormatter, fieldFormatter, }), changedTo: item => changedFieldsFormatter({ fieldValue: item.newValue, fieldName: item.fieldName, - listItemFormatter: itemFormatter, + listItemFormatter: itemFormatter || defaultItemFormatter, fieldFormatter, }), }; @@ -91,6 +93,7 @@ AuditLogModal.propTypes = { })).isRequired, fieldFormatter: PropTypes.object, fieldLabelsMap: PropTypes.object, + itemFormatter: PropTypes.func, label: PropTypes.node.isRequired, onClose: PropTypes.func, open: PropTypes.bool.isRequired, diff --git a/lib/AuditLog/AuditLogPane.js b/lib/AuditLog/AuditLogPane.js index fa35db304..4ffc15f67 100644 --- a/lib/AuditLog/AuditLogPane.js +++ b/lib/AuditLog/AuditLogPane.js @@ -19,6 +19,7 @@ const AuditLogPane = ({ actionsMap, columnWidths, fieldFormatter, + itemFormatter, fieldLabelsMap, handleLoadMore, isInitialLoading, @@ -82,13 +83,14 @@ const AuditLogPane = ({ fieldChanges={fieldChanges} fieldLabelsMap={fieldLabelsMap} fieldFormatter={fieldFormatter} + itemFormatter={itemFormatter} actionsMap={actionsMap} columnWidths={columnWidths} modalFieldChanges={modalFieldChanges} /> ); }); - }, [versions, fieldLabelsMap, fieldFormatter, actionsMap, columnWidths, showSharedLabel]); + }, [versions, fieldLabelsMap, fieldFormatter, itemFormatter, actionsMap, columnWidths, showSharedLabel]); return ( ({ name, value }))} + items={map(fieldValue, (value, name) => ({ name, value, collectionName: fieldName }))} itemFormatter={listItemFormatter} listStyle="bullets" marginBottom0 diff --git a/lib/AuditLog/readme.md b/lib/AuditLog/readme.md index 9fb679994..6edb86ffe 100644 --- a/lib/AuditLog/readme.md +++ b/lib/AuditLog/readme.md @@ -32,6 +32,21 @@ const fieldFormatter = { primary: value => value.toString(), }; +const itemFormatter = (fieldLabelsMap, fieldFormatter) => (element, i) => { + if (!element) return null; + + const { name: fieldName, value } = element; + const label = fieldLabelsMap?.[fieldName] || fieldName; + const formattedValue = fieldFormatter?.[fieldName]?.(value) || value; + + return ( +
  • + {fieldName && {label}: } + {formattedValue} +
  • + ); +}; + const handleClose = () => console.log('Pane closed'); const handleLoadMore = () => console.log('Load more clicked'); const isLoading = false; @@ -48,6 +63,7 @@ return ( handleLoadMore={handleLoadMore} isLoading={isLoading} isInitialLoading={isInitialLoading} + itemFormatter={itemFormatter} showSharedLabel={showSharedLabel} fieldLabelsMap={fieldLabelsMap} fieldFormatter={fieldFormatter} @@ -68,6 +84,7 @@ handleLoadMore | func | Callback fired when the "Load more" button is clicked isInitialLoading | bool | Flag that indicates whether data is being loaded for the first time | | false isLoading | bool | Flag that indicates whether data is being loaded | | false isLoadMoreVisible | bool | Flag that indicates whether "Load more" button visible or not | true | false +itemFormatter | func | Formats changed field values of objects or arrays in modal content, used to format oldValue/newValue items of object or array, e.g. showing the field name before the field value. Receives a field object with `name`, `value`, and `collectionName` properties and the item index. Returns a list item element. | `
  • {field.value}
  • ` | false onClose | func | Callback fired when the pane is closed using its dismiss button | | false showSharedLabel | bool | Flag indicating whether the original version should display "Shared" label | false | false totalVersions | number | Total number of versions | | false diff --git a/lib/AuditLog/tests/AuditLogModal-test.js b/lib/AuditLog/tests/AuditLogModal-test.js index d5437015f..aba933b81 100644 --- a/lib/AuditLog/tests/AuditLogModal-test.js +++ b/lib/AuditLog/tests/AuditLogModal-test.js @@ -155,4 +155,101 @@ describe('AuditLogModal', () => { it('the onClose callback should be fired', () => converge(() => onClose.called)); }); + + describe('itemFormatter', () => { + describe('when using DEFAULT itemFormatter', () => { + const contentDataWithArray = [{ + changeType: 'MODIFIED', + fieldName: 'arrayField', + newValue: ['item1', 'item2', 'item3'], + oldValue: ['oldItem1', 'oldItem2'], + }]; + + beforeEach(async () => { + await mountWithContext( + + + + ); + }); + + it('should render list items with default
  • tags', async () => { + await mcl.find(MultiColumnListCell({ row: 0, columnIndex: 2 })).perform(el => { + expect(el.querySelectorAll('li')).to.have.length(2); + expect(el.querySelectorAll('.custom-item')).to.have.length(0); + }); + + await mcl.find(MultiColumnListCell({ row: 0, columnIndex: 3 })).perform(el => { + expect(el.querySelectorAll('li')).to.have.length(3); + expect(el.querySelectorAll('.custom-item')).to.have.length(0); + }); + }); + }); + + describe('when using CUSTOM itemFormatter (object)', () => { + const customItemFormatter = (field, i) => { + return ( +
    + CUSTOM: {field.value} +
    + ); + }; + + const contentDataWithObject = [{ + changeType: 'MODIFIED', + fieldName: 'objectField', + newValue: { + a: 'item1', + b: 'item2', + c: 'item3', + }, + oldValue: { + x: 'oldItem1', + y: 'oldItem2', + }, + }]; + + beforeEach(async () => { + await mountWithContext( + + + + ); + }); + + it('should render object values using the custom itemFormatter', async () => { + await mcl.find(MultiColumnListCell({ row: 0, columnIndex: 2 })).perform(el => { + expect(el.textContent).to.include('CUSTOM: oldItem1'); + expect(el.textContent).to.include('CUSTOM: oldItem2'); + + const customItems = el.querySelectorAll('.custom-item'); + expect(customItems.length).to.equal(2); + }); + + await mcl.find(MultiColumnListCell({ row: 0, columnIndex: 3 })).perform(el => { + expect(el.textContent).to.include('CUSTOM: item1'); + expect(el.textContent).to.include('CUSTOM: item2'); + expect(el.textContent).to.include('CUSTOM: item3'); + + const customItems = el.querySelectorAll('.custom-item'); + expect(customItems.length).to.equal(3); + }); + }); + }); + }); });