From 4ee9e4fc59ae1d1e93572aa23896980ed47e5d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dro=C5=84?= Date: Mon, 1 Aug 2022 11:39:20 +0200 Subject: [PATCH] Fix numeric attribute filters (#2145) * Handle numeric attribute filters * Fix filter behaviour * Backwards compatibility --- .../FilterContent/FilterSingleSelectField.tsx | 3 +++ .../components/ProductListPage/filters.ts | 13 +++++++++++++ .../views/ProductList/ProductList.tsx | 4 ++-- src/products/views/ProductList/filters.ts | 19 +++++++++++++++---- src/utils/filters/fields.ts | 6 +++--- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/components/Filter/FilterContent/FilterSingleSelectField.tsx b/src/components/Filter/FilterContent/FilterSingleSelectField.tsx index 44f216c3f..ef2bb8a70 100644 --- a/src/components/Filter/FilterContent/FilterSingleSelectField.tsx +++ b/src/components/Filter/FilterContent/FilterSingleSelectField.tsx @@ -35,6 +35,9 @@ export const FilterSingleSelectField: React.FC = ( name: filter.name, update: { multiple: event.target.value === FilterType.MULTIPLE, + ...(event.target.value !== FilterType.MULTIPLE && { + value: filter.value.slice(0, 1) as string[], + }), }, }, type: "set-property", diff --git a/src/products/components/ProductListPage/filters.ts b/src/products/components/ProductListPage/filters.ts index 30c2157fb..14eefad06 100644 --- a/src/products/components/ProductListPage/filters.ts +++ b/src/products/components/ProductListPage/filters.ts @@ -14,6 +14,7 @@ import { createDateField, createDateTimeField, createKeyValueField, + createNumberField, createOptionsField, createPriceField, } from "@saleor/utils/filters/fields"; @@ -121,6 +122,9 @@ export function createFilterStructure( const dateTimeAttributes = attributes.filter( filterByType(AttributeInputTypeEnum.DATE_TIME), ); + const numericAttributes = attributes.filter( + filterByType(AttributeInputTypeEnum.NUMERIC), + ); const defaultAttributes = opts.attributes.filter( ({ inputType }) => @@ -128,6 +132,7 @@ export function createFilterStructure( AttributeInputTypeEnum.BOOLEAN, AttributeInputTypeEnum.DATE, AttributeInputTypeEnum.DATE_TIME, + AttributeInputTypeEnum.NUMERIC, ].includes(inputType), ); @@ -273,6 +278,14 @@ export function createFilterStructure( active: attr.active, group: ProductFilterKeys.attributes, })), + ...numericAttributes.map(attr => ({ + ...createNumberField(attr.slug, attr.name, { + min: attr.value[0], + max: attr.value[1] ?? attr.value[0], + }), + active: attr.active, + group: ProductFilterKeys.attributes, + })), ...defaultAttributes.map(attr => ({ ...createAutocompleteField( attr.slug, diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index 5ab2adf24..ce80142b8 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -467,8 +467,8 @@ export const ProductList: React.FC = ({ params }) => { confirmButtonState={exportProductsOpts.status} errors={exportProductsOpts.data?.exportProducts.errors || []} productQuantity={{ - all: countAllProducts.data?.products.totalCount, - filter: data?.products.totalCount, + all: countAllProducts.data?.products?.totalCount, + filter: data?.products?.totalCount, }} selectedProducts={listElements.length} warehouses={mapEdgesToItems(warehouses?.data?.warehouses) || []} diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 409779366..5498c5d6e 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -27,7 +27,6 @@ import { mapNodeToChoice, mapSlugNodeToChoice, } from "@saleor/utils/maps"; -import isArray from "lodash/isArray"; import moment from "moment-timezone"; import { @@ -238,12 +237,12 @@ const parseFilterValue = ( params: ProductListUrlFilters, key: string, ): { - type: "boolean" | "date" | "dateTime" | "string"; + type: "boolean" | "date" | "dateTime" | "numeric" | "string"; isMulti: boolean; value: string[]; } => { const value = params.attributes[key]; - const isMulti = isArray(params.attributes[key]); + const isMulti = params.attributes[key].length > 1; const isBooleanValue = value.every(val => val === "true" || val === "false"); const isDateValue = (isMulti ? value : [value]).some(val => @@ -252,8 +251,9 @@ const parseFilterValue = ( const isDateTimeValue = (isMulti ? value : [value]).some(val => moment(val, moment.ISO_8601, true).isValid(), ); + const isNumericValue = value.some(value => !isNaN(parseFloat(value))); - const data = { isMulti, value: (isMulti ? value : [value]) as string[] }; + const data = { isMulti, value }; if (isBooleanValue) { return { ...data, type: "boolean" }; @@ -261,6 +261,8 @@ const parseFilterValue = ( return { ...data, type: "date" }; } else if (isDateTimeValue) { return { ...data, type: "dateTime" }; + } else if (isNumericValue) { + return { ...data, type: "numeric" }; } return { ...data, type: "string" }; }; @@ -317,6 +319,15 @@ function getFilteredAttributeValue( }), }; + case "numeric": + return { + ...name, + valuesRange: { + gte: value[0] || undefined, + lte: isMulti ? value[1] || undefined : value[0] || undefined, + }, + }; + default: return { ...name, values: value }; } diff --git a/src/utils/filters/fields.ts b/src/utils/filters/fields.ts index b44616113..0c9aff7b8 100644 --- a/src/utils/filters/fields.ts +++ b/src/utils/filters/fields.ts @@ -70,15 +70,15 @@ export function createDateTimeField( export function createNumberField( name: K, label: string, - defaultValue: MinMax, + value: MinMax, ): FilterElementGeneric { return { active: false, label, - multiple: true, + multiple: value.min !== value.max, name, type: FieldType.number, - value: [defaultValue.min, defaultValue.max], + value: [value.min, value.max], }; }