From 514cabd36037e9e380a307e587f023ccabd571bb Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 7 Apr 2026 10:13:11 -0700 Subject: [PATCH 1/3] GitHub Issue 954: Add error for duplicate values for parent inputs --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- packages/components/releaseNotes/components.md | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 3c455ec912..1b8d603f7f 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.28.2", + "version": "7.28.3-fb-duplicateParents.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.28.2", + "version": "7.28.3-fb-duplicateParents.1", "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 cf648a7de8..cc460a4c2f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.28.2", + "version": "7.28.3-fb-duplicateParents.1", "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 77a1e9adbb..d9fce2b58b 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -5,6 +5,10 @@ Components, models, actions, and utility functions for LabKey applications and p *Released*: 7 April 2026 - Update `FEEZER_ITEM_SAMPLE_MAPPER` to return undefined if not matched, for consistency with other mappers +### version 7.X +*Released*: X April 2026 +- GitHub Issue 954: Add error for duplicate values for parent inputs + ### version 7.28.1 *Released*: 3 April 2026 - GitHub Issue 613: Use "Status" instead of "SampleState" in error messaging. From 9f201d47f0645fb9fc0d09ffdc2fddaadcddd1a7 Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 7 Apr 2026 10:14:28 -0700 Subject: [PATCH 2/3] GitHub Issue 954: Add error for duplicate values for parent inputs --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- .../src/internal/components/editable/actions.ts | 12 ++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 1b8d603f7f..dbdcad1fdd 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.28.3-fb-duplicateParents.1", + "version": "7.28.3-fb-duplicateParents.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.28.3-fb-duplicateParents.1", + "version": "7.28.3-fb-duplicateParents.2", "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 cc460a4c2f..8c30d410b7 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.28.3-fb-duplicateParents.1", + "version": "7.28.3-fb-duplicateParents.2", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/src/internal/components/editable/actions.ts b/packages/components/src/internal/components/editable/actions.ts index 119ca17617..2765b9fb5b 100644 --- a/packages/components/src/internal/components/editable/actions.ts +++ b/packages/components/src/internal/components/editable/actions.ts @@ -1118,6 +1118,7 @@ export function parsePastedLookup( let message: CellMessage; let values: ValueDescriptor[]; const unmatched: string[] = []; + const dupValues = new Set(); // Parse pasted strings to split properly around quoted values. // Remove the quotes for storing the actual values in the grid. @@ -1129,10 +1130,15 @@ export function parsePastedLookup( unmatched.push(vt); values = [{ display: vt, raw: vt }]; } else { + const foundValues = new Set(); values = parsedValues.flatMap(v => { const vt = v.trim(); if (!vt) return []; + if (foundValues.has(vt)) { + dupValues.add(vt); + } + foundValues.add(vt); const vl = vt.toLowerCase(); const vd = descriptors.find(d => d.display && d.display.toString().toLowerCase() === vl); if (vd) return [vd]; @@ -1148,6 +1154,12 @@ export function parsePastedLookup( .map(u => '"' + u + '"') .join(', '); message = { message: lookupValidationErrorMessage(valueStr, true) }; + } else if (dupValues.size > 0) { + const valueStr = Array.from(dupValues) + .slice(0, 4) + .map(u => '"' + u + '"') + .join(', '); + message = { message: `Duplicate values not allowed: ${valueStr}.` }; } return { message, valueDescriptors: List(values) }; From b4dfe83c172f66c296ba5182a34bd1db6c2908bb Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 7 Apr 2026 13:35:22 -0700 Subject: [PATCH 3/3] jest and api tests --- .../components/editable/actions.test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/components/src/internal/components/editable/actions.test.ts b/packages/components/src/internal/components/editable/actions.test.ts index ff3771c6f6..a9198a0bbc 100644 --- a/packages/components/src/internal/components/editable/actions.test.ts +++ b/packages/components/src/internal/components/editable/actions.test.ts @@ -836,6 +836,48 @@ describe('parsePastedLookup', () => { ]), }); }); + + test('duplicate values', () => { + // single duplicate + expect(parsePastedLookup(multiValueLookup, stringLookupValues, 'A,A')).toStrictEqual({ + message: { message: 'Duplicate values not allowed: "A".' }, + valueDescriptors: List([ + { display: 'A', raw: 'a' }, + { display: 'A', raw: 'a' }, + ]), + }); + + // multiple duplicates + expect(parsePastedLookup(multiValueLookup, stringLookupValues, 'A,b,A,b')).toStrictEqual({ + message: { message: 'Duplicate values not allowed: "A", "b".' }, + valueDescriptors: List([ + { display: 'A', raw: 'a' }, + { display: 'b', raw: 'B' }, + { display: 'A', raw: 'a' }, + { display: 'b', raw: 'B' }, + ]), + }); + + // duplicate with other valid values + expect(parsePastedLookup(multiValueLookup, stringLookupValues, 'A,C,A')).toStrictEqual({ + message: { message: 'Duplicate values not allowed: "A".' }, + valueDescriptors: List([ + { display: 'A', raw: 'a' }, + { display: 'C', raw: 'C' }, + { display: 'A', raw: 'a' }, + ]), + }); + + // unmatched takes precedence over duplicates + expect(parsePastedLookup(multiValueLookup, stringLookupValues, 'A,A,notfound')).toStrictEqual({ + message: { message: 'Could not find "notfound"' }, + valueDescriptors: List([ + { display: 'A', raw: 'a' }, + { display: 'A', raw: 'a' }, + { display: 'notfound', raw: 'notfound' }, + ]), + }); + }); }); describe('insertPastedData', () => {