From b2a949538b749030cfe4d8f03e74f62425ce5815 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Tue, 7 Apr 2026 13:03:37 -0700 Subject: [PATCH 01/11] Add optional `jobActionId` parameter to `updateSampleStorageData` --- packages/components/releaseNotes/components.md | 4 ++++ .../components/src/internal/components/samples/actions.ts | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 77a1e9adbb..5894611961 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,10 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version TBD +*Released*: TBD +- Add optional `jobActionId` parameter to `updateSampleStorageData` + ### version 7.28.2 *Released*: 7 April 2026 - Update `FEEZER_ITEM_SAMPLE_MAPPER` to return undefined if not matched, for consistency with other mappers diff --git a/packages/components/src/internal/components/samples/actions.ts b/packages/components/src/internal/components/samples/actions.ts index 836a2cc7fb..d86e6c4bf5 100644 --- a/packages/components/src/internal/components/samples/actions.ts +++ b/packages/components/src/internal/components/samples/actions.ts @@ -559,7 +559,8 @@ export function updateSampleStorageData( containerPath?: string, userComment?: string, isDiscard = false, - editMethod?: EDIT_METHOD + editMethod?: EDIT_METHOD, + jobActionId?: number ): Promise { if (sampleStorageData.length === 0) { return Promise.resolve(); @@ -569,6 +570,7 @@ export function updateSampleStorageData( return Ajax.request({ url: ActionURL.buildURL('inventory', 'updateSampleStorageData.api', containerPath), jsonData: { + jobActionId, sampleRows: sampleStorageData, [STORED_AMOUNT_FIELDS.AUDIT_COMMENT]: userComment, ...getRequestAuditDetail(editMethod), From ef065d21c40112f125a94cd82bdd457fa8e3ee01 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Tue, 7 Apr 2026 13:14:25 -0700 Subject: [PATCH 02/11] @labkey/components v7.28.3-workflowStorageActions.0 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 3c455ec912..0f2f25f856 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-workflowStorageActions.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.28.2", + "version": "7.28.3-workflowStorageActions.0", "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..3edfc70bda 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-workflowStorageActions.0", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From d4b55438d27cc3f69304bb921574ad21ca05d43c Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Tue, 7 Apr 2026 15:15:26 -0700 Subject: [PATCH 03/11] Add `dividedOptionsRenderer` and `filterDividedOptions` for rendering selectInputs with dividers between groups of options --- .../components/releaseNotes/components.md | 1 + packages/components/src/index.ts | 3 + .../input/DividedOptionsRenderer.test.tsx | 70 +++++++++++++++++++ .../forms/input/DividedOptionsRenderer.tsx | 42 +++++++++++ packages/components/src/theme/form.scss | 4 ++ 5 files changed, 120 insertions(+) create mode 100644 packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx create mode 100644 packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 5894611961..6ce455ae8a 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -4,6 +4,7 @@ Components, models, actions, and utility functions for LabKey applications and p ### version TBD *Released*: TBD - Add optional `jobActionId` parameter to `updateSampleStorageData` +- Add `dividedOptionsRenderer` and `filterDividedOptions` for rendering selectInputs with dividers between groups of options ### version 7.28.2 *Released*: 7 April 2026 diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index bee47e17d1..97863c9fef 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -308,6 +308,7 @@ import { import { QueryFormInputs } from './internal/components/forms/QueryFormInputs'; import { LookupSelectInput } from './internal/components/forms/input/LookupSelectInput'; import { SelectInput } from './internal/components/forms/input/SelectInput'; +import { dividedOptionsRenderer, filterDividedOptions } from './internal/components/forms/input/DividedOptionsRenderer'; import { DatePickerInput } from './internal/components/forms/input/DatePickerInput'; import { FileInput } from './internal/components/forms/input/FileInput'; import { TextInput } from './internal/components/forms/input/TextInput'; @@ -1248,6 +1249,7 @@ export { DisableableMenuItem, DiscardConsumedSamplesPanel, Discussions, + dividedOptionsRenderer, DOMAIN_FIELD_REQUIRED, DOMAIN_FIELD_TYPE, DOMAIN_RANGE_VALIDATOR, @@ -1304,6 +1306,7 @@ export { FilterAction, filterArrayToString, FilterCriteriaRenderer, + filterDividedOptions, FilterStatus, FIND_BY_IDS_QUERY_PARAM, FindByIdsModal, diff --git a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx new file mode 100644 index 0000000000..f5d5434c56 --- /dev/null +++ b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx @@ -0,0 +1,70 @@ +import { filterDividedOptions } from './DividedOptionsRenderer'; + +describe('filterDividedOptions', () => { + test('no options presented', () => { + expect(filterDividedOptions(undefined, undefined)).toStrictEqual([]); + }); + test('no dividers', () => { + expect(filterDividedOptions([{label: 'option1', value: 'option1'}], [])).toStrictEqual([{label: 'option1', value: 'option1'}]); + expect(filterDividedOptions([{label: 'option1', value: 'option1'}, {label: 'option2', value: 'option2'}], ['option2'])).toStrictEqual([{label: 'option1', value: 'option1'}]); + }); + test('all selected', () => { + expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {label: 'option2', value: 'o2'}], ['o2', 'o1'])).toStrictEqual([]); + + }); + test('none selected', () => { + expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {label: undefined, isDivider: true}, {label: 'option2', value: 'o2'}], [])) + .toStrictEqual([ + {label: 'option1', value: 'o1'}, {label: undefined, isDivider: true}, {label: 'option2', value: 'o2'} + ]); + }); + test('remove last divider', () => { + expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {label: undefined, isDivider: true}, {label: 'option2', value: 'o2'}], ['o2'])) + .toStrictEqual([ + {label: 'option1', value: 'o1'} + ]); + }); + test('remove middle divider', () => { + expect(filterDividedOptions([ + {label: 'option1', value: 'o1'}, + {label: undefined, isDivider: true}, + {label: 'option2', value: 'o2'}, + {label: undefined, isDivider: true}, + {label: 'option3', value: 'o3'}, + ], + ['o2'])) + .toStrictEqual([ + {label: 'option1', value: 'o1'}, + {label: undefined, isDivider: true}, + {label: 'option3', value: 'o3'} + ]); + }); + test('remove first divider', () => { + expect(filterDividedOptions([ + {label: 'option1', value: 'o1'}, + {label: undefined, isDivider: true}, + {label: 'option2', value: 'o2'}, + {label: undefined, isDivider: true}, + {label: 'option3', value: 'o3'}, + ], + ['o1'])) + .toStrictEqual([ + {label: 'option2', value: 'o2'}, + {label: undefined, isDivider: true}, + {label: 'option3', value: 'o3'} + ]); + }); + test('remove multiple divider', () => { + expect(filterDividedOptions([ + {label: 'option1', value: 'o1'}, + {label: 'd1', isDivider: true}, + {label: 'option2', value: 'o2'}, + {label: 'd2', isDivider: true}, + {label: 'option3', value: 'o3'}, + ], + ['o1', 'o2'])) + .toStrictEqual([ + {label: 'option3', value: 'o3'} + ]); + }); +}); diff --git a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx new file mode 100644 index 0000000000..30025373b4 --- /dev/null +++ b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx @@ -0,0 +1,42 @@ +import React, { FC, memo } from 'react'; + +interface DividedOptionsRendererProps { + isDivider: boolean; + label: string; +} + +// export for jest testing +export const DividedOptionsRenderer: FC = memo(({ label, isDivider }) => { + if (isDivider) { + return
; + } + return
{label}
+}); +DividedOptionsRenderer.displayName = 'DividedOptionsRenderer'; + +export function dividedOptionsRenderer(option) { + return ; +} + +export function filterDividedOptions(allOptions, selectedOptions): any[] { + if (!allOptions) + return []; + + const notSelected = allOptions + // remove options already selected + .filter(option => selectedOptions.indexOf(option.value) == -1); + const options = []; + // remove dividers that are no longer dividing anything + let lastDivider = -1; + notSelected.forEach((option, index) => { + if (!option.isDivider) + options.push(option); + else { + if (index-1 !== lastDivider && index !== notSelected.length-1) { + options.push(option); + } + lastDivider = index + } + }); + return options; +} diff --git a/packages/components/src/theme/form.scss b/packages/components/src/theme/form.scss index b0cef0890f..53bd0795d0 100644 --- a/packages/components/src/theme/form.scss +++ b/packages/components/src/theme/form.scss @@ -465,3 +465,7 @@ textarea.form-control { .has-warning .select-input__control:hover { border-color: $brand-warning; } + +.select-options-divider { + margin: 0 2px 0 2px; +} From ad984c589dc8b1fface269cf901d00fe476fa64a Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Tue, 7 Apr 2026 15:16:17 -0700 Subject: [PATCH 04/11] @labkey/components v7.28.3-workflowStorageActions.1 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 0f2f25f856..0c4309f688 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.28.3-workflowStorageActions.0", + "version": "7.28.3-workflowStorageActions.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.28.3-workflowStorageActions.0", + "version": "7.28.3-workflowStorageActions.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 3edfc70bda..9663b388ee 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.28.3-workflowStorageActions.0", + "version": "7.28.3-workflowStorageActions.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From f873125822312d63d6ffa7782ddf0e8f0fa93ef1 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Wed, 8 Apr 2026 08:24:04 -0700 Subject: [PATCH 05/11] @labkey/components v7.29.2-workflowStorageActions.1 --- packages/components/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 0c4309f688..54f26afa40 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.28.3-workflowStorageActions.1", + "version": "7.29.2-workflowStorageActions.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.28.3-workflowStorageActions.1", + "version": "7.29.2-workflowStorageActions.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", From 29097b96a6454a8127ab55e44179af38d8c71ab2 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Wed, 8 Apr 2026 13:17:58 -0700 Subject: [PATCH 06/11] Fix for filterDividedOptions --- .../input/DividedOptionsRenderer.test.tsx | 8 ++++++-- .../forms/input/DividedOptionsRenderer.tsx | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx index f5d5434c56..7ccf16fbe2 100644 --- a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx +++ b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.test.tsx @@ -19,7 +19,11 @@ describe('filterDividedOptions', () => { ]); }); test('remove last divider', () => { - expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {label: undefined, isDivider: true}, {label: 'option2', value: 'o2'}], ['o2'])) + expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {isDivider: true}, {label: 'option2', value: 'o2'}], ['o2'])) + .toStrictEqual([ + {label: 'option1', value: 'o1'} + ]); + expect(filterDividedOptions([{label: 'option1', value: 'o1'}, {isDivider: true}, {label: 'option2', value: 'o2'}, {isDivider: true}, {label: 'option3', value: 'o3'}], ['o2', 'o3'])) .toStrictEqual([ {label: 'option1', value: 'o1'} ]); @@ -54,7 +58,7 @@ describe('filterDividedOptions', () => { {label: 'option3', value: 'o3'} ]); }); - test('remove multiple divider', () => { + test('remove multiple dividers', () => { expect(filterDividedOptions([ {label: 'option1', value: 'o1'}, {label: 'd1', isDivider: true}, diff --git a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx index 30025373b4..a80660915c 100644 --- a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx +++ b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx @@ -27,15 +27,21 @@ export function filterDividedOptions(allOptions, selectedOptions): any[] { .filter(option => selectedOptions.indexOf(option.value) == -1); const options = []; // remove dividers that are no longer dividing anything - let lastDivider = -1; + let hasPreviousSection = false; + let pendingDivider; notSelected.forEach((option, index) => { - if (!option.isDivider) + if (!option.isDivider) { + if (pendingDivider) { + options.push(pendingDivider); + pendingDivider = undefined; + } options.push(option); - else { - if (index-1 !== lastDivider && index !== notSelected.length-1) { - options.push(option); + hasPreviousSection = true; + } else { + if (hasPreviousSection) { + pendingDivider = option; + hasPreviousSection = false; } - lastDivider = index } }); return options; From 1cd9dd4ea340110c35a9cb352ef91734d8b370b1 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Wed, 8 Apr 2026 13:19:56 -0700 Subject: [PATCH 07/11] @labkey/components v7.29.3-workflowStorageActions.2 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 520cce3738..e54458ff6e 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.29.3-workflowStorageActions.1", + "version": "7.29.3-workflowStorageActions.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.29.3-workflowStorageActions.1", + "version": "7.29.3-workflowStorageActions.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 171c895a28..434e62fdc6 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.29.3-workflowStorageActions.1", + "version": "7.29.3-workflowStorageActions.2", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From 7ebfa6c1f80f6fc2ce8fab9fc6f82c025155f8b8 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Thu, 9 Apr 2026 07:57:01 -0700 Subject: [PATCH 08/11] @labkey/components v7.29.4-workflowStorageActions.2 --- packages/components/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 5fc69e55f4..8dea897461 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.29.3", + "version": "7.29.4-workflowStorageActions.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.29.3", + "version": "7.29.4-workflowStorageActions.2", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", From 52f9cb5d08f2a4ac5f28475aa13615c751d3b945 Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Thu, 9 Apr 2026 10:01:45 -0700 Subject: [PATCH 09/11] code cleanup --- .../components/forms/input/DividedOptionsRenderer.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx index a80660915c..39f2797299 100644 --- a/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx +++ b/packages/components/src/internal/components/forms/input/DividedOptionsRenderer.tsx @@ -2,7 +2,7 @@ import React, { FC, memo } from 'react'; interface DividedOptionsRendererProps { isDivider: boolean; - label: string; + label?: string; } // export for jest testing @@ -22,14 +22,14 @@ export function filterDividedOptions(allOptions, selectedOptions): any[] { if (!allOptions) return []; - const notSelected = allOptions + const notSelected = selectedOptions ? allOptions // remove options already selected - .filter(option => selectedOptions.indexOf(option.value) == -1); + .filter(option => selectedOptions.indexOf(option.value) === -1) : allOptions; const options = []; // remove dividers that are no longer dividing anything let hasPreviousSection = false; let pendingDivider; - notSelected.forEach((option, index) => { + notSelected.forEach((option) => { if (!option.isDivider) { if (pendingDivider) { options.push(pendingDivider); From 8eb4bf949cba78252576ef2adf34fbb26948dc6a Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Thu, 9 Apr 2026 11:09:07 -0700 Subject: [PATCH 10/11] @labkey/components v7.29.4-workflowStorageActions.3 --- packages/components/package-lock.json | 4 ++-- packages/components/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 8dea897461..e54993e85b 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.29.4-workflowStorageActions.2", + "version": "7.29.4-workflowStorageActions.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.29.4-workflowStorageActions.2", + "version": "7.29.4-workflowStorageActions.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 0f58129c3f..d4062c8d1b 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.29.4-workflowStorageActions.2", + "version": "7.29.4-workflowStorageActions.3", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From cec1dc304962fd0baba37c8dfaa2f9aef4d99b8c Mon Sep 17 00:00:00 2001 From: labkey-susanh Date: Thu, 9 Apr 2026 12:25:40 -0700 Subject: [PATCH 11/11] @labkey/components v7.29.5-workflowStorageActions.3 --- packages/components/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index e54993e85b..1a4403e246 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.29.4-workflowStorageActions.3", + "version": "7.29.5-workflowStorageActions.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.29.4-workflowStorageActions.3", + "version": "7.29.5-workflowStorageActions.3", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1",