Introduce new column picker in Categories list (#3876)

This commit is contained in:
Paweł Chyła 2023-07-11 13:19:26 +02:00 committed by GitHub
parent 5b4d07d547
commit b86bb025a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 116 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---
Introduce new column picker in Categories list

View file

@ -1,11 +1,10 @@
// @ts-strict-ignore
import {
CategoryListUrlSortField,
categoryUrl,
} from "@dashboard/categories/urls";
import ColumnPicker from "@dashboard/components/ColumnPicker";
import { ColumnPicker } from "@dashboard/components/Datagrid/ColumnPicker/ColumnPicker";
import { useColumns } from "@dashboard/components/Datagrid/ColumnPicker/useColumns";
import Datagrid from "@dashboard/components/Datagrid/Datagrid";
import { useColumnsDefault } from "@dashboard/components/Datagrid/hooks/useColumnsDefault";
import {
DatagridChangeStateContext,
useDatagridChangeState,
@ -18,14 +17,16 @@ import { Box } from "@saleor/macaw-ui/next";
import React, { ReactNode, useCallback, useMemo } from "react";
import { useIntl } from "react-intl";
import { createGetCellContent, getColumns } from "./datagrid";
import {
categoryListStaticColumnsAdapter,
createGetCellContent,
} from "./datagrid";
import { messages } from "./messages";
interface CategoryListDatagridProps
extends Partial<SortPage<CategoryListUrlSortField>>,
PageListProps {
categories?: CategoryFragment[];
disabled: boolean;
extends PageListProps,
Partial<SortPage<CategoryListUrlSortField>> {
categories: CategoryFragment[];
onSelectCategoriesIds: (ids: number[], clearSelection: () => void) => void;
selectionActionButton?: ReactNode | null;
hasRowHover?: boolean;
@ -44,32 +45,40 @@ export const CategoryListDatagrid = ({
}: CategoryListDatagridProps) => {
const datagridState = useDatagridChangeState();
const intl = useIntl();
const availableColumns = useMemo(() => getColumns(intl, sort), [intl, sort]);
const {
availableColumnsChoices,
columnChoices,
columns,
defaultColumns,
onColumnMoved,
onColumnResize,
onColumnsChange,
picker,
} = useColumnsDefault(availableColumns);
const memoizedStaticColumns = useMemo(
() => categoryListStaticColumnsAdapter(intl, sort),
[intl, sort],
);
const handleColumnChange = useCallback(
picked => {
if (onUpdateListSettings) {
onUpdateListSettings("columns", picked.filter(Boolean));
}
},
[onUpdateListSettings],
);
const { handlers, selectedColumns, staticColumns, visibleColumns } =
useColumns({
staticColumns: memoizedStaticColumns,
selectedColumns: settings?.columns ?? [],
onSave: handleColumnChange,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
const getCellContent = useCallback(
createGetCellContent(categories, columns),
[categories, columns],
createGetCellContent(categories, visibleColumns),
[categories, visibleColumns],
);
const handleHeaderClick = useCallback(
(col: number) => {
if (sort !== undefined) {
onSort(columns[col].id as CategoryListUrlSortField);
if (sort !== undefined && onSort) {
onSort(visibleColumns[col].id as CategoryListUrlSortField);
}
},
[columns, onSort, sort],
[visibleColumns, onSort, sort],
);
const handleRowAnchor = useCallback(
@ -86,7 +95,7 @@ export const CategoryListDatagrid = ({
columnSelect={sort !== undefined ? "single" : undefined}
verticalBorder={col => col > 0}
rowMarkers="checkbox"
availableColumns={columns}
availableColumns={visibleColumns}
rows={categories?.length ?? 0}
getCellContent={getCellContent}
getCellError={() => false}
@ -96,21 +105,14 @@ export const CategoryListDatagrid = ({
menuItems={() => []}
actionButtonPosition="right"
selectionActions={() => selectionActionButton}
onColumnResize={onColumnResize}
onColumnMoved={onColumnMoved}
onColumnResize={handlers.onResize}
onColumnMoved={handlers.onMove}
onRowSelectionChange={onSelectCategoriesIds}
renderColumnPicker={defaultProps => (
renderColumnPicker={() => (
<ColumnPicker
{...defaultProps}
availableColumns={availableColumnsChoices}
initialColumns={columnChoices}
defaultColumns={defaultColumns}
onSave={onColumnsChange}
hasMore={false}
loading={false}
onFetchMore={() => undefined}
onQueryChange={picker.setQuery}
query={picker.query}
onSave={handlers.onChange}
selectedColumns={selectedColumns}
staticColumns={staticColumns}
/>
)}
/>

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import { CategoryListUrlSortField } from "@dashboard/categories/urls";
import { readonlyTextCell } from "@dashboard/components/Datagrid/customCells/cells";
import { AvailableColumn } from "@dashboard/components/Datagrid/types";
@ -10,57 +9,52 @@ import { IntlShape } from "react-intl";
import { columnsMessages } from "./messages";
export const getColumns = (
export const categoryListStaticColumnsAdapter = (
intl: IntlShape,
sort?: Sort<CategoryListUrlSortField>,
): AvailableColumn[] => [
): AvailableColumn[] =>
[
{
id: "name",
title: intl.formatMessage(columnsMessages.categoryName),
width: 350,
icon: sort
? getColumnSortDirectionIcon(sort, CategoryListUrlSortField.name)
: undefined,
},
{
id: "subcategories",
title: intl.formatMessage(columnsMessages.subcategories),
width: 300,
icon: sort
? getColumnSortDirectionIcon(
sort,
CategoryListUrlSortField.subcategoryCount,
)
: undefined,
},
{
id: "products",
title: intl.formatMessage(columnsMessages.numberOfProducts),
width: 300,
icon: sort
? getColumnSortDirectionIcon(sort, CategoryListUrlSortField.productCount)
: undefined,
},
];
].map(column => ({
...column,
icon: sort ? getColumnSortDirectionIcon(sort, column.id) : undefined,
}));
export const createGetCellContent =
(categories: CategoryFragment[], columns: AvailableColumn[]) =>
([column, row]: Item): GridCell => {
const columnId = columns[column]?.id;
const rowData: CategoryFragment | undefined = categories[row];
if (!columnId) {
if (!columnId || !rowData) {
return readonlyTextCell("");
}
const rowData = categories[row];
switch (columnId) {
case "name":
return readonlyTextCell(rowData?.name ?? "");
case "subcategories":
return readonlyTextCell(rowData?.children?.totalCount.toString() ?? "");
return readonlyTextCell(
rowData?.children?.totalCount?.toString() ?? "",
);
case "products":
return readonlyTextCell(rowData?.products?.totalCount.toString() ?? "");
return readonlyTextCell(
rowData?.products?.totalCount?.toString() ?? "",
);
default:
return readonlyTextCell("", false);
}

View file

@ -3,7 +3,7 @@ import { categoryAddUrl } from "@dashboard/categories/urls";
import { DashboardCard } from "@dashboard/components/Card";
import { InternalLink } from "@dashboard/components/InternalLink";
import { CategoryDetailsQuery } from "@dashboard/graphql";
import { RelayToFlat } from "@dashboard/types";
import { ListProps, ListViews, RelayToFlat } from "@dashboard/types";
import { Box, Button } from "@saleor/macaw-ui/next";
import React from "react";
import { FormattedMessage } from "react-intl";
@ -11,7 +11,11 @@ import { FormattedMessage } from "react-intl";
import { CategoryDeleteButton } from "../CategoryDeleteButton";
import { CategoryListDatagrid } from "../CategoryListDatagrid";
interface CategorySubcategoriesProps {
interface CategorySubcategoriesProps
extends Pick<
ListProps<ListViews.CATEGORY_LIST>,
"onUpdateListSettings" | "settings"
> {
categoryId: string;
disabled: boolean;
subcategories: RelayToFlat<CategoryDetailsQuery["category"]["children"]>;
@ -25,7 +29,10 @@ export const CategorySubcategories = ({
disabled,
onCategoriesDelete,
onSelectCategoriesIds,
}: CategorySubcategoriesProps) => (
settings,
onUpdateListSettings,
}: CategorySubcategoriesProps) => {
return (
<DashboardCard>
<DashboardCard.Title>
<Box display="flex" justifyContent="space-between" alignItems="center">
@ -47,6 +54,8 @@ export const CategorySubcategories = ({
</DashboardCard.Title>
<CategoryListDatagrid
settings={settings}
onUpdateListSettings={onUpdateListSettings}
categories={subcategories}
disabled={disabled}
onSelectCategoriesIds={onSelectCategoriesIds}
@ -62,4 +71,5 @@ export const CategorySubcategories = ({
}
/>
</DashboardCard>
);
);
};

View file

@ -16,7 +16,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe } from "../../../misc";
import { RelayToFlat } from "../../../types";
import { ListProps, ListViews, RelayToFlat } from "../../../types";
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
import CategoryBackground from "../CategoryBackground";
import { CategoryProducts } from "../CategoryProducts";
@ -28,7 +28,11 @@ export enum CategoryPageTab {
products = "products",
}
export interface CategoryUpdatePageProps {
export interface CategoryUpdatePageProps
extends Pick<
ListProps<ListViews.CATEGORY_LIST>,
"onUpdateListSettings" | "settings"
> {
categoryId: string;
changeTab: (index: CategoryPageTab) => void;
currentTab: CategoryPageTab;
@ -70,6 +74,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
onCategoriesDelete,
onProductsDelete,
onSelectProductsIds,
settings,
onUpdateListSettings,
}: CategoryUpdatePageProps) => {
const intl = useIntl();
const navigate = useNavigator();
@ -161,6 +167,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
{currentTab === CategoryPageTab.categories && (
<CategorySubcategories
disabled={disabled}
onUpdateListSettings={onUpdateListSettings}
settings={settings}
subcategories={subcategories}
onCategoriesDelete={onCategoriesDelete}
onSelectCategoriesIds={onSelectCategoriesIds}

View file

@ -15,6 +15,7 @@ import {
useUpdateMetadataMutation,
useUpdatePrivateMetadataMutation,
} from "@dashboard/graphql";
import useListSettings from "@dashboard/hooks/useListSettings";
import useLocalPaginator, {
useSectionLocalPaginationState,
} from "@dashboard/hooks/useLocalPaginator";
@ -23,6 +24,7 @@ import useNotifier from "@dashboard/hooks/useNotifier";
import { PaginatorContext } from "@dashboard/hooks/usePaginator";
import { useRowSelection } from "@dashboard/hooks/useRowSelection";
import { commonMessages, errorMessages } from "@dashboard/intl";
import { ListViews } from "@dashboard/types";
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
import createMetadataUpdateHandler from "@dashboard/utils/handlers/metadataUpdateHandler";
import { mapEdgesToItems } from "@dashboard/utils/maps";
@ -98,6 +100,9 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
setActiveTab(tab);
};
const { settings, updateListSettings } =
useListSettings<ListViews.CATEGORY_LIST>(ListViews.CATEGORY_LIST);
const { data, loading, refetch } = useCategoryDetailsQuery({
displayLoader: true,
variables: { ...paginationState, id },
@ -274,6 +279,8 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
<WindowTitle title={maybe(() => data.category.name)} />
<CategoryUpdatePage
categoryId={id}
settings={settings}
onUpdateListSettings={updateListSettings}
changeTab={changeTab}
currentTab={activeTab}
category={maybe(() => data.category)}

View file

@ -72,6 +72,7 @@ export const defaultListSettings: AppListViewSettings = {
},
[ListViews.CATEGORY_LIST]: {
rowNumber: PAGINATE_BY,
columns: ["name", "products", "subcategories"],
},
[ListViews.COLLECTION_LIST]: {
rowNumber: PAGINATE_BY,