Skip to content

Commit

Permalink
feat(manager-react-components): add filters in datagrid (#14203)
Browse files Browse the repository at this point in the history
* feat(manager-react-components): add filters in datagrid

- feat(manager-core-api): update filters categories
- feat(generator): update v6 listing filter

ref: MANAGER-15088

Signed-off-by: Alex Boungnaseng <[email protected]>

---------

Signed-off-by: Alex Boungnaseng <[email protected]>
  • Loading branch information
aboungnaseng-ovhcloud authored and Pierre-Philippe committed Feb 12, 2025
1 parent cbb8a26 commit 68eb9e5
Show file tree
Hide file tree
Showing 16 changed files with 451 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, { useState } from 'react';
import { ColumnSort } from '@tanstack/react-table';
import { OdsDivider } from '@ovhcloud/ods-components/react';
import { ODS_BUTTON_VARIANT } from '@ovhcloud/ods-components';
import { withRouter } from 'storybook-addon-react-router-v6';
import { Datagrid, DatagridProps } from './datagrid.component';
import { DataGridTextCell } from './text-cell.component';
import { useSearchParams } from 'react-router-dom';
import { Datagrid } from './datagrid.component';
import { useColumnFilters } from '../filters';
import { columns, columsFilters } from './datagrid.mock';
import { ActionMenu } from '../navigation';

interface Item {
Expand All @@ -12,30 +15,11 @@ interface Item {
actions: React.ReactElement;
}

const columns = [
{
id: 'label',
cell: (item: Item) => {
return <DataGridTextCell>{item.label}</DataGridTextCell>;
},
label: 'Label',
},
{
id: 'price',
cell: (item: Item) => {
return <DataGridTextCell>{item.price}</DataGridTextCell>;
},
label: 'Price',
},
];

const DatagridStory = ({
items,
isSortable,
...args
}: { isSortable?: boolean } & DatagridProps<unknown>) => {
const DatagridStory = (args) => {
const [sorting, setSorting] = useState<ColumnSort>();
const [data, setData] = useState(items);
const [data, setData] = useState(args.items);
const [searchParams] = useSearchParams();
const { filters, addFilter, removeFilter } = useColumnFilters();

const fetchNextPage = () => {
const itemsIndex = data?.length;
Expand All @@ -47,20 +31,29 @@ const DatagridStory = ({
};

return (
<Datagrid
{...args}
items={data}
hasNextPage={data?.length > 0 && data.length < 30}
onFetchNextPage={fetchNextPage}
totalItems={data?.length}
{...(isSortable
? {
sorting,
onSortChange: setSorting,
manualSorting: false,
}
: {})}
/>
<>
{`${searchParams}` && (
<>
<pre>Search params: ?{`${searchParams}`}</pre>
<OdsDivider />
</>
)}
<Datagrid
items={data}
columns={args.columns}
hasNextPage={data?.length > 0 && data.length < 30}
onFetchNextPage={fetchNextPage}
totalItems={data?.length}
filters={{ filters, add: addFilter, remove: removeFilter }}
{...(args.isSortable
? {
sorting,
onSortChange: setSorting,
manualSorting: false,
}
: {})}
/>
</>
);
};

Expand Down Expand Up @@ -143,6 +136,17 @@ WithActions.args = {
isSortable: true,
};

export const Filters = DatagridStory.bind({});

Filters.args = {
items: [...Array(10).keys()].map((_, i) => ({
label: `Item #${i}`,
price: Math.floor(1 + Math.random() * 100),
})),
isSortable: true,
columns: columsFilters,
};

export default {
title: 'Components/Datagrid Cursor',
component: Datagrid,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import {
ColumnDef,
ColumnSort as TanstackColumnSort,
Expand All @@ -8,15 +9,27 @@ import {
useReactTable,
getSortedRowModel,
} from '@tanstack/react-table';
import { ODS_ICON_NAME, ODS_BUTTON_VARIANT } from '@ovhcloud/ods-components';
import {
ODS_ICON_NAME,
ODS_BUTTON_VARIANT,
ODS_BUTTON_SIZE,
} from '@ovhcloud/ods-components';
import {
OdsPopover,
OdsButton,
OdsIcon,
OdsPagination,
OdsSkeleton,
OdsTable,
} from '@ovhcloud/ods-components/react';
import { useTranslation } from 'react-i18next';
import {
FilterComparator,
FilterCategories,
FilterTypeCategories,
} from '@ovh-ux/manager-core-api';
import { FilterAdd, FilterList } from '../filters';
import { ColumnFilter } from '../filters/filter-add.component';
import { FilterWithLabel } from '../filters/interface';
import { DataGridTextCell } from './text-cell.component';
import { defaultNumberOfLoadingRows } from './datagrid.contants';
import './translations';
Expand All @@ -37,6 +50,25 @@ export interface DatagridColumn<T> {
label: string;
/** is the column sortable ? (defaults is true) */
isSortable?: boolean;
/** set column comparator for the filter */
comparator?: FilterComparator[];
/** Filters displayed for the column */
type?: FilterTypeCategories;
/** Trigger the column filter */
isFilterable?: boolean;
}

type ColumnFilterProps = {
key: string;
value: string | string[];
comparator: FilterComparator;
label: string;
};

export interface FilterProps {
filters: FilterWithLabel[];
add: (filters: ColumnFilterProps) => void;
remove: (filter: FilterWithLabel) => void;
}

export interface DatagridProps<T> {
Expand Down Expand Up @@ -74,11 +106,14 @@ export interface DatagridProps<T> {
isLoading?: boolean;
/** number of loading rows to show when table is in loading state, defaults to pagination.pageSize or 5 */
numberOfLoadingRows?: number;
/** List of filters and handlers to add, remove */
filters?: FilterProps;
}

export const Datagrid = <T,>({
columns,
items,
filters,
totalItems,
pagination,
sorting,
Expand All @@ -95,6 +130,8 @@ export const Datagrid = <T,>({
numberOfLoadingRows,
}: DatagridProps<T>) => {
const { t } = useTranslation('datagrid');
const { t: tfilters } = useTranslation('filters');
const filterPopoverRef = useRef(null);
const pageCount = pagination
? Math.ceil(totalItems / pagination.pageSize)
: 1;
Expand Down Expand Up @@ -139,8 +176,64 @@ export const Datagrid = <T,>({
}),
});

const columnsFilters = useMemo<ColumnFilter[]>(
() =>
columns
.filter(
(item) =>
('comparator' in item || 'type' in item) &&
'isFilterable' in item &&
item.isFilterable,
)
.map((column) => ({
id: column.id,
label: column.label,
...(column?.type && { comparators: FilterCategories[column.type] }),
...(column?.comparator && { comparators: column.comparator }),
})),
[columns],
);

return (
<div>
{columnsFilters.length > 0 && (
<div className="flex flex-row-reverse py-[24px]">
<OdsButton
id="datagrid-filter-popover-trigger"
slot="datagrid-filter-popover-trigger"
size={ODS_BUTTON_SIZE.sm}
variant={ODS_BUTTON_VARIANT.ghost}
icon={ODS_ICON_NAME.filter}
aria-label={tfilters('common_criteria_adder_filter_label')}
label=""
/>
<OdsPopover
ref={filterPopoverRef}
triggerId="datagrid-filter-popover-trigger"
with-arrow
>
<FilterAdd
columns={columnsFilters}
onAddFilter={(addedFilter, column) => {
filters.add({
...addedFilter,
label: column.label,
});
filterPopoverRef.current?.hide();
}}
/>
</OdsPopover>
</div>
)}
{filters?.filters.length > 0 && (
<div id="datagrid-filter-list" className="mb-[24px]">
<FilterList
filters={filters.filters}
onRemoveFilter={filters.remove}
/>
</div>
)}

<div className={`contents px-[1px] ${className || ''}`}>
<OdsTable className="overflow-x-visible">
<table className="w-full border-collapse">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { FilterCategories } from '@ovh-ux/manager-core-api';
import { DataGridTextCell } from './text-cell.component';

export interface Item {
label: string;
price: number;
}

export const columns = [
{
id: 'label',
cell: (item: Item) => {
return <DataGridTextCell>{item.label}</DataGridTextCell>;
},
label: 'Label',
},
{
id: 'price',
cell: (item: Item) => {
return <DataGridTextCell>{item.price}</DataGridTextCell>;
},
label: 'Price',
},
];

export const columsFilters = [
{
id: 'label',
cell: (item: Item) => {
return <DataGridTextCell>{item.label}</DataGridTextCell>;
},
label: 'Label',
isFilterable: true,
comparator: FilterCategories.String,
},
{
id: 'price',
cell: (item: Item) => {
return <DataGridTextCell>{item.price}</DataGridTextCell>;
},
label: 'Price',
isFilterable: true,
comparator: FilterCategories.String,
},
];
Loading

0 comments on commit 68eb9e5

Please sign in to comment.