From a07424853c0fd267d1b02f3d25f0abf14c45c979 Mon Sep 17 00:00:00 2001 From: alanv Date: Wed, 2 Jul 2025 09:48:55 -0500 Subject: [PATCH 1/3] Fix Issue 53141 --- .../components/releaseNotes/components.md | 4 + .../src/internal/FilterCriteriaModal.tsx | 21 ++- .../assay/AssayDesignerPanels.tsx | 121 +++++++++--------- 3 files changed, 83 insertions(+), 63 deletions(-) diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index b3400b15be..5b2c583b20 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 6.53.2 +*Released*: 1 July 2025 +* Issue 53141: Should set a dirty bit when setting or updating the hit selection criteria for an assay + ### version 6.53.1 *Released*: 3 July 2025 - Issue 53153: Disable value validation for expInput, aliquotParent columns diff --git a/packages/components/src/internal/FilterCriteriaModal.tsx b/packages/components/src/internal/FilterCriteriaModal.tsx index 772887ef76..454aa70a4a 100644 --- a/packages/components/src/internal/FilterCriteriaModal.tsx +++ b/packages/components/src/internal/FilterCriteriaModal.tsx @@ -12,10 +12,10 @@ import { useLoadableState } from './useLoadableState'; import { LoadingSpinner } from './components/base/LoadingSpinner'; import { ChoicesListItem } from './components/base/ChoicesListItem'; import { FilterExpressionView } from './components/search/FilterExpressionView'; -import { useAppContext } from './AppContext'; import { FilterCriteriaColumns } from './components/assay/models'; import { AssayProtocolModel } from './components/domainproperties/assay/models'; import { Alert } from './components/base/Alert'; +import { ComponentsAPIWrapper, getDefaultAPIWrapper } from './APIWrapper'; type BaseFilterCriteriaField = Omit; interface FilterCriteriaField extends BaseFilterCriteriaField { @@ -100,16 +100,21 @@ FilterCriteriaChoice.displayName = 'FilterCriteriaChoice'; * openTo: The propertyId of the domain field you want to open the modal to */ interface Props { + api?: ComponentsAPIWrapper; onClose: () => void; onSave: (filterCriteria: FilterCriteriaMap) => void; openTo?: number; protocolModel: AssayProtocolModel; } -export const FilterCriteriaModal: FC = memo(({ onClose, onSave, openTo, protocolModel }) => { - const { api } = useAppContext(); +export const FilterCriteriaModal: FC = memo(props => { + // Note: we cannot fetch api from useAppContext, because this component can be rendered in LKS, which does not set + // up an AppContext + const { api = getDefaultAPIWrapper(), onClose, onSave, openTo, protocolModel } = props; const { protocolId, container } = protocolModel; const domain = useMemo(() => protocolModel.getDomainByNameSuffix('Data'), [protocolModel]); + // Intentionally not using withRouteLeave, that is handled above this component, after onSave is called + const [isDirty, setIsDirty] = useState(false); const [filterCriteria, setFilterCriteria] = useState(() => { return domain.fields.reduce((result, field) => { if (field.filterCriteria) result.set(field.propertyId, [...field.filterCriteria]); @@ -135,6 +140,7 @@ export const FilterCriteriaModal: FC = memo(({ onClose, onSave, openTo, p const onFieldFilterUpdate = useCallback( (newFilters: Filter.IFilter[]) => { + setIsDirty(true); setFilterCriteria(current => { const filterCriteriaField = filterCriteriaFields.find(field => field.propertyId === selectedFieldId); // Use the referencePropertyId if it exists, because all filterCriteria are stored on the parent field @@ -186,7 +192,14 @@ export const FilterCriteriaModal: FC = memo(({ onClose, onSave, openTo, p const hasFields = fieldsToRender !== undefined && fieldsToRender.length > 0; return ( - + {loading && } {!loading && !error && (
diff --git a/packages/components/src/internal/components/domainproperties/assay/AssayDesignerPanels.tsx b/packages/components/src/internal/components/domainproperties/assay/AssayDesignerPanels.tsx index 81af223700..1d4ebdf723 100644 --- a/packages/components/src/internal/components/domainproperties/assay/AssayDesignerPanels.tsx +++ b/packages/components/src/internal/components/domainproperties/assay/AssayDesignerPanels.tsx @@ -123,27 +123,27 @@ const AssayDomainForm: FC = memo(props => { ]); return (
{domain.description}
@@ -336,40 +336,43 @@ export class AssayDesignerPanelsImpl extends React.PureComponent { }; saveFilterCriteria = (filterCriteria: FilterCriteriaMap) => { - this.setState(current => { - const protocolModel = current.protocolModel; - const resultsIndex = current.protocolModel.domains.findIndex((domain: DomainDesign): boolean => - domain.isNameSuffixMatch('Data') - ); - const domains = current.protocolModel.domains; - let resultsDomain = domains.get(resultsIndex); - // Clear the existing values first - let fields = resultsDomain.fields.map(f => f.set('filterCriteria', []) as DomainField).toList(); - - filterCriteria.forEach((fieldCriteria, propertyId) => { - const domainFieldIdx = fields.findIndex(d => d.propertyId === propertyId); - - if (domainFieldIdx < 0) { - console.warn(`Unable to find domain field with property id ${propertyId}`); - return; - } + this.setState( + current => { + const protocolModel = current.protocolModel; + const resultsIndex = current.protocolModel.domains.findIndex((domain: DomainDesign): boolean => + domain.isNameSuffixMatch('Data') + ); + const domains = current.protocolModel.domains; + let resultsDomain = domains.get(resultsIndex); + // Clear the existing values first + let fields = resultsDomain.fields.map(f => f.set('filterCriteria', []) as DomainField).toList(); - let domainField = fields.get(domainFieldIdx); - domainField = domainField.set('filterCriteria', fieldCriteria) as DomainField; - fields = fields.set(domainFieldIdx, domainField); - }); + filterCriteria.forEach((fieldCriteria, propertyId) => { + const domainFieldIdx = fields.findIndex(d => d.propertyId === propertyId); - resultsDomain = resultsDomain.set('fields', fields) as DomainDesign; + if (domainFieldIdx < 0) { + console.warn(`Unable to find domain field with property id ${propertyId}`); + return; + } - return { - modalOpen: false, - openTo: undefined, - protocolModel: protocolModel.set( - 'domains', - protocolModel.domains.set(resultsIndex, resultsDomain) - ) as AssayProtocolModel, - }; - }); + let domainField = fields.get(domainFieldIdx); + domainField = domainField.set('filterCriteria', fieldCriteria) as DomainField; + fields = fields.set(domainFieldIdx, domainField); + }); + + resultsDomain = resultsDomain.set('fields', fields) as DomainDesign; + + return { + modalOpen: false, + openTo: undefined, + protocolModel: protocolModel.set( + 'domains', + protocolModel.domains.set(resultsIndex, resultsDomain) + ) as AssayProtocolModel, + }; + }, + () => this.props.onChange?.(this.state.protocolModel) + ); }; togglePropertiesPanel = (collapsed, callback): void => { @@ -409,32 +412,32 @@ export class AssayDesignerPanelsImpl extends React.PureComponent { return ( {/* Note: We cannot filter this array because onChange needs the correct index for each domain */} {protocolModel.domains.toArray().map((domain, i) => { @@ -445,16 +448,16 @@ export class AssayDesignerPanelsImpl extends React.PureComponent { @@ -463,8 +466,8 @@ export class AssayDesignerPanelsImpl extends React.PureComponent { {modalOpen && ( )} @@ -472,8 +475,8 @@ export class AssayDesignerPanelsImpl extends React.PureComponent { {appPropertiesOnly && allowFolderExclusion && ( Date: Thu, 3 Jul 2025 13:57:51 -0500 Subject: [PATCH 2/3] Bump version --- 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 ffb1076379..98524082c8 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "6.53.1", + "version": "6.53.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "6.53.1", + "version": "6.53.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 8127bf64b1..b9be0f31e5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "6.53.1", + "version": "6.53.2", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ From dfea7f770372ad6bf15c68bb3c13344ebb91f6fd Mon Sep 17 00:00:00 2001 From: alanv Date: Thu, 3 Jul 2025 14:04:28 -0500 Subject: [PATCH 3/3] Fix date --- packages/components/releaseNotes/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 5b2c583b20..d6db1cd6d0 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -2,7 +2,7 @@ Components, models, actions, and utility functions for LabKey applications and pages ### version 6.53.2 -*Released*: 1 July 2025 +*Released*: 3 July 2025 * Issue 53141: Should set a dirty bit when setting or updating the hit selection criteria for an assay ### version 6.53.1