diff --git a/frontend/src/metabase-lib/v1/parameters/utils/filters.js b/frontend/src/metabase-lib/v1/parameters/utils/filters.ts similarity index 72% rename from frontend/src/metabase-lib/v1/parameters/utils/filters.js rename to frontend/src/metabase-lib/v1/parameters/utils/filters.ts index b1430a59b0ae731b6ccd962a28e9f4b73588effc..dcce0f0c0a32c5facabb05b2196e24771d3f0c08 100644 --- a/frontend/src/metabase-lib/v1/parameters/utils/filters.js +++ b/frontend/src/metabase-lib/v1/parameters/utils/filters.ts @@ -1,12 +1,18 @@ import * as Lib from "metabase-lib"; +import type Dimension from "metabase-lib/v1/Dimension"; +import type Field from "metabase-lib/v1/metadata/Field"; import { getParameterOperatorName } from "metabase-lib/v1/parameters/utils/operators"; import { - getParameterType, getParameterSubType, + getParameterType, } from "metabase-lib/v1/parameters/utils/parameter-type"; import TemplateTagVariable from "metabase-lib/v1/variables/TemplateTagVariable"; +import type Variable from "metabase-lib/v1/variables/Variable"; +import type { Parameter, TemplateTag } from "metabase-types/api"; -export function fieldFilterForParameter(parameter) { +export function fieldFilterForParameter( + parameter: Parameter | string, +): (field: Field) => boolean { const type = getParameterType(parameter); switch (type) { case "date": @@ -26,7 +32,11 @@ export function fieldFilterForParameter(parameter) { return () => false; } -export function columnFilterForParameter(query, stageIndex, parameter) { +export function columnFilterForParameter( + query: Lib.Query, + stageIndex: number, + parameter: Parameter | string, +): (column: Lib.ColumnMetadata) => boolean { const type = getParameterType(parameter); switch (type) { @@ -50,17 +60,19 @@ export function columnFilterForParameter(query, stageIndex, parameter) { return () => false; } -export function dimensionFilterForParameter(parameter) { +export function dimensionFilterForParameter(parameter: Parameter | string) { const fieldFilter = fieldFilterForParameter(parameter); - return dimension => fieldFilter(dimension.field()); + return (dimension: Dimension) => fieldFilter(dimension.field()); } -export function getTagOperatorFilterForParameter(parameter) { +export function getTagOperatorFilterForParameter( + parameter: Parameter | string, +) { const subtype = getParameterSubType(parameter); const parameterOperatorName = getParameterOperatorName(subtype); - return tag => { - const { "widget-type": widgetType } = tag; + return (tag: TemplateTag) => { + const { "widget-type": widgetType = "" } = tag; const subtype = getParameterSubType(widgetType); const tagOperatorName = getParameterOperatorName(subtype); @@ -68,9 +80,9 @@ export function getTagOperatorFilterForParameter(parameter) { }; } -export function variableFilterForParameter(parameter) { +export function variableFilterForParameter(parameter: Parameter | string) { const tagFilter = tagFilterForParameter(parameter); - return variable => { + return (variable: Variable) => { if (variable instanceof TemplateTagVariable) { const tag = variable.tag(); return tag ? tagFilter(tag) : false; @@ -79,7 +91,9 @@ export function variableFilterForParameter(parameter) { }; } -function tagFilterForParameter(parameter) { +function tagFilterForParameter( + parameter: Parameter | string, +): (tag: TemplateTag) => boolean { const type = getParameterType(parameter); const subtype = getParameterSubType(parameter); const operator = getParameterOperatorName(subtype); diff --git a/frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.js b/frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.ts similarity index 66% rename from frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.js rename to frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.ts index fe2c1bf26d22c8a3beea929facce42f54be98f66..656d37036b6d5d6294c6856bcd5fa92cced682a8 100644 --- a/frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.js +++ b/frontend/src/metabase-lib/v1/parameters/utils/filters.unit.spec.ts @@ -1,3 +1,13 @@ +import { createMockMetadata } from "__support__/metadata"; +import { checkNotNull } from "metabase/lib/types"; +import Dimension from "metabase-lib/v1/Dimension"; +import Field from "metabase-lib/v1/metadata/Field"; +import { + createMockParameter, + createMockTemplateTag, +} from "metabase-types/api/mocks"; +import { PRODUCTS } from "metabase-types/api/mocks/presets"; + import { dimensionFilterForParameter, getTagOperatorFilterForParameter, @@ -5,7 +15,7 @@ import { describe("parameters/utils/field-filters", () => { describe("dimensionFilterForParameter", () => { - const field = { + const field = createMockField({ isDate: () => false, isID: () => false, isCategory: () => false, @@ -16,10 +26,11 @@ describe("parameters/utils/field-filters", () => { isNumber: () => false, isString: () => false, isLocation: () => false, - }; - const typelessDimension = { + }); + + const typelessDimension = createMockDimension({ field: () => field, - }; + }); [ [ @@ -99,55 +110,75 @@ describe("parameters/utils/field-filters", () => { ], ].forEach(([parameter, dimension]) => { it(`should return a predicate that evaluates to true for a ${dimension.type} dimension when given a ${parameter.type} parameter`, () => { - const predicate = dimensionFilterForParameter(parameter); + const predicate = dimensionFilterForParameter( + createMockParameter(parameter), + ); expect(predicate(typelessDimension)).toBe(false); - expect(predicate(dimension)).toBe(true); + expect(predicate(createMockDimension(dimension))).toBe(true); }); }); it("should return a predicate that evaluates to false for a coordinate dimension when given a number parameter", () => { - const coordinateDimension = { + const coordinateDimension = createMockDimension({ field: () => ({ ...field, isNumber: () => true, isCoordinate: () => true, }), - }; + }); - const predicate = dimensionFilterForParameter({ type: "number/between" }); + const predicate = dimensionFilterForParameter( + createMockParameter({ type: "number/between" }), + ); expect(predicate(coordinateDimension)).toBe(false); }); it("should return a predicate that evaluates to false for a location dimension when given a category parameter", () => { - const locationDimension = { + const locationDimension = createMockDimension({ field: () => ({ ...field, isLocation: () => true, }), - }; + }); - const predicate = dimensionFilterForParameter({ type: "category" }); + const predicate = dimensionFilterForParameter( + createMockParameter({ type: "category" }), + ); expect(predicate(locationDimension)).toBe(false); }); }); describe("getTagOperatorFilterForParameter", () => { it("should return a predicate that evaluates to true for a template tag that has the same subtype operator as the given parameter", () => { - const predicate = getTagOperatorFilterForParameter({ - type: "string/starts-with", - }); - const templateTag1 = { + const predicate = getTagOperatorFilterForParameter( + createMockParameter({ + type: "string/starts-with", + }), + ); + const templateTag1 = createMockTemplateTag({ "widget-type": "string/starts-with", - }; - const templateTag2 = { + }); + const templateTag2 = createMockTemplateTag({ "widget-type": "foo/starts-with", - }; - const templateTag3 = { + }); + const templateTag3 = createMockTemplateTag({ "widget-type": "string/ends-with", - }; + }); expect(predicate(templateTag1)).toBe(true); expect(predicate(templateTag2)).toBe(true); expect(predicate(templateTag3)).toBe(false); }); }); }); + +function createMockField(mocks: Record<string, unknown>): Field { + return Object.assign(new Field(), mocks); +} + +function createMockDimension(mocks: Record<string, unknown>): Dimension { + const metadata = createMockMetadata({}); + const dimension = checkNotNull( + Dimension.parseMBQL(["field", PRODUCTS.CREATED_AT, null], metadata), + ); + return Object.assign({}, dimension, mocks); +} diff --git a/frontend/src/metabase-lib/v1/parameters/utils/parameter-type.ts b/frontend/src/metabase-lib/v1/parameters/utils/parameter-type.ts index 5c6c774f3b9426d590da836bb174800adeb25ec7..9fbcc16cf59918aa868988351c0d16f3cea79e97 100644 --- a/frontend/src/metabase-lib/v1/parameters/utils/parameter-type.ts +++ b/frontend/src/metabase-lib/v1/parameters/utils/parameter-type.ts @@ -9,7 +9,7 @@ export function getParameterType(parameter: Parameter | string) { : parameter.sectionId || splitType(parameter)[0]; } -export function getParameterSubType(parameter: Parameter) { +export function getParameterSubType(parameter: Parameter | string) { const [, subtype] = splitType(parameter); return subtype; } diff --git a/frontend/src/metabase-lib/v1/parameters/utils/template-tag-options.ts b/frontend/src/metabase-lib/v1/parameters/utils/template-tag-options.ts index a5d3f6c200e5606d8c7af55b1c00c4a8cce53477..5c86db32f90d1f92600bad9df1a29b007cb1b4b7 100644 --- a/frontend/src/metabase-lib/v1/parameters/utils/template-tag-options.ts +++ b/frontend/src/metabase-lib/v1/parameters/utils/template-tag-options.ts @@ -1,7 +1,8 @@ import { t } from "ttag"; import type Field from "metabase-lib/v1/metadata/Field"; -import type { TemplateTag, ParameterOptions } from "metabase-types/api"; +import type { ParameterOptions, TemplateTag } from "metabase-types/api"; +import { createMockParameter } from "metabase-types/api/mocks"; import { ID_OPTION, @@ -32,7 +33,9 @@ export function getParameterOptions() { export function getParameterOptionsForField(field: Field) { return getParameterOptions() - .filter(option => fieldFilterForParameter(option)(field)) + .filter(option => + fieldFilterForParameter(createMockParameter(option))(field), + ) .map(option => { return { ...option,