Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expanding rows does not work when some pagination props are present #306

Closed
1 task done
FerranMontoliu opened this issue Apr 10, 2024 · 2 comments
Closed
1 task done
Labels
bug Something isn't working V1 Issue with MRT V1 V2 Issue with MRT V2

Comments

@FerranMontoliu
Copy link

FerranMontoliu commented Apr 10, 2024

mantine-react-table version

v1.3.4 & v2.0.0-beta.1

react & react-dom versions

v18.2.0

Describe the bug and the steps to reproduce it

I have a table with server-side pagination, for which I want to be able to expand rows.

If I set enablePagination to true and manualPagination to false, my table works perfectly, but if I set enablePagination to false and/or manualPagination to true, the expanding row functionality stops working.

In my case, I have a backend which sends the data for the table (which already include the subRows prop for each row). So, since I am using server-side pagination, I need to have manualPagination set to true, but then I cannot use the expanding rows option.

Updating my table to have manualPagination: false is not an option for me, since I can have up to hundreds of thousands of rows, so I need to use server-side pagination for that specific table.

Minimal, Reproducible Example - (Optional, but Recommended)

StackBlitz URL: https://stackblitz.com/edit/github-qqfn7c?file=src%2FTS.tsx

Code snippet:

import { useMemo } from 'react';
import { MantineReactTable, useMantineReactTable, type MRT_ColumnDef } from 'mantine-react-table';

export type Person = {
  firstName: string;
  lastName: string;
  address: string;
  city: string;
  state: string;
  subRows?: Person[]; //Each person can have sub rows of more people
};

export const data = [
  {
    firstName: 'Dylan',
    lastName: 'Murray',
    address: '261 Erdman Ford',
    city: 'East Daphne',
    state: 'Kentucky',
    subRows: [
      {
        firstName: 'Ervin',
        lastName: 'Reinger',
        address: '566 Brakus Inlet',
        city: 'South Linda',
        state: 'West Virginia',
        subRows: [
          {
            firstName: 'Jordane',
            lastName: 'Homenick',
            address: '1234 Brakus Inlet',
            city: 'South Linda',
            state: 'West Virginia',
          },
        ],
      },
      {
        firstName: 'Brittany',
        lastName: 'McCullough',
        address: '722 Emie Stream',
        city: 'Lincoln',
        state: 'Nebraska',
      },
    ],
  },
  {
    firstName: 'Raquel',
    lastName: 'Kohler',
    address: '769 Dominic Grove',
    city: 'Columbus',
    state: 'Ohio',
    subRows: [
      {
        firstName: 'Branson',
        lastName: 'Frami',
        address: '32188 Larkin Turnpike',
        city: 'Charleston',
        state: 'South Carolina',
      },
    ],
  },
];

const Example = () => {
  const columns = useMemo<MRT_ColumnDef<Person>[]>(
    () => [
      {
        accessorKey: 'firstName',
        header: 'First Name',
      },
      {
        accessorKey: 'lastName',
        header: 'Last Name',
      },

      {
        accessorKey: 'address',
        header: 'Address',
      },
      {
        accessorKey: 'city',
        header: 'City',
      },

      {
        accessorKey: 'state',
        enableColumnOrdering: false,
        header: 'State',
      },
    ],
    [],
  );

  const getSubRows = (row: Person): Array<Person> | undefined => {
      if ("subRows" in row) {
          return row.subRows;
      }

      return undefined;
  }

  const onPaginationChange = (newPagination: any) => {
    console.log("helloooooo", newPagination)
  }

  const table = useMantineReactTable({
    columns,
    data,
    enableExpanding: true,
    enableExpandAll: true,
    paginateExpandedRows: false,
    manualExpanding: false,
    filterFromLeafRows: false,
    maxLeafRowFilterDepth: 0,
    getSubRows,

    // Generic props
    enableDensityToggle: false,
    enableFullScreenToggle: false,
    enableColumnActions: false,

    // Toolbar props
    enableTopToolbar: true,

    // Sorting props
    enableSorting: true,
    enableMultiSort: false,
    onSortingChange: undefined,
    manualSorting: true,

    // Filtering props
    enableColumnFilters: false,
    onColumnFiltersChange: undefined,
    manualFiltering: true,
    enableGlobalFilter: false,
    enableFilterMatchHighlighting: false,
    columnFilterDisplayMode: "subheader",

    // Pagination props
    enableBottomToolbar: true,
    paginationDisplayMode: "default",
    onPaginationChange: onPaginationChange,
    mantinePaginationProps: {
        rowsPerPageOptions: ["10", "50"],
        showRowsPerPage: true,
        withEdges: true,
    },

    // WTF
    enablePagination: true,
    manualPagination: false,
  })

  return (
    <MantineReactTable
      table={table}
    />
  );
};

export default Example;

Screenshots or Videos (Optional)

No response

Do you intend to try to help solve this bug with your own PR?

No, because I do not have time to dig into it

Terms

  • I understand that if my bug cannot be reliably reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
