From be6adb28d5878cc4b4c25179a12fa36cd73e6f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=BBuraw?= <9116238+krzysztofzuraw@users.noreply.github.com> Date: Thu, 27 Jul 2023 10:34:59 +0200 Subject: [PATCH] Experimental filters: small bugfixes (#4019) --- .changeset/famous-zebras-fail.md | 5 +++ package-lock.json | 14 +++---- package.json | 2 +- .../AppLayout/ListFilters/ListFilters.tsx | 2 +- .../API/InitialStateResponse.ts | 4 +- .../API/ProductFilterAPIProvider.tsx | 25 +++++------ .../ConditionalFilter/API/queries.ts | 22 +++++++++- .../FilterElement/Condition.ts | 5 +-- .../FilterElement/FilterElement.ts | 42 +++++++++---------- .../ValueProvider/UrlToken.ts | 36 ++++++++-------- src/components/ConditionalFilter/constants.ts | 4 +- .../ConditionalFilter/controlsType.ts | 2 +- .../useFilterLeftOperands.ts | 4 +- src/graphql/hooks.generated.ts | 9 +++- 14 files changed, 103 insertions(+), 73 deletions(-) create mode 100644 .changeset/famous-zebras-fail.md diff --git a/.changeset/famous-zebras-fail.md b/.changeset/famous-zebras-fail.md new file mode 100644 index 000000000..e62615294 --- /dev/null +++ b/.changeset/famous-zebras-fail.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Experimental filters: fix small issues diff --git a/package-lock.json b/package-lock.json index 255a4b07c..582da893d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/styles": "^4.11.4", "@reach/auto-id": "^0.16.0", - "@saleor/macaw-ui": "0.8.0-pre.111", + "@saleor/macaw-ui": "0.8.0-pre.112", "@saleor/sdk": "0.6.0", "@sentry/react": "^6.0.0", "@types/faker": "^5.1.6", @@ -8795,9 +8795,9 @@ } }, "node_modules/@saleor/macaw-ui": { - "version": "0.8.0-pre.111", - "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.111.tgz", - "integrity": "sha512-MvanJYWg7ASHc4Qq+XbXYJ7HgubyRjOuD4j3plTBopyNrAMXNie7h4TOgBjuL3FYTTAM6gUt4CwQPnwWe378eA==", + "version": "0.8.0-pre.112", + "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.112.tgz", + "integrity": "sha512-USVepQkr3t4/6WWTyHOJ+vj6bxoPHUI0m7e13ZF012YJT1c+ORRtJtYnnKdxazCDl7GjEH1shDJmpexQmtqPJQ==", "dependencies": { "@dessert-box/react": "^0.4.0", "@floating-ui/react-dom-interactions": "^0.5.0", @@ -41264,9 +41264,9 @@ } }, "@saleor/macaw-ui": { - "version": "0.8.0-pre.111", - "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.111.tgz", - "integrity": "sha512-MvanJYWg7ASHc4Qq+XbXYJ7HgubyRjOuD4j3plTBopyNrAMXNie7h4TOgBjuL3FYTTAM6gUt4CwQPnwWe378eA==", + "version": "0.8.0-pre.112", + "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.112.tgz", + "integrity": "sha512-USVepQkr3t4/6WWTyHOJ+vj6bxoPHUI0m7e13ZF012YJT1c+ORRtJtYnnKdxazCDl7GjEH1shDJmpexQmtqPJQ==", "requires": { "@dessert-box/react": "^0.4.0", "@floating-ui/react-dom-interactions": "^0.5.0", diff --git a/package.json b/package.json index c41e058a5..b67f08f37 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/styles": "^4.11.4", "@reach/auto-id": "^0.16.0", - "@saleor/macaw-ui": "0.8.0-pre.111", + "@saleor/macaw-ui": "0.8.0-pre.112", "@saleor/sdk": "0.6.0", "@sentry/react": "^6.0.0", "@types/faker": "^5.1.6", diff --git a/src/components/AppLayout/ListFilters/ListFilters.tsx b/src/components/AppLayout/ListFilters/ListFilters.tsx index cf5111f9c..b475f4a7f 100644 --- a/src/components/AppLayout/ListFilters/ListFilters.tsx +++ b/src/components/AppLayout/ListFilters/ListFilters.tsx @@ -36,7 +36,7 @@ export const ListFilters = ({ <> { "isPublished", "isVisibleInListing", "hasCategory", - "giftCard" - ].includes(rowType) -} + "giftCard", + "price", + ].includes(rowType); +}; const createAPIHandler = ( selectedRow: FilterElement, client: ApolloClient, inputValue: string, ): Handler => { - const rowType = selectedRow.rowType() + const rowType = selectedRow.rowType(); if (rowType === "attribute" && selectedRow.value.type === "BOOLEAN") { return new BooleanValuesHandler([ @@ -53,15 +54,15 @@ const createAPIHandler = ( label: "Yes", value: "true", type: AttributeInputTypeEnum.BOOLEAN, - slug: "true" + slug: "true", }, { label: "No", value: "false", type: AttributeInputTypeEnum.BOOLEAN, - slug: "false" - } - ]) + slug: "false", + }, + ]); } if (rowType === "attribute") { @@ -78,15 +79,15 @@ const createAPIHandler = ( label: "Yes", value: "true", type: rowType, - slug: "true" + slug: "true", }, { label: "No", value: "false", type: rowType, - slug: "false" - } - ]) + slug: "false", + }, + ]); } if (rowType === "collection") { diff --git a/src/components/ConditionalFilter/API/queries.ts b/src/components/ConditionalFilter/API/queries.ts index c2f70132c..98b483a2f 100644 --- a/src/components/ConditionalFilter/API/queries.ts +++ b/src/components/ConditionalFilter/API/queries.ts @@ -2,15 +2,35 @@ import { gql } from "@apollo/client"; export const initialDynamicLeftOperands = gql` query _GetDynamicLeftOperands($first: Int!, $query: String!) { - attributes(first: $first, filter: { type: PRODUCT_TYPE, search: $query }) { + attributes( + first: $first + search: $query + where: { + type: { eq: PRODUCT_TYPE } + inputType: { + oneOf: [ + DROPDOWN + MULTISELECT + BOOLEAN + NUMERIC + DATE + DATE_TIME + SWATCH + ] + } + } + ) { edges { node { id name slug inputType + __typename } + __typename } + __typename } } `; diff --git a/src/components/ConditionalFilter/FilterElement/Condition.ts b/src/components/ConditionalFilter/FilterElement/Condition.ts index 2eda6d1df..0938db6c3 100644 --- a/src/components/ConditionalFilter/FilterElement/Condition.ts +++ b/src/components/ConditionalFilter/FilterElement/Condition.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/member-ordering */ import { InitialStateResponse } from "../API/InitialStateResponse"; import { LeftOperand } from "../LeftOperandsProvider"; import { UrlToken } from "./../ValueProvider/UrlToken"; @@ -26,7 +25,7 @@ export class Condition { } public isEmpty() { - return this.options.isEmpty() || this.selected.isEmpty() + return this.options.isEmpty() || this.selected.isEmpty(); } public static createEmpty() { @@ -81,7 +80,7 @@ export class Condition { if (token.isAttribute()) { const attribute = response.attributeByName(token.name); const options = ConditionOptions.fromAtributeType(attribute.inputType); - const option = options.find(item => item.label === token.conditionKind)! + const option = options.find(item => item.label === token.conditionKind)!; const value = response.filterByUrlToken(token); return new Condition( diff --git a/src/components/ConditionalFilter/FilterElement/FilterElement.ts b/src/components/ConditionalFilter/FilterElement/FilterElement.ts index 47d87b1ed..5be3245e2 100644 --- a/src/components/ConditionalFilter/FilterElement/FilterElement.ts +++ b/src/components/ConditionalFilter/FilterElement/FilterElement.ts @@ -1,10 +1,13 @@ -/* eslint-disable @typescript-eslint/member-ordering */ import { InitialStateResponse } from "../API/InitialStateResponse"; import { RowType, STATIC_OPTIONS } from "../constants"; import { LeftOperand } from "../LeftOperandsProvider"; import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken"; import { Condition } from "./Condition"; -import { ConditionItem, ConditionOptions, StaticElementName } from "./ConditionOptions"; +import { + ConditionItem, + ConditionOptions, + StaticElementName, +} from "./ConditionOptions"; import { ConditionSelected } from "./ConditionSelected"; import { ConditionValue, ItemOption } from "./ConditionValue"; import { Constraint } from "./Constraint"; @@ -14,22 +17,18 @@ class ExpressionValue { public value: string, public label: string, public type: string, - ) { } + ) {} public isEmpty() { - return this.value.length === 0 || this.label.length === 0 + return this.value.length === 0 || this.label.length === 0; } public static fromSlug(slug: string) { - const option = STATIC_OPTIONS.find(o => o.slug === slug) + const option = STATIC_OPTIONS.find(o => o.slug === slug); - if (!option) return ExpressionValue.emptyStatic() + if (!option) return ExpressionValue.emptyStatic(); - return new ExpressionValue( - option.slug, - option.label, - option.type, - ); + return new ExpressionValue(option.slug, option.label, option.type); } public static fromLeftOperand(leftOperand: LeftOperand) { @@ -75,10 +74,10 @@ export class FilterElement { public loading: boolean, public constraint?: Constraint, ) { - const newConstraint = Constraint.fromSlug(this.value.value) + const newConstraint = Constraint.fromSlug(this.value.value); if (newConstraint) { - this.constraint = newConstraint + this.constraint = newConstraint; } } @@ -126,7 +125,7 @@ export class FilterElement { } public isEmpty() { - return this.value.isEmpty() || this.condition.isEmpty() + return this.value.isEmpty() || this.condition.isEmpty(); } public isStatic() { @@ -150,14 +149,13 @@ export class FilterElement { } public setConstraint(constraint: Constraint) { - this.constraint = constraint + this.constraint = constraint; } public clearConstraint() { - this.constraint = undefined + this.constraint = undefined; } - public asUrlEntry(): UrlEntry { if (this.isAttribute()) { return UrlEntry.forAttribute(this.condition.selected, this.value.value); @@ -167,10 +165,12 @@ export class FilterElement { } public static isCompatible(element: unknown): element is FilterElement { - return typeof element === "object" && + return ( + typeof element === "object" && !Array.isArray(element) && element !== null && - 'value' in element + "value" in element + ); } public static fromValueEntry(valueEntry: any) { @@ -216,7 +216,7 @@ export class FilterElement { export const hasEmptyRows = (container: FilterContainer) => { return container .filter(FilterElement.isCompatible) - .some((e: FilterElement) => e.isEmpty()) -} + .some((e: FilterElement) => e.isEmpty()); +}; export type FilterContainer = Array; diff --git a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts index e9d32ffbf..061b28649 100644 --- a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts +++ b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts @@ -1,6 +1,6 @@ import { ParsedQs } from "qs"; -import { getAtributeInputType } from "../constants"; +import { getAttributeInputType } from "../constants"; import { ConditionSelected } from "../FilterElement/ConditionSelected"; import { slugFromConditionValue } from "../FilterElement/ConditionValue"; @@ -15,7 +15,7 @@ const STATIC_TO_LOAD = [ "isPublished", "isVisibleInListing", "hasCategory", - "giftCard" + "giftCard", ]; export const TokenType = { @@ -28,18 +28,17 @@ export const TokenType = { STATIC: "s", } as const; - export type TokenTypeValue = (typeof TokenType)[keyof typeof TokenType]; const resolveTokenType = (name: string): TokenTypeValue => { - const key = `ATTRIBUTE_${name}` as keyof typeof TokenType + const key = `ATTRIBUTE_${name}` as keyof typeof TokenType; if (key in TokenType) { - return TokenType[key] + return TokenType[key]; } - return TokenType.STATIC -} + return TokenType.STATIC; +}; export class UrlEntry { constructor(key: string, value: string | string[]) { @@ -54,14 +53,10 @@ export class UrlEntry { } public static forAttribute(condition: ConditionSelected, paramName: string) { - const inputType = getAtributeInputType(condition.conditionValue) - const tokenSlug = resolveTokenType(inputType || "") + const inputType = getAttributeInputType(condition.conditionValue); + const tokenSlug = resolveTokenType(inputType || ""); - return UrlEntry.fromConditionSelected( - condition, - paramName, - tokenSlug, - ); + return UrlEntry.fromConditionSelected(condition, paramName, tokenSlug); } public static forStatic(condition: ConditionSelected, paramName: string) { @@ -120,15 +115,18 @@ export class UrlToken { } public isAttribute() { - const result = Object.entries(TokenType) - .find(([_, slug]) => slug === this.type) + const result = Object.entries(TokenType).find( + ([_, slug]) => slug === this.type, + ); - return result && result[0].includes("ATTRIBUTE") + return result && result[0].includes("ATTRIBUTE"); } public hasDynamicValues() { - return TokenType.ATTRIBUTE_DROPDOWN === this.type - || TokenType.ATTRIBUTE_MULTISELECT === this.type + return ( + TokenType.ATTRIBUTE_DROPDOWN === this.type || + TokenType.ATTRIBUTE_MULTISELECT === this.type + ); } public isLoadable() { diff --git a/src/components/ConditionalFilter/constants.ts b/src/components/ConditionalFilter/constants.ts index 4b6e1d0ad..2ce3ab888 100644 --- a/src/components/ConditionalFilter/constants.ts +++ b/src/components/ConditionalFilter/constants.ts @@ -97,7 +97,7 @@ export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = { SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }], }; -export const getAtributeInputType = (item: ConditionItem | null) => { +export const getAttributeInputType = (item: ConditionItem | null) => { const result = Object.entries(ATTRIBUTE_INPUT_TYPE_CONDITIONS).find( ([_, value]) => value.find( @@ -129,7 +129,7 @@ export const createBooleanOptions = (type?: string): ItemOption[] => [ booleanOptionFalse(type), ]; -export const createBoleanOption = ( +export const createBooleanOption = ( flag: boolean, type?: string, ): ItemOption => { diff --git a/src/components/ConditionalFilter/controlsType.ts b/src/components/ConditionalFilter/controlsType.ts index d7b963ec9..1a53d8c30 100644 --- a/src/components/ConditionalFilter/controlsType.ts +++ b/src/components/ConditionalFilter/controlsType.ts @@ -3,7 +3,7 @@ import { ConditionValue } from "./FilterElement/ConditionValue"; export const CONTROL_DEFAULTS = { text: "", number: "", - "number.range": [] as unknown as [string, string], + "number.range": ["", ""] as [string, string], multiselect: [] as ConditionValue, select: "", combobox: "", diff --git a/src/components/ConditionalFilter/useFilterLeftOperands.ts b/src/components/ConditionalFilter/useFilterLeftOperands.ts index cd992c6a8..a5d029bda 100644 --- a/src/components/ConditionalFilter/useFilterLeftOperands.ts +++ b/src/components/ConditionalFilter/useFilterLeftOperands.ts @@ -1,15 +1,15 @@ +import unionBy from "lodash/unionBy"; import { useState } from "react"; import { STATIC_OPTIONS } from "./constants"; import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider"; - export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => { const [operands, setOperands] = useState(STATIC_OPTIONS); return { operands, setOperands: (options: LeftOperand[]) => - setOperands(prev => [...prev, ...options]), + setOperands(prev => unionBy([...prev, ...options], "value")), }; }; diff --git a/src/graphql/hooks.generated.ts b/src/graphql/hooks.generated.ts index 73b058da2..7a147480d 100644 --- a/src/graphql/hooks.generated.ts +++ b/src/graphql/hooks.generated.ts @@ -5460,15 +5460,22 @@ export type AddressValidationRulesLazyQueryHookResult = ReturnType; export const _GetDynamicLeftOperandsDocument = gql` query _GetDynamicLeftOperands($first: Int!, $query: String!) { - attributes(first: $first, filter: {type: PRODUCT_TYPE, search: $query}) { + attributes( + first: $first + search: $query + where: {type: {eq: PRODUCT_TYPE}, inputType: {oneOf: [DROPDOWN, MULTISELECT, BOOLEAN, NUMERIC, DATE, DATE_TIME, SWATCH]}} + ) { edges { node { id name slug inputType + __typename } + __typename } + __typename } } `;