Experimental filters: add search params to query and unit tests (#4057)
This commit is contained in:
parent
a63af3ab73
commit
df1459949d
12 changed files with 192 additions and 23 deletions
5
.changeset/funny-poems-beam.md
Normal file
5
.changeset/funny-poems-beam.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-dashboard": patch
|
||||
---
|
||||
|
||||
Experimental filters: add search params to query and unit tests
|
|
@ -0,0 +1,35 @@
|
|||
import { Constraint } from "./Constraint";
|
||||
|
||||
describe("ConditionalFilter / FilterElement / Constraint", () => {
|
||||
it("should get dependency for a valid slug", () => {
|
||||
// Arrange & Act
|
||||
const dependency = Constraint.getDependency("price");
|
||||
// Assert
|
||||
expect(dependency).toBe("channel");
|
||||
});
|
||||
|
||||
it("should return null for an invalid slug", () => {
|
||||
// Arrange & Act
|
||||
const dependency = Constraint.getDependency("invalidSlug");
|
||||
// Assert
|
||||
expect(dependency).toBeNull();
|
||||
});
|
||||
|
||||
it("should create an instance from a valid slug", () => {
|
||||
// Arrange & Act
|
||||
const constraint = Constraint.fromSlug("channel");
|
||||
// Assert
|
||||
expect(constraint).toEqual({
|
||||
dependsOn: ["price", "isVisibleInListing", "isAvailable", "isPublished"],
|
||||
disabled: ["left", "condition"],
|
||||
removable: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null for an invalid slug", () => {
|
||||
// Arrange & Act
|
||||
const constraint = Constraint.fromSlug("invalidSlug");
|
||||
// Assert
|
||||
expect(constraint).toBeNull();
|
||||
});
|
||||
});
|
41
src/components/ConditionalFilter/Validation/numeric.test.ts
Normal file
41
src/components/ConditionalFilter/Validation/numeric.test.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { Condition, FilterElement } from "../FilterElement";
|
||||
import { ConditionOptions } from "../FilterElement/ConditionOptions";
|
||||
import { ConditionSelected } from "../FilterElement/ConditionSelected";
|
||||
import { ExpressionValue } from "../FilterElement/FilterElement";
|
||||
import { numeric } from "./numeric";
|
||||
|
||||
describe("ConditionalFilter / validation / numeric", () => {
|
||||
it.each([
|
||||
[
|
||||
"12345678901234567890123456",
|
||||
{ rightText: "The value is too long.", row: 0 },
|
||||
],
|
||||
["123", false],
|
||||
[["123", "321"], false],
|
||||
[["100", "1"], { rightText: "The value must be higher", row: 0 }],
|
||||
])("should validate %p", (value, expected) => {
|
||||
// Arrange
|
||||
const element = new FilterElement(
|
||||
new ExpressionValue("price", "Price", "price"),
|
||||
new Condition(
|
||||
ConditionOptions.fromStaticElementName("price"),
|
||||
new ConditionSelected(
|
||||
value,
|
||||
{
|
||||
type: "price",
|
||||
value: "price",
|
||||
label: "Price",
|
||||
},
|
||||
[],
|
||||
false,
|
||||
),
|
||||
false,
|
||||
),
|
||||
false,
|
||||
);
|
||||
// Act
|
||||
const result = numeric(element, 0);
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
|
@ -1,41 +1,41 @@
|
|||
import { FilterElement } from "../FilterElement"
|
||||
import { FilterElement } from "../FilterElement";
|
||||
|
||||
const isTooLong = (value: string, row: number) => {
|
||||
if (value.length > 25) {
|
||||
return {
|
||||
row,
|
||||
rightText: "The value is too long."
|
||||
}
|
||||
rightText: "The value is too long.",
|
||||
};
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const numeric = (element: FilterElement, row: number) => {
|
||||
const { value } = element.condition.selected
|
||||
const { value } = element.condition.selected;
|
||||
|
||||
if (Array.isArray(value) && value.length === 2) {
|
||||
const [sLte, sGte] = value as [string, string]
|
||||
const errorsLte = isTooLong(sLte, row)
|
||||
const errorsGte = isTooLong(sLte, row)
|
||||
const [sLte, sGte] = value as [string, string];
|
||||
const errorsLte = isTooLong(sLte, row);
|
||||
const errorsGte = isTooLong(sLte, row);
|
||||
|
||||
if (errorsLte) return errorsGte
|
||||
if (errorsGte) return errorsGte
|
||||
if (errorsLte) return errorsGte;
|
||||
if (errorsGte) return errorsGte;
|
||||
|
||||
const lte = parseFloat(sLte)
|
||||
const gte = parseFloat(sGte)
|
||||
const lte = parseFloat(sLte);
|
||||
const gte = parseFloat(sGte);
|
||||
|
||||
if (lte > gte) {
|
||||
return {
|
||||
row,
|
||||
rightText: "The value must be higher"
|
||||
}
|
||||
rightText: "The value must be higher",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
return isTooLong(value, row)
|
||||
return isTooLong(value, row);
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { ConditionSelected } from "../FilterElement/ConditionSelected";
|
||||
import { UrlEntry } from "./UrlToken";
|
||||
|
||||
describe("UrlEntry", () => {
|
||||
it("should create an instance with a single value", () => {
|
||||
// Arrange & Act
|
||||
const entry = new UrlEntry("key", "value");
|
||||
// Assert
|
||||
expect(entry).toEqual({ key: "value" });
|
||||
});
|
||||
|
||||
it("should create an instance with an array value", () => {
|
||||
// Arrange & Act
|
||||
const entry = new UrlEntry("key", ["value1", "value2"]);
|
||||
// Assert
|
||||
expect(entry).toEqual({ key: ["value1", "value2"] });
|
||||
});
|
||||
|
||||
it("should create an instance from a ParsedQs object", () => {
|
||||
// Arrange & Act
|
||||
const entry = UrlEntry.fromQs({ key: "value" });
|
||||
// Assert
|
||||
expect(entry).toEqual({ key: "value" });
|
||||
});
|
||||
|
||||
it("should create an instance for an attribute condition", () => {
|
||||
// Arrange
|
||||
const condition = new ConditionSelected(
|
||||
{
|
||||
label: "5l",
|
||||
slug: "value-id",
|
||||
value: "value-id",
|
||||
originalSlug: "5l",
|
||||
},
|
||||
{
|
||||
type: "DROPDOWN",
|
||||
value: "bottle-size",
|
||||
label: "Bottle Size",
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
// Act
|
||||
const entry = UrlEntry.forAttribute(condition, "bottle-size");
|
||||
// Assert
|
||||
expect(entry).toEqual({ "s-1.bottle-size": "value-id" });
|
||||
});
|
||||
|
||||
it.skip("should create an instance for a static condition", () => {
|
||||
// Arrange
|
||||
const condition = new ConditionSelected(
|
||||
"value",
|
||||
{
|
||||
type: "price",
|
||||
value: "price",
|
||||
label: "Price",
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
// Act
|
||||
const entry = UrlEntry.forStatic(condition, "price");
|
||||
// Assert
|
||||
expect(entry).toEqual({ static: "value" });
|
||||
});
|
||||
|
||||
it("should return the correct info", () => {
|
||||
// Arrange
|
||||
const entry = new UrlEntry("s0.price", "value");
|
||||
// Act
|
||||
const info = entry.getInfo();
|
||||
// Assert
|
||||
expect(info).toEqual({
|
||||
key: "s0.price",
|
||||
value: "value",
|
||||
entryName: "price",
|
||||
type: "s",
|
||||
conditionKid: "is",
|
||||
});
|
||||
});
|
||||
});
|
|
@ -33,9 +33,11 @@ export const useUrlValueProvider = (
|
|||
const [value, setValue] = useState<FilterContainer>([]);
|
||||
|
||||
const activeTab = params.get("activeTab");
|
||||
const query = params.get("query");
|
||||
params.delete("asc");
|
||||
params.delete("sort");
|
||||
params.delete("activeTab");
|
||||
params.delete("query");
|
||||
|
||||
const tokenizedUrl = new TokenArray(params.toString());
|
||||
const fetchingParams = tokenizedUrl.getFetchingParams();
|
||||
|
@ -56,6 +58,7 @@ export const useUrlValueProvider = (
|
|||
search: stringify({
|
||||
...prepareStructure(filterValue),
|
||||
...{ activeTab: activeTab || undefined },
|
||||
...{ query: query || undefined },
|
||||
}),
|
||||
});
|
||||
setValue(filterValue);
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ExpressionValue } from "./FilterElement/FilterElement";
|
|||
import { FilterValueProvider } from "./FilterValueProvider";
|
||||
import { useContainerState } from "./useContainerState";
|
||||
|
||||
describe("useContainerState", () => {
|
||||
describe("ConditionalFilter / useContainerState", () => {
|
||||
const valueProvider: FilterValueProvider = {
|
||||
loading: false,
|
||||
value: [],
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { AttributeInputTypeEnum } from "@dashboard/graphql";
|
||||
import { act,renderHook } from "@testing-library/react-hooks";
|
||||
import { act, renderHook } from "@testing-library/react-hooks";
|
||||
|
||||
import { STATIC_OPTIONS } from "./constants";
|
||||
import { useFilterLeftOperandsProvider } from "./useFilterLeftOperands";
|
||||
|
||||
describe("useFilterLeftOperandsProvider", () => {
|
||||
describe("ConditionalFilter / useFilterLeftOperandsProvider", () => {
|
||||
it("should set unique operands", () => {
|
||||
// Arrange
|
||||
const { result } = renderHook(() => useFilterLeftOperandsProvider());
|
||||
|
|
|
@ -13867,13 +13867,14 @@ export type InitialProductFilterProductTypesQueryHookResult = ReturnType<typeof
|
|||
export type InitialProductFilterProductTypesLazyQueryHookResult = ReturnType<typeof useInitialProductFilterProductTypesLazyQuery>;
|
||||
export type InitialProductFilterProductTypesQueryResult = Apollo.QueryResult<Types.InitialProductFilterProductTypesQuery, Types.InitialProductFilterProductTypesQueryVariables>;
|
||||
export const ProductListDocument = gql`
|
||||
query ProductList($first: Int, $after: String, $last: Int, $before: String, $filter: ProductFilterInput, $where: ProductWhereInput, $channel: String, $sort: ProductOrder, $hasChannel: Boolean!) {
|
||||
query ProductList($first: Int, $after: String, $last: Int, $before: String, $filter: ProductFilterInput, $search: String, $where: ProductWhereInput, $channel: String, $sort: ProductOrder, $hasChannel: Boolean!) {
|
||||
products(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
search: $search
|
||||
where: $where
|
||||
sortBy: $sort
|
||||
channel: $channel
|
||||
|
@ -13917,6 +13918,7 @@ ${ProductListAttributeFragmentDoc}`;
|
|||
* last: // value for 'last'
|
||||
* before: // value for 'before'
|
||||
* filter: // value for 'filter'
|
||||
* search: // value for 'search'
|
||||
* where: // value for 'where'
|
||||
* channel: // value for 'channel'
|
||||
* sort: // value for 'sort'
|
||||
|
|
|
@ -10677,6 +10677,7 @@ export type ProductListQueryVariables = Exact<{
|
|||
last?: InputMaybe<Scalars['Int']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<ProductFilterInput>;
|
||||
search?: InputMaybe<Scalars['String']>;
|
||||
where?: InputMaybe<ProductWhereInput>;
|
||||
channel?: InputMaybe<Scalars['String']>;
|
||||
sort?: InputMaybe<ProductOrder>;
|
||||
|
|
|
@ -61,6 +61,7 @@ export const productListQuery = gql`
|
|||
$last: Int
|
||||
$before: String
|
||||
$filter: ProductFilterInput
|
||||
$search: String
|
||||
$where: ProductWhereInput
|
||||
$channel: String
|
||||
$sort: ProductOrder
|
||||
|
@ -72,6 +73,7 @@ export const productListQuery = gql`
|
|||
first: $first
|
||||
last: $last
|
||||
filter: $filter
|
||||
search: $search
|
||||
where: $where
|
||||
sortBy: $sort
|
||||
channel: $channel
|
||||
|
|
|
@ -66,7 +66,6 @@ export function getSortQueryVariables(
|
|||
}
|
||||
|
||||
const field = getSortQueryField(params.sort);
|
||||
// TODO: apply fix after https://github.com/saleor/saleor/issues/13557 is done
|
||||
return {
|
||||
direction,
|
||||
field,
|
||||
|
|
Loading…
Reference in a new issue