Merge pull request #1269 from mirumee/SALEOR-3695-collection-list-adjustments
Add filter bar to collection view
This commit is contained in:
commit
e26d6b5a66
22 changed files with 204 additions and 54 deletions
|
@ -1,5 +1,7 @@
|
||||||
import { TableBody, TableCell, TableFooter, TableRow } from "@material-ui/core";
|
import { TableBody, TableCell, TableFooter, TableRow } from "@material-ui/core";
|
||||||
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
|
import { canBeSorted } from "@saleor/collections/views/CollectionList/sort";
|
||||||
|
import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel";
|
||||||
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
|
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
|
@ -15,6 +17,7 @@ import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
|
import { messages } from "./messages";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -116,6 +119,12 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
}
|
}
|
||||||
onClick={() => onSort(CollectionListUrlSortField.available)}
|
onClick={() => onSort(CollectionListUrlSortField.available)}
|
||||||
className={classes.colAvailability}
|
className={classes.colAvailability}
|
||||||
|
disabled={
|
||||||
|
!canBeSorted(
|
||||||
|
CollectionListUrlSortField.available,
|
||||||
|
!!selectedChannelId
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Availability"
|
defaultMessage="Availability"
|
||||||
|
@ -178,19 +187,20 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
data-test="availability"
|
data-test="availability"
|
||||||
data-test-availability={!!collection?.channelListings?.length}
|
data-test-availability={!!collection?.channelListings?.length}
|
||||||
>
|
>
|
||||||
{collection && !collection?.channelListings?.length ? (
|
{(!collection && <Skeleton />) ||
|
||||||
"-"
|
(!collection?.channelListings?.length && "-") ||
|
||||||
) : collection?.channelListings !== undefined ? (
|
(collection?.channelListings !== undefined && channel ? (
|
||||||
channel ? (
|
<AvailabilityStatusLabel
|
||||||
|
channel={channel}
|
||||||
|
messages={messages}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<ChannelsAvailabilityDropdown
|
<ChannelsAvailabilityDropdown
|
||||||
allChannelsCount={channelsCount}
|
allChannelsCount={channelsCount}
|
||||||
channels={collection?.channelListings}
|
channels={collection?.channelListings}
|
||||||
showStatus
|
showStatus
|
||||||
/>
|
/>
|
||||||
) : null
|
))}
|
||||||
) : (
|
|
||||||
<Skeleton />
|
|
||||||
)}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
|
16
src/collections/components/CollectionList/messages.ts
Normal file
16
src/collections/components/CollectionList/messages.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const messages = defineMessages({
|
||||||
|
published: {
|
||||||
|
defaultMessage: "Published on {date}",
|
||||||
|
description: "collection publication date"
|
||||||
|
},
|
||||||
|
unpublished: {
|
||||||
|
defaultMessage: "Unpublished",
|
||||||
|
description: "collection publication date"
|
||||||
|
},
|
||||||
|
willBePublished: {
|
||||||
|
defaultMessage: "Becomes published on {date}",
|
||||||
|
description: "collection publication date"
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,11 +1,12 @@
|
||||||
import { Button, Card } from "@material-ui/core";
|
import { Button, Card } from "@material-ui/core";
|
||||||
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
import { Container } from "@saleor/components/Container";
|
import { Container } from "@saleor/components/Container";
|
||||||
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SearchBar from "@saleor/components/SearchBar";
|
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
ChannelProps,
|
ChannelProps,
|
||||||
|
FilterPageProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
@ -17,13 +18,18 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
|
||||||
import CollectionList from "../CollectionList/CollectionList";
|
import CollectionList from "../CollectionList/CollectionList";
|
||||||
|
import {
|
||||||
|
CollectionFilterKeys,
|
||||||
|
CollectionListFilterOpts,
|
||||||
|
createFilterStructure
|
||||||
|
} from "./filters";
|
||||||
export interface CollectionListPageProps
|
export interface CollectionListPageProps
|
||||||
extends PageListProps,
|
extends PageListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
SortPage<CollectionListUrlSortField>,
|
SortPage<CollectionListUrlSortField>,
|
||||||
TabPageProps,
|
TabPageProps,
|
||||||
|
FilterPageProps<CollectionFilterKeys, CollectionListFilterOpts>,
|
||||||
ChannelProps {
|
ChannelProps {
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
|
@ -42,9 +48,13 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
||||||
onTabSave,
|
onTabSave,
|
||||||
selectedChannelId,
|
selectedChannelId,
|
||||||
tabs,
|
tabs,
|
||||||
|
filterOpts,
|
||||||
|
onFilterChange,
|
||||||
|
onFilterAttributeFocus,
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
@ -63,22 +73,25 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Card>
|
<Card>
|
||||||
<SearchBar
|
<FilterBar
|
||||||
allTabLabel={intl.formatMessage({
|
allTabLabel={intl.formatMessage({
|
||||||
defaultMessage: "All Collections",
|
defaultMessage: "All Collections",
|
||||||
description: "tab name"
|
description: "tab name"
|
||||||
})}
|
})}
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
|
filterStructure={filterStructure}
|
||||||
initialSearch={initialSearch}
|
initialSearch={initialSearch}
|
||||||
searchPlaceholder={intl.formatMessage({
|
|
||||||
defaultMessage: "Search Collection"
|
|
||||||
})}
|
|
||||||
tabs={tabs}
|
|
||||||
onAll={onAll}
|
onAll={onAll}
|
||||||
|
onFilterChange={onFilterChange}
|
||||||
|
onFilterAttributeFocus={onFilterAttributeFocus}
|
||||||
onSearchChange={onSearchChange}
|
onSearchChange={onSearchChange}
|
||||||
onTabChange={onTabChange}
|
onTabChange={onTabChange}
|
||||||
onTabDelete={onTabDelete}
|
onTabDelete={onTabDelete}
|
||||||
onTabSave={onTabSave}
|
onTabSave={onTabSave}
|
||||||
|
searchPlaceholder={intl.formatMessage({
|
||||||
|
defaultMessage: "Search Collections"
|
||||||
|
})}
|
||||||
|
tabs={tabs}
|
||||||
/>
|
/>
|
||||||
<CollectionList
|
<CollectionList
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { IFilter } from "@saleor/components/Filter";
|
import { IFilter } from "@saleor/components/Filter";
|
||||||
|
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { FilterOpts } from "@saleor/types";
|
import { FilterOpts } from "@saleor/types";
|
||||||
import { CollectionPublished } from "@saleor/types/globalTypes";
|
import { CollectionPublished } from "@saleor/types/globalTypes";
|
||||||
|
@ -7,10 +8,12 @@ import { defineMessages, IntlShape } from "react-intl";
|
||||||
|
|
||||||
export interface CollectionListFilterOpts {
|
export interface CollectionListFilterOpts {
|
||||||
status: FilterOpts<CollectionPublished>;
|
status: FilterOpts<CollectionPublished>;
|
||||||
|
channel: FilterOpts<string> & { choices: MultiAutocompleteChoiceType[] };
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CollectionFilterKeys {
|
export enum CollectionFilterKeys {
|
||||||
status = "status"
|
status = "status",
|
||||||
|
channel = "channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -46,7 +49,18 @@ export function createFilterStructure(
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
active: opts.status.active
|
active: opts.status.active,
|
||||||
|
dependencies: [CollectionFilterKeys.channel]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...createOptionsField(
|
||||||
|
CollectionFilterKeys.channel,
|
||||||
|
intl.formatMessage(commonMessages.channel),
|
||||||
|
[opts.channel.value],
|
||||||
|
false,
|
||||||
|
opts.channel.choices
|
||||||
|
),
|
||||||
|
active: opts.channel.active
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,29 @@
|
||||||
|
import { CollectionPublished } from "@saleor/types/globalTypes";
|
||||||
|
|
||||||
import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json";
|
import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json";
|
||||||
|
import { CollectionListFilterOpts } from "./components/CollectionListPage";
|
||||||
import { CollectionDetails_collection } from "./types/CollectionDetails";
|
import { CollectionDetails_collection } from "./types/CollectionDetails";
|
||||||
import { CollectionList_collections_edges_node } from "./types/CollectionList";
|
import { CollectionList_collections_edges_node } from "./types/CollectionList";
|
||||||
|
|
||||||
const content = richTextEditorFixtures.richTextEditor;
|
const content = richTextEditorFixtures.richTextEditor;
|
||||||
|
|
||||||
|
export const collectionListFilterOpts: CollectionListFilterOpts = {
|
||||||
|
channel: {
|
||||||
|
active: false,
|
||||||
|
value: "default-channel",
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: "default-channel",
|
||||||
|
label: "Default channel"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
active: false,
|
||||||
|
value: CollectionPublished.PUBLISHED
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const collections: CollectionList_collections_edges_node[] = [
|
export const collections: CollectionList_collections_edges_node[] = [
|
||||||
{
|
{
|
||||||
__typename: "Collection",
|
__typename: "Collection",
|
||||||
|
|
|
@ -25,6 +25,7 @@ export const collectionList = gql`
|
||||||
$before: String
|
$before: String
|
||||||
$filter: CollectionFilterInput
|
$filter: CollectionFilterInput
|
||||||
$sort: CollectionSortingInput
|
$sort: CollectionSortingInput
|
||||||
|
$channel: String
|
||||||
) {
|
) {
|
||||||
collections(
|
collections(
|
||||||
first: $first
|
first: $first
|
||||||
|
@ -33,6 +34,7 @@ export const collectionList = gql`
|
||||||
last: $last
|
last: $last
|
||||||
filter: $filter
|
filter: $filter
|
||||||
sortBy: $sort
|
sortBy: $sort
|
||||||
|
channel: $channel
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|
|
@ -17,7 +17,8 @@ const collectionSectionUrl = "/collections/";
|
||||||
export const collectionListPath = collectionSectionUrl;
|
export const collectionListPath = collectionSectionUrl;
|
||||||
export enum CollectionListUrlFiltersEnum {
|
export enum CollectionListUrlFiltersEnum {
|
||||||
status = "status",
|
status = "status",
|
||||||
query = "query"
|
query = "query",
|
||||||
|
channel = "channel"
|
||||||
}
|
}
|
||||||
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
|
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
|
||||||
export type CollectionListUrlDialog = "remove" | TabActionDialog;
|
export type CollectionListUrlDialog = "remove" | TabActionDialog;
|
||||||
|
|
|
@ -17,10 +17,12 @@ import { commonMessages } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
|
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
|
||||||
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
import createSortHandler from "@saleor/utils/handlers/sortHandler";
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems, mapNodeToChoice } from "@saleor/utils/maps";
|
||||||
import { getSortParams } from "@saleor/utils/sort";
|
import { getSortParams } from "@saleor/utils/sort";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useEffect } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
|
import CollectionListPage from "../../components/CollectionListPage/CollectionListPage";
|
||||||
|
@ -37,11 +39,13 @@ import {
|
||||||
areFiltersApplied,
|
areFiltersApplied,
|
||||||
deleteFilterTab,
|
deleteFilterTab,
|
||||||
getActiveFilters,
|
getActiveFilters,
|
||||||
|
getFilterOpts,
|
||||||
|
getFilterQueryParam,
|
||||||
getFilterTabs,
|
getFilterTabs,
|
||||||
getFilterVariables,
|
getFilterVariables,
|
||||||
saveFilterTab
|
saveFilterTab
|
||||||
} from "./filters";
|
} from "./filters";
|
||||||
import { getSortQueryVariables } from "./sort";
|
import { canBeSorted, DEFAULT_SORT_KEY, getSortQueryVariables } from "./sort";
|
||||||
|
|
||||||
interface CollectionListProps {
|
interface CollectionListProps {
|
||||||
params: CollectionListUrlQueryParams;
|
params: CollectionListUrlQueryParams;
|
||||||
|
@ -59,12 +63,33 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [
|
||||||
|
changeFilters,
|
||||||
|
resetFilters,
|
||||||
|
handleSearchChange
|
||||||
|
] = createFilterHandlers({
|
||||||
|
cleanupFn: reset,
|
||||||
|
createUrl: collectionListUrl,
|
||||||
|
getFilterQueryParam,
|
||||||
|
navigate,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
|
||||||
|
const { availableChannels } = useAppChannel(false);
|
||||||
|
const channelOpts = availableChannels
|
||||||
|
? mapNodeToChoice(availableChannels, channel => channel.slug)
|
||||||
|
: null;
|
||||||
|
const selectedChannel = availableChannels.find(
|
||||||
|
channel => channel.slug === params.channel
|
||||||
|
);
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
filter: getFilterVariables(params),
|
filter: getFilterVariables(params),
|
||||||
sort: getSortQueryVariables(params)
|
sort: getSortQueryVariables(params),
|
||||||
|
channel: selectedChannel?.slug
|
||||||
}),
|
}),
|
||||||
[params]
|
[params]
|
||||||
);
|
);
|
||||||
|
@ -90,10 +115,21 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { availableChannels, channel } = useAppChannel();
|
const filterOpts = getFilterOpts(params, channelOpts);
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canBeSorted(params.sort, !!selectedChannel)) {
|
||||||
|
navigate(
|
||||||
|
collectionListUrl({
|
||||||
|
...params,
|
||||||
|
sort: DEFAULT_SORT_KEY
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
params.activeTab === undefined
|
params.activeTab === undefined
|
||||||
? areFiltersApplied(params)
|
? areFiltersApplied(params)
|
||||||
|
@ -101,16 +137,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
: 0
|
: 0
|
||||||
: parseInt(params.activeTab, 0);
|
: parseInt(params.activeTab, 0);
|
||||||
|
|
||||||
const handleSearchChange = (query: string) => {
|
|
||||||
navigate(
|
|
||||||
collectionListUrl({
|
|
||||||
...getActiveFilters(params),
|
|
||||||
activeTab: undefined,
|
|
||||||
query
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
CollectionListUrlDialog,
|
CollectionListUrlDialog,
|
||||||
CollectionListUrlQueryParams
|
CollectionListUrlQueryParams
|
||||||
|
@ -152,7 +178,7 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
onSearchChange={handleSearchChange}
|
onSearchChange={handleSearchChange}
|
||||||
onAdd={() => navigate(collectionAddUrl())}
|
onAdd={() => navigate(collectionAddUrl())}
|
||||||
onAll={() => navigate(collectionListUrl())}
|
onAll={resetFilters}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
onTabDelete={() => openModal("delete-search")}
|
onTabDelete={() => openModal("delete-search")}
|
||||||
onTabSave={() => openModal("save-search")}
|
onTabSave={() => openModal("save-search")}
|
||||||
|
@ -184,7 +210,9 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
channelsCount={availableChannels?.length}
|
channelsCount={availableChannels?.length}
|
||||||
selectedChannelId={channel?.id}
|
selectedChannelId={selectedChannel?.id}
|
||||||
|
filterOpts={filterOpts}
|
||||||
|
onFilterChange={changeFilters}
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "remove" && maybe(() => params.ids.length > 0)}
|
open={params.action === "remove" && maybe(() => params.ids.length > 0)}
|
||||||
|
|
|
@ -34,7 +34,8 @@ describe("Filtering URL params", () => {
|
||||||
status: {
|
status: {
|
||||||
active: false,
|
active: false,
|
||||||
value: CollectionPublished.PUBLISHED
|
value: CollectionPublished.PUBLISHED
|
||||||
}
|
},
|
||||||
|
channel: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be empty if no active filters", () => {
|
it("should be empty if no active filters", () => {
|
||||||
|
@ -51,7 +52,8 @@ describe("Filtering URL params", () => {
|
||||||
status: {
|
status: {
|
||||||
active: true,
|
active: true,
|
||||||
value: CollectionPublished.PUBLISHED
|
value: CollectionPublished.PUBLISHED
|
||||||
}
|
},
|
||||||
|
channel: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const filterQueryParams = getFilterQueryParams(
|
const filterQueryParams = getFilterQueryParams(
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
CollectionListFilterOpts
|
CollectionListFilterOpts
|
||||||
} from "@saleor/collections/components/CollectionListPage";
|
} from "@saleor/collections/components/CollectionListPage";
|
||||||
import { IFilterElement } from "@saleor/components/Filter";
|
import { IFilterElement } from "@saleor/components/Filter";
|
||||||
|
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import { findValueInEnum, maybe } from "@saleor/misc";
|
import { findValueInEnum, maybe } from "@saleor/misc";
|
||||||
import {
|
import {
|
||||||
CollectionFilterInput,
|
CollectionFilterInput,
|
||||||
|
@ -12,7 +13,8 @@ import {
|
||||||
import {
|
import {
|
||||||
createFilterTabUtils,
|
createFilterTabUtils,
|
||||||
createFilterUtils,
|
createFilterUtils,
|
||||||
getSingleEnumValueQueryParam
|
getSingleEnumValueQueryParam,
|
||||||
|
getSingleValueQueryParam
|
||||||
} from "../../../utils/filters";
|
} from "../../../utils/filters";
|
||||||
import {
|
import {
|
||||||
CollectionListUrlFilters,
|
CollectionListUrlFilters,
|
||||||
|
@ -23,9 +25,15 @@ import {
|
||||||
export const COLLECTION_FILTERS_KEY = "collectionFilters";
|
export const COLLECTION_FILTERS_KEY = "collectionFilters";
|
||||||
|
|
||||||
export function getFilterOpts(
|
export function getFilterOpts(
|
||||||
params: CollectionListUrlFilters
|
params: CollectionListUrlFilters,
|
||||||
|
channels: SingleAutocompleteChoiceType[]
|
||||||
): CollectionListFilterOpts {
|
): CollectionListFilterOpts {
|
||||||
return {
|
return {
|
||||||
|
channel: {
|
||||||
|
active: params?.channel !== undefined,
|
||||||
|
choices: channels,
|
||||||
|
value: params?.channel
|
||||||
|
},
|
||||||
status: {
|
status: {
|
||||||
active: maybe(() => params.status !== undefined, false),
|
active: maybe(() => params.status !== undefined, false),
|
||||||
value: maybe(() => findValueInEnum(status, CollectionPublished))
|
value: maybe(() => findValueInEnum(status, CollectionPublished))
|
||||||
|
@ -56,6 +64,11 @@ export function getFilterQueryParam(
|
||||||
CollectionListUrlFiltersEnum.status,
|
CollectionListUrlFiltersEnum.status,
|
||||||
CollectionPublished
|
CollectionPublished
|
||||||
);
|
);
|
||||||
|
case CollectionFilterKeys.channel:
|
||||||
|
return getSingleValueQueryParam(
|
||||||
|
filter,
|
||||||
|
CollectionListUrlFiltersEnum.channel
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,23 @@ import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
import { CollectionSortField } from "@saleor/types/globalTypes";
|
import { CollectionSortField } from "@saleor/types/globalTypes";
|
||||||
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
import { createGetSortQueryVariables } from "@saleor/utils/sort";
|
||||||
|
|
||||||
|
export const DEFAULT_SORT_KEY = CollectionListUrlSortField.name;
|
||||||
|
|
||||||
|
export function canBeSorted(
|
||||||
|
sort: CollectionListUrlSortField,
|
||||||
|
isChannelSelected: boolean
|
||||||
|
) {
|
||||||
|
switch (sort) {
|
||||||
|
case CollectionListUrlSortField.name:
|
||||||
|
case CollectionListUrlSortField.productCount:
|
||||||
|
return true;
|
||||||
|
case CollectionListUrlSortField.available:
|
||||||
|
return isChannelSelected;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getSortQueryField(
|
export function getSortQueryField(
|
||||||
sort: CollectionListUrlSortField
|
sort: CollectionListUrlSortField
|
||||||
): CollectionSortField {
|
): CollectionSortField {
|
||||||
|
|
|
@ -3,9 +3,7 @@ import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { messages } from "./messages";
|
export const AvailabilityStatusLabel = ({ channel, messages }) => {
|
||||||
|
|
||||||
export const ProductAvailabilityStatusLabel = ({ channel }) => {
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const localizeDate = useDateLocalize();
|
const localizeDate = useDateLocalize();
|
||||||
|
|
||||||
|
@ -32,4 +30,4 @@ export const ProductAvailabilityStatusLabel = ({ channel }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProductAvailabilityStatusLabel;
|
export default AvailabilityStatusLabel;
|
2
src/components/AvailabilityStatusLabel/index.ts
Normal file
2
src/components/AvailabilityStatusLabel/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./AvailabilityStatusLabel";
|
||||||
|
export * from "./AvailabilityStatusLabel";
|
|
@ -74,6 +74,7 @@ const FilterAutocompleteField: React.FC<FilterAutocompleteFieldProps> = ({
|
||||||
payload: {
|
payload: {
|
||||||
name: filterField.name,
|
name: filterField.name,
|
||||||
update: {
|
update: {
|
||||||
|
active: true,
|
||||||
value: toggle(option.value, filterField.value, (a, b) => a === b)
|
value: toggle(option.value, filterField.value, (a, b) => a === b)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -165,13 +165,14 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
filter: IFilterElement<string>
|
filter: IFilterElement<string>
|
||||||
) {
|
) {
|
||||||
const switchToActive = action.payload.update.active;
|
const switchToActive = action.payload.update.active;
|
||||||
|
|
||||||
if (switchToActive && filter.name !== openedFilter?.name) {
|
if (switchToActive && filter.name !== openedFilter?.name) {
|
||||||
handleFilterAttributeFocus(filter);
|
handleFilterAttributeFocus(filter);
|
||||||
} else if (!switchToActive && filter.name === openedFilter?.name) {
|
} else if (!switchToActive && filter.name === openedFilter?.name) {
|
||||||
handleFilterAttributeFocus(undefined);
|
handleFilterAttributeFocus(undefined);
|
||||||
}
|
}
|
||||||
|
if (!switchToActive) {
|
||||||
|
action.payload.update.value = [];
|
||||||
|
}
|
||||||
onFilterPropertyChange(action);
|
onFilterPropertyChange(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,7 +180,6 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
||||||
action: FilterReducerAction<T>
|
action: FilterReducerAction<T>
|
||||||
) {
|
) {
|
||||||
const { update } = action.payload;
|
const { update } = action.payload;
|
||||||
|
|
||||||
onFilterPropertyChange({
|
onFilterPropertyChange({
|
||||||
...action,
|
...action,
|
||||||
payload: { ...action.payload, update: { ...update, active: true } }
|
payload: { ...action.payload, update: { ...update, active: true } }
|
||||||
|
|
|
@ -32,6 +32,7 @@ const FilterOptionField: React.FC<FilterBaseFieldProps> = ({
|
||||||
payload: {
|
payload: {
|
||||||
name: filterField.name,
|
name: filterField.name,
|
||||||
update: {
|
update: {
|
||||||
|
active: true,
|
||||||
value: filterField.multiple
|
value: filterField.multiple
|
||||||
? toggle(value, filterField.value, (a, b) => a === b)
|
? toggle(value, filterField.value, (a, b) => a === b)
|
||||||
: [value]
|
: [value]
|
||||||
|
|
|
@ -307,7 +307,10 @@ export const searchPageProps: SearchPageProps = {
|
||||||
export const filterPageProps: FilterPageProps<string, {}> = {
|
export const filterPageProps: FilterPageProps<string, {}> = {
|
||||||
...searchPageProps,
|
...searchPageProps,
|
||||||
...tabPageProps,
|
...tabPageProps,
|
||||||
filterOpts: {},
|
filterOpts: {
|
||||||
|
status: { value: undefined, active: false },
|
||||||
|
channel: { value: undefined, active: false }
|
||||||
|
},
|
||||||
onFilterChange: () => undefined
|
onFilterChange: () => undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ export const commonMessages = defineMessages({
|
||||||
defaultMessage: "Choose file",
|
defaultMessage: "Choose file",
|
||||||
description: "button"
|
description: "button"
|
||||||
},
|
},
|
||||||
|
channel: {
|
||||||
|
defaultMessage: "Channel"
|
||||||
|
},
|
||||||
customApps: {
|
customApps: {
|
||||||
defaultMessage: "Local Apps"
|
defaultMessage: "Local Apps"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default } from "./ProductAvailabilityStatusLabel";
|
|
||||||
export * from "./ProductAvailabilityStatusLabel";
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
TableRow,
|
TableRow,
|
||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel";
|
||||||
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
|
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import MoneyRange from "@saleor/components/MoneyRange";
|
import MoneyRange from "@saleor/components/MoneyRange";
|
||||||
|
@ -35,7 +36,7 @@ import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import ProductAvailabilityStatusLabel from "../ProductAvailabilityStatusLabel";
|
import { messages } from "./messages";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -376,7 +377,10 @@ export const ProductList: React.FC<ProductListProps> = props => {
|
||||||
{(!product && <Skeleton />) ||
|
{(!product && <Skeleton />) ||
|
||||||
(!product?.channelListings?.length && "-") ||
|
(!product?.channelListings?.length && "-") ||
|
||||||
(product?.channelListings !== undefined && channel ? (
|
(product?.channelListings !== undefined && channel ? (
|
||||||
<ProductAvailabilityStatusLabel channel={channel} />
|
<AvailabilityStatusLabel
|
||||||
|
channel={channel}
|
||||||
|
messages={messages}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ChannelsAvailabilityDropdown
|
<ChannelsAvailabilityDropdown
|
||||||
allChannelsCount={channelsCount}
|
allChannelsCount={channelsCount}
|
||||||
|
|
|
@ -5,11 +5,14 @@ import React from "react";
|
||||||
import CollectionListPage, {
|
import CollectionListPage, {
|
||||||
CollectionListPageProps
|
CollectionListPageProps
|
||||||
} from "../../../collections/components/CollectionListPage";
|
} from "../../../collections/components/CollectionListPage";
|
||||||
import { collections } from "../../../collections/fixtures";
|
|
||||||
import {
|
import {
|
||||||
|
collectionListFilterOpts,
|
||||||
|
collections
|
||||||
|
} from "../../../collections/fixtures";
|
||||||
|
import {
|
||||||
|
filterPageProps,
|
||||||
listActionsProps,
|
listActionsProps,
|
||||||
pageListProps,
|
pageListProps,
|
||||||
searchPageProps,
|
|
||||||
sortPageProps,
|
sortPageProps,
|
||||||
tabPageProps
|
tabPageProps
|
||||||
} from "../../../fixtures";
|
} from "../../../fixtures";
|
||||||
|
@ -18,7 +21,7 @@ import Decorator from "../../Decorator";
|
||||||
const props: CollectionListPageProps = {
|
const props: CollectionListPageProps = {
|
||||||
...listActionsProps,
|
...listActionsProps,
|
||||||
...pageListProps.default,
|
...pageListProps.default,
|
||||||
...searchPageProps,
|
...filterPageProps,
|
||||||
...sortPageProps,
|
...sortPageProps,
|
||||||
channelsCount: 2,
|
channelsCount: 2,
|
||||||
sort: {
|
sort: {
|
||||||
|
@ -27,7 +30,8 @@ const props: CollectionListPageProps = {
|
||||||
},
|
},
|
||||||
...tabPageProps,
|
...tabPageProps,
|
||||||
collections,
|
collections,
|
||||||
selectedChannelId: "123"
|
selectedChannelId: "123",
|
||||||
|
filterOpts: collectionListFilterOpts
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Collections / Collection list", module)
|
storiesOf("Views / Collections / Collection list", module)
|
||||||
|
|
Loading…
Reference in a new issue