Add sorting ability to product list

This commit is contained in:
dominik-zeglen 2019-09-13 13:33:42 +02:00
parent 5dea135b2e
commit 87fca41f07
13 changed files with 270 additions and 41 deletions

View file

@ -24,6 +24,12 @@ const useStyles = makeStyles((theme: Theme) => ({
display: "flex", display: "flex",
height: 24 height: 24
}, },
labelContainerCenter: {
justifyContent: "center"
},
labelContainerRight: {
justifyContent: "flex-end"
},
root: { root: {
cursor: "pointer" cursor: "pointer"
} }
@ -31,18 +37,31 @@ const useStyles = makeStyles((theme: Theme) => ({
export type TableCellHeaderArrowDirection = "asc" | "desc"; export type TableCellHeaderArrowDirection = "asc" | "desc";
export type TableCellHeaderArrowPosition = "left" | "right"; export type TableCellHeaderArrowPosition = "left" | "right";
export interface TableCellHeader extends TableCellProps { export interface TableCellHeaderProps extends TableCellProps {
arrowPosition?: TableCellHeaderArrowPosition; arrowPosition?: TableCellHeaderArrowPosition;
direction?: TableCellHeaderArrowDirection; direction?: TableCellHeaderArrowDirection;
textAlign?: "left" | "center" | "right";
} }
const TableCellHeader: React.FC<TableCellHeader> = props => { const TableCellHeader: React.FC<TableCellHeaderProps> = props => {
const classes = useStyles(props); const classes = useStyles(props);
const { arrowPosition, children, className, direction, ...rest } = props; const {
arrowPosition,
children,
className,
direction,
textAlign,
...rest
} = props;
return ( return (
<TableCell {...rest} className={classNames(className, classes.root)}> <TableCell {...rest} className={classNames(classes.root, className)}>
<div className={classes.labelContainer}> <div
className={classNames(classes.labelContainer, {
[classes.labelContainerCenter]: textAlign === "center",
[classes.labelContainerRight]: textAlign === "right"
})}
>
{!!direction && arrowPosition === "left" && ( {!!direction && arrowPosition === "left" && (
<ArrowSort <ArrowSort
className={classNames(classes.arrow, classes.arrowLeft, { className={classNames(classes.arrow, classes.arrowLeft, {
@ -65,6 +84,7 @@ const TableCellHeader: React.FC<TableCellHeader> = props => {
TableCellHeader.displayName = "TableCellHeader"; TableCellHeader.displayName = "TableCellHeader";
TableCellHeader.defaultProps = { TableCellHeader.defaultProps = {
arrowPosition: "left" arrowPosition: "left",
textAlign: "left"
}; };
export default TableCellHeader; export default TableCellHeader;

View file

@ -4,11 +4,13 @@ import urlJoin from "url-join";
import { defineMessages, IntlShape } from "react-intl"; import { defineMessages, IntlShape } from "react-intl";
import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton"; import { ConfirmButtonTransitionState } from "./components/ConfirmButton/ConfirmButton";
import { TableCellHeaderArrowDirection } from "./components/TableCellHeader";
import { APP_MOUNT_URI } from "./config"; import { APP_MOUNT_URI } from "./config";
import { AddressType } from "./customers/types"; import { AddressType } from "./customers/types";
import { PartialMutationProviderOutput, UserError } from "./types"; import { PartialMutationProviderOutput, UserError } from "./types";
import { import {
AuthorizationKeyType, AuthorizationKeyType,
OrderDirection,
OrderStatus, OrderStatus,
PaymentChargeStatusEnum, PaymentChargeStatusEnum,
TaxRateType TaxRateType
@ -478,3 +480,17 @@ export function findInEnum<TEnum extends object>(
throw new Error(`Key ${needle} not found in enum`); throw new Error(`Key ${needle} not found in enum`);
} }
export function getOrderDirection(asc: boolean): OrderDirection {
return asc ? OrderDirection.ASC : OrderDirection.DESC;
}
export function getArrowDirection(
order: OrderDirection
): TableCellHeaderArrowDirection {
return order === OrderDirection.ASC ? "asc" : "desc";
}
export function parseBoolean(a: string): boolean {
return a === "true";
}

View file

@ -20,18 +20,22 @@ import StatusLabel from "@saleor/components/StatusLabel";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import TableCellHeader from "@saleor/components/TableCellHeader";
import TableHead from "@saleor/components/TableHead"; import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination"; import TablePagination from "@saleor/components/TablePagination";
import { ProductListColumns } from "@saleor/config"; import { ProductListColumns } from "@saleor/config";
import { maybe, renderCollection } from "@saleor/misc"; import { getArrowDirection, maybe, renderCollection } from "@saleor/misc";
import { import {
getAttributeIdFromColumnValue, getAttributeIdFromColumnValue,
isAttributeColumnValue isAttributeColumnValue
} from "@saleor/products/components/ProductListPage/utils"; } from "@saleor/products/components/ProductListPage/utils";
import { AvailableInGridAttributes_grid_edges_node } from "@saleor/products/types/AvailableInGridAttributes"; import { AvailableInGridAttributes_grid_edges_node } from "@saleor/products/types/AvailableInGridAttributes";
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList"; import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
import { ListActions, ListProps } from "@saleor/types"; import { ListActions, ListProps, SortPage } from "@saleor/types";
import TDisplayColumn from "@saleor/utils/columns/DisplayColumn"; import { ProductOrder, ProductOrderField } from "@saleor/types/globalTypes";
import TDisplayColumn, {
DisplayColumnProps
} from "@saleor/utils/columns/DisplayColumn";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@ -87,12 +91,18 @@ const styles = (theme: Theme) =>
} }
}); });
const DisplayColumn = TDisplayColumn as React.FunctionComponent<
DisplayColumnProps<ProductListColumns>
>;
interface ProductListProps interface ProductListProps
extends ListProps<ProductListColumns>, extends ListProps<ProductListColumns>,
ListActions, ListActions,
SortPage<ProductOrderField>,
WithStyles<typeof styles> { WithStyles<typeof styles> {
gridAttributes: AvailableInGridAttributes_grid_edges_node[]; gridAttributes: AvailableInGridAttributes_grid_edges_node[];
products: ProductList_products_edges_node[]; products: ProductList_products_edges_node[];
sort: ProductOrder;
} }
export const ProductList = withStyles(styles, { name: "ProductList" })( export const ProductList = withStyles(styles, { name: "ProductList" })(
@ -105,19 +115,18 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
pageInfo, pageInfo,
products, products,
selected, selected,
sort,
toggle, toggle,
toggleAll, toggleAll,
toolbar, toolbar,
onNextPage, onNextPage,
onPreviousPage, onPreviousPage,
onUpdateListSettings, onUpdateListSettings,
onRowClick onRowClick,
onSort
}: ProductListProps) => { }: ProductListProps) => {
const intl = useIntl(); const intl = useIntl();
const DisplayColumn: React.FC<{ column: ProductListColumns }> = props => (
<TDisplayColumn displayColumns={settings.columns} {...props} />
);
const gridAttributesFromSettings = settings.columns.filter( const gridAttributesFromSettings = settings.columns.filter(
isAttributeColumnValue isAttributeColumnValue
); );
@ -129,16 +138,22 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
<colgroup> <colgroup>
<col /> <col />
<col className={classes.colName} /> <col className={classes.colName} />
<DisplayColumn column="productType"> <DisplayColumn
column="productType"
displayColumns={settings.columns}
>
<col className={classes.colType} /> <col className={classes.colType} />
</DisplayColumn> </DisplayColumn>
<DisplayColumn column="isPublished"> <DisplayColumn
column="isPublished"
displayColumns={settings.columns}
>
<col className={classes.colPublished} /> <col className={classes.colPublished} />
</DisplayColumn> </DisplayColumn>
{gridAttributesFromSettings.map(gridAttribute => ( {gridAttributesFromSettings.map(gridAttribute => (
<col className={classes.colAttribute} key={gridAttribute} /> <col className={classes.colAttribute} key={gridAttribute} />
))} ))}
<DisplayColumn column="price"> <DisplayColumn column="price" displayColumns={settings.columns}>
<col className={classes.colPrice} /> <col className={classes.colPrice} />
</DisplayColumn> </DisplayColumn>
</colgroup> </colgroup>
@ -150,30 +165,59 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
toggleAll={toggleAll} toggleAll={toggleAll}
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell <TableCellHeader
arrowPosition="right"
className={classNames(classes.colName, { className={classNames(classes.colName, {
[classes.colNameFixed]: settings.columns.length > 4 [classes.colNameFixed]: settings.columns.length > 4
})} })}
direction={
sort.field === ProductOrderField.NAME
? getArrowDirection(sort.direction)
: undefined
}
onClick={() => onSort(ProductOrderField.NAME)}
> >
<span className={classes.colNameHeader}> <span className={classes.colNameHeader}>
<FormattedMessage defaultMessage="Name" description="product" /> <FormattedMessage defaultMessage="Name" description="product" />
</span> </span>
</TableCell> </TableCellHeader>
<DisplayColumn column="productType"> <DisplayColumn
<TableCell className={classes.colType}> column="productType"
displayColumns={settings.columns}
>
<TableCellHeader
className={classes.colType}
direction={
sort.field === ProductOrderField.TYPE
? getArrowDirection(sort.direction)
: undefined
}
onClick={() => onSort(ProductOrderField.TYPE)}
>
<FormattedMessage <FormattedMessage
defaultMessage="Type" defaultMessage="Type"
description="product type" description="product type"
/> />
</TableCell> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
<DisplayColumn column="isPublished"> <DisplayColumn
<TableCell className={classes.colPublished}> column="isPublished"
displayColumns={settings.columns}
>
<TableCellHeader
className={classes.colPublished}
direction={
sort.field === ProductOrderField.PUBLISHED
? getArrowDirection(sort.direction)
: undefined
}
onClick={() => onSort(ProductOrderField.PUBLISHED)}
>
<FormattedMessage <FormattedMessage
defaultMessage="Published" defaultMessage="Published"
description="product status" description="product status"
/> />
</TableCell> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
{gridAttributesFromSettings.map(gridAttributeFromSettings => ( {gridAttributesFromSettings.map(gridAttributeFromSettings => (
<TableCell <TableCell
@ -192,13 +236,22 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
)} )}
</TableCell> </TableCell>
))} ))}
<DisplayColumn column="price"> <DisplayColumn column="price" displayColumns={settings.columns}>
<TableCell className={classes.colPrice}> <TableCellHeader
className={classes.colPrice}
direction={
sort.field === ProductOrderField.PRICE
? getArrowDirection(sort.direction)
: undefined
}
textAlign="right"
onClick={() => onSort(ProductOrderField.PRICE)}
>
<FormattedMessage <FormattedMessage
defaultMessage="Price" defaultMessage="Price"
description="product price" description="product price"
/> />
</TableCell> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -246,7 +299,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
> >
{maybe<React.ReactNode>(() => product.name, <Skeleton />)} {maybe<React.ReactNode>(() => product.name, <Skeleton />)}
</TableCellAvatar> </TableCellAvatar>
<DisplayColumn column="productType"> <DisplayColumn
column="productType"
displayColumns={settings.columns}
>
<TableCell className={classes.colType}> <TableCell className={classes.colType}>
{product && product.productType ? ( {product && product.productType ? (
product.productType.name product.productType.name
@ -255,7 +311,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
)} )}
</TableCell> </TableCell>
</DisplayColumn> </DisplayColumn>
<DisplayColumn column="isPublished"> <DisplayColumn
column="isPublished"
displayColumns={settings.columns}
>
<TableCell className={classes.colPublished}> <TableCell className={classes.colPublished}>
{product && {product &&
maybe(() => product.isAvailable !== undefined) ? ( maybe(() => product.isAvailable !== undefined) ? (
@ -299,7 +358,10 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
}, <Skeleton />)} }, <Skeleton />)}
</TableCell> </TableCell>
))} ))}
<DisplayColumn column="price"> <DisplayColumn
column="price"
displayColumns={settings.columns}
>
<TableCell className={classes.colPrice}> <TableCell className={classes.colPrice}>
{maybe(() => product.basePrice) && {maybe(() => product.basePrice) &&
maybe(() => product.basePrice.amount) !== undefined && maybe(() => product.basePrice.amount) !== undefined &&

View file

@ -1,7 +1,6 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import { Theme } from "@material-ui/core/styles"; import { Theme } from "@material-ui/core/styles";
import makeStyles from "@material-ui/styles/makeStyles"; import makeStyles from "@material-ui/styles/makeStyles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -22,8 +21,11 @@ import {
FetchMoreProps, FetchMoreProps,
FilterPageProps, FilterPageProps,
ListActions, ListActions,
PageListProps PageListProps,
SortPage
} from "@saleor/types"; } from "@saleor/types";
import { ProductOrder, ProductOrderField } from "@saleor/types/globalTypes";
import { ProductListUrlFilters } from "../../urls";
import ProductList from "../ProductList"; import ProductList from "../ProductList";
import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter"; import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter";
@ -31,12 +33,14 @@ export interface ProductListPageProps
extends PageListProps<ProductListColumns>, extends PageListProps<ProductListColumns>,
ListActions, ListActions,
FilterPageProps<ProductFilterKeys>, FilterPageProps<ProductFilterKeys>,
FetchMoreProps { FetchMoreProps,
SortPage<ProductOrderField> {
availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[]; availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[];
currencySymbol: string; currencySymbol: string;
gridAttributes: AvailableInGridAttributes_grid_edges_node[]; gridAttributes: AvailableInGridAttributes_grid_edges_node[];
totalGridAttributes: number; totalGridAttributes: number;
products: ProductList_products_edges_node[]; products: ProductList_products_edges_node[];
sort: ProductOrder;
} }
const useStyles = makeStyles((theme: Theme) => ({ const useStyles = makeStyles((theme: Theme) => ({

View file

@ -4,6 +4,7 @@ import { useIntl } from "react-intl";
import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { Route, RouteComponentProps, Switch } from "react-router-dom";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { parseBoolean } from "@saleor/misc";
import { WindowTitle } from "../components/WindowTitle"; import { WindowTitle } from "../components/WindowTitle";
import { import {
productAddPath, productAddPath,
@ -28,7 +29,10 @@ const ProductList: React.StatelessComponent<RouteComponentProps<any>> = ({
location location
}) => { }) => {
const qs = parseQs(location.search.substr(1)); const qs = parseQs(location.search.substr(1));
const params: ProductListUrlQueryParams = qs; const params: ProductListUrlQueryParams = {
...qs,
asc: parseBoolean(qs.asc)
};
return <ProductListComponent params={params} />; return <ProductListComponent params={params} />;
}; };

View file

@ -218,6 +218,7 @@ const productListQuery = gql`
$last: Int $last: Int
$before: String $before: String
$filter: ProductFilterInput $filter: ProductFilterInput
$sort: ProductOrder
) { ) {
products( products(
before: $before before: $before
@ -225,6 +226,7 @@ const productListQuery = gql`
first: $first first: $first
last: $last last: $last
filter: $filter filter: $filter
sortBy: $sort
) { ) {
edges { edges {
node { node {

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductFilterInput } from "./../../types/globalTypes"; import { ProductFilterInput, ProductOrder } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: ProductList // GraphQL query operation: ProductList
@ -82,4 +82,5 @@ export interface ProductListVariables {
last?: number | null; last?: number | null;
before?: string | null; before?: string | null;
filter?: ProductFilterInput | null; filter?: ProductFilterInput | null;
sort?: ProductOrder | null;
} }

View file

@ -7,6 +7,7 @@ import {
Dialog, Dialog,
Filters, Filters,
Pagination, Pagination,
Sort,
TabActionDialog TabActionDialog
} from "../types"; } from "../types";
@ -29,9 +30,17 @@ export enum ProductListUrlFiltersEnum {
query = "query" query = "query"
} }
export type ProductListUrlFilters = Filters<ProductListUrlFiltersEnum>; export type ProductListUrlFilters = Filters<ProductListUrlFiltersEnum>;
export enum ProductListUrlSortFields {
name = "name",
productType = "type",
status = "status",
price = "price"
}
export type ProductListUrlSort = Sort<ProductListUrlSortFields>;
export type ProductListUrlQueryParams = BulkAction & export type ProductListUrlQueryParams = BulkAction &
Dialog<ProductListUrlDialog> & Dialog<ProductListUrlDialog> &
ProductListUrlFilters & ProductListUrlFilters &
ProductListUrlSort &
Pagination & Pagination &
ActiveTab; ActiveTab;
export const productListUrl = (params?: ProductListUrlQueryParams): string => export const productListUrl = (params?: ProductListUrlQueryParams): string =>

View file

@ -22,6 +22,7 @@ import usePaginator, {
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ProductListVariables } from "@saleor/products/types/ProductList";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import ProductListPage from "../../components/ProductListPage"; import ProductListPage from "../../components/ProductListPage";
import { import {
@ -52,6 +53,7 @@ import {
getFilterVariables, getFilterVariables,
saveFilterTab saveFilterTab
} from "./filters"; } from "./filters";
import { getSortUrlVariables, getSortQueryVariables } from "./sort";
interface ProductListProps { interface ProductListProps {
params: ProductListUrlQueryParams; params: ProductListUrlQueryParams;
@ -152,10 +154,13 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
const paginationState = createPaginationState(settings.rowNumber, params); const paginationState = createPaginationState(settings.rowNumber, params);
const currencySymbol = maybe(() => shop.defaultCurrency, "USD"); const currencySymbol = maybe(() => shop.defaultCurrency, "USD");
const queryVariables = React.useMemo( const filter = getFilterVariables(params);
const sort = getSortQueryVariables(params);
const queryVariables = React.useMemo<ProductListVariables>(
() => ({ () => ({
...paginationState, ...paginationState,
filter: getFilterVariables(params) filter,
sort
}), }),
[params, settings.rowNumber] [params, settings.rowNumber]
); );
@ -224,6 +229,19 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
return ( return (
<> <>
<ProductListPage <ProductListPage
sort={sort}
onSort={field =>
navigate(
productListUrl({
...params,
...getSortUrlVariables(
field,
sort.field,
params
)
})
)
}
availableInGridAttributes={maybe( availableInGridAttributes={maybe(
() => () =>
attributes.data.availableInGrid.edges.map( attributes.data.availableInGrid.edges.map(

View file

@ -0,0 +1,66 @@
import { getOrderDirection } from "@saleor/misc";
import {
ProductListUrlQueryParams,
ProductListUrlSortFields as ProductListUrlSortField
} from "@saleor/products/urls";
import { Sort } from "@saleor/types";
import { ProductOrderField } from "@saleor/types/globalTypes";
export function getSortQueryVariables(params: ProductListUrlQueryParams) {
return {
direction: getOrderDirection(params.asc),
field: getSortQueryField(params.sort)
};
}
export function getSortQueryField(
sort: ProductListUrlSortField
): ProductOrderField {
switch (sort) {
case ProductListUrlSortField.name:
return ProductOrderField.NAME;
case ProductListUrlSortField.price:
return ProductOrderField.PRICE;
case ProductListUrlSortField.productType:
return ProductOrderField.TYPE;
case ProductListUrlSortField.status:
return ProductOrderField.PUBLISHED;
default:
return ProductOrderField.NAME;
}
}
export function getSortUrlField(
sort: ProductOrderField
): ProductListUrlSortField {
switch (sort) {
case ProductOrderField.NAME:
return ProductListUrlSortField.name;
case ProductOrderField.PRICE:
return ProductListUrlSortField.price;
case ProductOrderField.TYPE:
return ProductListUrlSortField.productType;
case ProductOrderField.PUBLISHED:
return ProductListUrlSortField.status;
default:
return ProductListUrlSortField.name;
}
}
export function getSortUrlVariables(
field: ProductOrderField,
selectedField: ProductOrderField,
params: Sort<ProductListUrlSortField>
): Sort<ProductListUrlSortField> {
if (field === selectedField) {
return {
asc: !params.asc,
sort: getSortUrlField(field)
};
}
return {
asc: true,
sort: getSortUrlField(field)
};
}

View file

@ -47,6 +47,10 @@ export interface ListProps<TColumns extends string = string> {
) => void; ) => void;
onListSettingsReset?: () => void; onListSettingsReset?: () => void;
} }
export interface SortPage<TSortKey extends string> {
onSort: (field: TSortKey) => void;
}
export interface ListActionsWithoutToolbar { export interface ListActionsWithoutToolbar {
toggle: (id: string) => void; toggle: (id: string) => void;
toggleAll: (items: React.ReactNodeArray, selected: number) => void; toggleAll: (items: React.ReactNodeArray, selected: number) => void;
@ -131,6 +135,10 @@ export type FiltersWithMultipleValues<TFilters extends string> = Partial<
export type SingleAction = Partial<{ export type SingleAction = Partial<{
id: string; id: string;
}>; }>;
export type Sort<TSort extends string = string> = Partial<{
asc: boolean;
sort: TSort;
}>;
export type BulkAction = Partial<{ export type BulkAction = Partial<{
ids: string[]; ids: string[];
}>; }>;

View file

@ -112,6 +112,11 @@ export enum OrderAction {
VOID = "VOID", VOID = "VOID",
} }
export enum OrderDirection {
ASC = "ASC",
DESC = "DESC",
}
export enum OrderEventsEmailsEnum { export enum OrderEventsEmailsEnum {
DIGITAL_LINKS = "DIGITAL_LINKS", DIGITAL_LINKS = "DIGITAL_LINKS",
FULFILLMENT_CONFIRMATION = "FULFILLMENT_CONFIRMATION", FULFILLMENT_CONFIRMATION = "FULFILLMENT_CONFIRMATION",
@ -187,6 +192,15 @@ export enum PermissionEnum {
MANAGE_USERS = "MANAGE_USERS", MANAGE_USERS = "MANAGE_USERS",
} }
export enum ProductOrderField {
DATE = "DATE",
MINIMAL_PRICE = "MINIMAL_PRICE",
NAME = "NAME",
PRICE = "PRICE",
PUBLISHED = "PUBLISHED",
TYPE = "TYPE",
}
export enum ProductTypeConfigurable { export enum ProductTypeConfigurable {
CONFIGURABLE = "CONFIGURABLE", CONFIGURABLE = "CONFIGURABLE",
SIMPLE = "SIMPLE", SIMPLE = "SIMPLE",
@ -574,6 +588,11 @@ export interface ProductFilterInput {
minimalPrice?: PriceRangeInput | null; minimalPrice?: PriceRangeInput | null;
} }
export interface ProductOrder {
field: ProductOrderField;
direction: OrderDirection;
}
export interface ProductTypeFilterInput { export interface ProductTypeFilterInput {
search?: string | null; search?: string | null;
configurable?: ProductTypeConfigurable | null; configurable?: ProductTypeConfigurable | null;

View file

@ -12,12 +12,12 @@ const DisplayColumn: React.FC<DisplayColumnProps> = ({
children, children,
column column
}) => { }) => {
const displayColumn = React.useCallback( const display = React.useMemo(
(column: string) => isSelected(column, displayColumns, (a, b) => a === b), () => isSelected(column, displayColumns, (a, b) => a === b),
[displayColumns] [column, displayColumns]
); );
return <>{displayColumn(column) && children}</>; return <>{display && children}</>;
}; };
DisplayColumn.displayName = "DisplayColumn"; DisplayColumn.displayName = "DisplayColumn";