Experimental filters: small bugfixes (#4019)

This commit is contained in:
Krzysztof Żuraw 2023-07-27 10:34:59 +02:00 committed by GitHub
parent d9c600452c
commit be6adb28d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 103 additions and 73 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---
Experimental filters: fix small issues

14
package-lock.json generated
View file

@ -27,7 +27,7 @@
"@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/styles": "^4.11.4", "@material-ui/styles": "^4.11.4",
"@reach/auto-id": "^0.16.0", "@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", "@saleor/sdk": "0.6.0",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6", "@types/faker": "^5.1.6",
@ -8795,9 +8795,9 @@
} }
}, },
"node_modules/@saleor/macaw-ui": { "node_modules/@saleor/macaw-ui": {
"version": "0.8.0-pre.111", "version": "0.8.0-pre.112",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.111.tgz", "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.112.tgz",
"integrity": "sha512-MvanJYWg7ASHc4Qq+XbXYJ7HgubyRjOuD4j3plTBopyNrAMXNie7h4TOgBjuL3FYTTAM6gUt4CwQPnwWe378eA==", "integrity": "sha512-USVepQkr3t4/6WWTyHOJ+vj6bxoPHUI0m7e13ZF012YJT1c+ORRtJtYnnKdxazCDl7GjEH1shDJmpexQmtqPJQ==",
"dependencies": { "dependencies": {
"@dessert-box/react": "^0.4.0", "@dessert-box/react": "^0.4.0",
"@floating-ui/react-dom-interactions": "^0.5.0", "@floating-ui/react-dom-interactions": "^0.5.0",
@ -41264,9 +41264,9 @@
} }
}, },
"@saleor/macaw-ui": { "@saleor/macaw-ui": {
"version": "0.8.0-pre.111", "version": "0.8.0-pre.112",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.111.tgz", "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.112.tgz",
"integrity": "sha512-MvanJYWg7ASHc4Qq+XbXYJ7HgubyRjOuD4j3plTBopyNrAMXNie7h4TOgBjuL3FYTTAM6gUt4CwQPnwWe378eA==", "integrity": "sha512-USVepQkr3t4/6WWTyHOJ+vj6bxoPHUI0m7e13ZF012YJT1c+ORRtJtYnnKdxazCDl7GjEH1shDJmpexQmtqPJQ==",
"requires": { "requires": {
"@dessert-box/react": "^0.4.0", "@dessert-box/react": "^0.4.0",
"@floating-ui/react-dom-interactions": "^0.5.0", "@floating-ui/react-dom-interactions": "^0.5.0",

View file

@ -34,7 +34,7 @@
"@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/styles": "^4.11.4", "@material-ui/styles": "^4.11.4",
"@reach/auto-id": "^0.16.0", "@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", "@saleor/sdk": "0.6.0",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6", "@types/faker": "^5.1.6",

View file

@ -36,7 +36,7 @@ export const ListFilters = <TFilterKeys extends string = string>({
<> <>
<Box <Box
display="grid" display="grid"
gridTemplateColumns={2} __gridTemplateColumns="auto 1fr"
gap={4} gap={4}
paddingBottom={2} paddingBottom={2}
paddingX={6} paddingX={6}

View file

@ -1,6 +1,6 @@
import { AttributeInputTypeEnum } from "@dashboard/graphql"; import { AttributeInputTypeEnum } from "@dashboard/graphql";
import { createBoleanOption } from "../constants"; import { createBooleanOption } from "../constants";
import { AttributeInputType } from "../FilterElement/ConditionOptions"; import { AttributeInputType } from "../FilterElement/ConditionOptions";
import { ItemOption } from "../FilterElement/ConditionValue"; import { ItemOption } from "../FilterElement/ConditionValue";
import { UrlToken } from "../ValueProvider/UrlToken"; import { UrlToken } from "../ValueProvider/UrlToken";
@ -58,7 +58,7 @@ export class InitialStateResponse implements InitialState {
if (token.isAttribute()) { if (token.isAttribute()) {
const attr = this.attribute[token.name]; const attr = this.attribute[token.name];
return attr.inputType === "BOOLEAN" return attr.inputType === "BOOLEAN"
? createBoleanOption( ? createBooleanOption(
token.value === "true", token.value === "true",
AttributeInputTypeEnum.BOOLEAN, AttributeInputTypeEnum.BOOLEAN,
) )

View file

@ -36,16 +36,17 @@ const isStaticBoolean = (rowType: RowType) => {
"isPublished", "isPublished",
"isVisibleInListing", "isVisibleInListing",
"hasCategory", "hasCategory",
"giftCard" "giftCard",
].includes(rowType) "price",
} ].includes(rowType);
};
const createAPIHandler = ( const createAPIHandler = (
selectedRow: FilterElement, selectedRow: FilterElement,
client: ApolloClient<unknown>, client: ApolloClient<unknown>,
inputValue: string, inputValue: string,
): Handler => { ): Handler => {
const rowType = selectedRow.rowType() const rowType = selectedRow.rowType();
if (rowType === "attribute" && selectedRow.value.type === "BOOLEAN") { if (rowType === "attribute" && selectedRow.value.type === "BOOLEAN") {
return new BooleanValuesHandler([ return new BooleanValuesHandler([
@ -53,15 +54,15 @@ const createAPIHandler = (
label: "Yes", label: "Yes",
value: "true", value: "true",
type: AttributeInputTypeEnum.BOOLEAN, type: AttributeInputTypeEnum.BOOLEAN,
slug: "true" slug: "true",
}, },
{ {
label: "No", label: "No",
value: "false", value: "false",
type: AttributeInputTypeEnum.BOOLEAN, type: AttributeInputTypeEnum.BOOLEAN,
slug: "false" slug: "false",
} },
]) ]);
} }
if (rowType === "attribute") { if (rowType === "attribute") {
@ -78,15 +79,15 @@ const createAPIHandler = (
label: "Yes", label: "Yes",
value: "true", value: "true",
type: rowType, type: rowType,
slug: "true" slug: "true",
}, },
{ {
label: "No", label: "No",
value: "false", value: "false",
type: rowType, type: rowType,
slug: "false" slug: "false",
} },
]) ]);
} }
if (rowType === "collection") { if (rowType === "collection") {

View file

@ -2,15 +2,35 @@ import { gql } from "@apollo/client";
export const initialDynamicLeftOperands = gql` export const initialDynamicLeftOperands = gql`
query _GetDynamicLeftOperands($first: Int!, $query: String!) { 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 { edges {
node { node {
id id
name name
slug slug
inputType inputType
__typename
} }
__typename
} }
__typename
} }
} }
`; `;

View file

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { InitialStateResponse } from "../API/InitialStateResponse"; import { InitialStateResponse } from "../API/InitialStateResponse";
import { LeftOperand } from "../LeftOperandsProvider"; import { LeftOperand } from "../LeftOperandsProvider";
import { UrlToken } from "./../ValueProvider/UrlToken"; import { UrlToken } from "./../ValueProvider/UrlToken";
@ -26,7 +25,7 @@ export class Condition {
} }
public isEmpty() { public isEmpty() {
return this.options.isEmpty() || this.selected.isEmpty() return this.options.isEmpty() || this.selected.isEmpty();
} }
public static createEmpty() { public static createEmpty() {
@ -81,7 +80,7 @@ export class Condition {
if (token.isAttribute()) { if (token.isAttribute()) {
const attribute = response.attributeByName(token.name); const attribute = response.attributeByName(token.name);
const options = ConditionOptions.fromAtributeType(attribute.inputType); 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); const value = response.filterByUrlToken(token);
return new Condition( return new Condition(

View file

@ -1,10 +1,13 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { InitialStateResponse } from "../API/InitialStateResponse"; import { InitialStateResponse } from "../API/InitialStateResponse";
import { RowType, STATIC_OPTIONS } from "../constants"; import { RowType, STATIC_OPTIONS } from "../constants";
import { LeftOperand } from "../LeftOperandsProvider"; import { LeftOperand } from "../LeftOperandsProvider";
import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken"; import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken";
import { Condition } from "./Condition"; import { Condition } from "./Condition";
import { ConditionItem, ConditionOptions, StaticElementName } from "./ConditionOptions"; import {
ConditionItem,
ConditionOptions,
StaticElementName,
} from "./ConditionOptions";
import { ConditionSelected } from "./ConditionSelected"; import { ConditionSelected } from "./ConditionSelected";
import { ConditionValue, ItemOption } from "./ConditionValue"; import { ConditionValue, ItemOption } from "./ConditionValue";
import { Constraint } from "./Constraint"; import { Constraint } from "./Constraint";
@ -17,19 +20,15 @@ class ExpressionValue {
) {} ) {}
public isEmpty() { 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) { 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( return new ExpressionValue(option.slug, option.label, option.type);
option.slug,
option.label,
option.type,
);
} }
public static fromLeftOperand(leftOperand: LeftOperand) { public static fromLeftOperand(leftOperand: LeftOperand) {
@ -75,10 +74,10 @@ export class FilterElement {
public loading: boolean, public loading: boolean,
public constraint?: Constraint, public constraint?: Constraint,
) { ) {
const newConstraint = Constraint.fromSlug(this.value.value) const newConstraint = Constraint.fromSlug(this.value.value);
if (newConstraint) { if (newConstraint) {
this.constraint = newConstraint this.constraint = newConstraint;
} }
} }
@ -126,7 +125,7 @@ export class FilterElement {
} }
public isEmpty() { public isEmpty() {
return this.value.isEmpty() || this.condition.isEmpty() return this.value.isEmpty() || this.condition.isEmpty();
} }
public isStatic() { public isStatic() {
@ -150,14 +149,13 @@ export class FilterElement {
} }
public setConstraint(constraint: Constraint) { public setConstraint(constraint: Constraint) {
this.constraint = constraint this.constraint = constraint;
} }
public clearConstraint() { public clearConstraint() {
this.constraint = undefined this.constraint = undefined;
} }
public asUrlEntry(): UrlEntry { public asUrlEntry(): UrlEntry {
if (this.isAttribute()) { if (this.isAttribute()) {
return UrlEntry.forAttribute(this.condition.selected, this.value.value); return UrlEntry.forAttribute(this.condition.selected, this.value.value);
@ -167,10 +165,12 @@ export class FilterElement {
} }
public static isCompatible(element: unknown): element is FilterElement { public static isCompatible(element: unknown): element is FilterElement {
return typeof element === "object" && return (
typeof element === "object" &&
!Array.isArray(element) && !Array.isArray(element) &&
element !== null && element !== null &&
'value' in element "value" in element
);
} }
public static fromValueEntry(valueEntry: any) { public static fromValueEntry(valueEntry: any) {
@ -216,7 +216,7 @@ export class FilterElement {
export const hasEmptyRows = (container: FilterContainer) => { export const hasEmptyRows = (container: FilterContainer) => {
return container return container
.filter(FilterElement.isCompatible) .filter(FilterElement.isCompatible)
.some((e: FilterElement) => e.isEmpty()) .some((e: FilterElement) => e.isEmpty());
} };
export type FilterContainer = Array<string | FilterElement | FilterContainer>; export type FilterContainer = Array<string | FilterElement | FilterContainer>;

View file

@ -1,6 +1,6 @@
import { ParsedQs } from "qs"; import { ParsedQs } from "qs";
import { getAtributeInputType } from "../constants"; import { getAttributeInputType } from "../constants";
import { ConditionSelected } from "../FilterElement/ConditionSelected"; import { ConditionSelected } from "../FilterElement/ConditionSelected";
import { slugFromConditionValue } from "../FilterElement/ConditionValue"; import { slugFromConditionValue } from "../FilterElement/ConditionValue";
@ -15,7 +15,7 @@ const STATIC_TO_LOAD = [
"isPublished", "isPublished",
"isVisibleInListing", "isVisibleInListing",
"hasCategory", "hasCategory",
"giftCard" "giftCard",
]; ];
export const TokenType = { export const TokenType = {
@ -28,18 +28,17 @@ export const TokenType = {
STATIC: "s", STATIC: "s",
} as const; } as const;
export type TokenTypeValue = (typeof TokenType)[keyof typeof TokenType]; export type TokenTypeValue = (typeof TokenType)[keyof typeof TokenType];
const resolveTokenType = (name: string): TokenTypeValue => { 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) { if (key in TokenType) {
return TokenType[key] return TokenType[key];
} }
return TokenType.STATIC return TokenType.STATIC;
} };
export class UrlEntry { export class UrlEntry {
constructor(key: string, value: string | string[]) { constructor(key: string, value: string | string[]) {
@ -54,14 +53,10 @@ export class UrlEntry {
} }
public static forAttribute(condition: ConditionSelected, paramName: string) { public static forAttribute(condition: ConditionSelected, paramName: string) {
const inputType = getAtributeInputType(condition.conditionValue) const inputType = getAttributeInputType(condition.conditionValue);
const tokenSlug = resolveTokenType(inputType || "") const tokenSlug = resolveTokenType(inputType || "");
return UrlEntry.fromConditionSelected( return UrlEntry.fromConditionSelected(condition, paramName, tokenSlug);
condition,
paramName,
tokenSlug,
);
} }
public static forStatic(condition: ConditionSelected, paramName: string) { public static forStatic(condition: ConditionSelected, paramName: string) {
@ -120,15 +115,18 @@ export class UrlToken {
} }
public isAttribute() { public isAttribute() {
const result = Object.entries(TokenType) const result = Object.entries(TokenType).find(
.find(([_, slug]) => slug === this.type) ([_, slug]) => slug === this.type,
);
return result && result[0].includes("ATTRIBUTE") return result && result[0].includes("ATTRIBUTE");
} }
public hasDynamicValues() { public hasDynamicValues() {
return TokenType.ATTRIBUTE_DROPDOWN === this.type return (
|| TokenType.ATTRIBUTE_MULTISELECT === this.type TokenType.ATTRIBUTE_DROPDOWN === this.type ||
TokenType.ATTRIBUTE_MULTISELECT === this.type
);
} }
public isLoadable() { public isLoadable() {

View file

@ -97,7 +97,7 @@ export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = {
SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }], 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( const result = Object.entries(ATTRIBUTE_INPUT_TYPE_CONDITIONS).find(
([_, value]) => ([_, value]) =>
value.find( value.find(
@ -129,7 +129,7 @@ export const createBooleanOptions = (type?: string): ItemOption[] => [
booleanOptionFalse(type), booleanOptionFalse(type),
]; ];
export const createBoleanOption = ( export const createBooleanOption = (
flag: boolean, flag: boolean,
type?: string, type?: string,
): ItemOption => { ): ItemOption => {

View file

@ -3,7 +3,7 @@ import { ConditionValue } from "./FilterElement/ConditionValue";
export const CONTROL_DEFAULTS = { export const CONTROL_DEFAULTS = {
text: "", text: "",
number: "", number: "",
"number.range": [] as unknown as [string, string], "number.range": ["", ""] as [string, string],
multiselect: [] as ConditionValue, multiselect: [] as ConditionValue,
select: "", select: "",
combobox: "", combobox: "",

View file

@ -1,15 +1,15 @@
import unionBy from "lodash/unionBy";
import { useState } from "react"; import { useState } from "react";
import { STATIC_OPTIONS } from "./constants"; import { STATIC_OPTIONS } from "./constants";
import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider"; import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider";
export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => { export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => {
const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS); const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS);
return { return {
operands, operands,
setOperands: (options: LeftOperand[]) => setOperands: (options: LeftOperand[]) =>
setOperands(prev => [...prev, ...options]), setOperands(prev => unionBy([...prev, ...options], "value")),
}; };
}; };

View file

@ -5460,15 +5460,22 @@ export type AddressValidationRulesLazyQueryHookResult = ReturnType<typeof useAdd
export type AddressValidationRulesQueryResult = Apollo.QueryResult<Types.AddressValidationRulesQuery, Types.AddressValidationRulesQueryVariables>; export type AddressValidationRulesQueryResult = Apollo.QueryResult<Types.AddressValidationRulesQuery, Types.AddressValidationRulesQueryVariables>;
export const _GetDynamicLeftOperandsDocument = gql` export const _GetDynamicLeftOperandsDocument = gql`
query _GetDynamicLeftOperands($first: Int!, $query: String!) { 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 { edges {
node { node {
id id
name name
slug slug
inputType inputType
__typename
} }
__typename
} }
__typename
} }
} }
`; `;