Merge pull request #160 from mirumee/add/ready-to-fulfill-orders
Add support for multiple values in filters
This commit is contained in:
commit
2287411d5a
28 changed files with 823 additions and 232 deletions
|
@ -17,3 +17,4 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Add fallback locale - #153 by @dominik-zeglen
|
||||
- Replace checkbox with switch component in "product type has variants" - #152 by @dominik-zeglen
|
||||
- Add password reset flow - #147 by @dominik-zeglen
|
||||
- Add support for multiple values in filters - #160 by @dominik-zeglen
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2019-09-06T13:56:36.897Z\n"
|
||||
"POT-Creation-Date: 2019-09-10T11:00:36.829Z\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -1439,6 +1439,10 @@ msgctxt "description"
|
|||
msgid "Availability"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2157131639] - product status
|
||||
#. defaultMessage is:
|
||||
#. Available
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.2157131639] - product status
|
||||
#. defaultMessage is:
|
||||
|
@ -3447,14 +3451,6 @@ msgctxt "order history message"
|
|||
msgid "Fulfilled {quantity} items"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.2943502192] - order
|
||||
#. defaultMessage is:
|
||||
#. Fulfillment Status
|
||||
msgctxt "order"
|
||||
msgid "Fulfillment Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderHistory/OrderHistory.json
|
||||
#. [src.orders.components.OrderHistory.3081292385] - order history message
|
||||
#. defaultMessage is:
|
||||
|
@ -3555,14 +3551,10 @@ msgctxt "subheader"
|
|||
msgid "Here is some information we gathered about your store"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.77815154]
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.77815154] - product is hidden
|
||||
#. defaultMessage is:
|
||||
#. Hidden
|
||||
msgctxt "description"
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.77815154] - product is hidden
|
||||
#. defaultMessage is:
|
||||
|
@ -3571,6 +3563,14 @@ msgctxt "product is hidden"
|
|||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.77815154]
|
||||
#. defaultMessage is:
|
||||
#. Hidden
|
||||
msgctxt "description"
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/views/ProductList/filters.json
|
||||
#. [src.products.views.ProductList.hidden] - filter products by visibility
|
||||
#. defaultMessage is:
|
||||
|
@ -4871,6 +4871,14 @@ msgctxt "description"
|
|||
msgid "Order History"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.2222765704]
|
||||
#. defaultMessage is:
|
||||
#. Order Status
|
||||
msgctxt "description"
|
||||
msgid "Order Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderHistory/OrderHistory.json
|
||||
#. [src.orders.components.OrderHistory.1230178536] - order history message
|
||||
#. defaultMessage is:
|
||||
|
@ -5027,6 +5035,10 @@ msgctxt "description"
|
|||
msgid "Original String"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1640493122] - product status
|
||||
#. defaultMessage is:
|
||||
#. Out Of Stock
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1640493122] - product status
|
||||
#. defaultMessage is:
|
||||
|
@ -5363,6 +5375,18 @@ msgctxt "order payment"
|
|||
msgid "Preauthorized amount"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
msgctxt "description"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/categories/components/CategoryProductList/CategoryProductList.json
|
||||
#. [src.categories.components.CategoryProductList.1134347598] - product price
|
||||
#. defaultMessage is:
|
||||
|
@ -5411,14 +5435,6 @@ msgctxt "product unit price"
|
|||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1134347598]
|
||||
#. defaultMessage is:
|
||||
#. Price
|
||||
msgctxt "description"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariants/ProductVariants.json
|
||||
#. [src.products.components.ProductVariants.1134347598] - product variant price
|
||||
#. defaultMessage is:
|
||||
|
@ -5903,6 +5919,10 @@ msgctxt "description"
|
|||
msgid "Quick Pick"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2545228781]
|
||||
#. defaultMessage is:
|
||||
#. Range
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.2545228781]
|
||||
#. defaultMessage is:
|
||||
|
@ -5939,6 +5959,18 @@ msgctxt "shipping method price"
|
|||
msgid "Rate Price"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.2415661583] - order status
|
||||
#. defaultMessage is:
|
||||
#. Ready to Capture
|
||||
#: build/locale/src/orders/views/OrderList/filters.json
|
||||
#. [src.orders.views.OrderList.readyToCapture] - order status
|
||||
#. defaultMessage is:
|
||||
#. Ready to Capture
|
||||
msgctxt "order status"
|
||||
msgid "Ready to Capture"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/components/CustomerOrders/CustomerOrders.json
|
||||
#. [src.customers.components.CustomerOrders.3878642352] - section header
|
||||
#. defaultMessage is:
|
||||
|
@ -6803,6 +6835,10 @@ msgctxt "description"
|
|||
msgid "Specific Date"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.2844426531]
|
||||
#. defaultMessage is:
|
||||
#. Specific Price
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.2844426531]
|
||||
#. defaultMessage is:
|
||||
|
@ -6883,6 +6919,18 @@ msgctxt "voucher is active from date"
|
|||
msgid "Starts"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
msgctxt "product status"
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/customers/components/CustomerOrders/CustomerOrders.json
|
||||
#. [src.customers.components.CustomerOrders.1756106276] - order status
|
||||
#. defaultMessage is:
|
||||
|
@ -6907,14 +6955,6 @@ msgctxt "plugin status"
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1756106276] - product status
|
||||
#. defaultMessage is:
|
||||
#. Status
|
||||
msgctxt "product status"
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductVariants/ProductVariants.json
|
||||
#. [src.products.components.ProductVariants.1756106276] - product variant status
|
||||
#. defaultMessage is:
|
||||
|
@ -6923,6 +6963,10 @@ msgctxt "product variant status"
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3841616483] - product stock
|
||||
#. defaultMessage is:
|
||||
#. Stock
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3841616483] - product stock
|
||||
#. defaultMessage is:
|
||||
|
@ -6939,6 +6983,10 @@ msgctxt "product variant stock, section header"
|
|||
msgid "Stock"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3645081351]
|
||||
#. defaultMessage is:
|
||||
#. Stock quantity
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3645081351]
|
||||
#. defaultMessage is:
|
||||
|
@ -8107,6 +8155,18 @@ msgctxt "description"
|
|||
msgid "View and update your site settings"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
msgctxt "product visibility"
|
||||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.1459686496] - section header
|
||||
#. defaultMessage is:
|
||||
|
@ -8123,14 +8183,6 @@ msgctxt "page status"
|
|||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1459686496] - product visibility
|
||||
#. defaultMessage is:
|
||||
#. Visibility
|
||||
msgctxt "product visibility"
|
||||
msgid "Visibility"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeList/AttributeList.json
|
||||
#. [src.attributes.components.AttributeList.643174786] - attribute is visible
|
||||
#. defaultMessage is:
|
||||
|
@ -8139,14 +8191,10 @@ msgctxt "attribute is visible"
|
|||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.643174786]
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.643174786] - product is visible
|
||||
#. defaultMessage is:
|
||||
#. Visible
|
||||
msgctxt "description"
|
||||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.643174786] - product is visible
|
||||
#. defaultMessage is:
|
||||
|
@ -8155,6 +8203,14 @@ msgctxt "product is visible"
|
|||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/VisibilityCard/VisibilityCard.json
|
||||
#. [src.components.VisibilityCard.643174786]
|
||||
#. defaultMessage is:
|
||||
#. Visible
|
||||
msgctxt "description"
|
||||
msgid "Visible"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/attributes/components/AttributeProperties/AttributeProperties.json
|
||||
#. [src.attributes.components.AttributeProperties.3876764312] - attribute
|
||||
#. defaultMessage is:
|
||||
|
@ -8303,14 +8359,10 @@ msgctxt "order does not require shipping"
|
|||
msgid "does not apply"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.3477667254]
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.3477667254] - product price
|
||||
#. defaultMessage is:
|
||||
#. equals
|
||||
msgctxt "description"
|
||||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.3477667254] - product price
|
||||
#. defaultMessage is:
|
||||
|
@ -8319,6 +8371,14 @@ msgctxt "product price"
|
|||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.3477667254]
|
||||
#. defaultMessage is:
|
||||
#. equals
|
||||
msgctxt "description"
|
||||
msgid "equals"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/components/Filter/FilterElement.json
|
||||
#. [src.components.Filter.2755325844]
|
||||
#. defaultMessage is:
|
||||
|
@ -8343,14 +8403,10 @@ msgctxt "weight"
|
|||
msgid "from {value} {unit}"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.1438173764] - date is set as
|
||||
#: build/locale/src/attributes/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.attributes.components.ProductListFilter.1438173764] - product status is set as
|
||||
#. defaultMessage is:
|
||||
#. is set as
|
||||
msgctxt "date is set as"
|
||||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/products/components/ProductListFilter/ProductListFilter.json
|
||||
#. [src.products.components.ProductListFilter.1438173764] - product status is set as
|
||||
#. defaultMessage is:
|
||||
|
@ -8359,6 +8415,14 @@ msgctxt "product status is set as"
|
|||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/orders/components/OrderListFilter/OrderListFilter.json
|
||||
#. [src.orders.components.OrderListFilter.1438173764] - date is set as
|
||||
#. defaultMessage is:
|
||||
#. is set as
|
||||
msgctxt "date is set as"
|
||||
msgid "is set as"
|
||||
msgstr ""
|
||||
|
||||
#: build/locale/src/staff/views/StaffDetails.json
|
||||
#. [src.staff.views.2240444792] - dialog header
|
||||
#. defaultMessage is:
|
||||
|
|
|
@ -19,9 +19,9 @@ import { FilterContent } from ".";
|
|||
import { FilterContentSubmitData } from "./FilterContent";
|
||||
import { IFilter } from "./types";
|
||||
|
||||
export interface FilterProps {
|
||||
export interface FilterProps<TFilterKeys = string> {
|
||||
currencySymbol: string;
|
||||
menu: IFilter;
|
||||
menu: IFilter<TFilterKeys>;
|
||||
filterLabel: string;
|
||||
onFilterAdd: (filter: FilterContentSubmitData) => void;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ import SingleSelectField from "../SingleSelectField";
|
|||
import FilterElement from "./FilterElement";
|
||||
import { IFilter } from "./types";
|
||||
|
||||
export interface FilterContentSubmitData {
|
||||
name: string;
|
||||
export interface FilterContentSubmitData<TKeys = string> {
|
||||
name: TKeys;
|
||||
value: string | string[];
|
||||
}
|
||||
export interface FilterContentProps {
|
||||
currencySymbol: string;
|
||||
filters: IFilter;
|
||||
filters: IFilter<string>;
|
||||
onSubmit: (data: FilterContentSubmitData) => void;
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,10 @@ function checkFilterValue(value: string | string[]): boolean {
|
|||
return value.some(v => !!v);
|
||||
}
|
||||
|
||||
function getFilterChoices(items: IFilter) {
|
||||
function getFilterChoices(items: IFilter<string>) {
|
||||
return items.map(filterItem => ({
|
||||
label: filterItem.label,
|
||||
value: filterItem.value
|
||||
value: filterItem.value.toString()
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
onSubmit
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [menuValue, setMenuValue] = React.useState<string>("");
|
||||
const [menuValue, setMenuValue] = React.useState<string>(null);
|
||||
const [filterValue, setFilterValue] = React.useState<string | string[]>("");
|
||||
const classes = useStyles({});
|
||||
|
||||
|
@ -95,7 +95,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
}}
|
||||
value={
|
||||
filterItemIndex === menus.length - 1
|
||||
? menuValue
|
||||
? menuValue.toString()
|
||||
: menus[filterItemIndex - 1].label.toString()
|
||||
}
|
||||
placeholder={intl.formatMessage({
|
||||
|
|
|
@ -10,9 +10,9 @@ import PriceField from "../PriceField";
|
|||
import SingleSelectField from "../SingleSelectField";
|
||||
import { FieldType, IFilterItem } from "./types";
|
||||
|
||||
export interface FilterElementProps {
|
||||
export interface FilterElementProps<TFilterKeys = string> {
|
||||
className?: string;
|
||||
filter: IFilterItem;
|
||||
filter: IFilterItem<TFilterKeys>;
|
||||
value: string | string[];
|
||||
onChange: (value: string | string[]) => void;
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ const useStyles = makeStyles({
|
|||
}
|
||||
});
|
||||
|
||||
export interface FilterElementProps {
|
||||
export interface FilterElementProps<TFilterKeys = string> {
|
||||
className?: string;
|
||||
currencySymbol: string;
|
||||
filter: IFilterItem;
|
||||
filter: IFilterItem<TFilterKeys>;
|
||||
value: string | string[];
|
||||
onChange: (value: string | string[]) => void;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,6 @@ export interface FilterData {
|
|||
value?: string;
|
||||
}
|
||||
|
||||
export type IFilterItem = IMenuItem<FilterData>;
|
||||
export type IFilterItem<TKeys> = IMenuItem<FilterData, TKeys>;
|
||||
|
||||
export type IFilter = IMenu<FilterData>;
|
||||
export type IFilter<TKeys> = IMenu<FilterData, TKeys>;
|
||||
|
|
|
@ -6,9 +6,9 @@ import Debounce from "../Debounce";
|
|||
import { IFilter } from "../Filter/types";
|
||||
import FilterTabs, { FilterChips, FilterTab } from "../TableFilter";
|
||||
|
||||
export interface FilterBarProps<TUrlFilters = object>
|
||||
extends FilterProps<TUrlFilters> {
|
||||
filterMenu: IFilter;
|
||||
export interface FilterBarProps<TUrlFilters = object, TFilterKeys = any>
|
||||
extends FilterProps<TUrlFilters, TFilterKeys> {
|
||||
filterMenu: IFilter<TFilterKeys>;
|
||||
}
|
||||
|
||||
const FilterBar: React.FC<FilterBarProps> = ({
|
||||
|
|
|
@ -99,16 +99,16 @@ const useStyles = makeStyles(
|
|||
}
|
||||
);
|
||||
|
||||
interface FilterChipProps {
|
||||
interface FilterChipProps<TFilterKeys = string> {
|
||||
currencySymbol: string;
|
||||
menu: IFilter;
|
||||
menu: IFilter<TFilterKeys>;
|
||||
filtersList: Filter[];
|
||||
filterLabel: string;
|
||||
placeholder: string;
|
||||
search: string;
|
||||
isCustomSearch: boolean;
|
||||
onSearchChange: (event: React.ChangeEvent<any>) => void;
|
||||
onFilterAdd: (filter: FilterContentSubmitData) => void;
|
||||
onFilterAdd: (filter: FilterContentSubmitData<TFilterKeys>) => void;
|
||||
onFilterDelete: () => void;
|
||||
onFilterSave: () => void;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export const countries = [
|
|||
{ code: "AS", label: "American Samoa" }
|
||||
];
|
||||
|
||||
export const filterPageProps: FilterPageProps<{}> = {
|
||||
export const filterPageProps: FilterPageProps<{}, unknown> = {
|
||||
currencySymbol: "USD",
|
||||
currentTab: 0,
|
||||
filterTabs: [
|
||||
|
|
|
@ -31,14 +31,17 @@ const HomeSection = () => {
|
|||
onOrdersToCaptureClick={() =>
|
||||
navigate(
|
||||
orderListUrl({
|
||||
status: OrderStatusFilter.READY_TO_CAPTURE
|
||||
status: [OrderStatusFilter.READY_TO_CAPTURE]
|
||||
})
|
||||
)
|
||||
}
|
||||
onOrdersToFulfillClick={() =>
|
||||
navigate(
|
||||
orderListUrl({
|
||||
status: OrderStatusFilter.READY_TO_FULFILL
|
||||
status: [
|
||||
OrderStatusFilter.UNFULFILLED,
|
||||
OrderStatusFilter.PARTIALLY_FULFILLED
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
12
src/misc.ts
12
src/misc.ts
|
@ -466,3 +466,15 @@ export function generateCode(charNum: number) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findInEnum<TEnum extends object>(
|
||||
needle: string,
|
||||
haystack: TEnum
|
||||
) {
|
||||
const match = Object.keys(haystack).find(key => key === needle);
|
||||
if (!!match) {
|
||||
return haystack[needle as keyof TEnum];
|
||||
}
|
||||
|
||||
throw new Error(`Key ${needle} not found in enum`);
|
||||
}
|
||||
|
|
|
@ -10,17 +10,16 @@ import { FilterProps } from "../../../types";
|
|||
import { OrderStatusFilter } from "../../../types/globalTypes";
|
||||
import { OrderListUrlFilters } from "../../urls";
|
||||
|
||||
type OrderListFilterProps = FilterProps<OrderListUrlFilters>;
|
||||
type OrderListFilterProps = FilterProps<OrderListUrlFilters, OrderFilterKeys>;
|
||||
|
||||
export enum OrderFilterKeys {
|
||||
date,
|
||||
dateEqual,
|
||||
dateRange,
|
||||
dateLastWeek,
|
||||
dateLastMonth,
|
||||
dateLastYear,
|
||||
email,
|
||||
fulfillment
|
||||
date = "date",
|
||||
dateEqual = "dateEqual",
|
||||
dateRange = "dateRange",
|
||||
dateLastWeek = "dateLastWeek",
|
||||
dateLastMonth = "dateLastMonth",
|
||||
dateLastYear = "dateLastYear",
|
||||
status = "status"
|
||||
}
|
||||
|
||||
const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
||||
|
@ -28,7 +27,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
const tz = React.useContext(TimezoneContext);
|
||||
const intl = useIntl();
|
||||
|
||||
const filterMenu: IFilter = [
|
||||
const filterMenu: IFilter<OrderFilterKeys> = [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
|
@ -44,7 +43,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Last 7 Days"
|
||||
}),
|
||||
value: OrderFilterKeys.dateLastWeek.toString()
|
||||
value: OrderFilterKeys.dateLastWeek
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -59,7 +58,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Last 30 Days"
|
||||
}),
|
||||
value: OrderFilterKeys.dateLastMonth.toString()
|
||||
value: OrderFilterKeys.dateLastMonth
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -74,7 +73,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Last Year"
|
||||
}),
|
||||
value: OrderFilterKeys.dateLastYear.toString()
|
||||
value: OrderFilterKeys.dateLastYear
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -88,7 +87,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Specific Date"
|
||||
}),
|
||||
value: OrderFilterKeys.dateEqual.toString()
|
||||
value: OrderFilterKeys.dateEqual
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -101,7 +100,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Range"
|
||||
}),
|
||||
value: OrderFilterKeys.dateRange.toString()
|
||||
value: OrderFilterKeys.dateRange
|
||||
}
|
||||
],
|
||||
data: {
|
||||
|
@ -113,7 +112,7 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Date"
|
||||
}),
|
||||
value: OrderFilterKeys.date.toString()
|
||||
value: OrderFilterKeys.date
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -147,15 +146,21 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
|
|||
description: "order fulfillment status"
|
||||
}),
|
||||
value: OrderStatusFilter.UNFULFILLED.toString()
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Ready to Capture",
|
||||
description: "order status"
|
||||
}),
|
||||
value: OrderStatusFilter.READY_TO_CAPTURE.toString()
|
||||
}
|
||||
],
|
||||
type: FieldType.select
|
||||
},
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Fulfillment Status",
|
||||
description: "order"
|
||||
defaultMessage: "Order Status"
|
||||
}),
|
||||
value: OrderFilterKeys.fulfillment.toString()
|
||||
value: OrderFilterKeys.status
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import Container from "@saleor/components/Container";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
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";
|
||||
|
@ -16,7 +17,7 @@ import OrderListFilter from "../OrderListFilter";
|
|||
export interface OrderListPageProps
|
||||
extends PageListProps,
|
||||
ListActions,
|
||||
FilterPageProps<OrderListUrlFilters> {
|
||||
FilterPageProps<OrderListUrlFilters, OrderFilterKeys> {
|
||||
orders: OrderList_orders_edges_node[];
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
BulkAction,
|
||||
Dialog,
|
||||
Filters,
|
||||
FiltersWithMultipleValues,
|
||||
Pagination,
|
||||
SingleAction
|
||||
} from "../types";
|
||||
|
@ -16,11 +17,14 @@ export const orderListPath = orderSectionUrl;
|
|||
export enum OrderListUrlFiltersEnum {
|
||||
dateFrom = "dateFrom",
|
||||
dateTo = "dateTo",
|
||||
status = "status",
|
||||
email = "email",
|
||||
payment = "payment"
|
||||
}
|
||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum>;
|
||||
export enum OrderListUrlFiltersWithMultipleValuesEnum {
|
||||
status = "status"
|
||||
}
|
||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
||||
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
||||
export type OrderListUrlDialog = "cancel" | "save-search" | "delete-search";
|
||||
export type OrderListUrlQueryParams = BulkAction &
|
||||
Dialog<OrderListUrlDialog> &
|
||||
|
|
|
@ -229,8 +229,8 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
|
|||
</Button>
|
||||
}
|
||||
onSearchChange={email => changeFilterField({ email })}
|
||||
onFilterAdd={filter =>
|
||||
changeFilterField(createFilter(filter))
|
||||
onFilterAdd={data =>
|
||||
changeFilterField(createFilter(params, data))
|
||||
}
|
||||
onFilterSave={() => openModal("save-search")}
|
||||
onFilterDelete={() => openModal("delete-search")}
|
||||
|
|
109
src/orders/views/OrderList/__snapshots__/filters.test.ts.snap
Normal file
109
src/orders/views/OrderList/__snapshots__/filters.test.ts.snap
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Crate filter chips 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"label": "Date from 2019-09-01",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Date to 2019-09-10",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Fulfilled",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Partially Fulfilled",
|
||||
"onClick": [Function],
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Create filter object with date 1`] = `
|
||||
Object {
|
||||
"dateFrom": "2019-09-01",
|
||||
"dateTo": "2019-09-01",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with date last month 1`] = `
|
||||
Object {
|
||||
"dateFrom": "2019-09-01",
|
||||
"dateTo": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with date last week 1`] = `
|
||||
Object {
|
||||
"dateFrom": "2019-09-01",
|
||||
"dateTo": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with date last year 1`] = `
|
||||
Object {
|
||||
"dateFrom": "2019-09-01",
|
||||
"dateTo": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with date range 1`] = `
|
||||
Object {
|
||||
"dateFrom": "2019-09-01",
|
||||
"dateTo": "2019-09-10",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with fulfillment status 1`] = `
|
||||
Object {
|
||||
"status": Array [
|
||||
"PARTIALLY_FULFILLED",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with multiple deduped values 1`] = `
|
||||
Object {
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with multiple values 1`] = `
|
||||
Object {
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
"PARTIALLY_FULFILLED",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Get filter variables from multiple status value 1`] = `
|
||||
Object {
|
||||
"created": Object {
|
||||
"gte": "2019-09-01",
|
||||
"lte": "2019-09-10",
|
||||
},
|
||||
"customer": "email@example.com",
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
"PARTIALLY_FULFILLED",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Get filter variables from single status value 1`] = `
|
||||
Object {
|
||||
"created": Object {
|
||||
"gte": "2019-09-01",
|
||||
"lte": "2019-09-10",
|
||||
},
|
||||
"customer": "email@example.com",
|
||||
"status": Array [
|
||||
"FULFILLED",
|
||||
],
|
||||
}
|
||||
`;
|
155
src/orders/views/OrderList/filters.test.ts
Normal file
155
src/orders/views/OrderList/filters.test.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import { createIntl } from "react-intl";
|
||||
|
||||
import { OrderFilterKeys } from "@saleor/orders/components/OrderListFilter";
|
||||
import { OrderStatus, OrderStatusFilter } from "@saleor/types/globalTypes";
|
||||
import { createFilter, createFilterChips, getFilterVariables } from "./filters";
|
||||
|
||||
const mockIntl = createIntl({
|
||||
locale: "en"
|
||||
});
|
||||
|
||||
describe("Create filter object", () => {
|
||||
it("with date", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.dateEqual,
|
||||
value: "2019-09-01"
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with date range", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.dateRange,
|
||||
value: ["2019-09-01", "2019-09-10"]
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with date last week", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.dateLastWeek,
|
||||
value: "2019-09-01"
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with date last month", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.dateLastMonth,
|
||||
value: "2019-09-01"
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with date last year", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.dateLastYear,
|
||||
value: "2019-09-01"
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with fulfillment status", () => {
|
||||
const filter = createFilter(
|
||||
{},
|
||||
{
|
||||
name: OrderFilterKeys.status,
|
||||
value: OrderStatusFilter.PARTIALLY_FULFILLED
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with multiple values", () => {
|
||||
const filter = createFilter(
|
||||
{
|
||||
status: [OrderStatusFilter.FULFILLED]
|
||||
},
|
||||
{
|
||||
name: OrderFilterKeys.status,
|
||||
value: OrderStatusFilter.PARTIALLY_FULFILLED
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with multiple deduped values", () => {
|
||||
const filter = createFilter(
|
||||
{
|
||||
status: [OrderStatusFilter.FULFILLED]
|
||||
},
|
||||
{
|
||||
name: OrderFilterKeys.status,
|
||||
value: OrderStatusFilter.FULFILLED
|
||||
}
|
||||
);
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test("Crate filter chips", () => {
|
||||
const chips = createFilterChips(
|
||||
{
|
||||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
status: [OrderStatus.FULFILLED, OrderStatus.PARTIALLY_FULFILLED]
|
||||
},
|
||||
{
|
||||
formatDate: date => date
|
||||
},
|
||||
jest.fn(),
|
||||
mockIntl as any
|
||||
);
|
||||
|
||||
expect(chips).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("Get filter variables", () => {
|
||||
it("from single status value", () => {
|
||||
const filter = getFilterVariables({
|
||||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
email: "email@example.com",
|
||||
status: OrderStatus.FULFILLED.toString()
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("from multiple status value", () => {
|
||||
const filter = getFilterVariables({
|
||||
dateFrom: "2019-09-01",
|
||||
dateTo: "2019-09-10",
|
||||
email: "email@example.com",
|
||||
status: [
|
||||
OrderStatus.FULFILLED.toString(),
|
||||
OrderStatus.PARTIALLY_FULFILLED.toString()
|
||||
]
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,5 +1,7 @@
|
|||
import { defineMessages, IntlShape } from "react-intl";
|
||||
|
||||
import { findInEnum } from "@saleor/misc";
|
||||
import { removeAtIndex } from "@saleor/utils/lists";
|
||||
import { FilterContentSubmitData } from "../../../components/Filter";
|
||||
import { Filter } from "../../../components/TableFilter";
|
||||
import {
|
||||
|
@ -7,8 +9,12 @@ import {
|
|||
OrderStatusFilter
|
||||
} from "../../../types/globalTypes";
|
||||
import {
|
||||
arrayOrUndefined,
|
||||
arrayOrValue,
|
||||
createFilterTabUtils,
|
||||
createFilterUtils
|
||||
createFilterUtils,
|
||||
dedupeFilter,
|
||||
valueOrFirst
|
||||
} from "../../../utils/filters";
|
||||
import { OrderFilterKeys } from "../../components/OrderListFilter";
|
||||
import {
|
||||
|
@ -40,6 +46,10 @@ const filterMessages = defineMessages({
|
|||
defaultMessage: "Partially Fulfilled",
|
||||
description: "order status"
|
||||
},
|
||||
readyToCapture: {
|
||||
defaultMessage: "Ready to Capture",
|
||||
description: "order status"
|
||||
},
|
||||
unfulfilled: {
|
||||
defaultMessage: "Unfulfilled",
|
||||
description: "order status"
|
||||
|
@ -56,6 +66,9 @@ function getStatusLabel(status: string, intl: IntlShape): string {
|
|||
|
||||
case OrderStatusFilter.UNFULFILLED.toString():
|
||||
return intl.formatMessage(filterMessages.unfulfilled);
|
||||
|
||||
case OrderStatusFilter.READY_TO_CAPTURE.toString():
|
||||
return intl.formatMessage(filterMessages.readyToCapture);
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -70,22 +83,25 @@ export function getFilterVariables(
|
|||
lte: params.dateTo
|
||||
},
|
||||
customer: params.email,
|
||||
status: OrderStatusFilter[params.status]
|
||||
status: Array.isArray(params.status)
|
||||
? params.status.map(status => findInEnum(status, OrderStatusFilter))
|
||||
: params.status
|
||||
? [findInEnum(params.status, OrderStatusFilter)]
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function createFilter(
|
||||
filter: FilterContentSubmitData
|
||||
filter: OrderListUrlFilters,
|
||||
data: FilterContentSubmitData<OrderFilterKeys>
|
||||
): OrderListUrlFilters {
|
||||
const filterName = filter.name;
|
||||
if (filterName === OrderFilterKeys.dateEqual.toString()) {
|
||||
const value = filter.value as string;
|
||||
const { name: filterName, value } = data;
|
||||
if (filterName === OrderFilterKeys.dateEqual) {
|
||||
return {
|
||||
dateFrom: value,
|
||||
dateTo: value
|
||||
dateFrom: valueOrFirst(value),
|
||||
dateTo: valueOrFirst(value)
|
||||
};
|
||||
} else if (filterName === OrderFilterKeys.dateRange.toString()) {
|
||||
const { value } = filter;
|
||||
} else if (filterName === OrderFilterKeys.dateRange) {
|
||||
return {
|
||||
dateFrom: value[0],
|
||||
dateTo: value[1]
|
||||
|
@ -95,19 +111,19 @@ export function createFilter(
|
|||
OrderFilterKeys.dateLastWeek,
|
||||
OrderFilterKeys.dateLastMonth,
|
||||
OrderFilterKeys.dateLastYear
|
||||
]
|
||||
.map(value => value.toString())
|
||||
.includes(filterName)
|
||||
].includes(filterName)
|
||||
) {
|
||||
const { value } = filter;
|
||||
return {
|
||||
dateFrom: value as string,
|
||||
dateFrom: valueOrFirst(value),
|
||||
dateTo: undefined
|
||||
};
|
||||
} else if (filterName === OrderFilterKeys.fulfillment.toString()) {
|
||||
const { value } = filter;
|
||||
} else if (filterName === OrderFilterKeys.status) {
|
||||
return {
|
||||
status: value as string
|
||||
status: dedupeFilter(
|
||||
filter.status
|
||||
? [...(filter.status as string[]), valueOrFirst(value)]
|
||||
: arrayOrValue(value)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -175,17 +191,29 @@ export function createFilterChips(
|
|||
}
|
||||
|
||||
if (!!filters.status) {
|
||||
filterChips = [
|
||||
...filterChips,
|
||||
{
|
||||
label: getStatusLabel(filters.status, intl),
|
||||
onClick: () =>
|
||||
onFilterDelete({
|
||||
...filters,
|
||||
status: undefined
|
||||
})
|
||||
}
|
||||
];
|
||||
const statusFilterChips = Array.isArray(filters.status)
|
||||
? filters.status.map((status, statusIndex) => ({
|
||||
label: getStatusLabel(status, intl),
|
||||
onClick: () =>
|
||||
onFilterDelete({
|
||||
...filters,
|
||||
status: arrayOrUndefined(
|
||||
removeAtIndex(filters.status as string[], statusIndex)
|
||||
)
|
||||
})
|
||||
}))
|
||||
: [
|
||||
{
|
||||
label: getStatusLabel(filters.status, intl),
|
||||
onClick: () =>
|
||||
onFilterDelete({
|
||||
...filters,
|
||||
status: undefined
|
||||
})
|
||||
}
|
||||
];
|
||||
|
||||
filterChips = [...filterChips, ...statusFilterChips];
|
||||
}
|
||||
|
||||
return filterChips;
|
||||
|
|
|
@ -7,21 +7,23 @@ import { FilterProps } from "@saleor/types";
|
|||
import { StockAvailability } from "@saleor/types/globalTypes";
|
||||
import { ProductListUrlFilters } from "../../urls";
|
||||
|
||||
type ProductListFilterProps = FilterProps<ProductListUrlFilters>;
|
||||
type ProductListFilterProps = FilterProps<
|
||||
ProductListUrlFilters,
|
||||
ProductFilterKeys
|
||||
>;
|
||||
|
||||
export enum ProductFilterKeys {
|
||||
published,
|
||||
price,
|
||||
priceEqual,
|
||||
priceRange,
|
||||
stock,
|
||||
query
|
||||
published = "published",
|
||||
price = "price",
|
||||
priceEqual = "priceEqual",
|
||||
priceRange = "priceRange",
|
||||
stock = "stock"
|
||||
}
|
||||
|
||||
const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
||||
const intl = useIntl();
|
||||
|
||||
const filterMenu: IFilter = [
|
||||
const filterMenu: IFilter<ProductFilterKeys> = [
|
||||
{
|
||||
children: [],
|
||||
data: {
|
||||
|
@ -55,7 +57,7 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
defaultMessage: "Visibility",
|
||||
description: "product visibility"
|
||||
}),
|
||||
value: ProductFilterKeys.published.toString()
|
||||
value: ProductFilterKeys.published
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -85,7 +87,7 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
defaultMessage: "Stock",
|
||||
description: "product stock"
|
||||
}),
|
||||
value: ProductFilterKeys.stock.toString()
|
||||
value: ProductFilterKeys.stock
|
||||
},
|
||||
{
|
||||
children: [
|
||||
|
@ -102,7 +104,7 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Specific Price"
|
||||
}),
|
||||
value: ProductFilterKeys.priceEqual.toString()
|
||||
value: ProductFilterKeys.priceEqual
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
|
@ -115,7 +117,7 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Range"
|
||||
}),
|
||||
value: ProductFilterKeys.priceRange.toString()
|
||||
value: ProductFilterKeys.priceRange
|
||||
}
|
||||
],
|
||||
data: {
|
||||
|
@ -127,7 +129,7 @@ const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
|||
label: intl.formatMessage({
|
||||
defaultMessage: "Price"
|
||||
}),
|
||||
value: ProductFilterKeys.price.toString()
|
||||
value: ProductFilterKeys.price
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@ import {
|
|||
} from "@saleor/types";
|
||||
import { ProductListUrlFilters } from "../../urls";
|
||||
import ProductList from "../ProductList";
|
||||
import ProductListFilter from "../ProductListFilter";
|
||||
import ProductListFilter, { ProductFilterKeys } from "../ProductListFilter";
|
||||
|
||||
export interface ProductListPageProps
|
||||
extends PageListProps<ProductListColumns>,
|
||||
ListActions,
|
||||
FilterPageProps<ProductListUrlFilters>,
|
||||
FilterPageProps<ProductListUrlFilters, ProductFilterKeys>,
|
||||
FetchMoreProps {
|
||||
availableInGridAttributes: AvailableInGridAttributes_availableInGrid_edges_node[];
|
||||
currencySymbol: string;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Crate filter chips 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"label": "Price from $10.00",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Price to $20.00",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Available",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"label": "Published",
|
||||
"onClick": [Function],
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Create filter object with price 1`] = `
|
||||
Object {
|
||||
"priceFrom": "10",
|
||||
"priceTo": "10",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with price range 1`] = `
|
||||
Object {
|
||||
"priceFrom": Array [
|
||||
"10",
|
||||
"20",
|
||||
],
|
||||
"priceTo": Array [
|
||||
"10",
|
||||
"20",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with publication status 1`] = `
|
||||
Object {
|
||||
"isPublished": "false",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Create filter object with stock status 1`] = `
|
||||
Object {
|
||||
"status": "OUT_OF_STOCK",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Get filter variables 1`] = `
|
||||
Object {
|
||||
"isPublished": true,
|
||||
"price": Object {
|
||||
"gte": 10,
|
||||
"lte": 20,
|
||||
},
|
||||
"search": undefined,
|
||||
"stockAvailability": "IN_STOCK",
|
||||
}
|
||||
`;
|
77
src/products/views/ProductList/filters.test.ts
Normal file
77
src/products/views/ProductList/filters.test.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { createIntl } from "react-intl";
|
||||
|
||||
import { ProductFilterKeys } from "@saleor/products/components/ProductListFilter";
|
||||
import { StockAvailability } from "@saleor/types/globalTypes";
|
||||
import { createFilter, createFilterChips, getFilterVariables } from "./filters";
|
||||
|
||||
const mockIntl = createIntl({
|
||||
locale: "en"
|
||||
});
|
||||
|
||||
describe("Create filter object", () => {
|
||||
it("with price", () => {
|
||||
const filter = createFilter({
|
||||
name: ProductFilterKeys.priceEqual,
|
||||
value: "10"
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with price range", () => {
|
||||
const filter = createFilter({
|
||||
name: ProductFilterKeys.priceEqual,
|
||||
value: ["10", "20"]
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with publication status", () => {
|
||||
const filter = createFilter({
|
||||
name: ProductFilterKeys.published,
|
||||
value: "false"
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with stock status", () => {
|
||||
const filter = createFilter({
|
||||
name: ProductFilterKeys.stock,
|
||||
value: StockAvailability.OUT_OF_STOCK
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test("Crate filter chips", () => {
|
||||
const chips = createFilterChips(
|
||||
{
|
||||
isPublished: "true",
|
||||
priceFrom: "10",
|
||||
priceTo: "20",
|
||||
status: StockAvailability.IN_STOCK
|
||||
},
|
||||
{
|
||||
currencySymbol: "USD",
|
||||
locale: "en"
|
||||
},
|
||||
jest.fn(),
|
||||
mockIntl as any
|
||||
);
|
||||
|
||||
expect(chips).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("Get filter variables", () => {
|
||||
const filter = getFilterVariables({
|
||||
isPublished: "true",
|
||||
priceFrom: "10",
|
||||
priceTo: "20",
|
||||
status: StockAvailability.IN_STOCK
|
||||
});
|
||||
|
||||
expect(filter).toMatchSnapshot();
|
||||
});
|
|
@ -34,26 +34,26 @@ export function getFilterVariables(
|
|||
}
|
||||
|
||||
export function createFilter(
|
||||
filter: FilterContentSubmitData
|
||||
filter: FilterContentSubmitData<ProductFilterKeys>
|
||||
): ProductListUrlFilters {
|
||||
const filterName = filter.name;
|
||||
if (filterName === ProductFilterKeys.priceEqual.toString()) {
|
||||
if (filterName === ProductFilterKeys.priceEqual) {
|
||||
const value = filter.value as string;
|
||||
return {
|
||||
priceFrom: value,
|
||||
priceTo: value
|
||||
};
|
||||
} else if (filterName === ProductFilterKeys.priceRange.toString()) {
|
||||
} else if (filterName === ProductFilterKeys.priceRange) {
|
||||
const { value } = filter;
|
||||
return {
|
||||
priceFrom: value[0],
|
||||
priceTo: value[1]
|
||||
};
|
||||
} else if (filterName === ProductFilterKeys.published.toString()) {
|
||||
} else if (filterName === ProductFilterKeys.published) {
|
||||
return {
|
||||
isPublished: filter.value as string
|
||||
};
|
||||
} else if (filterName === ProductFilterKeys.stock.toString()) {
|
||||
} else if (filterName === ProductFilterKeys.stock) {
|
||||
const value = filter.value as string;
|
||||
return {
|
||||
status: StockAvailability[value]
|
||||
|
|
10
src/types.ts
10
src/types.ts
|
@ -65,7 +65,7 @@ export interface PageListProps<TColumns extends string = string>
|
|||
defaultSettings?: ListSettings<TColumns>;
|
||||
onAdd: () => void;
|
||||
}
|
||||
export interface FilterPageProps<TUrlFilters> {
|
||||
export interface FilterPageProps<TUrlFilters, TFilterKeys> {
|
||||
currencySymbol: string;
|
||||
currentTab: number;
|
||||
filterTabs: GetFilterTabsOutput<TUrlFilters>;
|
||||
|
@ -73,12 +73,13 @@ export interface FilterPageProps<TUrlFilters> {
|
|||
initialSearch: string;
|
||||
onAll: () => void;
|
||||
onSearchChange: (value: string) => void;
|
||||
onFilterAdd: (filter: FilterContentSubmitData) => void;
|
||||
onFilterAdd: (filter: FilterContentSubmitData<TFilterKeys>) => void;
|
||||
onFilterDelete: () => void;
|
||||
onFilterSave: () => void;
|
||||
onTabChange: (tab: number) => void;
|
||||
}
|
||||
export interface FilterProps<TUrlFilters> extends FilterPageProps<TUrlFilters> {
|
||||
export interface FilterProps<TUrlFilters, TFilterKeys>
|
||||
extends FilterPageProps<TUrlFilters, TFilterKeys> {
|
||||
allTabLabel: string;
|
||||
filterLabel: string;
|
||||
searchPlaceholder: string;
|
||||
|
@ -112,6 +113,9 @@ export type ActiveTab<TTab extends string = string> = Partial<{
|
|||
export type Filters<TFilters extends string> = Partial<
|
||||
Record<TFilters, string>
|
||||
>;
|
||||
export type FiltersWithMultipleValues<TFilters extends string> = Partial<
|
||||
Record<TFilters, string | string[]>
|
||||
>;
|
||||
export type SingleAction = Partial<{
|
||||
id: string;
|
||||
}>;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
function createFilterUtils<TQueryParams, TFilters>(filters: object) {
|
||||
function createFilterUtils<
|
||||
TQueryParams extends object,
|
||||
TFilters extends object
|
||||
>(filters: object) {
|
||||
function getActiveFilters(params: TQueryParams): TFilters {
|
||||
return Object.keys(params)
|
||||
.filter(key => Object.keys(filters).includes(key))
|
||||
|
@ -21,4 +24,32 @@ function createFilterUtils<TQueryParams, TFilters>(filters: object) {
|
|||
};
|
||||
}
|
||||
|
||||
export function valueOrFirst<T>(value: T | T[]): T {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function arrayOrValue<T>(value: T | T[]): T[] {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return [value];
|
||||
}
|
||||
|
||||
export function arrayOrUndefined<T>(array: T[]): T[] | undefined {
|
||||
if (array.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
export function dedupeFilter<T>(array: T[]): T[] {
|
||||
return Array.from(new Set(array));
|
||||
}
|
||||
|
||||
export default createFilterUtils;
|
||||
|
|
|
@ -8,7 +8,7 @@ Array [
|
|||
"label": "1",
|
||||
"parent": null,
|
||||
"sort": 0,
|
||||
"value": "1",
|
||||
"value": 0,
|
||||
},
|
||||
Object {
|
||||
"data": null,
|
||||
|
@ -16,7 +16,7 @@ Array [
|
|||
"label": "2",
|
||||
"parent": null,
|
||||
"sort": 1,
|
||||
"value": "2",
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"data": null,
|
||||
|
@ -24,7 +24,7 @@ Array [
|
|||
"label": "3",
|
||||
"parent": null,
|
||||
"sort": 2,
|
||||
"value": "3",
|
||||
"value": 2,
|
||||
},
|
||||
Object {
|
||||
"data": null,
|
||||
|
@ -32,7 +32,7 @@ Array [
|
|||
"label": "4",
|
||||
"parent": null,
|
||||
"sort": 3,
|
||||
"value": "4",
|
||||
"value": 3,
|
||||
},
|
||||
Object {
|
||||
"data": null,
|
||||
|
@ -40,7 +40,7 @@ Array [
|
|||
"label": "4.1",
|
||||
"parent": "3",
|
||||
"sort": 0,
|
||||
"value": "4.1",
|
||||
"value": 4,
|
||||
},
|
||||
Object {
|
||||
"data": null,
|
||||
|
@ -48,7 +48,7 @@ Array [
|
|||
"label": "4.2",
|
||||
"parent": "3",
|
||||
"sort": 1,
|
||||
"value": "4.2",
|
||||
"value": 5,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -61,24 +61,24 @@ Array [
|
|||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.1",
|
||||
"value": "4.1",
|
||||
"value": 4,
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.2",
|
||||
"value": "4.2",
|
||||
"value": 5,
|
||||
},
|
||||
],
|
||||
"data": null,
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
"value": 3,
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.1",
|
||||
"value": "4.1",
|
||||
"value": 4,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -89,7 +89,7 @@ Array [
|
|||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.1",
|
||||
"value": "4.1",
|
||||
"value": 4,
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
|
@ -97,18 +97,18 @@ Array [
|
|||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.1",
|
||||
"value": "4.1",
|
||||
"value": 4,
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"data": null,
|
||||
"label": "4.2",
|
||||
"value": "4.2",
|
||||
"value": 5,
|
||||
},
|
||||
],
|
||||
"data": null,
|
||||
"label": "4",
|
||||
"value": "4",
|
||||
"value": 3,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -9,24 +9,33 @@ import {
|
|||
walkToRoot
|
||||
} from "./menu";
|
||||
|
||||
const validMenu: IMenu = [
|
||||
enum MenuKey {
|
||||
one,
|
||||
two,
|
||||
three,
|
||||
four,
|
||||
fourOne,
|
||||
foutTwo
|
||||
}
|
||||
|
||||
const validMenu: IMenu<null, MenuKey> = [
|
||||
{
|
||||
children: [],
|
||||
data: null,
|
||||
label: "1",
|
||||
value: "1"
|
||||
value: MenuKey.one
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
data: null,
|
||||
label: "2",
|
||||
value: "2"
|
||||
value: MenuKey.two
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
data: null,
|
||||
label: "3",
|
||||
value: "3"
|
||||
value: MenuKey.three
|
||||
},
|
||||
{
|
||||
children: [
|
||||
|
@ -34,18 +43,18 @@ const validMenu: IMenu = [
|
|||
children: [],
|
||||
data: null,
|
||||
label: "4.1",
|
||||
value: "4.1"
|
||||
value: MenuKey.fourOne
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
data: null,
|
||||
label: "4.2",
|
||||
value: "4.2"
|
||||
value: MenuKey.foutTwo
|
||||
}
|
||||
],
|
||||
data: null,
|
||||
label: "4",
|
||||
value: "4"
|
||||
value: MenuKey.four
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,62 +1,76 @@
|
|||
interface IBaseMenuItem<TMenuData = {}> {
|
||||
interface IBaseMenuItem<TMenuData = {}, TValue = string> {
|
||||
label: React.ReactNode;
|
||||
value?: string;
|
||||
value?: TValue;
|
||||
data: TMenuData | null;
|
||||
}
|
||||
export type IFlatMenuItem<TMenuData = {}> = IBaseMenuItem<TMenuData> & {
|
||||
export type IFlatMenuItem<TMenuData = {}, TValue = string> = IBaseMenuItem<
|
||||
TMenuData,
|
||||
TValue
|
||||
> & {
|
||||
id: string;
|
||||
parent: string | null;
|
||||
sort: number;
|
||||
};
|
||||
export type IMenuItem<TMenuData = {}> = IBaseMenuItem<TMenuData> & {
|
||||
children: Array<IMenuItem<TMenuData>>;
|
||||
export type IMenuItem<TMenuData = {}, TValue = string> = IBaseMenuItem<
|
||||
TMenuData,
|
||||
TValue
|
||||
> & {
|
||||
children: Array<IMenuItem<TMenuData, TValue>>;
|
||||
};
|
||||
export type IMenu<TMenuData = {}> = Array<IMenuItem<TMenuData>>;
|
||||
export type IFlatMenu<TMenuData = {}> = Array<IFlatMenuItem<TMenuData>>;
|
||||
export type IMenu<TMenuData = {}, TValue = string> = Array<
|
||||
IMenuItem<TMenuData, TValue>
|
||||
>;
|
||||
export type IFlatMenu<TMenuData = {}, TValue = string> = Array<
|
||||
IFlatMenuItem<TMenuData, TValue>
|
||||
>;
|
||||
|
||||
export function validateMenuOptions<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>
|
||||
export function validateMenuOptions<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>
|
||||
): boolean {
|
||||
const values: string[] = toFlat(menu)
|
||||
const values: TValue[] = toFlat(menu)
|
||||
.map(menuItem => menuItem.value)
|
||||
.filter(value => value !== undefined);
|
||||
const uniqueValues = Array.from(new Set(values));
|
||||
return uniqueValues.length === values.length;
|
||||
}
|
||||
|
||||
function _getMenuItemByPath<TMenuData = {}>(
|
||||
menuItem: IMenuItem<TMenuData>,
|
||||
function _getMenuItemByPath<TMenuData = {}, TValue = string>(
|
||||
menuItem: IMenuItem<TMenuData, TValue>,
|
||||
path: number[]
|
||||
): IMenuItem<TMenuData> {
|
||||
): IMenuItem<TMenuData, TValue> {
|
||||
if (path.length === 0) {
|
||||
return menuItem;
|
||||
}
|
||||
return _getMenuItemByPath(menuItem.children[path[0]], path.slice(1));
|
||||
}
|
||||
|
||||
export function getMenuItemByPath<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>,
|
||||
export function getMenuItemByPath<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>,
|
||||
path: number[]
|
||||
): IMenuItem<TMenuData> {
|
||||
): IMenuItem<TMenuData, TValue> {
|
||||
return _getMenuItemByPath(menu[path[0]], path.slice(1));
|
||||
}
|
||||
|
||||
export function getMenuItemByValue<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>,
|
||||
value: string
|
||||
): IMenuItem<TMenuData> {
|
||||
export function getMenuItemByValue<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>,
|
||||
value: TValue
|
||||
): IMenuItem<TMenuData, TValue> {
|
||||
const flatMenu = toFlat(menu);
|
||||
const flatMenuItem: IFlatMenuItem<TMenuData> = flatMenu.find(
|
||||
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = flatMenu.find(
|
||||
menuItem => menuItem.value === value
|
||||
);
|
||||
|
||||
if (flatMenuItem === undefined) {
|
||||
throw new Error(`Value ${value} does not exist in menu`);
|
||||
}
|
||||
|
||||
return _fromFlat(flatMenu, flatMenuItem);
|
||||
}
|
||||
|
||||
function _walkToMenuItem<TMenuData = {}>(
|
||||
menuItem: IMenuItem<TMenuData>,
|
||||
function _walkToMenuItem<TMenuData = {}, TValue = string>(
|
||||
menuItem: IMenuItem<TMenuData, TValue>,
|
||||
path: number[]
|
||||
): IMenu<TMenuData> {
|
||||
): IMenu<TMenuData, TValue> {
|
||||
const node = menuItem.children[path[0]];
|
||||
|
||||
if (path.length === 1) {
|
||||
|
@ -66,18 +80,18 @@ function _walkToMenuItem<TMenuData = {}>(
|
|||
return [node, ..._walkToMenuItem(node, path.slice(1))];
|
||||
}
|
||||
|
||||
export function walkToMenuItem<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>,
|
||||
export function walkToMenuItem<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>,
|
||||
path: number[]
|
||||
): IMenu<TMenuData> {
|
||||
): IMenu<TMenuData, TValue> {
|
||||
const walkByNode = menu[path[0]];
|
||||
return [walkByNode, ..._walkToMenuItem(walkByNode, path.slice(1))];
|
||||
}
|
||||
|
||||
function _walkToRoot<TMenuData = {}>(
|
||||
flatMenu: IFlatMenu<TMenuData>,
|
||||
function _walkToRoot<TMenuData = {}, TValue = string>(
|
||||
flatMenu: IFlatMenu<TMenuData, TValue>,
|
||||
parent: string
|
||||
): IFlatMenu<TMenuData> {
|
||||
): IFlatMenu<TMenuData, TValue> {
|
||||
const menuItem = flatMenu.find(menuItem => menuItem.id === parent);
|
||||
|
||||
if (menuItem.parent === null) {
|
||||
|
@ -86,10 +100,10 @@ function _walkToRoot<TMenuData = {}>(
|
|||
|
||||
return [menuItem, ..._walkToRoot(flatMenu, menuItem.parent)];
|
||||
}
|
||||
export function walkToRoot<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>,
|
||||
value: string
|
||||
): IMenu<TMenuData> {
|
||||
export function walkToRoot<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>,
|
||||
value: TValue
|
||||
): IMenu<TMenuData, TValue> {
|
||||
const flatMenu = toFlat(menu);
|
||||
const menuItem = flatMenu.find(menuItem => menuItem.value === value);
|
||||
|
||||
|
@ -99,13 +113,13 @@ export function walkToRoot<TMenuData = {}>(
|
|||
).map(flatMenuItem => _fromFlat(flatMenu, flatMenuItem));
|
||||
}
|
||||
|
||||
function _toFlat<TMenuData = {}>(
|
||||
menuItem: IMenuItem<TMenuData>,
|
||||
function _toFlat<TMenuData = {}, TValue = string>(
|
||||
menuItem: IMenuItem<TMenuData, TValue>,
|
||||
sort: number,
|
||||
parent: string
|
||||
): IFlatMenu<TMenuData> {
|
||||
): IFlatMenu<TMenuData, TValue> {
|
||||
const id = parent ? [parent, sort].join(":") : sort.toString();
|
||||
const flatMenuItem: IFlatMenuItem<TMenuData> = {
|
||||
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = {
|
||||
data: menuItem.data,
|
||||
id,
|
||||
label: menuItem.label,
|
||||
|
@ -117,22 +131,28 @@ function _toFlat<TMenuData = {}>(
|
|||
flatMenuItem,
|
||||
...menuItem.children
|
||||
.map((child, childIndex) => _toFlat(child, childIndex, id))
|
||||
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<TMenuData>)
|
||||
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<
|
||||
TMenuData,
|
||||
TValue
|
||||
>)
|
||||
];
|
||||
}
|
||||
export function toFlat<TMenuData = {}>(
|
||||
menu: IMenu<TMenuData>
|
||||
): IFlatMenu<TMenuData> {
|
||||
export function toFlat<TMenuData = {}, TValue = string>(
|
||||
menu: IMenu<TMenuData, TValue>
|
||||
): IFlatMenu<TMenuData, TValue> {
|
||||
return menu
|
||||
.map((menuItem, menuItemIndex) => _toFlat(menuItem, menuItemIndex, null))
|
||||
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<TMenuData>);
|
||||
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<
|
||||
TMenuData,
|
||||
TValue
|
||||
>);
|
||||
}
|
||||
|
||||
function _fromFlat<TMenuData = {}>(
|
||||
menu: IFlatMenu<TMenuData>,
|
||||
flatMenuItem: IFlatMenuItem<TMenuData>
|
||||
): IMenuItem<TMenuData> {
|
||||
const children: Array<IMenuItem<TMenuData>> = menu
|
||||
function _fromFlat<TMenuData = {}, TValue = string>(
|
||||
menu: IFlatMenu<TMenuData, TValue>,
|
||||
flatMenuItem: IFlatMenuItem<TMenuData, TValue>
|
||||
): IMenuItem<TMenuData, TValue> {
|
||||
const children: Array<IMenuItem<TMenuData, TValue>> = menu
|
||||
.filter(menuItem => menuItem.parent === flatMenuItem.id)
|
||||
.map(menuItem => _fromFlat(menu, menuItem));
|
||||
|
||||
|
@ -143,16 +163,16 @@ function _fromFlat<TMenuData = {}>(
|
|||
value: flatMenuItem.value
|
||||
};
|
||||
}
|
||||
export function fromFlat<TMenuData = {}>(
|
||||
menu: IFlatMenu<TMenuData>
|
||||
): IMenu<TMenuData> {
|
||||
export function fromFlat<TMenuData = {}, TValue = string>(
|
||||
menu: IFlatMenu<TMenuData, TValue>
|
||||
): IMenu<TMenuData, TValue> {
|
||||
return menu
|
||||
.filter(menuItem => menuItem.parent === null)
|
||||
.map(menuItem => _fromFlat(menu, menuItem));
|
||||
}
|
||||
|
||||
export function isLeaf<TMenuData = {}>(
|
||||
menuItem: IMenuItem<TMenuData>
|
||||
export function isLeaf<TMenuData = {}, TValue = string>(
|
||||
menuItem: IMenuItem<TMenuData, TValue>
|
||||
): boolean {
|
||||
return menuItem.children.length === 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue