-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pci-databases-analytics): refactor data table and add filtering (#…
…14524) ref: DATATR-1763, DATATR-681 Signed-off-by: Jonathan Perchoc <[email protected]>
- Loading branch information
Showing
61 changed files
with
1,736 additions
and
523 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
...es/manager/apps/pci-databases-analytics/src/components/data-table/DataTable.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import React, { ReactElement, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { flexRender } from '@tanstack/react-table'; | ||
import { ChevronDown, ChevronUp } from 'lucide-react'; | ||
import { useDataTableContext } from './DataTable.context'; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from '@/components/ui/table'; | ||
import { Button } from '../ui/button'; | ||
|
||
export const MENU_COLUMN_ID = 'actions_menu_column'; | ||
|
||
interface DatatableProps<TData> { | ||
renderRowExpansion?: (row: TData) => ReactElement | null; | ||
} | ||
|
||
export function DataTable<TData>({ | ||
renderRowExpansion, | ||
}: DatatableProps<TData>) { | ||
const { table, rows } = useDataTableContext(); | ||
const { t } = useTranslation('pci-databases-analytics/components/data-table'); | ||
const [expandedRows, setExpandedRows] = useState<Record<string, boolean>>({}); | ||
|
||
const toggleRowExpansion = (rowId: string) => { | ||
setExpandedRows((prev) => ({ | ||
...prev, | ||
[rowId]: !prev[rowId], | ||
})); | ||
}; | ||
|
||
const headerGroups = table.getHeaderGroups(); | ||
return ( | ||
<Table> | ||
<TableHeader className="border bg-gray-50"> | ||
{headerGroups.map((headerGroup) => ( | ||
<TableRow key={headerGroup.id}> | ||
{renderRowExpansion && ( | ||
<TableHead className="border-r-0 w-6"></TableHead> | ||
)} | ||
{headerGroup.headers.map((header, index) => { | ||
const isEmptyHeader = header.id === MENU_COLUMN_ID; | ||
// Get a reference to the previous header | ||
const isEmptyNextHeader = | ||
headerGroup.headers[index + 1]?.id === MENU_COLUMN_ID; | ||
return ( | ||
<TableHead | ||
key={header.id} | ||
className={`border font-semibold text-primary-800 ${ | ||
isEmptyHeader || renderRowExpansion | ||
? 'border-l-0' // Remove left border for empty headers and row extend column | ||
: '' | ||
} ${ | ||
isEmptyNextHeader | ||
? 'border-r-0' // Remove right border from current column if next header is empty | ||
: '' | ||
}`} | ||
> | ||
{header.isPlaceholder | ||
? null | ||
: flexRender( | ||
header.column.columnDef.header, | ||
header.getContext(), | ||
)} | ||
</TableHead> | ||
); | ||
})} | ||
</TableRow> | ||
))} | ||
</TableHeader> | ||
<TableBody className="border"> | ||
{rows?.length ? ( | ||
rows.map((row) => ( | ||
<React.Fragment key={row.id}> | ||
<TableRow data-state={row.getIsSelected() && 'selected'}> | ||
{renderRowExpansion && ( | ||
<TableCell> | ||
<Button | ||
variant="ghost" | ||
onClick={() => toggleRowExpansion(row.id)} | ||
data-testid="table-row-expand-button" | ||
> | ||
{expandedRows[row.id] ? ( | ||
<ChevronUp className="h-4 w-4" /> | ||
) : ( | ||
<ChevronDown className="h-4 w-4" /> | ||
)} | ||
</Button> | ||
</TableCell> | ||
)} | ||
{row.getVisibleCells().map((cell) => ( | ||
<TableCell key={cell.id}> | ||
{flexRender(cell.column.columnDef.cell, cell.getContext())} | ||
</TableCell> | ||
))} | ||
</TableRow> | ||
{expandedRows[row.id] && renderRowExpansion && ( | ||
<TableRow> | ||
<TableCell colSpan={headerGroups[0].headers.length + 1}> | ||
{renderRowExpansion(row.original as TData)} | ||
</TableCell> | ||
</TableRow> | ||
)} | ||
</React.Fragment> | ||
)) | ||
) : ( | ||
<TableRow> | ||
<TableCell | ||
colSpan={headerGroups[0].headers.length} | ||
className="h-24 text-center" | ||
> | ||
{t('noResult')} | ||
</TableCell> | ||
</TableRow> | ||
)} | ||
</TableBody> | ||
</Table> | ||
); | ||
} |
122 changes: 122 additions & 0 deletions
122
...ages/manager/apps/pci-databases-analytics/src/components/data-table/DataTable.context.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { | ||
ColumnDef, | ||
Row, | ||
SortingState, | ||
Table, | ||
getCoreRowModel, | ||
getFilteredRowModel, | ||
getPaginationRowModel, | ||
getSortedRowModel, | ||
useReactTable, | ||
} from '@tanstack/react-table'; | ||
import { ReactNode, createContext, useContext, useMemo, useState } from 'react'; | ||
import { useColumnFilters } from './useColumnFilters.hook'; | ||
import { applyFilters } from '@/lib/filters'; | ||
import { ColumnFilter } from './DatatableDefaultFilterButton.component'; | ||
import { DataTable } from './DataTable.component'; | ||
import { DataTablePagination } from './DatatablePagination.component'; | ||
|
||
interface DataTableProviderProps<TData, TValue> { | ||
columns: ColumnDef<TData, TValue>[]; | ||
data: TData[]; | ||
pageSize?: number; | ||
itemNumber?: number; | ||
filtersDefinition?: ColumnFilter[]; | ||
children?: ReactNode; | ||
} | ||
|
||
interface DataTableContextValue<TData> { | ||
table: Table<TData>; | ||
filtersDefinition?: ColumnFilter[]; | ||
columnFilters: ReturnType<typeof useColumnFilters>; | ||
globalFilter: string; | ||
data: TData[]; | ||
filteredData: TData[]; | ||
sorting: SortingState; | ||
rows: Row<TData>[]; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const DataTableContext = createContext<DataTableContextValue<any> | null>(null); | ||
|
||
export function DataTableProvider<TData, TValue>({ | ||
columns, | ||
data, | ||
pageSize, | ||
filtersDefinition, | ||
children, | ||
}: DataTableProviderProps<TData, TValue>) { | ||
const [sorting, setSorting] = useState<SortingState>([ | ||
{ | ||
id: columns[0]?.id as string, | ||
desc: false, | ||
}, | ||
]); | ||
const [globalFilter, setGlobalFilter] = useState<string>(''); | ||
const columnFilters = useColumnFilters(); | ||
|
||
const filteredData = useMemo( | ||
() => applyFilters(data || [], columnFilters.filters) as TData[], | ||
[columnFilters.filters, data], | ||
); | ||
const table = useReactTable({ | ||
data: filteredData, | ||
columns, | ||
getCoreRowModel: getCoreRowModel(), | ||
getPaginationRowModel: getPaginationRowModel(), | ||
onSortingChange: setSorting, | ||
getSortedRowModel: getSortedRowModel(), | ||
getFilteredRowModel: getFilteredRowModel(), | ||
state: { | ||
sorting, | ||
globalFilter, | ||
}, | ||
initialState: { | ||
pagination: { pageSize: pageSize ?? 5 }, | ||
}, | ||
onGlobalFilterChange: (e) => { | ||
setGlobalFilter(e); | ||
}, | ||
globalFilterFn: 'auto', | ||
}); | ||
|
||
const rows = useMemo(() => table.getRowModel()?.rows, [ | ||
table, | ||
globalFilter, | ||
columnFilters.filters, | ||
data, | ||
sorting, | ||
]); | ||
|
||
const contextValue: DataTableContextValue<TData> = { | ||
table, | ||
filtersDefinition, | ||
columnFilters, | ||
globalFilter, | ||
data, | ||
filteredData, | ||
sorting, | ||
rows, | ||
}; | ||
|
||
return ( | ||
<DataTableContext.Provider value={contextValue}> | ||
{children || ( | ||
<> | ||
<DataTable /> | ||
<DataTablePagination /> | ||
</> | ||
)} | ||
</DataTableContext.Provider> | ||
); | ||
} | ||
|
||
export function useDataTableContext<TData>() { | ||
const context = useContext<DataTableContextValue<TData>>(DataTableContext); | ||
if (!context) { | ||
throw new Error( | ||
'useDataTableContext must be used within a DataTableProvider', | ||
); | ||
} | ||
return context as DataTableContextValue<TData>; | ||
} |
5 changes: 5 additions & 0 deletions
5
...ager/apps/pci-databases-analytics/src/components/data-table/DatatableAction.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { ReactNode } from 'react'; | ||
|
||
export function DatatableAction({ children }: { children: ReactNode }) { | ||
return <>{children || <></>}</>; | ||
} |
Oops, something went wrong.