mirror of https://github.com/portainer/portainer
refactor(ui/datatables): use object type for table data
parent
531f88b947
commit
bccab06abb
|
@ -28,7 +28,7 @@ import { DatatableFooter } from './DatatableFooter';
|
|||
import { defaultGetRowId } from './defaultGetRowId';
|
||||
import { Table } from './Table';
|
||||
import { useGoToHighlightedRow } from './useGoToHighlightedRow';
|
||||
import { BasicTableSettings } from './types';
|
||||
import { BasicTableSettings, DefaultType } from './types';
|
||||
import { DatatableContent } from './DatatableContent';
|
||||
import { createSelectColumn } from './select-column';
|
||||
import { TableRow } from './TableRow';
|
||||
|
@ -49,7 +49,7 @@ export type PaginationProps =
|
|||
};
|
||||
|
||||
export interface Props<
|
||||
D extends Record<string, unknown>,
|
||||
D extends DefaultType,
|
||||
TMeta extends TableMeta<D> = TableMeta<D>
|
||||
> extends AutomationTestingProps {
|
||||
dataset: D[];
|
||||
|
@ -74,7 +74,7 @@ export interface Props<
|
|||
}
|
||||
|
||||
export function Datatable<
|
||||
D extends Record<string, unknown>,
|
||||
D extends DefaultType,
|
||||
TMeta extends TableMeta<D> = TableMeta<D>
|
||||
>({
|
||||
columns,
|
||||
|
@ -221,7 +221,7 @@ export function Datatable<
|
|||
}
|
||||
}
|
||||
|
||||
function defaultRenderRow<D extends Record<string, unknown>>(
|
||||
function defaultRenderRow<D extends DefaultType>(
|
||||
row: Row<D>,
|
||||
highlightedItemId?: string
|
||||
) {
|
||||
|
@ -235,7 +235,7 @@ function defaultRenderRow<D extends Record<string, unknown>>(
|
|||
);
|
||||
}
|
||||
|
||||
function getIsSelectionEnabled<D extends Record<string, unknown>>(
|
||||
function getIsSelectionEnabled<D extends DefaultType>(
|
||||
disabledSelect?: boolean,
|
||||
isRowSelectable?: Props<D>['isRowSelectable']
|
||||
) {
|
||||
|
|
|
@ -3,9 +3,9 @@ import { Row, Table as TableInstance } from '@tanstack/react-table';
|
|||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Table } from './Table';
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<D extends Record<string, unknown>>
|
||||
extends AutomationTestingProps {
|
||||
interface Props<D extends DefaultType> extends AutomationTestingProps {
|
||||
tableInstance: TableInstance<D>;
|
||||
renderRow(row: Row<D>): React.ReactNode;
|
||||
onSortChange?(colId: string, desc: boolean): void;
|
||||
|
@ -13,7 +13,7 @@ interface Props<D extends Record<string, unknown>>
|
|||
emptyContentLabel?: string;
|
||||
}
|
||||
|
||||
export function DatatableContent<D extends Record<string, unknown>>({
|
||||
export function DatatableContent<D extends DefaultType>({
|
||||
tableInstance,
|
||||
renderRow,
|
||||
onSortChange,
|
||||
|
|
|
@ -7,14 +7,15 @@ import {
|
|||
Props as DatatableProps,
|
||||
PaginationProps,
|
||||
} from './Datatable';
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<D extends Record<string, unknown>>
|
||||
interface Props<D extends DefaultType>
|
||||
extends Omit<DatatableProps<D>, 'renderRow' | 'expandable'> {
|
||||
renderSubRow(row: Row<D>): ReactNode;
|
||||
expandOnRowClick?: boolean;
|
||||
}
|
||||
|
||||
export function ExpandableDatatable<D extends Record<string, unknown>>({
|
||||
export function ExpandableDatatable<D extends DefaultType>({
|
||||
renderSubRow,
|
||||
getRowCanExpand = () => true,
|
||||
expandOnRowClick,
|
||||
|
|
|
@ -2,15 +2,16 @@ import { ReactNode } from 'react';
|
|||
import { Row } from '@tanstack/react-table';
|
||||
|
||||
import { TableRow } from './TableRow';
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<D extends Record<string, unknown>> {
|
||||
interface Props<D extends DefaultType> {
|
||||
row: Row<D>;
|
||||
disableSelect?: boolean;
|
||||
renderSubRow(row: Row<D>): ReactNode;
|
||||
expandOnClick?: boolean;
|
||||
}
|
||||
|
||||
export function ExpandableDatatableTableRow<D extends Record<string, unknown>>({
|
||||
export function ExpandableDatatableTableRow<D extends DefaultType>({
|
||||
row,
|
||||
disableSelect,
|
||||
renderSubRow,
|
||||
|
|
|
@ -8,6 +8,8 @@ import { getValueAsArrayOfStrings } from '@/portainer/helpers/array';
|
|||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface MultipleSelectionFilterProps {
|
||||
options: string[];
|
||||
value: string[];
|
||||
|
@ -70,9 +72,7 @@ export function MultipleSelectionFilter({
|
|||
}
|
||||
}
|
||||
|
||||
export function filterHOC<TData extends Record<string, unknown>>(
|
||||
menuTitle: string
|
||||
) {
|
||||
export function filterHOC<TData extends DefaultType>(menuTitle: string) {
|
||||
return function Filter({
|
||||
column: { getFilterValue, setFilterValue, getFacetedRowModel, id },
|
||||
}: {
|
||||
|
|
|
@ -12,9 +12,9 @@ import { defaultGetRowId } from './defaultGetRowId';
|
|||
import { Table } from './Table';
|
||||
import { NestedTable } from './NestedTable';
|
||||
import { DatatableContent } from './DatatableContent';
|
||||
import { BasicTableSettings } from './types';
|
||||
import { BasicTableSettings, DefaultType } from './types';
|
||||
|
||||
interface Props<D extends Record<string, unknown>> {
|
||||
interface Props<D extends DefaultType> {
|
||||
dataset: D[];
|
||||
columns: TableOptions<D>['columns'];
|
||||
|
||||
|
@ -25,7 +25,7 @@ interface Props<D extends Record<string, unknown>> {
|
|||
initialSortBy?: BasicTableSettings['sortBy'];
|
||||
}
|
||||
|
||||
export function NestedDatatable<D extends Record<string, unknown>>({
|
||||
export function NestedDatatable<D extends DefaultType>({
|
||||
columns,
|
||||
dataset,
|
||||
getRowId = defaultGetRowId,
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { Fragment, PropsWithChildren } from 'react';
|
||||
import { Row } from '@tanstack/react-table';
|
||||
|
||||
interface Props<T extends Record<string, unknown> = Record<string, unknown>> {
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<T extends DefaultType = DefaultType> {
|
||||
isLoading?: boolean;
|
||||
rows: Row<T>[];
|
||||
emptyContent?: string;
|
||||
renderRow(row: Row<T>): React.ReactNode;
|
||||
}
|
||||
|
||||
export function TableContent<
|
||||
T extends Record<string, unknown> = Record<string, unknown>
|
||||
>({
|
||||
export function TableContent<T extends DefaultType = DefaultType>({
|
||||
isLoading = false,
|
||||
rows,
|
||||
emptyContent = 'No items available',
|
||||
|
|
|
@ -2,15 +2,17 @@ import { Header, flexRender } from '@tanstack/react-table';
|
|||
|
||||
import { filterHOC } from './Filter';
|
||||
import { TableHeaderCell } from './TableHeaderCell';
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<D extends Record<string, unknown> = Record<string, unknown>> {
|
||||
interface Props<D extends DefaultType = DefaultType> {
|
||||
headers: Header<D, unknown>[];
|
||||
onSortChange?(colId: string, desc: boolean): void;
|
||||
}
|
||||
|
||||
export function TableHeaderRow<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
>({ headers, onSortChange }: Props<D>) {
|
||||
export function TableHeaderRow<D extends DefaultType = DefaultType>({
|
||||
headers,
|
||||
onSortChange,
|
||||
}: Props<D>) {
|
||||
return (
|
||||
<tr>
|
||||
{headers.map((header) => {
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { Cell, flexRender } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface Props<D extends Record<string, unknown> = Record<string, unknown>> {
|
||||
import { DefaultType } from './types';
|
||||
|
||||
interface Props<D extends DefaultType = DefaultType> {
|
||||
cells: Cell<D, unknown>[];
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function TableRow<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
>({ cells, className, onClick }: Props<D>) {
|
||||
export function TableRow<D extends DefaultType = DefaultType>({
|
||||
cells,
|
||||
className,
|
||||
onClick,
|
||||
}: Props<D>) {
|
||||
return (
|
||||
<tr
|
||||
className={clsx(className, { 'cursor-pointer': !!onClick })}
|
||||
|
|
|
@ -2,13 +2,16 @@ import { ColumnDef, CellContext } from '@tanstack/react-table';
|
|||
|
||||
import { Link } from '@@/Link';
|
||||
|
||||
export function buildNameColumn<T extends Record<string, unknown>>(
|
||||
import { DefaultType } from './types';
|
||||
import { defaultGetRowId } from './defaultGetRowId';
|
||||
|
||||
export function buildNameColumn<T extends DefaultType>(
|
||||
nameKey: keyof T,
|
||||
idKey: string,
|
||||
path: string,
|
||||
idParam = 'id'
|
||||
idParam = 'id',
|
||||
idGetter: (row: T) => string = defaultGetRowId<T>
|
||||
): ColumnDef<T> {
|
||||
const cell = createCell<T>();
|
||||
const cell = createCell();
|
||||
|
||||
return {
|
||||
header: 'Name',
|
||||
|
@ -19,7 +22,7 @@ export function buildNameColumn<T extends Record<string, unknown>>(
|
|||
enableHiding: false,
|
||||
};
|
||||
|
||||
function createCell<T extends Record<string, unknown>>() {
|
||||
function createCell() {
|
||||
return function NameCell({ renderValue, row }: CellContext<T, unknown>) {
|
||||
const name = renderValue() || '';
|
||||
|
||||
|
@ -30,7 +33,7 @@ export function buildNameColumn<T extends Record<string, unknown>>(
|
|||
return (
|
||||
<Link
|
||||
to={path}
|
||||
params={{ [idParam]: row.original[idKey] }}
|
||||
params={{ [idParam]: idGetter(row.original) }}
|
||||
title={name}
|
||||
>
|
||||
{name}
|
|
@ -1,15 +1,24 @@
|
|||
export function defaultGetRowId<D extends Record<string, unknown>>(
|
||||
row: D
|
||||
): string {
|
||||
if (row.id && (typeof row.id === 'string' || typeof row.id === 'number')) {
|
||||
import { DefaultType } from './types';
|
||||
|
||||
export function defaultGetRowId<D extends DefaultType>(row: D): string {
|
||||
if (
|
||||
'id' in row &&
|
||||
(typeof row.id === 'string' || typeof row.id === 'number')
|
||||
) {
|
||||
return row.id.toString();
|
||||
}
|
||||
|
||||
if (row.Id && (typeof row.Id === 'string' || typeof row.Id === 'number')) {
|
||||
if (
|
||||
'Id' in row &&
|
||||
(typeof row.Id === 'string' || typeof row.Id === 'number')
|
||||
) {
|
||||
return row.Id.toString();
|
||||
}
|
||||
|
||||
if (row.ID && (typeof row.ID === 'string' || typeof row.ID === 'number')) {
|
||||
if (
|
||||
'ID' in row &&
|
||||
(typeof row.ID === 'string' || typeof row.ID === 'number')
|
||||
) {
|
||||
return row.ID.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import { ColumnDef } from '@tanstack/react-table';
|
|||
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
export function buildExpandColumn<
|
||||
T extends Record<string, unknown>
|
||||
>(): ColumnDef<T> {
|
||||
import { DefaultType } from './types';
|
||||
|
||||
export function buildExpandColumn<T extends DefaultType>(): ColumnDef<T> {
|
||||
return {
|
||||
id: 'expand',
|
||||
header: ({ table }) => {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Row } from '@tanstack/react-table';
|
||||
|
||||
export function multiple<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
>({ getValue }: Row<D>, columnId: string, filterValue: string[]): boolean {
|
||||
import { DefaultType } from './types';
|
||||
|
||||
export function multiple<D extends DefaultType = DefaultType>(
|
||||
{ getValue }: Row<D>,
|
||||
columnId: string,
|
||||
filterValue: string[]
|
||||
): boolean {
|
||||
if (filterValue.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import { persist } from 'zustand/middleware';
|
|||
|
||||
import { keyBuilder } from '@/react/hooks/useLocalStorage';
|
||||
|
||||
export type DefaultType = object;
|
||||
|
||||
export interface PaginationTableSettings {
|
||||
pageSize: number;
|
||||
setPageSize: (pageSize: number) => void;
|
||||
|
|
|
@ -3,14 +3,14 @@ import { createColumnHelper } from '@tanstack/react-table';
|
|||
import { isoDate } from '@/portainer/filters/filters';
|
||||
import { createOwnershipColumn } from '@/react/docker/components/datatable-helpers/createOwnershipColumn';
|
||||
|
||||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
|
||||
import { DockerConfig } from '../../types';
|
||||
|
||||
const columnHelper = createColumnHelper<DockerConfig>();
|
||||
|
||||
export const columns = [
|
||||
buildNameColumn<DockerConfig>('Name', 'Id', 'docker.configs.config'),
|
||||
buildNameColumn<DockerConfig>('Name', 'docker.configs.config'),
|
||||
columnHelper.accessor('CreatedAt', {
|
||||
header: 'Creation Date',
|
||||
cell: ({ getValue }) => {
|
||||
|
|
|
@ -4,7 +4,7 @@ import _ from 'lodash';
|
|||
import { isoDateFromTimestamp } from '@/portainer/filters/filters';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
|
||||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
import { Link } from '@@/Link';
|
||||
|
||||
import { StatusType } from '../../types';
|
||||
|
@ -16,12 +16,7 @@ import { DeploymentCounter } from './DeploymentCounter';
|
|||
const columnHelper = createColumnHelper<DecoratedEdgeStack>();
|
||||
|
||||
export const columns = _.compact([
|
||||
buildNameColumn<DecoratedEdgeStack>(
|
||||
'Name',
|
||||
'Id',
|
||||
'edge.stacks.edit',
|
||||
'stackId'
|
||||
),
|
||||
buildNameColumn<DecoratedEdgeStack>('Name', 'edge.stacks.edit', 'stackId'),
|
||||
columnHelper.accessor(
|
||||
(item) => item.aggregatedStatus[StatusType.Acknowledged] || 0,
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
|
||||
import { EdgeUpdateListItemResponse } from '../../queries/list';
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { scheduledTime } from './scheduled-time';
|
|||
import { scheduleType } from './type';
|
||||
|
||||
export const columns = [
|
||||
buildNameColumn<EdgeUpdateListItemResponse>('name', 'id', '.item'),
|
||||
buildNameColumn<EdgeUpdateListItemResponse>('name', '.item'),
|
||||
scheduledTime,
|
||||
groups,
|
||||
scheduleType,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Profile } from '@/portainer/hostmanagement/fdo/model';
|
||||
|
||||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
|
||||
import { created } from './created';
|
||||
|
||||
export const columns = [
|
||||
buildNameColumn<Profile>('name', 'id', 'portainer.endpoints.profile.edit'),
|
||||
buildNameColumn<Profile>('name', 'portainer.endpoints.profile.edit'),
|
||||
created,
|
||||
];
|
||||
|
|
|
@ -10,14 +10,14 @@ import { deleteTeam } from '@/react/portainer/users/teams/teams.service';
|
|||
import { confirmDelete } from '@@/modals/confirm';
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { Button } from '@@/buttons';
|
||||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||
import { createPersistedStore } from '@@/datatables/types';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
const storageKey = 'teams';
|
||||
|
||||
const columns: ColumnDef<Team>[] = [
|
||||
buildNameColumn<Team>('Name', 'Id', 'portainer.teams.team'),
|
||||
buildNameColumn<Team>('Name', 'portainer.teams.team'),
|
||||
];
|
||||
|
||||
interface Props {
|
||||
|
|
Loading…
Reference in New Issue