diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 34ce8ead6e..fe4dc951db 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "6.4.0", + "version": "6.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "6.4.0", + "version": "6.4.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "17.0.0", diff --git a/packages/components/package.json b/packages/components/package.json index 973e061055..90241bba2d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "6.4.0", + "version": "6.4.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 e4037bd225..08317bed09 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,12 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version 6.4.1 +*Released*: 9 December 2024 +- Issue 51432: LKSM: special character not working well on various pages + - Warn user about missing quotes when pasting data into editable grid that contains a comma + - Replace `&` with a similar unicode character for lineage graph labels display as vis-network doesn't work well with `&` + ### version 6.4.0 *Released*: 4 December 2024 - Remove assays.scss diff --git a/packages/components/src/internal/components/editable/actions.test.ts b/packages/components/src/internal/components/editable/actions.test.ts index 0bfcd83902..646fae008f 100644 --- a/packages/components/src/internal/components/editable/actions.test.ts +++ b/packages/components/src/internal/components/editable/actions.test.ts @@ -789,7 +789,7 @@ describe('parsePastedLookup', () => { valueDescriptors: List([{ display: 'abc', raw: 'abc' }]), }); expect(parsePastedLookup(stringLookupCol, stringLookupValues, 'abc, valueD')).toStrictEqual({ - message: { message: 'Could not find "abc", "valueD"' }, + message: { message: 'Could not find "abc", "valueD". Please make sure values that contain commas are properly quoted.' }, valueDescriptors: List([ { display: 'abc', raw: 'abc' }, { display: 'valueD', raw: 'valueD' }, @@ -820,7 +820,7 @@ describe('parsePastedLookup', () => { valueDescriptors: List([{ display: 'abc', raw: 'abc' }]), }); expect(parsePastedLookup(intLookupCol, intLookupValues, 'abc, valueD')).toStrictEqual({ - message: { message: 'Could not find "abc", "valueD"' }, + message: { message: 'Could not find "abc", "valueD". Please make sure values that contain commas are properly quoted.' }, valueDescriptors: List([ { display: 'abc', raw: 'abc' }, { display: 'valueD', raw: 'valueD' }, diff --git a/packages/components/src/internal/components/editable/actions.ts b/packages/components/src/internal/components/editable/actions.ts index e047a4ea34..a2a2139568 100644 --- a/packages/components/src/internal/components/editable/actions.ts +++ b/packages/components/src/internal/components/editable/actions.ts @@ -291,8 +291,12 @@ async function getLookupValueDescriptors( return descriptorMap; } -function lookupValidationError(value: string | number | boolean): CellMessage { - return { message: `Could not find ${value}` }; +function lookupValidationError(value: string | number | boolean, fromPaste?: boolean): CellMessage { + let suffix = ''; + if (fromPaste && typeof value === 'string' && value.toString().indexOf(',') > -1) { + suffix = '. Please make sure values that contain commas are properly quoted.'; + } + return { message: `Could not find ${value}${suffix}` }; } async function getLookupDisplayValue(column: QueryColumn, value: any, containerPath: string): Promise { @@ -455,16 +459,15 @@ export function addColumns( if (insertFieldKey && leftColIndex < editorModel.orderedColumns.size - 1) { let readOnlyEnded = false; editorModel.orderedColumns.forEach((fieldKey, ind) => { - if (ind <= leftColIndex || readOnlyEnded) - return; - if (!editorModel.columnMap.get(fieldKey).readOnly) - readOnlyEnded = true; - else - altInsertFieldKey = fieldKey; + if (ind <= leftColIndex || readOnlyEnded) return; + if (!editorModel.columnMap.get(fieldKey).readOnly) readOnlyEnded = true; + else altInsertFieldKey = fieldKey; }); if (altInsertFieldKey) - leftColIndex = editorModel.orderedColumns.findIndex(column => Utils.caseInsensitiveEquals(column, altInsertFieldKey)); + leftColIndex = editorModel.orderedColumns.findIndex(column => + Utils.caseInsensitiveEquals(column, altInsertFieldKey) + ); } const editorModelIndex = leftColIndex + 1; @@ -940,7 +943,7 @@ export function parsePastedLookup( .slice(0, 4) .map(u => '"' + u + '"') .join(', '); - message = lookupValidationError(valueStr); + message = lookupValidationError(valueStr, true); } return { diff --git a/packages/components/src/internal/components/lineage/models.test.ts b/packages/components/src/internal/components/lineage/models.test.ts index 22b1f28c45..a1e921d390 100644 --- a/packages/components/src/internal/components/lineage/models.test.ts +++ b/packages/components/src/internal/components/lineage/models.test.ts @@ -225,5 +225,38 @@ describe('lineage model', () => { expect(nodes[childThreeNode.lsid].level).toEqual(-1); expect(nodes[childFourNode.lsid].level).toEqual(0); }); + + it('Issue 51432: special character in sample names result in: SyntaxError: unterminated character class', () => { + const childLsid = 'child-lsid'; + const parentLsid = 'parent-lsid'; + + const childNode = LineageNode.create(childLsid, { + parents: [{ lsid: parentLsid, name: '&4[0' }], + name: '&4[0_1001' + }); + + const parentNode = LineageNode.create(parentLsid, { + children: [{ lsid: childLsid, name: '&4[0_1001' }], + name: '&4[0' + }); + + const result = LineageResult.create({ + nodes: { + [parentNode.lsid]: parentNode, + [childNode.lsid]: childNode, + }, + seed: childNode.lsid, + }); + + const { edges, nodes } = generateNodesAndEdges(result); + + expect(Object.keys(edges).length).toEqual(1); + expect(Object.keys(nodes).length).toEqual(2); + + expect(nodes[parentNode.lsid].level).toEqual(-1); + expect(nodes[parentNode.lsid].label).toEqual('&4[0'); + expect(nodes[childNode.lsid].level).toEqual(0); + expect(nodes[childNode.lsid].label).toEqual('&4[0_1001'); + }); }); }); diff --git a/packages/components/src/internal/components/lineage/models.ts b/packages/components/src/internal/components/lineage/models.ts index c982407722..4ef0cd00cd 100644 --- a/packages/components/src/internal/components/lineage/models.ts +++ b/packages/components/src/internal/components/lineage/models.ts @@ -781,6 +781,7 @@ function addEdges( } } +const FULLWIDTH_AMPERSAND = '&'; // U+FF06, use as alt display for & so vis-network won't error out function createVisNode( node: LineageNode, id: string, @@ -791,11 +792,20 @@ function createVisNode( // show the alternate icon image color if this node is the seed or has been selected const { image, imageBackup, imageSelected, imageShape } = node.iconProps; + let nodeLabel = node.name; + // Issue 51432: LKSM: special character in sample names result in client side exception: SyntaxError: unterminated character class + // vis-network does special processing for labels that evaluates to true for `/&/.test()` + if (nodeLabel?.indexOf('&') > -1) { + // for labels that starts with '&', the entire label is replaced with '<' by vis + // vis-network is not able to tolerate the presence of '&' in certain strings due to Uncaught SyntaxError from `new RegExp()`. Encoding does not help. + nodeLabel = nodeLabel.replaceAll('&', FULLWIDTH_AMPERSAND); + } + return { kind: 'node', id, lineageNode: node, - label: node.name, + label: nodeLabel, level: level(depth, dir, false), title: getLineageNodeTitle(node, true), image: {