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 }) => {