From 9be15343ef00e5f928cf4108265683ee840f0cce Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Wed, 15 Dec 2021 16:09:00 -0700 Subject: [PATCH 1/5] Formatting for qualified lists --- .../input/FieldTextInputContainer.jsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/containers/input/FieldTextInputContainer.jsx b/src/containers/input/FieldTextInputContainer.jsx index c25e5f9d..02ab2c66 100644 --- a/src/containers/input/FieldTextInputContainer.jsx +++ b/src/containers/input/FieldTextInputContainer.jsx @@ -2,6 +2,9 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { components as inputComponents } from 'cspace-input'; import { injectIntl, intlShape } from 'react-intl'; +import get from 'lodash/get'; +import { NS_PREFIX } from '../../constants/xmlNames'; +import { configKey } from '../../helpers/configHelpers'; import { formatRecordTypeSourceField } from '../../helpers/formatHelpers'; import withConfig from '../../enhancers/withConfig'; import withCsid from '../../enhancers/withCsid'; @@ -16,18 +19,37 @@ const propTypes = { value: PropTypes.string, }; +export const getFirstChild = (recordType, partName, fieldName, { config }) => { + const recordTypeConfig = config.recordTypes[recordType]; + const partDescriptor = get(recordTypeConfig, ['fields', 'document', `${NS_PREFIX}:${partName}`]); + if (!partDescriptor) { + return fieldName; + } + + let updatedField = fieldName; + const fieldDescriptor = partDescriptor[fieldName]; + if (fieldDescriptor) { + updatedField = Object.keys(fieldDescriptor).filter((key) => key !== configKey); + } + + return updatedField; +}; + export const formatHumanReadable = (type, value, context) => { let formatted; // the key is created with schema:fieldName:listIndex // we have 3 outcomes -- an array of index 1 (unqualified), 2 (qualified), or 3 (qualified list) + // for qualified lists, use the child key if available // see: AuditDocumentHandler.java const parts = value.split(':'); + const [partName, fieldName] = parts; if (parts.length === 2) { formatted = formatRecordTypeSourceField(type, value, context); } else if (parts.length === 3) { - // todo: get child of the key - formatted = formatRecordTypeSourceField(type, `${parts[0]}:${parts[1]}`, context); + // todo: display index? + const childName = getFirstChild(type, partName, fieldName, context); + formatted = formatRecordTypeSourceField(type, `${partName}:${childName}`, context); } else { formatted = value; } From 8d7cb8c5bf9053636f1ed261a928460cfef5339d Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Fri, 17 Dec 2021 15:34:03 -0700 Subject: [PATCH 2/5] Format Group names and fields --- src/containers/input/FieldTextInputContainer.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/containers/input/FieldTextInputContainer.jsx b/src/containers/input/FieldTextInputContainer.jsx index 02ab2c66..d9fb6ca1 100644 --- a/src/containers/input/FieldTextInputContainer.jsx +++ b/src/containers/input/FieldTextInputContainer.jsx @@ -45,7 +45,18 @@ export const formatHumanReadable = (type, value, context) => { const parts = value.split(':'); const [partName, fieldName] = parts; if (parts.length === 2) { - formatted = formatRecordTypeSourceField(type, value, context); + let searchField = fieldName; + + // two named groups to capture the name of the group and the field + const groupRegex = /(?\w+GroupList)(\/\d+\/(?\w+$))?/; + const match = groupRegex.exec(fieldName); + if (match) { + searchField = match.groups.groupField + ? match.groups.groupField + : getFirstChild(type, partName, fieldName, context); + } + + formatted = formatRecordTypeSourceField(type, `${partName}:${searchField}`, context); } else if (parts.length === 3) { // todo: display index? const childName = getFirstChild(type, partName, fieldName, context); From 7a8aae435534fd14c959a3163bc009b1af5f0fa6 Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Tue, 21 Dec 2021 12:18:48 -0700 Subject: [PATCH 3/5] Capture scalar values computed --- src/containers/input/FieldTextInputContainer.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/containers/input/FieldTextInputContainer.jsx b/src/containers/input/FieldTextInputContainer.jsx index d9fb6ca1..9932dbd5 100644 --- a/src/containers/input/FieldTextInputContainer.jsx +++ b/src/containers/input/FieldTextInputContainer.jsx @@ -47,13 +47,15 @@ export const formatHumanReadable = (type, value, context) => { if (parts.length === 2) { let searchField = fieldName; - // two named groups to capture the name of the group and the field - const groupRegex = /(?\w+GroupList)(\/\d+\/(?\w+$))?/; - const match = groupRegex.exec(fieldName); + // two regexes to capture either the last field or the full name of the group list + const groupRegex = /(?\w+GroupList)$/; + const fieldRegex = /\w+\/\d+\/(?\w+\/?)+$/; + + const match = fieldRegex.exec(fieldName); if (match) { - searchField = match.groups.groupField - ? match.groups.groupField - : getFirstChild(type, partName, fieldName, context); + searchField = match.groups.field; + } else if (groupRegex.exec(fieldName)) { + searchField = getFirstChild(type, partName, fieldName, context); } formatted = formatRecordTypeSourceField(type, `${partName}:${searchField}`, context); From 6d93cdee674ba3e80255fd7137dc208c2e0b437c Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Wed, 19 Jan 2022 13:38:04 -0700 Subject: [PATCH 4/5] Additional formatting for group fields --- .../input/FieldTextInputContainer.jsx | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/containers/input/FieldTextInputContainer.jsx b/src/containers/input/FieldTextInputContainer.jsx index 9932dbd5..05fb0759 100644 --- a/src/containers/input/FieldTextInputContainer.jsx +++ b/src/containers/input/FieldTextInputContainer.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { components as inputComponents } from 'cspace-input'; -import { injectIntl, intlShape } from 'react-intl'; +import { injectIntl, intlShape, defineMessages } from 'react-intl'; import get from 'lodash/get'; import { NS_PREFIX } from '../../constants/xmlNames'; import { configKey } from '../../helpers/configHelpers'; @@ -19,6 +19,14 @@ const propTypes = { value: PropTypes.string, }; +const messages = defineMessages({ + listItem: { + id: 'fieldTextInput.listItem', + description: 'Item in a group list', + defaultMessage: '{groupList} #{index} — {listItem}', + }, +}); + export const getFirstChild = (recordType, partName, fieldName, { config }) => { const recordTypeConfig = config.recordTypes[recordType]; const partDescriptor = get(recordTypeConfig, ['fields', 'document', `${NS_PREFIX}:${partName}`]); @@ -47,20 +55,29 @@ export const formatHumanReadable = (type, value, context) => { if (parts.length === 2) { let searchField = fieldName; - // two regexes to capture either the last field or the full name of the group list + // capture either the last field or the full name of the group list const groupRegex = /(?\w+GroupList)$/; - const fieldRegex = /\w+\/\d+\/(?\w+\/?)+$/; + const fieldRegex = /(?\w+)\/(?\d+)\/(?\w+\/?)+$/; const match = fieldRegex.exec(fieldName); if (match) { - searchField = match.groups.field; - } else if (groupRegex.exec(fieldName)) { + const { intl } = context; + const groupChild = getFirstChild(type, partName, match.groups.groupList, context); + const formattedGroup = formatRecordTypeSourceField(type, `${partName}:${groupChild}`, context); + const formattedListItem = formatRecordTypeSourceField(type, `${partName}:${match.groups.field}`, context); + return intl.formatMessage(messages.listItem, { + groupList: formattedGroup, + index: match.groups.index, + listItem: formattedListItem, + }); + } + + if (groupRegex.exec(fieldName)) { searchField = getFirstChild(type, partName, fieldName, context); } formatted = formatRecordTypeSourceField(type, `${partName}:${searchField}`, context); } else if (parts.length === 3) { - // todo: display index? const childName = getFirstChild(type, partName, fieldName, context); formatted = formatRecordTypeSourceField(type, `${partName}:${childName}`, context); } else { From 7ee5df3da0ba0210073e579ee6ef606560579c0c Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Thu, 31 Mar 2022 15:32:10 -0600 Subject: [PATCH 5/5] Add test to cover other cases --- .../input/FieldTextInputContainer.spec.jsx | 76 ++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/test/specs/containers/input/FieldTextInputContainer.spec.jsx b/test/specs/containers/input/FieldTextInputContainer.spec.jsx index 8df4a154..24f77a7c 100644 --- a/test/specs/containers/input/FieldTextInputContainer.spec.jsx +++ b/test/specs/containers/input/FieldTextInputContainer.spec.jsx @@ -18,12 +18,51 @@ const intl = { const recordType = 'collectionobject'; const objectNumberConfig = { - messages: defineMessages({ - name: { - id: 'field.collectionobjects_common.objectNumber.name', - defaultMessage: 'Identification number', + [configKey]: { + messages: defineMessages({ + name: { + id: 'field.collectionobjects_common.objectNumber.name', + defaultMessage: 'Identification number', + }, + }), + }, +}; + +const commentsConfig = { + comment: { + [configKey]: { + messages: defineMessages({ + name: { + id: 'field.collectionobjects_common.comment.name', + defaultMessage: 'Comment', + }, + }), + }, + }, +}; + +const titleGroupListConfig = { + titleGroup: { + [configKey]: { + messages: defineMessages({ + name: { + id: 'field.collectionobjects_common.titleGroup.name', + defaultMessage: 'Title', + }, + }), + + }, + titleLanguage: { + [configKey]: { + messages: defineMessages({ + name: { + id: 'field.collectionobjects_common.titleLanguage.name', + defaultMessage: 'Title language', + }, + }), + }, }, - }), + }, }; const config = { @@ -32,9 +71,9 @@ const config = { fields: { document: { 'ns2:collectionobjects_common': { - objectNumber: { - [configKey]: objectNumberConfig, - }, + objectNumber: objectNumberConfig, + comments: commentsConfig, + titleGroupList: titleGroupListConfig, }, }, }, @@ -43,12 +82,31 @@ const config = { }; describe('FieldTextInputContainer', () => { - it('should return human readable input when available', () => { + it('should return the default message for fields', () => { const field = 'collectionobjects_common:objectNumber'; const formatted = formatHumanReadable(recordType, field, { intl, config }); formatted.should.equal('Identification number'); }); + it('should return the child message for lists', () => { + const field = 'collectionobjects_common:comments:03E8'; + const formatted = formatHumanReadable(recordType, field, { intl, config }); + formatted.should.equal('Comment'); + }); + + it('should return the group message for GroupLists', () => { + const field = 'collectionobjects_common:titleGroupList'; + const formatted = formatHumanReadable(recordType, field, { intl, config }); + formatted.should.equal('Title'); + }); + + it('should return the group, index, and item for GroupList items', () => { + const field = 'collectionobjects_common:titleGroupList/0/titleLanguage'; + const formatted = formatHumanReadable(recordType, field, { intl, config }); + // would be nice to have an actual intl fill this in + formatted.should.equal('{groupList} #{index} — {listItem}'); + }); + it('should return the field when formatting is not available', () => { const field = 'collectionspace_core:uri'; const formatted = formatHumanReadable(recordType, field, { intl, config });