From 5f551bb7c4046a3697b967d746dc7d38a0015e4c Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 12 Jan 2026 10:54:19 -0800 Subject: [PATCH 1/4] Multi value text choices --- CHANGELOG.md | 4 +++ package-lock.json | 4 +-- package.json | 2 +- src/labkey/filter/Types.ts | 60 +++++++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edd6caa..b108827d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 1.X - 2026-01-X +- Add Array filter types + - ARRAY_CONTAINS_ALL, ARRAY_CONTAINS_ANY, ARRAY_CONTAINS_EXACT, ARRAY_ISEMPTY, ARRAY_ISNOTEMPTY + ### 1.44.1 - 2026-01-07 - Lineage: add "restricted" property diff --git a/package-lock.json b/package-lock.json index 45b738ad..927d82de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/api", - "version": "1.44.1", + "version": "1.45.0-fb-mvtc.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/api", - "version": "1.44.1", + "version": "1.45.0-fb-mvtc.2", "license": "Apache-2.0", "devDependencies": { "@babel/core": "7.28.5", diff --git a/package.json b/package.json index 17a36fca..7aab6ad2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/api", - "version": "1.44.1", + "version": "1.45.0-fb-mvtc.2", "description": "JavaScript client API for LabKey Server", "scripts": { "build": "npm run build:dist && npm run build:docs", diff --git a/src/labkey/filter/Types.ts b/src/labkey/filter/Types.ts index 60f14e12..4d3d73c1 100644 --- a/src/labkey/filter/Types.ts +++ b/src/labkey/filter/Types.ts @@ -128,6 +128,39 @@ export const Types: Record = { // These operators require a data value // + ARRAY_CONTAINS_ALL: registerFilterType( + 'Contains All', + null, + 'arraycontainsall', + true, + ';', + 'Contains All Of' + ), + ARRAY_CONTAINS_ANY: registerFilterType( + 'Contains Any', + null, + 'arraycontainsany', + true, + ';', + 'Contains At Least One Of' + ), + ARRAY_CONTAINS_EXACT: registerFilterType( + 'Contains Exactly', + null, + 'arraymatches', + true, + ';', + 'Contains Exactly the Selected Values' + ), + ARRAY_CONTAINS_NONE: registerFilterType( + 'Contains None', + null, + 'arraycontainsnone', + true, + ';', + 'Contains None Of' + ), + EQUAL, DATE_EQUAL: registerFilterType( EQUAL.getDisplayText(), @@ -271,6 +304,29 @@ export const Types: Record = { // These are the 'no data value' operators // + ARRAY_ISEMPTY: registerFilterType( + 'Is Empty', + null, + 'arrayisempty', + false, + undefined, + undefined, + undefined, + undefined, + false, + ), + ARRAY_ISNOTEMPTY: registerFilterType( + 'Is Not Empty', + null, + 'arrayisnotempty', + false, + undefined, + undefined, + undefined, + undefined, + false, + ), + // NOTE: This type, for better or worse, uses empty string as it's urlSuffix. // The result is a filter that is encoded as ".~=". HAS_ANY_VALUE: registerFilterType('Has Any Value', null, ''), @@ -359,9 +415,10 @@ export const Types: Record = { EXP_LINEAGE_OF: registerFilterType('In The Lineage Of', null, 'exp:lineageof', true, ',', ' in the lineage of'), }; -export type JsonType = 'boolean' | 'date' | 'float' | 'int' | 'string' | 'time'; +export type JsonType = 'array' | 'boolean' | 'date' | 'float' | 'int' | 'string' | 'time'; export const TYPES_BY_JSON_TYPE: Record = { + array: [Types.ARRAY_ISEMPTY, Types.ARRAY_ISNOTEMPTY, Types.ARRAY_CONTAINS_ALL, Types.ARRAY_CONTAINS_NONE, Types.ARRAY_CONTAINS_ANY, Types.ARRAY_CONTAINS_EXACT], boolean: [Types.HAS_ANY_VALUE, Types.EQUAL, Types.NEQ_OR_NULL, Types.ISBLANK, Types.NONBLANK], date: [ Types.DATE_EQUAL, @@ -440,6 +497,7 @@ export const TYPES_BY_JSON_TYPE: Record = { // TODO: Update to Record export const TYPES_BY_JSON_TYPE_DEFAULT: Record = { + array: Types.ARRAY_CONTAINS_ALL, boolean: Types.EQUAL, date: Types.DATE_EQUAL, float: Types.EQUAL, From 4a7592ffcae8b40276648aff9d33d6e0101e95c9 Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 12 Jan 2026 12:10:30 -0800 Subject: [PATCH 2/4] fix test --- src/test/data/filter_types_snapshot.json | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/test/data/filter_types_snapshot.json b/src/test/data/filter_types_snapshot.json index cd3e6334..4ee2bb8f 100644 --- a/src/test/data/filter_types_snapshot.json +++ b/src/test/data/filter_types_snapshot.json @@ -1,4 +1,106 @@ { + "ARRAY_CONTAINS_ALL": { + "getDisplaySymbol": null, + "getDisplayText": "Contains All", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Contains All Of", + "getMultiValueFilter": null, + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": ";", + "getOpposite": null, + "getSingleValueFilter": "undefined", + "getURLParameterValue": "undefined", + "getURLSuffix": "arraycontainsall", + "isDataValueRequired": true, + "isMultiValued": true, + "isTableWise": false + }, + "ARRAY_CONTAINS_ANY": { + "getDisplaySymbol": null, + "getDisplayText": "Contains Any", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Contains At Least One Of", + "getMultiValueFilter": null, + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": ";", + "getOpposite": null, + "getSingleValueFilter": "undefined", + "getURLParameterValue": "undefined", + "getURLSuffix": "arraycontainsany", + "isDataValueRequired": true, + "isMultiValued": true, + "isTableWise": false + }, + "ARRAY_CONTAINS_EXACT": { + "getDisplaySymbol": null, + "getDisplayText": "Contains Exactly", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Contains Exactly the Selected Values", + "getMultiValueFilter": null, + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": ";", + "getOpposite": null, + "getSingleValueFilter": "undefined", + "getURLParameterValue": "undefined", + "getURLSuffix": "arraymatches", + "isDataValueRequired": true, + "isMultiValued": true, + "isTableWise": false + }, + "ARRAY_CONTAINS_NONE": { + "getDisplaySymbol": null, + "getDisplayText": "Contains None", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Contains None Of", + "getMultiValueFilter": null, + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": ";", + "getOpposite": null, + "getSingleValueFilter": "undefined", + "getURLParameterValue": "undefined", + "getURLSuffix": "arraycontainsnone", + "isDataValueRequired": true, + "isMultiValued": true, + "isTableWise": false + }, + "ARRAY_ISEMPTY": { + "getDisplaySymbol": null, + "getDisplayText": "Is Empty", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Is Empty", + "getMultiValueFilter": "undefined", + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": null, + "getOpposite": null, + "getSingleValueFilter": "arrayisempty", + "getURLParameterValue": "", + "getURLSuffix": "arrayisempty", + "isDataValueRequired": false, + "isMultiValued": false, + "isTableWise": false + }, + "ARRAY_ISNOTEMPTY": { + "getDisplaySymbol": null, + "getDisplayText": "Is Not Empty", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Is Not Empty", + "getMultiValueFilter": "undefined", + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": null, + "getOpposite": null, + "getSingleValueFilter": "arrayisnotempty", + "getURLParameterValue": "", + "getURLSuffix": "arrayisnotempty", + "isDataValueRequired": false, + "isMultiValued": false, + "isTableWise": false + }, "BETWEEN": { "getDisplaySymbol": null, "getDisplayText": "Between", From e3b94d3a3b1c89b9e571c3147e37d730196ab81b Mon Sep 17 00:00:00 2001 From: XingY Date: Mon, 12 Jan 2026 19:09:35 -0800 Subject: [PATCH 3/4] support filters --- package-lock.json | 4 ++-- package.json | 2 +- src/labkey/filter/Types.ts | 18 +++++++++++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 927d82de..1f03519f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/api", - "version": "1.45.0-fb-mvtc.2", + "version": "1.45.0-fb-mvtc.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/api", - "version": "1.45.0-fb-mvtc.2", + "version": "1.45.0-fb-mvtc.4", "license": "Apache-2.0", "devDependencies": { "@babel/core": "7.28.5", diff --git a/package.json b/package.json index 7aab6ad2..07055c26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/api", - "version": "1.45.0-fb-mvtc.2", + "version": "1.45.0-fb-mvtc.4", "description": "JavaScript client API for LabKey Server", "scripts": { "build": "npm run build:dist && npm run build:docs", diff --git a/src/labkey/filter/Types.ts b/src/labkey/filter/Types.ts index 4d3d73c1..a992ea64 100644 --- a/src/labkey/filter/Types.ts +++ b/src/labkey/filter/Types.ts @@ -133,7 +133,7 @@ export const Types: Record = { null, 'arraycontainsall', true, - ';', + ',', 'Contains All Of' ), ARRAY_CONTAINS_ANY: registerFilterType( @@ -141,7 +141,7 @@ export const Types: Record = { null, 'arraycontainsany', true, - ';', + ',', 'Contains At Least One Of' ), ARRAY_CONTAINS_EXACT: registerFilterType( @@ -149,15 +149,23 @@ export const Types: Record = { null, 'arraymatches', true, - ';', + ',', 'Contains Exactly the Selected Values' ), + ARRAY_CONTAINS_NOT_EXACT: registerFilterType( + 'Does Not Contain Exactly', + null, + 'arraynotmatches', + true, + ',', + 'Does Not Contains Exactly the Selected Values' + ), ARRAY_CONTAINS_NONE: registerFilterType( 'Contains None', null, 'arraycontainsnone', true, - ';', + ',', 'Contains None Of' ), @@ -418,7 +426,7 @@ export const Types: Record = { export type JsonType = 'array' | 'boolean' | 'date' | 'float' | 'int' | 'string' | 'time'; export const TYPES_BY_JSON_TYPE: Record = { - array: [Types.ARRAY_ISEMPTY, Types.ARRAY_ISNOTEMPTY, Types.ARRAY_CONTAINS_ALL, Types.ARRAY_CONTAINS_NONE, Types.ARRAY_CONTAINS_ANY, Types.ARRAY_CONTAINS_EXACT], + array: [Types.ARRAY_ISEMPTY, Types.ARRAY_ISNOTEMPTY, Types.ARRAY_CONTAINS_ALL, Types.ARRAY_CONTAINS_NONE, Types.ARRAY_CONTAINS_ANY, Types.ARRAY_CONTAINS_EXACT, Types.ARRAY_CONTAINS_NOT_EXACT], boolean: [Types.HAS_ANY_VALUE, Types.EQUAL, Types.NEQ_OR_NULL, Types.ISBLANK, Types.NONBLANK], date: [ Types.DATE_EQUAL, From 6be2fbe48bb7fa5242b62de405542e6c844728ee Mon Sep 17 00:00:00 2001 From: XingY Date: Tue, 13 Jan 2026 10:51:40 -0800 Subject: [PATCH 4/4] update snapshot --- src/test/data/filter_types_snapshot.json | 25 ++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/test/data/filter_types_snapshot.json b/src/test/data/filter_types_snapshot.json index 4ee2bb8f..103b7a3d 100644 --- a/src/test/data/filter_types_snapshot.json +++ b/src/test/data/filter_types_snapshot.json @@ -7,7 +7,7 @@ "getMultiValueFilter": null, "getMultiValueMaxOccurs": "undefined", "getMultiValueMinOccurs": "undefined", - "getMultiValueSeparator": ";", + "getMultiValueSeparator": ",", "getOpposite": null, "getSingleValueFilter": "undefined", "getURLParameterValue": "undefined", @@ -24,7 +24,7 @@ "getMultiValueFilter": null, "getMultiValueMaxOccurs": "undefined", "getMultiValueMinOccurs": "undefined", - "getMultiValueSeparator": ";", + "getMultiValueSeparator": ",", "getOpposite": null, "getSingleValueFilter": "undefined", "getURLParameterValue": "undefined", @@ -41,7 +41,7 @@ "getMultiValueFilter": null, "getMultiValueMaxOccurs": "undefined", "getMultiValueMinOccurs": "undefined", - "getMultiValueSeparator": ";", + "getMultiValueSeparator": ",", "getOpposite": null, "getSingleValueFilter": "undefined", "getURLParameterValue": "undefined", @@ -58,7 +58,7 @@ "getMultiValueFilter": null, "getMultiValueMaxOccurs": "undefined", "getMultiValueMinOccurs": "undefined", - "getMultiValueSeparator": ";", + "getMultiValueSeparator": ",", "getOpposite": null, "getSingleValueFilter": "undefined", "getURLParameterValue": "undefined", @@ -67,6 +67,23 @@ "isMultiValued": true, "isTableWise": false }, + "ARRAY_CONTAINS_NOT_EXACT": { + "getDisplaySymbol": null, + "getDisplayText": "Does Not Contain Exactly", + "getLabKeySqlOperator": "undefined", + "getLongDisplayText": "Does Not Contains Exactly the Selected Values", + "getMultiValueFilter": null, + "getMultiValueMaxOccurs": "undefined", + "getMultiValueMinOccurs": "undefined", + "getMultiValueSeparator": ",", + "getOpposite": null, + "getSingleValueFilter": "undefined", + "getURLParameterValue": "undefined", + "getURLSuffix": "arraynotmatches", + "isDataValueRequired": true, + "isMultiValued": true, + "isTableWise": false + }, "ARRAY_ISEMPTY": { "getDisplaySymbol": null, "getDisplayText": "Is Empty",