diff --git a/packages/app-store/routing-forms/__tests__/config.test.ts b/packages/app-store/routing-forms/__tests__/config.test.ts index d1112f3911..53acc5ae7b 100644 --- a/packages/app-store/routing-forms/__tests__/config.test.ts +++ b/packages/app-store/routing-forms/__tests__/config.test.ts @@ -60,9 +60,13 @@ describe("Query Builder Config", () => { assertCommonStructure(FormFieldsBaseConfig); }); - it("should not support multiselect_contains and multiselect_not_contains - because they are not supported in Prisma for reporting(probably)", () => { - expect(FormFieldsBaseConfig.operators).not.toHaveProperty("multiselect_contains"); - expect(FormFieldsBaseConfig.operators).not.toHaveProperty("multiselect_not_contains"); + it("should not support multiselect_some_in and multiselect_not_some_in - because they are not supported in Prisma for reporting(probably)", () => { + expect(FormFieldsBaseConfig.types.multiselect.widgets.multiselect.operators).not.toContain( + "multiselect_some_in" + ); + expect(FormFieldsBaseConfig.types.multiselect.widgets.multiselect.operators).not.toContain( + "multiselect_not_some_in" + ); }); it("should support select_any_in, select_not_any_in, select_equals, select_not_equals - Verify both types.widgets and operators", () => { @@ -83,9 +87,23 @@ describe("Query Builder Config", () => { assertCommonStructure(AttributesBaseConfig); }); - it("should support multiselect_contains and multiselect_not_contains operators", () => { - expect(AttributesBaseConfig.operators).toHaveProperty("multiselect_contains"); - expect(AttributesBaseConfig.operators).toHaveProperty("multiselect_not_contains"); + it("should support multiselect_some_in operator for multiselect", () => { + expect(AttributesBaseConfig.types.multiselect.widgets.multiselect.operators).toContain( + "multiselect_some_in" + ); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const jsonLogic = AttributesBaseConfig.operators.multiselect_some_in.jsonLogic( + ["A"], + "multiselect_some_in", + ["A", "B"] + ); + + // Verifies that it is using some-in implementation + expect(jsonLogic).toEqual({ + some: [["A"], { in: [{ var: "" }, ["A", "B"]] }], + }); }); it("should support select_any_in, select_not_any_in, select_equals, select_not_equals - Verify both types.widgets and operators", () => { diff --git a/packages/app-store/routing-forms/components/react-awesome-query-builder/config/BasicConfig.ts b/packages/app-store/routing-forms/components/react-awesome-query-builder/config/BasicConfig.ts index c3876cb9ce..5f1edef063 100644 --- a/packages/app-store/routing-forms/components/react-awesome-query-builder/config/BasicConfig.ts +++ b/packages/app-store/routing-forms/components/react-awesome-query-builder/config/BasicConfig.ts @@ -179,21 +179,30 @@ const operators: Operators = { labelForFormat: "NOT IN", reversedOp: "select_any_in", }, + // We define this operator but use it conditionally for multiselect for Attributes only + multiselect_some_in: { + label: "Any in", + jsonLogic: (field: any, operator: any, vals: any) => { + return { + // Tested in jsonLogic.test.ts + some: [field, { in: [{ var: "" }, vals] }], + }; + }, + }, multiselect_equals: { - // TODO: Consider renaming it to "includes" or similar due to faulty implementation of jsonLogic and the operator is in use by users. - label: "Equals", - labelForFormat: "==", + label: "All in", reversedOp: "multiselect_not_equals", // jsonLogic2: "all-in", - jsonLogic: (field: any, op: any, vals: any) => ({ - // This is wrongly implemented as "includes". This isn't "equals". Because if field is ["a" ] and vals is ["a", "b"], it still matches. Expectation would probably be that it should be a strict match(["a", "b"] or ["b", "a"]) - all: [field, { in: [{ var: "" }, vals] }], - }), + jsonLogic: (field: any, op: any, vals: any, ...rest) => { + return { + // This is wrongly implemented as "includes". This isn't "equals". Because if field is ["a" ] and vals is ["a", "b"], it still matches. Expectation would probably be that it should be a strict match(["a", "b"] or ["b", "a"]) + all: [field, { in: [{ var: "" }, vals] }], + }; + }, }, multiselect_not_equals: { isNotOp: true, - label: "Not equals", - labelForFormat: "!=", + label: "Not all in", reversedOp: "multiselect_equals", }, some: { @@ -356,14 +365,7 @@ const types: Types = { defaultOperator: "multiselect_equals", widgets: { multiselect: { - operators: [ - "multiselect_equals", - "multiselect_not_equals", - // "is_empty", - // "is_not_empty", - "is_null", - "is_not_null", - ], + operators: ["multiselect_equals", "multiselect_not_equals", "is_null", "is_not_null"], }, }, }, diff --git a/packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.tsx b/packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.tsx index f1c66fe765..f731978b36 100644 --- a/packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.tsx +++ b/packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.tsx @@ -129,7 +129,15 @@ function getWidgets(_configFor: ConfigFor) { return widgets; } -function getTypes(_configFor: ConfigFor) { +function getTypes(configFor: ConfigFor) { + const multiSelectOperators = BasicConfig.types.multiselect.widgets.multiselect.operators || []; + + if (configFor === ConfigFor.Attributes) { + // Attributes don't need reporting at the moment. So, we can support multiselect_some_in operator for attributes. + // We could probably use them in FormFields later once they are supported through Prisma query as well + multiSelectOperators.push("multiselect_some_in"); + } + const types: Types = { ...BasicConfig.types, phone: { @@ -150,12 +158,7 @@ function getTypes(_configFor: ConfigFor) { ...BasicConfig.types.multiselect.widgets, multiselect: { ...BasicConfig.types.multiselect.widgets.multiselect, - operators: [ - ...(BasicConfig.types.multiselect.widgets.multiselect.operators || []), - // TODO: First verify the definition of multiselect_contains and multiselect_not_contains and then uncomment these operators - // "multiselect_contains", - // "multiselect_not_contains", - ], + operators: [...multiSelectOperators], }, }, }, @@ -167,58 +170,6 @@ function getOperators(configFor: ConfigFor) { // Clone to avoid mutating the original object const operators: Operators = { ...BasicConfig.operators, - // Attributes don't need reporting at the moment. So, we can support contains and not contains operators for attributes. - // We could probably use them in FormFields if they are supported through Prisma query as well. - ...(configFor === ConfigFor.Attributes - ? { - multiselect_contains: { - label: "Contains", - labelForFormat: "CONTAINS", - reversedOp: "multiselect_not_contains", - // jsonLogic2: "some-in", - jsonLogic: function (e, t, r) { - return { - some: [ - e, - { - in: [ - { - var: "", - }, - r, - ], - }, - ], - }; - }, - }, - multiselect_not_contains: { - isNotOp: !0, - label: "Not contains", - labelForFormat: "NOT CONTAINS", - reversedOp: "multiselect_contains", - // jsonLogic2: "!some-in", - jsonLogic: function (e, t, r) { - return { - "!": { - some: [ - e, - { - in: [ - { - var: "", - }, - r, - ], - }, - ], - }, - }; - }, - _jsonLogicIsExclamationOp: !0, - }, - } - : {}), }; return operators; diff --git a/packages/app-store/routing-forms/lib/jsonLogic.test.ts b/packages/app-store/routing-forms/lib/jsonLogic.test.ts index ed269991b9..19628df7ad 100644 --- a/packages/app-store/routing-forms/lib/jsonLogic.test.ts +++ b/packages/app-store/routing-forms/lib/jsonLogic.test.ts @@ -67,7 +67,45 @@ describe("jsonLogic", () => { }); }); - describe("all-in", () => { + describe("all-in -> used by multiselect_equals", () => { + it("should return true if all elements of the array in 'data' is in the jsonLogic 'in' array using case insensitivity", () => { + expect( + jsonLogic.apply( + { + and: [ + { + all: [{ var: "location" }, { in: [{ var: "" }, ["nevada", "abc"]] }], + }, + ], + }, + // Data + { + there: "no", + location: ["nevada"], + } + ) + ).toBe(true); + }); + + it("should return false if even one element of the array in 'data' is not in the jsonLogic 'in' array using case insensitivity", () => { + expect( + jsonLogic.apply( + { + and: [ + { + all: [{ var: "location" }, { in: [{ var: "" }, ["India", "Canada"]] }], + }, + ], + }, + // Data + { + there: "no", + location: ["India", "Australia"], + } + ) + ).toBe(false); + }); + it("should return true if the variable value is an array and contains the string", () => { expect( jsonLogic.apply( @@ -78,17 +116,18 @@ describe("jsonLogic", () => { }, ], }, + // Data { there: "no", - location: ["nevada"], + location: "nevada", } ) - ).toBe(true); + ).toBe(false); }); }); - describe("some-in", () => { - it("should return true if the variable value is an array and contains the string", () => { + describe("some-in -> used by multiselect_some_in", () => { + it("should return true if the only element of the array in 'data' is in the jsonLogic 'in' array using case insensitivity", () => { expect( jsonLogic.apply( { @@ -99,17 +138,68 @@ describe("jsonLogic", () => { { var: "", }, - ["nevada", "ABC"], + ["India", "USA"], ], }, ], }, + // data { there: "no", - location: ["nevada"], + location: ["india"], } ) ).toBe(true); }); + + it("should return true if at least one element of the array in 'data' is in the jsonLogic 'in' array using case insensitivity", () => { + expect( + jsonLogic.apply( + { + some: [ + { var: "location" }, + { + in: [ + { + var: "", + }, + ["India", "USA"], + ], + }, + ], + }, + // Data + { + there: "no", + location: ["india", "Australia"], + } + ) + ).toBe(true); + }); + + it("should return false if none of the elements of the array in 'data' is in the jsonLogic 'in' array using case insensitivity", () => { + expect( + jsonLogic.apply( + { + some: [ + { var: "location" }, + { + in: [ + { + var: "", + }, + ["India", "Canada"], + ], + }, + ], + }, + // Data + { + there: "no", + location: ["Australia", "Iceland"], + } + ) + ).toBe(false); + }); }); });