From 4e8942f90ed97e2d56125d7f7603df17ae3762de Mon Sep 17 00:00:00 2001 From: Patryk Andrzejewski Date: Tue, 18 Jul 2023 14:55:56 +0200 Subject: [PATCH] Cover attributes based on inputType (#3945) * Cover attributes based on inputType * Changeset * Remove logs * Lint --- .changeset/rude-walls-tell.md | 5 ++ .../API/InitialStateResponse.ts | 12 ++++- .../API/ProductFilterAPIProvider.tsx | 18 ++++++++ .../API/initialState/helpers.ts | 5 +- .../FilterElement/Condition.ts | 6 ++- .../TokenArray/fetchingParams.ts | 14 +++++- .../ValueProvider/UrlToken.ts | 34 ++++++++++++-- src/components/ConditionalFilter/constants.ts | 46 ++++++++++++++----- 8 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 .changeset/rude-walls-tell.md diff --git a/.changeset/rude-walls-tell.md b/.changeset/rude-walls-tell.md new file mode 100644 index 000000000..fbadd75a5 --- /dev/null +++ b/.changeset/rude-walls-tell.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": minor +--- + +Cover attributes based on input type diff --git a/src/components/ConditionalFilter/API/InitialStateResponse.ts b/src/components/ConditionalFilter/API/InitialStateResponse.ts index 33efab3dd..7a5c053b9 100644 --- a/src/components/ConditionalFilter/API/InitialStateResponse.ts +++ b/src/components/ConditionalFilter/API/InitialStateResponse.ts @@ -1,3 +1,6 @@ +import { AttributeInputTypeEnum } from "@dashboard/graphql"; + +import { createBoleanOption } from "../constants"; import { AttributeInputType } from "../FilterElement/ConditionOptions"; import { ItemOption } from "../FilterElement/ConditionValue"; import { UrlToken } from "../ValueProvider/UrlToken"; @@ -46,12 +49,19 @@ export class InitialStateResponse implements InitialState { } public filterByUrlToken(token: UrlToken) { - if (token.isAttribute()) { + if (token.isAttribute() && token.hasDynamicValues()) { return this.attribute[token.name].choices.filter(({ value }) => token.value.includes(value), ); } + if (token.isAttribute()) { + const attr = this.attribute[token.name] + return attr.inputType === "BOOLEAN" + ? createBoleanOption(token.value === "true", AttributeInputTypeEnum.BOOLEAN) + : token.value + } + if (!token.isLoadable()) { return [token.value] as string[]; } diff --git a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx index b75d92b4d..8f4f7ce7d 100644 --- a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx +++ b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx @@ -1,4 +1,5 @@ import { ApolloClient, useApolloClient } from "@apollo/client"; +import { AttributeInputTypeEnum } from "@dashboard/graphql"; import { RowType } from "../constants"; import { FilterContainer, FilterElement } from "../FilterElement"; @@ -46,6 +47,23 @@ const createAPIHandler = ( ): Handler => { const rowType = selectedRow.rowType() + if (rowType === "attribute" && selectedRow.value.type === "BOOLEAN") { + return new BooleanValuesHandler([ + { + label: "Yes", + value: "true", + type: AttributeInputTypeEnum.BOOLEAN, + slug: "true" + }, + { + label: "No", + value: "false", + type: AttributeInputTypeEnum.BOOLEAN, + slug: "false" + } + ]) + } + if (rowType === "attribute") { return new AttributeChoicesHandler( client, diff --git a/src/components/ConditionalFilter/API/initialState/helpers.ts b/src/components/ConditionalFilter/API/initialState/helpers.ts index 5ee704964..36445ee35 100644 --- a/src/components/ConditionalFilter/API/initialState/helpers.ts +++ b/src/components/ConditionalFilter/API/initialState/helpers.ts @@ -85,7 +85,10 @@ export const createInitialStateFromData = ( (acc, { node }) => ({ ...acc, [node.slug ?? ""]: { - choices: createOptionsFromAPI(node.choices?.edges ?? []), + choices: + node.inputType === "BOOLEAN" + ? createBooleanOptions() + : createOptionsFromAPI(node.choices?.edges ?? []), slug: node?.slug, value: node?.id, label: node?.name, diff --git a/src/components/ConditionalFilter/FilterElement/Condition.ts b/src/components/ConditionalFilter/FilterElement/Condition.ts index 07fce4d0d..ae9afaace 100644 --- a/src/components/ConditionalFilter/FilterElement/Condition.ts +++ b/src/components/ConditionalFilter/FilterElement/Condition.ts @@ -4,6 +4,7 @@ import { LeftOperand } from "../LeftOperandsProvider"; import { UrlToken } from "./../ValueProvider/UrlToken"; import { ConditionOptions } from "./ConditionOptions"; import { ConditionSelected } from "./ConditionSelected"; +import { ItemOption } from "./ConditionValue"; export class Condition { private constructor( @@ -46,7 +47,7 @@ export class Condition { if (ConditionOptions.isStaticName(token.name)) { const staticOptions = ConditionOptions.fromStaticElementName(token.name); const selectedOption = staticOptions.findByLabel(token.conditionKind); - const valueItems = response.filterByUrlToken(token); + const valueItems = response.filterByUrlToken(token) as ItemOption[]; const value = selectedOption?.type === "multiselect" && valueItems.length > 0 ? valueItems @@ -66,11 +67,12 @@ 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 value = response.filterByUrlToken(token); return new Condition( options, - ConditionSelected.fromConditionItemAndValue(options.first(), value), + ConditionSelected.fromConditionItemAndValue(option, value), false, ); } diff --git a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts index 02c275113..3a4c9ffa8 100644 --- a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts +++ b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts @@ -1,4 +1,4 @@ -import { UrlToken } from "../UrlToken"; +import { TokenType, UrlToken } from "../UrlToken"; export interface FetchingParams { category: string[]; @@ -20,6 +20,10 @@ export const emptyFetchingParams: FetchingParams = { const unique = (array: Iterable) => Array.from(new Set(array)); +const includedInParams = (c: UrlToken) => + TokenType.ATTRIBUTE_DROPDOWN === c.type + || TokenType.ATTRIBUTE_MULTISELECT === c.type + export const toFetchingParams = (p: FetchingParams, c: UrlToken) => { const key = c.name as FetchingParamsKeys; @@ -31,12 +35,18 @@ export const toFetchingParams = (p: FetchingParams, c: UrlToken) => { p.attribute[c.name] = []; } - if (c.isAttribute()) { + if (c.isAttribute() && includedInParams(c)) { p.attribute[c.name] = unique(p.attribute[c.name].concat(c.value)); return p; } + if (c.isAttribute() && !includedInParams(c)) { + p.attribute[c.name] = [] + + return p; + } + p[key] = unique(p[key].concat(c.value)); return p; diff --git a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts index 5ead9fd90..e9d32ffbf 100644 --- a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts +++ b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts @@ -1,5 +1,6 @@ import { ParsedQs } from "qs"; +import { getAtributeInputType } from "../constants"; import { ConditionSelected } from "../FilterElement/ConditionSelected"; import { slugFromConditionValue } from "../FilterElement/ConditionValue"; @@ -18,12 +19,28 @@ const STATIC_TO_LOAD = [ ]; export const TokenType = { - ATTRIBUTE: "a", + ATTRIBUTE_DROPDOWN: "o", + ATTRIBUTE_MULTISELECT: "m", + ATTRIBUTE_NUMERIC: "n", + ATTRIBUTE_DATE_TIME: "t", + ATTRIBUTE_DATE: "d", + ATTRIBUTE_BOOLEAN: "b", 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 + + if (key in TokenType) { + return TokenType[key] + } + + return TokenType.STATIC +} + export class UrlEntry { constructor(key: string, value: string | string[]) { (this as unknown as Record)[key] = value; @@ -37,10 +54,13 @@ export class UrlEntry { } public static forAttribute(condition: ConditionSelected, paramName: string) { + const inputType = getAtributeInputType(condition.conditionValue) + const tokenSlug = resolveTokenType(inputType || "") + return UrlEntry.fromConditionSelected( condition, paramName, - TokenType.ATTRIBUTE, + tokenSlug, ); } @@ -100,7 +120,15 @@ export class UrlToken { } public isAttribute() { - return this.type === TokenType.ATTRIBUTE; + const result = Object.entries(TokenType) + .find(([_, slug]) => slug === this.type) + + return result && result[0].includes("ATTRIBUTE") + } + + public hasDynamicValues() { + 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 4ab2253ae..5e0464eee 100644 --- a/src/components/ConditionalFilter/constants.ts +++ b/src/components/ConditionalFilter/constants.ts @@ -1,3 +1,4 @@ +import { ConditionItem } from "./FilterElement/ConditionOptions"; import { ItemOption } from "./FilterElement/ConditionValue"; import { LeftOperand } from "./LeftOperandsProvider"; @@ -58,18 +59,39 @@ export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = { SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }], }; +export const getAtributeInputType = (item: ConditionItem | null) => { + const result = Object.entries(ATTRIBUTE_INPUT_TYPE_CONDITIONS) + .find(([_, value]) => + value.find(entry => entry.type === item?.type && entry.label === item.label) + ) + + return result && result[0] +} + export type RowType = keyof typeof STATIC_CONDITIONS | "attribute" -export const createBooleanOptions = (): ItemOption[] => [ - { - label: "Yes", - value: "true", - slug: "true" - }, - { - label: "No", - value: "false", - slug: "false" - } -] \ No newline at end of file +export const booleanOptionTrue = (type?: string) => ({ + label: "Yes", + value: "true", + slug: "true", + ...({ type }) +}) + +export const booleanOptionFalse = (type?: string) => ({ + label: "No", + value: "false", + slug: "false", + ...({ type }) +}) + +export const createBooleanOptions = (type?: string): ItemOption[] => [ + booleanOptionTrue(type), + booleanOptionFalse(type) +] + +export const createBoleanOption = (flag: boolean, type?: string): ItemOption => { + if (flag) return booleanOptionTrue(type) + + return booleanOptionFalse(type) +} \ No newline at end of file