From 544d13754a21e8a3bcbe8d3888a1589241b71383 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 10 Sep 2019 17:14:11 +0200 Subject: [PATCH] Split filters, searches and tabs --- src/components/Filter/FilterActions.tsx | 109 +++++++++++ src/components/FilterBar/FilterBar.tsx | 21 ++- src/components/SearchBar/SearchBar.tsx | 122 +++++++++++++ src/components/SearchBar/index.ts | 2 + src/components/TableFilter/FilterChips.tsx | 169 +++++++----------- src/fixtures.ts | 37 ++-- .../OrderListFilter/OrderListFilter.tsx | 3 +- .../OrderListPage/OrderListPage.tsx | 7 +- src/orders/urls.ts | 5 +- src/orders/views/OrderList/OrderList.tsx | 2 +- .../ProductListFilter/ProductListFilter.tsx | 24 ++- .../ProductListPage/ProductListPage.tsx | 17 +- src/products/urls.ts | 12 +- .../views/ProductList/ProductList.tsx | 2 +- src/products/views/ProductList/filters.ts | 1 + src/types.ts | 37 ++-- 16 files changed, 392 insertions(+), 178 deletions(-) create mode 100644 src/components/Filter/FilterActions.tsx create mode 100644 src/components/SearchBar/SearchBar.tsx create mode 100644 src/components/SearchBar/index.ts diff --git a/src/components/Filter/FilterActions.tsx b/src/components/Filter/FilterActions.tsx new file mode 100644 index 000000000..bf048a9d7 --- /dev/null +++ b/src/components/Filter/FilterActions.tsx @@ -0,0 +1,109 @@ +import { Theme } from "@material-ui/core/styles"; +import TextField, { TextFieldProps } from "@material-ui/core/TextField"; +import { makeStyles } from "@material-ui/styles"; +import React from "react"; + +import { FilterContentSubmitData, IFilter } from "../Filter"; +import Filter from "./Filter"; + +const useInputStyles = makeStyles({ + input: { + padding: "10px 12px" + }, + root: { + flex: 1 + } +}); + +const Search: React.FC = props => { + const classes = useInputStyles({}); + return ( + + ); +}; + +const useStyles = makeStyles( + (theme: Theme) => ({ + actionContainer: { + display: "flex", + flexWrap: "wrap", + padding: `${theme.spacing.unit * 1.5}px ${theme.spacing.unit * 3}px ${ + theme.spacing.unit + }px` + } + }), + { + name: "FilterActions" + } +); + +export interface FilterActionsPropsSearch { + placeholder: string; + search: string; + onSearchChange: (event: React.ChangeEvent) => void; +} +export interface FilterActionsPropsFilters { + currencySymbol: string; + menu: IFilter; + filterLabel: string; + onFilterAdd: (filter: FilterContentSubmitData) => void; +} + +export const FilterActionsOnlySearch: React.FC< + FilterActionsPropsSearch +> = props => { + const { onSearchChange, placeholder, search } = props; + const classes = useStyles(props); + + return ( +
+ +
+ ); +}; + +export type FilterActionsProps = FilterActionsPropsSearch & + FilterActionsPropsFilters; +const FilterActions: React.FC = props => { + const { + currencySymbol, + filterLabel, + menu, + onFilterAdd, + onSearchChange, + placeholder, + search + } = props; + const classes = useStyles(props); + + return ( +
+ + +
+ ); +}; + +FilterActions.displayName = "FilterActions"; +export default FilterActions; diff --git a/src/components/FilterBar/FilterBar.tsx b/src/components/FilterBar/FilterBar.tsx index e55ca25da..b90ee9c13 100644 --- a/src/components/FilterBar/FilterBar.tsx +++ b/src/components/FilterBar/FilterBar.tsx @@ -6,9 +6,8 @@ import Debounce from "../Debounce"; import { IFilter } from "../Filter/types"; import FilterTabs, { FilterChips, FilterTab } from "../TableFilter"; -export interface FilterBarProps - extends FilterProps { - filterMenu: IFilter; +export interface FilterBarProps extends FilterProps { + filterMenu: IFilter; } const FilterBar: React.FC = ({ @@ -16,32 +15,32 @@ const FilterBar: React.FC = ({ currencySymbol, filterLabel, filtersList, - filterTabs, filterMenu, currentTab, initialSearch, searchPlaceholder, + tabs, onAll, onSearchChange, onFilterAdd, - onFilterSave, onTabChange, - onFilterDelete + onTabDelete, + onTabSave }) => { const intl = useIntl(); const [search, setSearch] = React.useState(initialSearch); React.useEffect(() => setSearch(initialSearch), [currentTab, initialSearch]); - const isCustom = currentTab === filterTabs.length + 1; + const isCustom = currentTab === tabs.length + 1; return ( <> - {filterTabs.map((tab, tabIndex) => ( + {tabs.map((tab, tabIndex) => ( onTabChange(tabIndex + 1)} - label={tab.name} + label={tab} key={tabIndex} /> ))} @@ -72,9 +71,9 @@ const FilterBar: React.FC = ({ search={search} onSearchChange={handleSearchChange} onFilterAdd={onFilterAdd} - onFilterSave={onFilterSave} + onFilterSave={onTabDelete} isCustomSearch={isCustom} - onFilterDelete={onFilterDelete} + onFilterDelete={onTabSave} /> ); }} diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 000000000..15f13cede --- /dev/null +++ b/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,122 @@ +import { Theme } from "@material-ui/core/styles"; +import { makeStyles } from "@material-ui/styles"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { SearchPageProps, TabPageProps } from "@saleor/types"; +import Debounce from "../Debounce"; +import { FilterActionsOnlySearch } from "../Filter/FilterActions"; +import Hr from "../Hr"; +import Link from "../Link"; +import FilterTabs, { FilterTab } from "../TableFilter"; + +export interface SearchBarProps extends SearchPageProps, TabPageProps { + searchPlaceholder: string; +} + +const useStyles = makeStyles((theme: Theme) => ({ + tabAction: { + display: "inline-block" + }, + tabActionContainer: { + borderBottom: "1px solid #e8e8e8", + display: "flex", + justifyContent: "flex-end", + marginTop: theme.spacing.unit, + padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px` + } +})); + +const SearchBar: React.FC = props => { + const { + currentTab, + initialSearch, + onSearchChange, + searchPlaceholder, + tabs, + onAll, + onTabChange, + onTabDelete, + onTabSave + } = props; + const classes = useStyles(props); + const [search, setSearch] = React.useState(initialSearch); + const intl = useIntl(); + React.useEffect(() => setSearch(initialSearch), [initialSearch]); + + const isCustom = currentTab === tabs.length + 1; + + return ( + <> + + + {tabs.map((tab, tabIndex) => ( + onTabChange(tabIndex + 1)} + label={tab} + key={tabIndex} + /> + ))} + {isCustom && ( + undefined} + label={intl.formatMessage({ + defaultMessage: "Custom Filter" + })} + /> + )} + + + {debounceSearchChange => { + const handleSearchChange = (event: React.ChangeEvent) => { + const value = event.target.value; + setSearch(value); + debounceSearchChange(value); + }; + + return ( + <> + +
+
+ {search || (tabs && tabs.length) > 0 ? ( + isCustom ? ( + + + + ) : ( + + + + ) + ) : ( +
+ )} +
+
+ + ); + }} +
+ + ); +}; +SearchBar.displayName = "SearchBar"; +export default SearchBar; diff --git a/src/components/SearchBar/index.ts b/src/components/SearchBar/index.ts new file mode 100644 index 000000000..169b18c9e --- /dev/null +++ b/src/components/SearchBar/index.ts @@ -0,0 +1,2 @@ +export { default } from "./SearchBar"; +export * from "./SearchBar"; diff --git a/src/components/TableFilter/FilterChips.tsx b/src/components/TableFilter/FilterChips.tsx index b32de4652..770684cce 100644 --- a/src/components/TableFilter/FilterChips.tsx +++ b/src/components/TableFilter/FilterChips.tsx @@ -1,14 +1,14 @@ import ButtonBase from "@material-ui/core/ButtonBase"; import { Theme } from "@material-ui/core/styles"; import { fade } from "@material-ui/core/styles/colorManipulator"; -import TextField, { TextFieldProps } from "@material-ui/core/TextField"; import Typography from "@material-ui/core/Typography"; import ClearIcon from "@material-ui/icons/Clear"; -import { createStyles, makeStyles, useTheme } from "@material-ui/styles"; +import { makeStyles, useTheme } from "@material-ui/styles"; import React from "react"; import { FormattedMessage } from "react-intl"; -import Filter, { FilterContentSubmitData, IFilter } from "../Filter"; +import Filter from "../Filter"; +import FilterActions, { FilterActionsProps } from "../Filter/FilterActions"; import Hr from "../Hr"; import Link from "../Link"; @@ -17,104 +17,68 @@ export interface Filter { onClick: () => void; } -const useInputStyles = makeStyles({ - input: { - padding: "10px 12px" - }, - root: { - flex: 1 - } -}); - -const Search: React.FC = props => { - const classes = useInputStyles({}); - return ( - - ); -}; - const useStyles = makeStyles( - (theme: Theme) => - createStyles({ - actionContainer: { - display: "flex", - flexWrap: "wrap", - padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${ - theme.spacing.unit - }px ${theme.spacing.unit * 3}px` + (theme: Theme) => ({ + filterButton: { + alignItems: "center", + backgroundColor: fade(theme.palette.primary.main, 0.8), + borderRadius: "19px", + display: "flex", + height: "38px", + justifyContent: "space-around", + margin: `0 ${theme.spacing.unit * 2}px ${theme.spacing.unit}px`, + marginLeft: 0, + padding: "0 16px" + }, + filterChipContainer: { + display: "flex", + flex: 1, + flexWrap: "wrap" + }, + filterContainer: { + "& a": { + paddingBottom: 10 }, - filterButton: { - alignItems: "center", - backgroundColor: fade(theme.palette.primary.main, 0.8), - borderRadius: "19px", - display: "flex", - height: "38px", - justifyContent: "space-around", - margin: `0 ${theme.spacing.unit * 2}px ${theme.spacing.unit}px`, - marginLeft: 0, - padding: "0 16px" - }, - filterChipContainer: { - display: "flex", - flex: 1, - flexWrap: "wrap" - }, - filterContainer: { - "& a": { - paddingBottom: 10 - }, - borderBottom: `1px solid ${theme.palette.divider}`, - display: "flex", - marginTop: theme.spacing.unit, - padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px` - }, - filterIcon: { - color: theme.palette.common.white, - height: 16, - width: 16 - }, - filterIconContainer: { - WebkitAppearance: "none", - background: "transparent", - border: "none", - borderRadius: "100%", - cursor: "pointer", - height: 32, - marginRight: -13, - padding: 8, - width: 32 - }, - filterLabel: { - marginBottom: theme.spacing.unit - }, - filterText: { - color: theme.palette.common.white, - fontSize: 14, - fontWeight: 400 as 400, - lineHeight: "38px" - } - }), + borderBottom: `1px solid ${theme.palette.divider}`, + display: "flex", + marginTop: theme.spacing.unit, + padding: `0 ${theme.spacing.unit * 3}px ${theme.spacing.unit}px` + }, + filterIcon: { + color: theme.palette.common.white, + height: 16, + width: 16 + }, + filterIconContainer: { + WebkitAppearance: "none", + background: "transparent", + border: "none", + borderRadius: "100%", + cursor: "pointer", + height: 32, + marginRight: -13, + padding: 8, + width: 32 + }, + filterLabel: { + marginBottom: theme.spacing.unit + }, + filterText: { + color: theme.palette.common.white, + fontSize: 14, + fontWeight: 400 as 400, + lineHeight: "38px" + } + }), { name: "FilterChips" } ); -interface FilterChipProps { - currencySymbol: string; - menu: IFilter; +interface FilterChipProps extends FilterActionsProps { filtersList: Filter[]; - filterLabel: string; - placeholder: string; search: string; isCustomSearch: boolean; - onSearchChange: (event: React.ChangeEvent) => void; - onFilterAdd: (filter: FilterContentSubmitData) => void; onFilterDelete: () => void; onFilterSave: () => void; } @@ -137,20 +101,15 @@ export const FilterChips: React.FC = ({ return ( <> -
- - -
+ {search || (filtersList && filtersList.length) ? (
diff --git a/src/fixtures.ts b/src/fixtures.ts index 2b290708b..e0c4676db 100644 --- a/src/fixtures.ts +++ b/src/fixtures.ts @@ -3,7 +3,9 @@ import { FetchMoreProps, FilterPageProps, ListActions, - PageListProps + PageListProps, + SearchPageProps, + TabPageProps } from "./types"; const pageInfo = { @@ -46,23 +48,26 @@ export const countries = [ { code: "AS", label: "American Samoa" } ]; -export const filterPageProps: FilterPageProps<{}, unknown> = { - currencySymbol: "USD", +export const tabPageProps: TabPageProps = { currentTab: 0, - filterTabs: [ - { - data: {}, - name: "Tab X" - } - ], - filtersList: [], - initialSearch: "", onAll: () => undefined, - onFilterAdd: () => undefined, - onFilterDelete: () => undefined, - onFilterSave: () => undefined, - onSearchChange: () => undefined, - onTabChange: () => undefined + onTabChange: () => undefined, + onTabDelete: () => undefined, + onTabSave: () => undefined, + tabs: ["Tab X"] +}; + +export const searchPageProps: SearchPageProps = { + initialSearch: "", + onSearchChange: () => undefined +}; + +export const filterPageProps: FilterPageProps = { + ...searchPageProps, + ...tabPageProps, + currencySymbol: "USD", + filtersList: [], + onFilterAdd: () => undefined }; export const filters: Filter[] = [ diff --git a/src/orders/components/OrderListFilter/OrderListFilter.tsx b/src/orders/components/OrderListFilter/OrderListFilter.tsx index ca7547276..4da8d652c 100644 --- a/src/orders/components/OrderListFilter/OrderListFilter.tsx +++ b/src/orders/components/OrderListFilter/OrderListFilter.tsx @@ -8,9 +8,8 @@ import FilterBar from "@saleor/components/FilterBar"; import TimezoneContext from "@saleor/components/Timezone"; import { FilterProps } from "../../../types"; import { OrderStatusFilter } from "../../../types/globalTypes"; -import { OrderListUrlFilters } from "../../urls"; -type OrderListFilterProps = FilterProps; +type OrderListFilterProps = FilterProps; export enum OrderFilterKeys { date = "date", diff --git a/src/orders/components/OrderListPage/OrderListPage.tsx b/src/orders/components/OrderListPage/OrderListPage.tsx index b05d08c59..80f21887f 100644 --- a/src/orders/components/OrderListPage/OrderListPage.tsx +++ b/src/orders/components/OrderListPage/OrderListPage.tsx @@ -10,14 +10,13 @@ import { sectionNames } from "@saleor/intl"; import { OrderFilterKeys } from "@saleor/orders/components/OrderListFilter"; import { FilterPageProps, ListActions, PageListProps } from "@saleor/types"; import { OrderList_orders_edges_node } from "../../types/OrderList"; -import { OrderListUrlFilters } from "../../urls"; import OrderList from "../OrderList"; import OrderListFilter from "../OrderListFilter"; export interface OrderListPageProps extends PageListProps, ListActions, - FilterPageProps { + FilterPageProps { orders: OrderList_orders_edges_node[]; } @@ -25,8 +24,8 @@ const OrderListPage: React.FC = ({ currencySymbol, currentTab, filtersList, - filterTabs, initialSearch, + tabs, onAdd, onAll, onSearchChange, @@ -59,7 +58,7 @@ const OrderListPage: React.FC = ({ filterLabel={intl.formatMessage({ defaultMessage: "Select all orders where:" })} - filterTabs={filterTabs} + filterTabs={tabs} filtersList={filtersList} initialSearch={initialSearch} searchPlaceholder={intl.formatMessage({ diff --git a/src/orders/urls.ts b/src/orders/urls.ts index b53fdd2e5..b76f2ac8d 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -8,7 +8,8 @@ import { Filters, FiltersWithMultipleValues, Pagination, - SingleAction + SingleAction, + TabActionDialog } from "../types"; const orderSectionUrl = "/orders"; @@ -25,7 +26,7 @@ export enum OrderListUrlFiltersWithMultipleValuesEnum { } export type OrderListUrlFilters = Filters & FiltersWithMultipleValues; -export type OrderListUrlDialog = "cancel" | "save-search" | "delete-search"; +export type OrderListUrlDialog = "cancel" | TabActionDialog; export type OrderListUrlQueryParams = BulkAction & Dialog & OrderListUrlFilters & diff --git a/src/orders/views/OrderList/OrderList.tsx b/src/orders/views/OrderList/OrderList.tsx index 5b84e64b2..4768620b7 100644 --- a/src/orders/views/OrderList/OrderList.tsx +++ b/src/orders/views/OrderList/OrderList.tsx @@ -236,7 +236,7 @@ export const OrderList: React.StatelessComponent = ({ onFilterDelete={() => openModal("delete-search")} onTabChange={handleTabChange} initialSearch={params.email || ""} - filterTabs={getFilterTabs()} + filterTabs={getFilterTabs().map(tab => tab.name)} onAll={() => changeFilters({ status: undefined diff --git a/src/products/components/ProductListFilter/ProductListFilter.tsx b/src/products/components/ProductListFilter/ProductListFilter.tsx index 6bd04e778..52074f5ed 100644 --- a/src/products/components/ProductListFilter/ProductListFilter.tsx +++ b/src/products/components/ProductListFilter/ProductListFilter.tsx @@ -5,11 +5,10 @@ import { FieldType, IFilter } from "@saleor/components/Filter"; import FilterBar from "@saleor/components/FilterBar"; import { FilterProps } from "@saleor/types"; import { StockAvailability } from "@saleor/types/globalTypes"; -import { ProductListUrlFilters } from "../../urls"; -type ProductListFilterProps = FilterProps< - ProductListUrlFilters, - ProductFilterKeys +type ProductListFilterProps = Omit< + FilterProps, + "allTabLabel" | "filterLabel" | "searchPlaceholder" >; export enum ProductFilterKeys { @@ -133,7 +132,22 @@ const ProductListFilter: React.FC = props => { } ]; - return ; + return ( + + ); }; ProductListFilter.displayName = "ProductListFilter"; export default ProductListFilter; diff --git a/src/products/components/ProductListPage/ProductListPage.tsx b/src/products/components/ProductListPage/ProductListPage.tsx index 9187a7f92..9d41d62cd 100644 --- a/src/products/components/ProductListPage/ProductListPage.tsx +++ b/src/products/components/ProductListPage/ProductListPage.tsx @@ -24,14 +24,13 @@ import { ListActions, PageListProps } from "@saleor/types"; -import { ProductListUrlFilters } from "../../urls"; import ProductList from "../ProductList"; import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter"; export interface ProductListPageProps extends PageListProps, ListActions, - FilterPageProps, + FilterPageProps, FetchMoreProps { availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[]; currencySymbol: string; @@ -52,13 +51,13 @@ export const ProductListPage: React.FC = props => { currentTab, defaultSettings, filtersList, - filterTabs, gridAttributes, availableInGridAttributes, hasMore, initialSearch, loading, settings, + tabs, totalGridAttributes, onAdd, onAll, @@ -137,21 +136,11 @@ export const ProductListPage: React.FC = props => { = ({ onFilterDelete={() => openModal("delete-search")} onTabChange={handleTabChange} initialSearch={params.query || ""} - filterTabs={getFilterTabs()} + tabs={getFilterTabs().map(tab => tab.name)} /> defaultSettings?: ListSettings; onAdd: () => void; } -export interface FilterPageProps { - currencySymbol: string; - currentTab: number; - filterTabs: GetFilterTabsOutput; - filtersList: Filter[]; + +export interface SearchPageProps { initialSearch: string; - onAll: () => void; onSearchChange: (value: string) => void; - onFilterAdd: (filter: FilterContentSubmitData) => void; - onFilterDelete: () => void; - onFilterSave: () => void; - onTabChange: (tab: number) => void; } -export interface FilterProps - extends FilterPageProps { +export interface FilterPageProps extends SearchPageProps, TabPageProps { + currencySymbol: string; + filtersList: Filter[]; + onFilterAdd: (filter: FilterContentSubmitData) => void; +} + +export interface SearchProps { + searchPlaceholder: string; +} +export interface FilterProps extends FilterPageProps, SearchProps { allTabLabel: string; filterLabel: string; - searchPlaceholder: string; +} + +export interface TabPageProps { + currentTab: number; + tabs: string[]; + onAll: () => void; + onTabChange: (tab: number) => void; + onTabDelete: () => void; + onTabSave: () => void; } export interface PartialMutationProviderOutput< @@ -134,3 +141,5 @@ export interface FetchMoreProps { hasMore: boolean; onFetchMore: () => void; } + +export type TabActionDialog = "save-search" | "delete-search";