Skip to content

Commit 2d77730

Browse files
Add support for sample creation actions in workflow jobs (#1951)
1 parent faeb6d8 commit 2d77730

15 files changed

Lines changed: 85 additions & 39 deletions

File tree

packages/components/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@labkey/components",
3-
"version": "7.24.2",
3+
"version": "7.25.0",
44
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
55
"sideEffects": false,
66
"files": [

packages/components/releaseNotes/components.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# @labkey/components
22
Components, models, actions, and utility functions for LabKey applications and pages
33

4+
### version 7.25.0
5+
*Released*: 25 March 2026
6+
- Update `isAllSamplesSchema` to account for move of `JobInputSamples` to `workflow` schema
7+
- Add placement prop for `DisableableButton`
8+
- add `fitlerArrayToString` method in QueryModel utils
9+
- add `pronoun` utility method for the it/they or it/them text choices
10+
411
### version 7.24.2
512
*Released*: 25 March 2026
613
- GitHub Issue 955: limit text choice option length to 200 characters

packages/components/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
makeCommaSeparatedString,
7070
parseCsvString,
7171
parseScientificInt,
72+
pronoun,
7273
quoteValueWithDelimiters,
7374
setIsTestEnv,
7475
uncapitalizeFirstChar,
@@ -541,6 +542,7 @@ import {
541542
createOrderedSnapshotSelectionKey,
542543
createSnapshotSelectionKey,
543544
createSnapshotSelectionKeyStr,
545+
filterArrayToString,
544546
runDetailsColumnsForQueryModel,
545547
} from './public/QueryModel/utils';
546548
import { CONFIRM_MESSAGE, useRouteLeave } from './internal/util/RouteLeave';
@@ -1300,6 +1302,7 @@ export {
13001302
FileInput,
13011303
FileTree,
13021304
FilterAction,
1305+
filterArrayToString,
13031306
FilterCriteriaRenderer,
13041307
FilterStatus,
13051308
FIND_BY_IDS_QUERY_PARAM,
@@ -1594,6 +1597,7 @@ export {
15941597
ProductMenuModel,
15951598
ProductNavigationMenu,
15961599
Progress,
1600+
pronoun,
15971601
pushParameters,
15981602
QUERY_UPDATE_AUDIT_QUERY,
15991603
QueryColumn,

packages/components/src/internal/app/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,6 @@ export const FREEZER_MANAGER_APP_PROPERTIES: AppProperties = {
198198
export const APPLICATION_PROPERTIES = {
199199
[FREEZER_MANAGER_PRODUCT_ID]: FREEZER_MANAGER_APP_PROPERTIES,
200200
[SAMPLE_MANAGER_PRODUCT_ID]: SAMPLE_MANAGER_APP_PROPERTIES,
201-
[LIMS_PRODUCT_ID] : LIMS_APP_PROPERTIES,
201+
[LIMS_PRODUCT_ID]: LIMS_APP_PROPERTIES,
202202
[BIOLOGICS_PRODUCT_ID]: BIOLOGICS_APP_PROPERTIES
203-
}
203+
};

packages/components/src/internal/components/buttons/DisableableButton.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { memo, FC, useMemo, PropsWithChildren } from 'react';
1+
import React, { FC, memo, PropsWithChildren, useMemo } from 'react';
22

33
import { createPortal } from 'react-dom';
44

@@ -10,23 +10,24 @@ interface Props extends PropsWithChildren {
1010
className?: string;
1111
disabledMsg?: string;
1212
onClick?: () => void;
13+
placement?: 'bottom' | 'left' | 'right' | 'top';
1314
title?: string;
1415
}
1516

1617
export const DisableableButton: FC<Props> = memo(props => {
17-
const { bsStyle = 'default', children, className = '', disabledMsg, onClick, title } = props;
18+
const { bsStyle = 'default', children, className = '', disabledMsg, onClick, placement = 'bottom', title } = props;
1819
const { onMouseEnter, onMouseLeave, portalEl, show, targetRef } = useOverlayTriggerState<HTMLButtonElement>(
1920
'disabled-button-overlay',
2021
disabledMsg !== undefined,
2122
false
2223
);
2324
const popover = useMemo(
2425
() => (
25-
<Popover id="disabled-button-popover" title={title} placement="bottom" targetRef={targetRef}>
26+
<Popover id="disabled-button-popover" placement={placement} targetRef={targetRef} title={title}>
2627
{disabledMsg}
2728
</Popover>
2829
),
29-
[disabledMsg, targetRef, title]
30+
[disabledMsg, placement, targetRef, title]
3031
);
3132

3233
// Note: we use onPointerEnter/Leave so events propagate when the button is disabled
@@ -37,8 +38,8 @@ export const DisableableButton: FC<Props> = memo(props => {
3738
onClick={onClick}
3839
onPointerEnter={onMouseEnter}
3940
onPointerLeave={onMouseLeave}
40-
type="button"
4141
ref={targetRef}
42+
type="button"
4243
>
4344
{children}
4445
{show && createPortal(popover, portalEl)}

packages/components/src/internal/components/entities/EntityMoveConfirmationModal.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('EntityMoveConfirmationModal', () => {
4444
expect(document.body.textContent).toContain('This is an error message.');
4545
});
4646

47-
test('no insert perm to any conatiners', async () => {
47+
test('no insert perm to any containers', async () => {
4848
await act(async () => {
4949
renderWithAppContext(<EntityMoveConfirmationModal {...getDefaultProps()} />, {
5050
serverContext: DEFAULT_SERVER_CONTEXT,

packages/components/src/internal/components/entities/EntityMoveModal.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { LoadingSpinner } from '../base/LoadingSpinner';
66
import { Alert } from '../base/Alert';
77
import { Container } from '../base/models/Container';
88
import { useNotificationsContext } from '../notifications/NotificationsContext';
9-
import { capitalizeFirstChar, makeCommaSeparatedString } from '../../util/utils';
9+
import { capitalizeFirstChar, makeCommaSeparatedString, pronoun } from '../../util/utils';
1010
import { HelpLink, MOVE_SAMPLES_TOPIC } from '../../util/helpLinks';
1111
import { isLoading, LoadingState } from '../../../public/LoadingState';
1212
import { AppURL } from '../../url/AppURL';
@@ -241,12 +241,12 @@ export const getMoveConfirmationProperties = (
241241
text = `${text} ${noun} will be moved.`;
242242
} else {
243243
const cannotMoveNoun = numCannotMove === 1 ? nounSingular : nounPlural;
244-
const pronoun = numCannotMove === 1 ? 'it' : 'they';
244+
const _pronoun = pronoun(numCannotMove, 'they');
245245
const verb = numCannotMove === 1 ? 'has' : 'have';
246246
const parts = [];
247247
if (numNotPermitted > 0) parts.push('you lack the proper permissions');
248-
if (numNotAllowed > 0) parts.push(`${pronoun} ${verb} a status or related data that prevents moving`);
249-
if (numMissing > 0) parts.push(`${pronoun} may have been deleted`);
248+
if (numNotAllowed > 0) parts.push(`${_pronoun} ${verb} a status or related data that prevents moving`);
249+
if (numMissing > 0) parts.push(`${_pronoun} may have been deleted`);
250250
const error = makeCommaSeparatedString(parts, ', or ', '.');
251251

252252
if (numCanMove === 0) {

packages/components/src/internal/components/entities/actions.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { getSelected, getSelectedDataDeprecated } from '../../actions';
1515

1616
import { SampleOperation } from '../samples/constants';
1717
import { SchemaQuery } from '../../../public/SchemaQuery';
18-
import { getFilterForSampleOperation, isSamplesSchema } from '../samples/utils';
18+
import { getFilterForSampleOperation, isSamplesSchema, isWorkflowInputSamplesSchema } from '../samples/utils';
1919
import { getQueryDetails, getRequestAuditDetail, importData, InsertOptions, selectDistinctRows } from '../../query/api';
2020
import { caseInsensitive, generateId } from '../../util/utils';
2121
import { request } from '../../request';
@@ -326,6 +326,7 @@ function resolveSampleParentTypes(
326326
* @param creationType
327327
* @param isItemSamples
328328
* @param targetQueryName
329+
* @param jobId
329330
*/
330331
async function initParents(
331332
initialParents: string[],
@@ -334,7 +335,8 @@ async function initParents(
334335
isSnapshotSelection: boolean,
335336
creationType?: EntityCreationType,
336337
isItemSamples?: boolean,
337-
targetQueryName?: string
338+
targetQueryName?: string,
339+
jobId?: string,
338340
): Promise<List<EntityParentType>> {
339341
const isAliquotParent = creationType === EntityCreationType.Aliquots;
340342

@@ -354,6 +356,9 @@ async function initParents(
354356
Filter.create('RowId', selectionResponse.selected, Filter.Types.IN),
355357
Filter.create('Container', insertPermissionContainers, Filter.Types.IN),
356358
];
359+
if (isWorkflowInputSamplesSchema(schemaQuery) && jobId) {
360+
filterArray.push(Filter.create('JobId', jobId, Filter.Types.EQUAL));
361+
}
357362
const opFilter = getFilterForSampleOperation(SampleOperation.EditLineage);
358363
if (opFilter) {
359364
filterArray.push(opFilter);
@@ -489,7 +494,8 @@ export async function getChosenParentData(
489494
isSnapshotSelection,
490495
creationType,
491496
isItemSamples,
492-
targetQueryName
497+
targetQueryName,
498+
model.jobId
493499
);
494500

495501
// if we have an initial parent, we want to start with a row in the grid (entityCount = 1) otherwise we start with none
@@ -620,7 +626,6 @@ export async function getFolderConfigurableEntityTypeOptions(
620626
* @param targetQueryName the name of the listing schema query that represents the initial target for creation.
621627
* @param allowParents are parents of this entity type allowed or not
622628
* @param isItemSamples use the selectionKey from inventory.items table to query sample parents
623-
* @param combineParentTypes
624629
*/
625630
export function getEntityTypeData(
626631
model: EntityIdCreationModel,

packages/components/src/internal/components/entities/models.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ export class EntityIdCreationModel extends Record({
193193
initialEntityType: undefined,
194194
isError: false,
195195
isInit: false,
196+
jobId: undefined,
196197
originalParents: Array<string>(),
197198
parentOptions: Map<string, List<IParentOption>>(),
198199
entityParents: Map<string, List<EntityParentType>>(),
@@ -211,6 +212,7 @@ export class EntityIdCreationModel extends Record({
211212
declare initialEntityType: any;
212213
declare isError: boolean;
213214
declare isInit: boolean;
215+
declare jobId: string;
214216
declare originalParents: string[]; // taken from the query string
215217
declare parentOptions: Map<string, List<IParentOption>>; // map from query name to the options for the different types of parents allowed
216218
declare entityParents: Map<string, List<EntityParentType>>; // map from query name to the parents already selected for that query

0 commit comments

Comments
 (0)