Fix numeric attribute filters (#2145)

* Handle numeric attribute filters

* Fix filter behaviour

* Backwards compatibility
This commit is contained in:
Michał Droń 2022-08-01 11:39:20 +02:00 committed by GitHub
parent 1a974e380c
commit 4ee9e4fc59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 9 deletions

View file

@ -35,6 +35,9 @@ export const FilterSingleSelectField: React.FC<FilterSingleSelectFieldProps> = (
name: filter.name, name: filter.name,
update: { update: {
multiple: event.target.value === FilterType.MULTIPLE, multiple: event.target.value === FilterType.MULTIPLE,
...(event.target.value !== FilterType.MULTIPLE && {
value: filter.value.slice(0, 1) as string[],
}),
}, },
}, },
type: "set-property", type: "set-property",

View file

@ -14,6 +14,7 @@ import {
createDateField, createDateField,
createDateTimeField, createDateTimeField,
createKeyValueField, createKeyValueField,
createNumberField,
createOptionsField, createOptionsField,
createPriceField, createPriceField,
} from "@saleor/utils/filters/fields"; } from "@saleor/utils/filters/fields";
@ -121,6 +122,9 @@ export function createFilterStructure(
const dateTimeAttributes = attributes.filter( const dateTimeAttributes = attributes.filter(
filterByType(AttributeInputTypeEnum.DATE_TIME), filterByType(AttributeInputTypeEnum.DATE_TIME),
); );
const numericAttributes = attributes.filter(
filterByType(AttributeInputTypeEnum.NUMERIC),
);
const defaultAttributes = opts.attributes.filter( const defaultAttributes = opts.attributes.filter(
({ inputType }) => ({ inputType }) =>
@ -128,6 +132,7 @@ export function createFilterStructure(
AttributeInputTypeEnum.BOOLEAN, AttributeInputTypeEnum.BOOLEAN,
AttributeInputTypeEnum.DATE, AttributeInputTypeEnum.DATE,
AttributeInputTypeEnum.DATE_TIME, AttributeInputTypeEnum.DATE_TIME,
AttributeInputTypeEnum.NUMERIC,
].includes(inputType), ].includes(inputType),
); );
@ -273,6 +278,14 @@ export function createFilterStructure(
active: attr.active, active: attr.active,
group: ProductFilterKeys.attributes, 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 => ({ ...defaultAttributes.map(attr => ({
...createAutocompleteField( ...createAutocompleteField(
attr.slug, attr.slug,

View file

@ -467,8 +467,8 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
confirmButtonState={exportProductsOpts.status} confirmButtonState={exportProductsOpts.status}
errors={exportProductsOpts.data?.exportProducts.errors || []} errors={exportProductsOpts.data?.exportProducts.errors || []}
productQuantity={{ productQuantity={{
all: countAllProducts.data?.products.totalCount, all: countAllProducts.data?.products?.totalCount,
filter: data?.products.totalCount, filter: data?.products?.totalCount,
}} }}
selectedProducts={listElements.length} selectedProducts={listElements.length}
warehouses={mapEdgesToItems(warehouses?.data?.warehouses) || []} warehouses={mapEdgesToItems(warehouses?.data?.warehouses) || []}

View file

@ -27,7 +27,6 @@ import {
mapNodeToChoice, mapNodeToChoice,
mapSlugNodeToChoice, mapSlugNodeToChoice,
} from "@saleor/utils/maps"; } from "@saleor/utils/maps";
import isArray from "lodash/isArray";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { import {
@ -238,12 +237,12 @@ const parseFilterValue = (
params: ProductListUrlFilters, params: ProductListUrlFilters,
key: string, key: string,
): { ): {
type: "boolean" | "date" | "dateTime" | "string"; type: "boolean" | "date" | "dateTime" | "numeric" | "string";
isMulti: boolean; isMulti: boolean;
value: string[]; value: string[];
} => { } => {
const value = params.attributes[key]; 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 isBooleanValue = value.every(val => val === "true" || val === "false");
const isDateValue = (isMulti ? value : [value]).some(val => const isDateValue = (isMulti ? value : [value]).some(val =>
@ -252,8 +251,9 @@ const parseFilterValue = (
const isDateTimeValue = (isMulti ? value : [value]).some(val => const isDateTimeValue = (isMulti ? value : [value]).some(val =>
moment(val, moment.ISO_8601, true).isValid(), 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) { if (isBooleanValue) {
return { ...data, type: "boolean" }; return { ...data, type: "boolean" };
@ -261,6 +261,8 @@ const parseFilterValue = (
return { ...data, type: "date" }; return { ...data, type: "date" };
} else if (isDateTimeValue) { } else if (isDateTimeValue) {
return { ...data, type: "dateTime" }; return { ...data, type: "dateTime" };
} else if (isNumericValue) {
return { ...data, type: "numeric" };
} }
return { ...data, type: "string" }; 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: default:
return { ...name, values: value }; return { ...name, values: value };
} }

View file

@ -70,15 +70,15 @@ export function createDateTimeField<K extends string>(
export function createNumberField<K extends string>( export function createNumberField<K extends string>(
name: K, name: K,
label: string, label: string,
defaultValue: MinMax, value: MinMax,
): FilterElementGeneric<K, FieldType.number> { ): FilterElementGeneric<K, FieldType.number> {
return { return {
active: false, active: false,
label, label,
multiple: true, multiple: value.min !== value.max,
name, name,
type: FieldType.number, type: FieldType.number,
value: [defaultValue.min, defaultValue.max], value: [value.min, value.max],
}; };
} }