Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/AuditLog/AuditLogCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const AuditLogCard = ({
fieldLabelsMap,
isOriginal,
isCurrentVersion,
itemFormatter,
showSharedLabel,
source,
userName,
Expand Down Expand Up @@ -89,6 +90,7 @@ const AuditLogCard = ({
onClose={() => setIsModalOpen(false)}
fieldLabelsMap={fieldLabelsMap}
fieldFormatter={fieldFormatter}
itemFormatter={itemFormatter}
actionsMap={actionsMap}
columnWidths={columnWidths}
/>
Expand All @@ -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,
};
Expand Down
9 changes: 6 additions & 3 deletions lib/AuditLog/AuditLogModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const AuditLogModal = ({
columnWidths,
contentData,
fieldFormatter,
itemFormatter,
fieldLabelsMap,
label,
onClose,
Expand All @@ -25,26 +26,27 @@ const AuditLogModal = ({
changedTo: <FormattedMessage id="stripes-components.auditLog.modal.changedTo" />,
};

const itemFormatter = (field, i) => {
const defaultItemFormatter = (field, i) => {
return (
<li key={i}>
{fieldFormatter?.[field.name]?.(field.value) || field.value}
</li>
);
};

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,
}),
};
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions lib/AuditLog/AuditLogPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const AuditLogPane = ({
actionsMap,
columnWidths,
fieldFormatter,
itemFormatter,
fieldLabelsMap,
handleLoadMore,
isInitialLoading,
Expand Down Expand Up @@ -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 (
<Pane
Expand Down Expand Up @@ -144,8 +146,9 @@ AuditLogPane.propTypes = {
isInitialLoading: PropTypes.bool,
isLoading: PropTypes.bool,
isLoadMoreVisible: PropTypes.bool,
itemFormatter: PropTypes.func,
onClose: PropTypes.func.isRequired,
showSharedLabel: PropTypes.bool,
onClose: PropTypes.func,
totalVersions: PropTypes.number,
versions: PropTypes.arrayOf(
PropTypes.shape({
Expand Down
2 changes: 1 addition & 1 deletion lib/AuditLog/changedFieldsFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const changedFieldsFormatter = ({
if (typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
return (
<List
items={map(fieldValue, (value, name) => ({ name, value }))}
items={map(fieldValue, (value, name) => ({ name, value, collectionName: fieldName }))}
itemFormatter={listItemFormatter}
listStyle="bullets"
marginBottom0
Expand Down
17 changes: 17 additions & 0 deletions lib/AuditLog/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<li key={i}>
{fieldName && <strong>{label}: </strong>}
{formattedValue}
</li>
);
};

const handleClose = () => console.log('Pane closed');
const handleLoadMore = () => console.log('Load more clicked');
const isLoading = false;
Expand All @@ -48,6 +63,7 @@ return (
handleLoadMore={handleLoadMore}
isLoading={isLoading}
isInitialLoading={isInitialLoading}
itemFormatter={itemFormatter}
showSharedLabel={showSharedLabel}
fieldLabelsMap={fieldLabelsMap}
fieldFormatter={fieldFormatter}
Expand All @@ -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. | `<li>{field.value}</li>` | 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
Expand Down
97 changes: 97 additions & 0 deletions lib/AuditLog/tests/AuditLogModal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Harness>
<AuditLogModal
open
label="Audit log modal with default formatter"
onClose={onClose}
contentData={contentDataWithArray}
fieldLabelsMap={{ arrayField: 'Array Field' }}
actionsMap={actionsMap}
/>
</Harness>
);
});

it('should render list items with default <li> 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 (
<div key={i} className="custom-item">
CUSTOM: {field.value}
</div>
);
};

const contentDataWithObject = [{
changeType: 'MODIFIED',
fieldName: 'objectField',
newValue: {
a: 'item1',
b: 'item2',
c: 'item3',
},
oldValue: {
x: 'oldItem1',
y: 'oldItem2',
},
}];

beforeEach(async () => {
await mountWithContext(
<Harness>
<AuditLogModal
open
label="Audit log modal with custom formatter"
onClose={onClose}
contentData={contentDataWithObject}
fieldLabelsMap={{ objectField: 'Object Field' }}
itemFormatter={customItemFormatter}
actionsMap={actionsMap}
/>
</Harness>
);
});

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);
});
});
});
});
});
Loading