diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index be65cfc7d7..34a2b8b29e 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "6.54.2", + "version": "6.54.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "6.54.2", + "version": "6.54.3", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index ed6a1f82ed..f31f8179eb 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "6.54.2", + "version": "6.54.3", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 3c59b55051..9b9304f132 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,11 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version 6.54.3 +*Released*: 10 July 2025 +- Issue 52657: LKSM: We shouldn't allow creating sample names that differ only in case + - Use original case in duplicate name error message + ### version 6.54.2 *Released*: 9 July 2025 - Merge from release25.7-SNAPSHOT to develop diff --git a/packages/components/src/internal/components/editable/models.test.ts b/packages/components/src/internal/components/editable/models.test.ts index 68e5863999..87a988013f 100644 --- a/packages/components/src/internal/components/editable/models.test.ts +++ b/packages/components/src/internal/components/editable/models.test.ts @@ -252,15 +252,16 @@ describe('EditorModel', () => { expect(uniqueKeyViolations.get(colOneCaption).size).toBe(3); expect(uniqueKeyViolations.get(colOneCaption).has('a')).toBe(true); expect(uniqueKeyViolations.get(colOneCaption).get('a')).toEqual(List([1, 2])); - expect(uniqueKeyViolations.get(colOneCaption).has('caseinsensitive')).toBe(true); - expect(uniqueKeyViolations.get(colOneCaption).get('caseinsensitive')).toEqual(List([3, 4])); - expect(uniqueKeyViolations.get(colOneCaption).has('spacedupe')).toBe(true); - expect(uniqueKeyViolations.get(colOneCaption).get('spacedupe')).toEqual(List([5, 6])); + expect(uniqueKeyViolations.get(colOneCaption).has('caseinsensitive')).toBe(false); + expect(uniqueKeyViolations.get(colOneCaption).has('caseInSenSiTive')).toBe(true); + expect(uniqueKeyViolations.get(colOneCaption).get('caseInSenSiTive')).toEqual(List([3, 4])); + expect(uniqueKeyViolations.get(colOneCaption).has('spaceDupe')).toBe(true); + expect(uniqueKeyViolations.get(colOneCaption).get('spaceDupe')).toEqual(List([5, 6])); const errors = editorModel.getValidationErrors(colOneFk); expect(errors.errors).toEqual([ `Duplicate value (a) for ${colOneCaption} on rows 1, 2.`, - `Duplicate value (caseinsensitive) for ${colOneCaption} on rows 3, 4.`, - `Duplicate value (spacedupe) for ${colOneCaption} on rows 5, 6.`, + `Duplicate value (caseInSenSiTive) for ${colOneCaption} on rows 3, 4.`, + `Duplicate value (spaceDupe) for ${colOneCaption} on rows 5, 6.`, ]); }); @@ -290,11 +291,11 @@ describe('EditorModel', () => { expect(uniqueKeyViolations.size).toBe(1); expect(uniqueKeyViolations.has(colOneCaption)).toBe(true); expect(uniqueKeyViolations.get(colOneCaption).size).toBe(1); - expect(uniqueKeyViolations.get(colOneCaption).has('caseinsensitive')).toBe(true); - expect(uniqueKeyViolations.get(colOneCaption).get('caseinsensitive')).toEqual(List([3, 4])); + expect(uniqueKeyViolations.get(colOneCaption).has('caseInSenSiTive')).toBe(true); + expect(uniqueKeyViolations.get(colOneCaption).get('caseInSenSiTive')).toEqual(List([3, 4])); const errors = editorModel.getValidationErrors(colOneFk); expect(errors.errors).toEqual([ - `Duplicate value (caseinsensitive) for ${colOneCaption} on rows 3, 4.`, + `Duplicate value (caseInSenSiTive) for ${colOneCaption} on rows 3, 4.`, `${colOneCaption} is missing from row 1.`, ]); expect(errors.cellMessages.toJS()).toStrictEqual({ @@ -929,7 +930,7 @@ describe('EditorModel', () => { }); test('with original multi values, multiple same values, but ordering is changed', () => { - let updatedRows = emMultipleComplexInputs.getUpdatedData( + const updatedRows = emMultipleComplexInputs.getUpdatedData( fromJS({ 0: { [expInputCol.fieldKey]: [ @@ -940,7 +941,7 @@ describe('EditorModel', () => { { value: 123, displayValue: 'Value 123', - } + }, ], }, 1: { diff --git a/packages/components/src/internal/components/editable/models.ts b/packages/components/src/internal/components/editable/models.ts index 2856b175c0..c13361d56f 100644 --- a/packages/components/src/internal/components/editable/models.ts +++ b/packages/components/src/internal/components/editable/models.ts @@ -426,6 +426,7 @@ export class EditorModel let keyValues = Map>(); // map from row number to list of key values on that row let uniqueKeyMap = Map>(); // map from value to rows with that value let missingRequired = Map>(); // map from column caption to list of rows missing a value for that column + const originalUniqueValMap = {}; for (let rn = 0; rn < this.rowCount; rn++) { columns.forEach(col => { const fieldKey = col.fieldKey; @@ -476,11 +477,13 @@ export class EditorModel // there better be only one of these const valueDescriptor = values.get(0); if (valueDescriptor && this.hasRawValue(valueDescriptor)) { - const stringVal = valueDescriptor.raw.toString().trim().toLowerCase(); - if (uniqueKeyMap.has(stringVal)) { - uniqueKeyMap = uniqueKeyMap.set(stringVal, uniqueKeyMap.get(stringVal).push(rn + 1)); + const rawStringVal = valueDescriptor.raw.toString().trim(); + const stringValLc = rawStringVal.toLowerCase(); + if (uniqueKeyMap.has(stringValLc)) { + uniqueKeyMap = uniqueKeyMap.set(stringValLc, uniqueKeyMap.get(stringValLc).push(rn + 1)); } else { - uniqueKeyMap = uniqueKeyMap.set(stringVal, List([rn + 1])); + originalUniqueValMap[stringValLc] = rawStringVal; + uniqueKeyMap = uniqueKeyMap.set(stringValLc, List([rn + 1])); } } } @@ -488,7 +491,14 @@ export class EditorModel } let uniqueKeyViolations = Map>>(); - const duplicates = uniqueKeyMap.filter(rowNumbers => rowNumbers.size > 1).toMap(); + const duplicates = uniqueKeyMap + .filter(rowNumbers => rowNumbers.size > 1) + .reduce((keyMap, values, uniqueValueLc) => { + const uniqueValueOriginal = originalUniqueValMap[uniqueValueLc]; + if (uniqueValueOriginal) return keyMap.set(uniqueValueOriginal, values); + else return keyMap.set(uniqueValueLc, values); + }, Map>()) + .toMap(); if (duplicates.size > 0 && uniqueFieldCol) { uniqueKeyViolations = uniqueKeyViolations.set(uniqueFieldCol.caption, duplicates); }