Fix double requests when search in order and product list (#3513)
* Fix double requests * Fix old handle filters * Remove not needed check in useFilterHandlers * Test useFilterHandlers hook
This commit is contained in:
parent
192be719fe
commit
0d690ff414
4 changed files with 470 additions and 36 deletions
310
src/hooks/useFilterHandlers.test.ts
Normal file
310
src/hooks/useFilterHandlers.test.ts
Normal file
|
@ -0,0 +1,310 @@
|
|||
import { renderHook } from "@testing-library/react-hooks";
|
||||
|
||||
import { useFilterHandlers } from "./useFilterHandlers";
|
||||
|
||||
jest.mock("./useNavigator", () => () => jest.fn());
|
||||
|
||||
describe("useFilterHandlers", () => {
|
||||
describe("resetFilters", () => {
|
||||
test("should run cleanup function and call createUrl function", () => {
|
||||
// Arrange
|
||||
const cleanupFn = jest.fn();
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(),
|
||||
createUrl,
|
||||
cleanupFn,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const resetFilters = result.current[1];
|
||||
|
||||
// Act
|
||||
resetFilters();
|
||||
|
||||
// Assert
|
||||
|
||||
expect(cleanupFn).toHaveBeenCalledTimes(1);
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeFilters", () => {
|
||||
test("should call cleanup function when provided", () => {
|
||||
const cleanupFn = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(),
|
||||
createUrl: jest.fn(),
|
||||
cleanupFn,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const [changeFilters] = result.current;
|
||||
|
||||
// Act
|
||||
changeFilters([]);
|
||||
|
||||
// Assert
|
||||
|
||||
expect(cleanupFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("should call createUrl function with with proper params when no filters", () => {
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(filter => ({
|
||||
[filter.name]: filter.value,
|
||||
})),
|
||||
createUrl,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const [changeFilters] = result.current;
|
||||
|
||||
// Act
|
||||
changeFilters([]);
|
||||
|
||||
// Assert
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
activeTab: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test("should call createUrl function with with proper params when filters selected", () => {
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(filter => ({
|
||||
[filter.name]: filter.value[0],
|
||||
})),
|
||||
createUrl,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const [changeFilters] = result.current;
|
||||
|
||||
// Act
|
||||
changeFilters([
|
||||
{
|
||||
name: "filter",
|
||||
value: ["value"],
|
||||
label: "test",
|
||||
active: true,
|
||||
multiple: false,
|
||||
},
|
||||
{
|
||||
name: "filterOther",
|
||||
value: ["valueOther"],
|
||||
label: "test",
|
||||
active: true,
|
||||
multiple: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// Assert
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
filter: "value",
|
||||
filterOther: "valueOther",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
activeTab: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test("should call createUrl function with active tab value when keepActiveTab is true", () => {
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(filter => ({
|
||||
[filter.name]: filter.value[0],
|
||||
})),
|
||||
createUrl,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const [changeFilters] = result.current;
|
||||
|
||||
// Act
|
||||
changeFilters([
|
||||
{
|
||||
name: "filter",
|
||||
value: ["value"],
|
||||
label: "test",
|
||||
active: true,
|
||||
multiple: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// Assert
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
filter: "value",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
activeTab: "tab",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleSearchChange", () => {
|
||||
test("should call createUrl function when provided", () => {
|
||||
const cleanupFn = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(),
|
||||
createUrl: jest.fn(),
|
||||
cleanupFn,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const handleSearchChange = result.current[2];
|
||||
|
||||
// Act
|
||||
handleSearchChange("queryTest");
|
||||
|
||||
// Assert
|
||||
|
||||
expect(cleanupFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("should run createUrl function with params and query", () => {
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(filter => ({
|
||||
[filter.name]: filter.value[0],
|
||||
})),
|
||||
createUrl,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
defaultSortField: "",
|
||||
keepActiveTab: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleSearchChange = result.current[2];
|
||||
|
||||
// Act
|
||||
handleSearchChange("queryTest");
|
||||
|
||||
// Assert
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
after: undefined,
|
||||
before: undefined,
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "queryTest",
|
||||
activeTab: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test("should run createUrl function with sort rank and asc false when hasSortWithRank is true", () => {
|
||||
const createUrl = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useFilterHandlers({
|
||||
getFilterQueryParam: jest.fn(filter => ({
|
||||
[filter.name]: filter.value[0],
|
||||
})),
|
||||
createUrl,
|
||||
params: {
|
||||
activeTab: "tab",
|
||||
asc: true,
|
||||
sort: "sort",
|
||||
query: "query",
|
||||
},
|
||||
hasSortWithRank: true,
|
||||
defaultSortField: "",
|
||||
keepActiveTab: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleSearchChange = result.current[2];
|
||||
|
||||
// Act
|
||||
handleSearchChange("queryTest");
|
||||
|
||||
// Assert
|
||||
expect(createUrl).toHaveBeenCalledWith({
|
||||
after: undefined,
|
||||
before: undefined,
|
||||
asc: false,
|
||||
sort: "rank",
|
||||
query: "queryTest",
|
||||
activeTab: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
141
src/hooks/useFilterHandlers.ts
Normal file
141
src/hooks/useFilterHandlers.ts
Normal file
|
@ -0,0 +1,141 @@
|
|||
import { IFilter } from "@dashboard/components/Filter";
|
||||
import { ActiveTab, Pagination, Search, Sort } from "@dashboard/types";
|
||||
import {
|
||||
GetFilterQueryParam,
|
||||
getFilterQueryParams,
|
||||
} from "@dashboard/utils/filters";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import useNavigator from "./useNavigator";
|
||||
|
||||
type RequiredParams = ActiveTab &
|
||||
Search &
|
||||
Sort<any> &
|
||||
Pagination & { presestesChanged?: string };
|
||||
type CreateUrl = (params: RequiredParams) => string;
|
||||
type CreateFilterHandlers<TFilterKeys extends string> = [
|
||||
(filter: IFilter<TFilterKeys>) => void,
|
||||
() => void,
|
||||
(query: string) => void,
|
||||
];
|
||||
|
||||
export const useFilterHandlers = <
|
||||
TFilterKeys extends string,
|
||||
TFilters extends {},
|
||||
SortField extends string,
|
||||
>(opts: {
|
||||
getFilterQueryParam: GetFilterQueryParam<TFilterKeys, TFilters>;
|
||||
createUrl: CreateUrl;
|
||||
params: RequiredParams;
|
||||
cleanupFn?: () => void;
|
||||
keepActiveTab?: boolean;
|
||||
defaultSortField: SortField;
|
||||
hasSortWithRank?: boolean;
|
||||
}): CreateFilterHandlers<TFilterKeys> => {
|
||||
const {
|
||||
getFilterQueryParam,
|
||||
createUrl,
|
||||
params,
|
||||
cleanupFn,
|
||||
keepActiveTab,
|
||||
defaultSortField,
|
||||
hasSortWithRank = false,
|
||||
} = opts;
|
||||
|
||||
const navigate = useNavigator();
|
||||
const prevAsc = useRef<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const hasQuery = !!params.query?.trim();
|
||||
if (hasQuery || params.sort === "rank") {
|
||||
prevAsc.current = params.asc;
|
||||
}
|
||||
}, [params.asc, params.query, params.sort]);
|
||||
|
||||
const getActiveTabValue = (removeActiveTab: boolean) => {
|
||||
if (!keepActiveTab || removeActiveTab) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return params.activeTab;
|
||||
};
|
||||
|
||||
const changeFilters = (filters: IFilter<TFilterKeys>) => {
|
||||
if (!!cleanupFn) {
|
||||
cleanupFn();
|
||||
}
|
||||
const filtersQueryParams = getFilterQueryParams(
|
||||
filters,
|
||||
getFilterQueryParam,
|
||||
);
|
||||
navigate(
|
||||
createUrl({
|
||||
...params,
|
||||
...filtersQueryParams,
|
||||
activeTab: getActiveTabValue(
|
||||
checkIfParamsEmpty(filtersQueryParams) && !params.query?.length,
|
||||
),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const resetFilters = () => {
|
||||
if (!!cleanupFn) {
|
||||
cleanupFn();
|
||||
}
|
||||
|
||||
navigate(
|
||||
createUrl({
|
||||
asc: params.asc,
|
||||
sort: params.sort,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleSearchChange = (query: string) => {
|
||||
if (!!cleanupFn) {
|
||||
cleanupFn();
|
||||
}
|
||||
const trimmedQuery = query?.trim() ?? "";
|
||||
const hasQuery = !!trimmedQuery;
|
||||
const sortWithoutQuery =
|
||||
params.sort === "rank" ? defaultSortField : params.sort;
|
||||
const sortWithQuery = "rank" as SortField;
|
||||
|
||||
const getAscParam = () => {
|
||||
if (hasQuery) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevAsc !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return params.asc;
|
||||
};
|
||||
|
||||
navigate(
|
||||
createUrl({
|
||||
...params,
|
||||
after: undefined,
|
||||
before: undefined,
|
||||
activeTab: getActiveTabValue(checkIfParamsEmpty(params) && hasQuery),
|
||||
query: hasQuery ? trimmedQuery : undefined,
|
||||
...(hasSortWithRank && {
|
||||
sort: hasQuery ? sortWithQuery : sortWithoutQuery,
|
||||
asc: getAscParam(),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return [changeFilters, resetFilters, handleSearchChange];
|
||||
};
|
||||
|
||||
function checkIfParamsEmpty(params: RequiredParams): boolean {
|
||||
const paramsToOmit = ["activeTab", "sort", "asc", "query"];
|
||||
|
||||
return Object.entries(params)
|
||||
.filter(([name]) => !paramsToOmit.includes(name))
|
||||
.every(([_, value]) => value === undefined);
|
||||
}
|
|
@ -9,6 +9,7 @@ import {
|
|||
useOrderDraftCreateMutation,
|
||||
useOrderListQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import { useFilterHandlers } from "@dashboard/hooks/useFilterHandlers";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
|
@ -17,7 +18,6 @@ import usePaginator, {
|
|||
createPaginationState,
|
||||
PaginatorContext,
|
||||
} from "@dashboard/hooks/usePaginator";
|
||||
import { useSortRedirects } from "@dashboard/hooks/useSortRedirects";
|
||||
import {
|
||||
getActiveTabIndexAfterTabDelete,
|
||||
getNextUniqueTabName,
|
||||
|
@ -25,7 +25,6 @@ import {
|
|||
import { ListViews } from "@dashboard/types";
|
||||
import { prepareQs } from "@dashboard/utils/filters/qs";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||
import createSortHandler from "@dashboard/utils/handlers/sortHandler";
|
||||
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
||||
import { getSortParams } from "@dashboard/utils/sort";
|
||||
|
@ -38,7 +37,6 @@ import {
|
|||
orderListUrl,
|
||||
OrderListUrlDialog,
|
||||
OrderListUrlQueryParams,
|
||||
OrderListUrlSortField,
|
||||
orderSettingsPath,
|
||||
orderUrl,
|
||||
} from "../../urls";
|
||||
|
@ -100,13 +98,12 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
const currentTab =
|
||||
params.activeTab !== undefined ? parseInt(params.activeTab, 10) : undefined;
|
||||
|
||||
const [changeFilters, resetFilters, handleSearchChange] =
|
||||
createFilterHandlers({
|
||||
const [changeFilters, resetFilters, handleSearchChange] = useFilterHandlers({
|
||||
createUrl: orderListUrl,
|
||||
getFilterQueryParam,
|
||||
navigate,
|
||||
params,
|
||||
keepActiveTab: true,
|
||||
defaultSortField: DEFAULT_SORT_KEY,
|
||||
hasSortWithRank: true,
|
||||
});
|
||||
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
|
@ -196,12 +193,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
|||
|
||||
const handleSort = createSortHandler(navigate, orderListUrl, params);
|
||||
|
||||
useSortRedirects<OrderListUrlSortField>({
|
||||
params,
|
||||
defaultSortField: DEFAULT_SORT_KEY,
|
||||
urlFunc: orderListUrl,
|
||||
});
|
||||
|
||||
return (
|
||||
<PaginatorContext.Provider value={paginationValues}>
|
||||
<OrderListPage
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
useWarehouseListQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import useBackgroundTask from "@dashboard/hooks/useBackgroundTask";
|
||||
import { useFilterHandlers } from "@dashboard/hooks/useFilterHandlers";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
|
@ -59,7 +60,6 @@ import useProductTypeSearch from "@dashboard/searches/useProductTypeSearch";
|
|||
import { ListViews } from "@dashboard/types";
|
||||
import { prepareQs } from "@dashboard/utils/filters/qs";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
|
||||
import { mapEdgesToItems, mapNodeToChoice } from "@dashboard/utils/maps";
|
||||
import { getSortUrlVariables } from "@dashboard/utils/sort";
|
||||
import { DialogContentText } from "@material-ui/core";
|
||||
|
@ -68,7 +68,6 @@ import { stringify } from "qs";
|
|||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { useSortRedirects } from "../../../hooks/useSortRedirects";
|
||||
import ProductListPage from "../../components/ProductListPage";
|
||||
import {
|
||||
deleteFilterTab,
|
||||
|
@ -79,7 +78,7 @@ import {
|
|||
saveFilterTab,
|
||||
updateFilterTab,
|
||||
} from "./filters";
|
||||
import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
||||
import { DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
||||
import {
|
||||
getActiveTabIndexAfterTabDelete,
|
||||
getAvailableProductKinds,
|
||||
|
@ -202,13 +201,6 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
|||
channel => channel.slug === params.channel,
|
||||
);
|
||||
|
||||
useSortRedirects<ProductListUrlSortField>({
|
||||
params,
|
||||
defaultSortField: DEFAULT_SORT_KEY,
|
||||
urlFunc: productListUrl,
|
||||
resetToDefault: !canBeSorted(params.sort, !!selectedChannel),
|
||||
});
|
||||
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
ProductListUrlDialog,
|
||||
ProductListUrlQueryParams
|
||||
|
@ -263,14 +255,14 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
|||
},
|
||||
});
|
||||
|
||||
const [changeFilters, resetFilters, handleSearchChange] =
|
||||
createFilterHandlers({
|
||||
const [changeFilters, resetFilters, handleSearchChange] = useFilterHandlers({
|
||||
cleanupFn: clearRowSelection,
|
||||
createUrl: productListUrl,
|
||||
getFilterQueryParam,
|
||||
navigate,
|
||||
params,
|
||||
keepActiveTab: true,
|
||||
defaultSortField: DEFAULT_SORT_KEY,
|
||||
hasSortWithRank: true,
|
||||
});
|
||||
|
||||
const handleTabChange = (tab: number) => {
|
||||
|
|
Loading…
Reference in a new issue