@alessandrojcm alessandrojcm added bug Something isn't working V1 Issue with MRT V1 V2 Issue with MRT V2 labels Apr 12, 2024
@alessandrojcm
Copy link
Collaborator

alessandrojcm commented Apr 14, 2024

This looks like a bug with TanStack Table itself; if you pass paginateExpandedRows as true with your example it then works but that might not be what you want. See TanStack/table#5110 for workarounds.

@FerranMontoliu
Copy link
Author

This looks like a bug with TanStack Table itself; if you pass paginateExpandedRows as true with your example it then works but that might not be what you want. See TanStack/table#5110 for workarounds.

Thanks @alessandrojcm, that was super useful!!

I used the following workaround, inspired from the answer from TanStack/table#5110:

  1. Create a file called hack.ts, and fill it with the following content:
// TODO: This is a hack until the following issues are fixed:
//  - https://github.com/KevinVandy/mantine-react-table/issues/306
//  - https://github.com/TanStack/table/issues/5110

import { RowData } from "./types";
import { MRT_Row, MRT_RowModel } from "mantine-react-table";

type NoInfer<T> = [T][T extends any ? 0 : never];

const memo = <TDeps extends readonly any[], TDepArgs, TResult>(
    getDeps: (depArgs?: TDepArgs) => [...TDeps],
    fn: (...args: NoInfer<[...TDeps]>) => TResult,
    opts: {
        key: any;
        debug?: () => any;
        onChange?: (result: TResult) => void;
    },
): ((depArgs?: TDepArgs) => TResult) => {
    let deps: any[] = [];
    let result: TResult | undefined;

    return (depArgs) => {
        let depTime: number;
        if (opts.key && opts.debug) depTime = Date.now();

        const newDeps = getDeps(depArgs);

        const depsChanged =
            newDeps.length !== deps.length ||
            newDeps.some((dep: any, index: number) => deps[index] !== dep);

        if (!depsChanged) {
            return result!;
        }

        deps = newDeps;

        let resultTime: number;
        if (opts.key && opts.debug) resultTime = Date.now();

        result = fn(...newDeps);
        opts?.onChange?.(result);

        if (opts.key && opts.debug) {
            if (opts?.debug()) {
                const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100;
                const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100;
                const resultFpsPercentage = resultEndTime / 16;

                const pad = (str: number | string, num: number) => {
                    str = String(str);
                    while (str.length < num) {
                        str = " " + str;
                    }
                    return str;
                };

                console.info(
                    `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
                    `
            font-size: .6rem;
            font-weight: bold;
            color: hsl(${Math.max(
                0,
                Math.min(120 - 120 * resultFpsPercentage, 120),
            )}deg 100% 31%);`,
                    opts?.key,
                );
            }
        }

        return result!;
    };
};

const expandRows = <T extends RowData>(rowModel: MRT_RowModel<T>): MRT_RowModel<T> => {
    const expandedRows: Array<MRT_Row<T>> = [];

    const handleRow = (row: MRT_Row<T>): void => {
        expandedRows.push(row);

        if (row.subRows?.length && row.getIsExpanded()) {
            row.subRows.forEach(handleRow);
        }
    };

    rowModel.rows.forEach(handleRow);

    return {
        rows: expandedRows,
        flatRows: rowModel.flatRows,
        rowsById: rowModel.rowsById,
    };
};

// The table parameter should have type MRT_Table, but it is not exported
// @ts-ignore
export const getExpandedRowModel = (table) =>
    memo(
        () => [
            table.getState().expanded,
            table.getPreExpandedRowModel(),
            table.options.paginateExpandedRows,
            table.options.manualPagination,
        ],
        (expanded, rowModel, paginateExpandedRows, manualPagination) => {
            if (
                !rowModel.rows.length ||
                (expanded !== true && !Object.keys(expanded ?? {}).length)
            ) {
                return rowModel;
            }

            // Here is the fix
            if (!paginateExpandedRows && !manualPagination) {
                // Only expand rows at this point if they are being paginated
                return rowModel;
            }

            return expandRows(rowModel);
        },
        {
            key: "getExpandedRowModel",
            debug: () => table.options.debugAll ?? table.options.debugTable,
        },
    );
  1. Create the table like this:
import { MantineReactTable } from "mantine-react-table";
import { getExpandedRowModel } from "./hack";

export const Table = (...): ReactElement => {
    ...
    const table = useMantineReactTable({
        ...

        // Sub-rows props
        enableExpanding: true,
        paginateExpandedRows: false,
        enableExpandAll: true,
        manualExpanding: false,
        filterFromLeafRows: false,
        maxLeafRowFilterDepth: 0,
        getExpandedRowModel,

        // Pagination props
        enablePagination: true,
        manualPagination: true,

        ...
    });

    return <MantineReactTable table={table} />;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working V1 Issue with MRT V1 V2 Issue with MRT V2
Projects
None yet
Development

No branches or pull requests

2 participants