From fcd64f65ebe82b5476c115e40ee07fd2c003d7cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Krzysztof=20=C5=BBuraw?=
<9116238+krzysztofzuraw@users.noreply.github.com>
Date: Thu, 13 Jul 2023 09:31:16 +0200
Subject: [PATCH] Experimental filters: use context for data providers (#3899)
---
.changeset/ninety-geese-grin.md | 5 ++++
.../components/ExpressionFilters.tsx | 4 +--
.../API/FilterAPIProvider.ts | 6 ++++
.../API/ProductFilterAPIProvider.tsx | 29 +++++++++++++++++++
.../API/initialState/useInitalAPIState.tsx | 9 +++++-
.../ConditionalFilter/ConditionalFilters.tsx | 24 +++++----------
.../ConditionalFilter/FiltersArea.tsx | 21 +++++---------
.../ValueProvider/useUrlValueProvider.ts | 8 +++--
.../ConditionalFilter/context/consumer.tsx | 15 ++++++++++
.../ConditionalFilter/context/context.ts | 11 +++++++
.../ConditionalFilter/context/index.ts | 2 ++
.../ConditionalFilter/context/provider.tsx | 24 +++++++++++++++
src/components/ConditionalFilter/index.ts | 1 +
src/components/ConditionalFilter/index.tsx | 25 ----------------
src/products/index.tsx | 7 ++++-
15 files changed, 129 insertions(+), 62 deletions(-)
create mode 100644 .changeset/ninety-geese-grin.md
create mode 100644 src/components/ConditionalFilter/context/consumer.tsx
create mode 100644 src/components/ConditionalFilter/context/context.ts
create mode 100644 src/components/ConditionalFilter/context/index.ts
create mode 100644 src/components/ConditionalFilter/context/provider.tsx
create mode 100644 src/components/ConditionalFilter/index.ts
delete mode 100644 src/components/ConditionalFilter/index.tsx
diff --git a/.changeset/ninety-geese-grin.md b/.changeset/ninety-geese-grin.md
new file mode 100644
index 000000000..8370e3454
--- /dev/null
+++ b/.changeset/ninety-geese-grin.md
@@ -0,0 +1,5 @@
+---
+"saleor-dashboard": patch
+---
+
+Experimental filters: use context for data providers
diff --git a/src/components/AppLayout/ListFilters/components/ExpressionFilters.tsx b/src/components/AppLayout/ListFilters/components/ExpressionFilters.tsx
index 13d1f6a6c..0ae82fd2d 100644
--- a/src/components/AppLayout/ListFilters/components/ExpressionFilters.tsx
+++ b/src/components/AppLayout/ListFilters/components/ExpressionFilters.tsx
@@ -1,4 +1,4 @@
-import { ConditionalProductFilters } from "@dashboard/components/ConditionalFilter";
+import { ConditionalFilters } from "@dashboard/components/ConditionalFilter";
import { Box, Button, Popover } from "@saleor/macaw-ui/next";
import React from "react";
@@ -10,7 +10,7 @@ export const ExpressionFilters = () => (
-
+
diff --git a/src/components/ConditionalFilter/API/FilterAPIProvider.ts b/src/components/ConditionalFilter/API/FilterAPIProvider.ts
index 0e448a05d..5c0d5b2a8 100644
--- a/src/components/ConditionalFilter/API/FilterAPIProvider.ts
+++ b/src/components/ConditionalFilter/API/FilterAPIProvider.ts
@@ -1,6 +1,8 @@
import { FilterContainer } from "../FilterElement";
import { ItemOption } from "../FilterElement/ConditionValue";
import { LeftOperand } from "../LeftOperandsProvider";
+import { FetchingParams } from "../ValueProvider/TokenArray/fetchingParams";
+import { InitialStateResponse } from "./InitialStateResponse";
export interface FilterAPIProvider {
fetchRightOptions: (
@@ -9,4 +11,8 @@ export interface FilterAPIProvider {
inputValue: string,
) => Promise;
fetchLeftOptions: (inputValue: string) => Promise;
+ useInitialState: (fetchingParams: FetchingParams) => {
+ data: InitialStateResponse;
+ loading: boolean;
+ };
}
diff --git a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx
index 0ccc5436b..4bc5a671a 100644
--- a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx
+++ b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx
@@ -1,6 +1,7 @@
import { ApolloClient, useApolloClient } from "@apollo/client";
import { FilterContainer, FilterElement } from "../FilterElement";
+import { FetchingParams } from "../ValueProvider/TokenArray/fetchingParams";
import { FilterAPIProvider } from "./FilterAPIProvider";
import {
AttributeChoicesHandler,
@@ -11,6 +12,11 @@ import {
Handler,
ProductTypeHandler,
} from "./Handler";
+import {
+ createInitialStateFromData,
+ useDataFromAPI,
+} from "./initialState/helpers";
+import { InitialStateResponse } from "./InitialStateResponse";
const getFilterElement = (
value: FilterContainer,
@@ -79,8 +85,31 @@ export const useProductFilterAPIProvider = (): FilterAPIProvider => {
return handler.fetch();
};
+ const useInitialState = (fetchingParams: FetchingParams) => {
+ const { data, loading } = useDataFromAPI({
+ ...fetchingParams,
+ });
+
+ const initialState = createInitialStateFromData(
+ data,
+ fetchingParams.channel,
+ );
+
+ return {
+ data: new InitialStateResponse(
+ initialState.category,
+ initialState.attribute,
+ initialState.channel,
+ initialState.collection,
+ initialState.producttype,
+ ),
+ loading,
+ };
+ };
+
return {
fetchRightOptions,
fetchLeftOptions,
+ useInitialState,
};
};
diff --git a/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx b/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx
index 1c3a80251..671d29c98 100644
--- a/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx
+++ b/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx
@@ -2,7 +2,14 @@ import { FetchingParams } from "../../ValueProvider/TokenArray/fetchingParams";
import { InitialStateResponse } from "../InitialStateResponse";
import { createInitialStateFromData, useDataFromAPI } from "./helpers";
-export const useInitialAPIState = (props: FetchingParams) => {
+export interface InitialStateAPIProvider {
+ data: InitialStateResponse;
+ loading: boolean;
+}
+
+export const useInitialAPIState = (
+ props: FetchingParams,
+): InitialStateAPIProvider => {
const { data, loading } = useDataFromAPI({
...props,
});
diff --git a/src/components/ConditionalFilter/ConditionalFilters.tsx b/src/components/ConditionalFilter/ConditionalFilters.tsx
index 968b3caa6..430be636c 100644
--- a/src/components/ConditionalFilter/ConditionalFilters.tsx
+++ b/src/components/ConditionalFilter/ConditionalFilters.tsx
@@ -1,24 +1,16 @@
import { Box, Text } from "@saleor/macaw-ui/next";
-import React from "react";
+import React, { FC } from "react";
-import { FilterAPIProvider } from "./API/FilterAPIProvider";
+import { useConditionalFilterContext } from "./context";
import { FilterContainer } from "./FilterElement";
import { FiltersArea } from "./FiltersArea";
-import { FilterValueProvider } from "./FilterValueProvider";
-import { LeftOperandsProvider } from "./LeftOperandsProvider";
-interface ConditionalFiltersProps {
- valueProvider: FilterValueProvider
- apiProvider: FilterAPIProvider
- leftOperandsProvider: LeftOperandsProvider
- onConfirm: (value: FilterContainer) => void
-}
+export const ConditionalFilters: FC = () => {
+ const { valueProvider } = useConditionalFilterContext();
-export const ConditionalFilters = ({ valueProvider, apiProvider, leftOperandsProvider, onConfirm }: ConditionalFiltersProps) => {
const handleConfirm = (value: FilterContainer) => {
- valueProvider.persist(value)
- onConfirm(value)
- }
+ valueProvider.persist(value);
+ };
return (
@@ -26,12 +18,10 @@ export const ConditionalFilters = ({ valueProvider, apiProvider, leftOperandsPro
Loading...
) : (
)}
);
-};
\ No newline at end of file
+};
diff --git a/src/components/ConditionalFilter/FiltersArea.tsx b/src/components/ConditionalFilter/FiltersArea.tsx
index 2caf7253a..5ce4af1b7 100644
--- a/src/components/ConditionalFilter/FiltersArea.tsx
+++ b/src/components/ConditionalFilter/FiltersArea.tsx
@@ -1,23 +1,18 @@
-import {
- _ExperimentalFilters,
- Box,
- FilterEvent,
-} from "@saleor/macaw-ui/next";
+import { _ExperimentalFilters, Box, FilterEvent } from "@saleor/macaw-ui/next";
import React from "react";
-import { FilterAPIProvider } from "./API/FilterAPIProvider";
+import { useConditionalFilterContext } from "./context";
import { FilterContainer } from "./FilterElement";
-import { LeftOperandsProvider } from "./LeftOperandsProvider";
import { useFilterContainer } from "./useFilterContainer";
interface FiltersAreaProps {
- filterValue: FilterContainer
- apiProvider: FilterAPIProvider
- leftOperandsProvider: LeftOperandsProvider
- onConfirm: (value: FilterContainer) => void
+ filterValue: FilterContainer;
+ onConfirm: (value: FilterContainer) => void;
}
-export const FiltersArea = ({ filterValue, apiProvider, leftOperandsProvider, onConfirm }: FiltersAreaProps) => {
+export const FiltersArea = ({ filterValue, onConfirm }: FiltersAreaProps) => {
+ const { apiProvider, leftOperandsProvider } = useConditionalFilterContext();
+
const {
value,
addEmpty,
@@ -30,7 +25,7 @@ export const FiltersArea = ({ filterValue, apiProvider, leftOperandsProvider, on
} = useFilterContainer(filterValue, apiProvider, leftOperandsProvider);
const handleStateChange = async (event: FilterEvent["detail"]) => {
- if (!event) return
+ if (!event) return;
if (event.type === "row.add") {
addEmpty();
diff --git a/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts b/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts
index 9a38c91af..7141d500f 100644
--- a/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts
+++ b/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts
@@ -1,7 +1,7 @@
import { stringify } from "qs";
import useRouter from "use-react-router";
-import { useInitialAPIState } from "../API/initialState/useInitalAPIState";
+import { FilterAPIProvider } from "../API/FilterAPIProvider";
import { FilterContainer } from "../FilterElement";
import { FilterValueProvider } from "../FilterValueProvider";
import { useTokenArray } from "./TokenArray";
@@ -22,7 +22,9 @@ const prepareStructure = (filterValue: FilterContainer): Structure =>
return f.asUrlEntry();
});
-export const useUrlValueProvider = (): FilterValueProvider => {
+export const useUrlValueProvider = (
+ apiProvider: FilterAPIProvider,
+): FilterValueProvider => {
const router = useRouter();
const params = new URLSearchParams(router.location.search);
params.delete("asc");
@@ -30,7 +32,7 @@ export const useUrlValueProvider = (): FilterValueProvider => {
const tokenizedUrl = useTokenArray(params.toString());
const fetchingParams = tokenizedUrl.getFetchingParams();
- const { data, loading } = useInitialAPIState(fetchingParams);
+ const { data, loading } = apiProvider.useInitialState(fetchingParams);
const value = loading ? [] : tokenizedUrl.asFilterValuesFromResponse(data);
const persist = (filterValue: FilterContainer) => {
diff --git a/src/components/ConditionalFilter/context/consumer.tsx b/src/components/ConditionalFilter/context/consumer.tsx
new file mode 100644
index 000000000..72b51775e
--- /dev/null
+++ b/src/components/ConditionalFilter/context/consumer.tsx
@@ -0,0 +1,15 @@
+import { useContext } from "react";
+
+import { ConditionalFilterContext } from "./context";
+
+export const useConditionalFilterContext = () => {
+ const context = useContext(ConditionalFilterContext);
+
+ if (!context) {
+ throw new Error(
+ "Filter context must be used within ConditionalFilterContext.Provider.",
+ );
+ }
+
+ return context;
+};
diff --git a/src/components/ConditionalFilter/context/context.ts b/src/components/ConditionalFilter/context/context.ts
new file mode 100644
index 000000000..36ad1ed25
--- /dev/null
+++ b/src/components/ConditionalFilter/context/context.ts
@@ -0,0 +1,11 @@
+import { createContext } from "react";
+
+import { FilterAPIProvider } from "../API/FilterAPIProvider";
+import { FilterValueProvider } from "../FilterValueProvider";
+import { LeftOperandsProvider } from "../LeftOperandsProvider";
+
+export const ConditionalFilterContext = createContext<{
+ apiProvider: FilterAPIProvider;
+ valueProvider: FilterValueProvider;
+ leftOperandsProvider: LeftOperandsProvider;
+} | null>(null);
diff --git a/src/components/ConditionalFilter/context/index.ts b/src/components/ConditionalFilter/context/index.ts
new file mode 100644
index 000000000..0cad21ac5
--- /dev/null
+++ b/src/components/ConditionalFilter/context/index.ts
@@ -0,0 +1,2 @@
+export * from "./consumer";
+export * from "./provider";
diff --git a/src/components/ConditionalFilter/context/provider.tsx b/src/components/ConditionalFilter/context/provider.tsx
new file mode 100644
index 000000000..97e6895d4
--- /dev/null
+++ b/src/components/ConditionalFilter/context/provider.tsx
@@ -0,0 +1,24 @@
+import React, { FC } from "react";
+
+import { useProductFilterAPIProvider } from "../API/ProductFilterAPIProvider";
+import { useFilterLeftOperandsProvider } from "../useFilterLeftOperands";
+import { useUrlValueProvider } from "../ValueProvider/useUrlValueProvider";
+import { ConditionalFilterContext } from "./context";
+
+export const ConditionalProductFilterProvider: FC = ({ children }) => {
+ const apiProvider = useProductFilterAPIProvider();
+ const valueProvider = useUrlValueProvider(apiProvider);
+ const leftOperandsProvider = useFilterLeftOperandsProvider();
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/ConditionalFilter/index.ts b/src/components/ConditionalFilter/index.ts
new file mode 100644
index 000000000..93d1e6a6d
--- /dev/null
+++ b/src/components/ConditionalFilter/index.ts
@@ -0,0 +1 @@
+export * from "./ConditionalFilters";
diff --git a/src/components/ConditionalFilter/index.tsx b/src/components/ConditionalFilter/index.tsx
deleted file mode 100644
index 986a942a9..000000000
--- a/src/components/ConditionalFilter/index.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from "react";
-
-import { useProductFilterAPIProvider } from "./API/ProductFilterAPIProvider";
-import { ConditionalFilters } from "./ConditionalFilters";
-import { FilterContainer } from "./FilterElement";
-import { useFilterLeftOperandsProvider } from "./useFilterLeftOperands";
-import { useUrlValueProvider } from "./ValueProvider/useUrlValueProvider";
-
-
-export const ConditionalProductFilters = () => {
- const provider = useUrlValueProvider();
- const apiProvider = useProductFilterAPIProvider();
- const leftOperandsProvider = useFilterLeftOperandsProvider();
-
- // @ts-expect-error
- const handleConfirm = (value: FilterContainer) => {
- }
-
- return
-}
\ No newline at end of file
diff --git a/src/products/index.tsx b/src/products/index.tsx
index 7fbf0c475..2311ddb9c 100644
--- a/src/products/index.tsx
+++ b/src/products/index.tsx
@@ -1,3 +1,4 @@
+import { ConditionalProductFilterProvider } from "@dashboard/components/ConditionalFilter/context";
import { sectionNames } from "@dashboard/intl";
import { asSortParams } from "@dashboard/utils/sort";
import { getArrayQueryParam } from "@dashboard/utils/urls";
@@ -43,7 +44,11 @@ const ProductList: React.FC> = ({ location }) => {
ProductListUrlSortField,
);
- return ;
+ return (
+
+
+
+ );
};
const ProductUpdate: React.FC> = ({ match }) => {