Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/components/Table/Table.variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const rowVariants = cva(
);

export const headVariants = cva([
'h-[18px]',
'h-4.5',
'bg-slate-100',
'[&>tr>th]:py-3',
'[&>tr>th]:px-4',
Expand Down
123 changes: 123 additions & 0 deletions lib/components/VirtualizedTable/VirtualizedTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';

import { VirtualizedTable } from './VirtualizedTable';
import { ColumnDef, Props } from './VirtualizedTable.types';

type Item = {
id: string;
name: string;
status: string;
};

const columns: ColumnDef<Item>[] = [
{
header: 'Name',
accessorKey: 'name',
},
{
header: 'Status',
accessorKey: 'status',
},
];

const data: Item[] = [
{ id: '1', name: 'Alpha', status: 'active' },
{ id: '2', name: 'Beta', status: 'inactive' },
{ id: '3', name: 'Gamma', status: 'active' },
];

describe('VirtualizedTable', () => {
const setup = (pagination?: { totalItems: number }) => {
const defaultProps = {
id: 'test-table',
ariaLabel: 'Test table',
columns,
data,
} satisfies Props<Item>;

const queryClient = new QueryClient();

const { container } = render(
<QueryClientProvider client={queryClient}>
{pagination ? (
<VirtualizedTable<Item>
{...defaultProps}
showPagination={true}
totalItems={pagination.totalItems}
/>
) : (
<VirtualizedTable<Item> {...defaultProps} />
)}
</QueryClientProvider>,
);

const getTable = () => screen.getByRole('table', { name: /test table/i });
const getWrapperBody = () => getTable().parentElement as HTMLElement;
const getLastCell = () => {
const cells = screen.getAllByRole('cell');

return cells[cells.length - 1];
};
const queryPaginationBar = () => screen.queryByText(/results/i);

return {
component: container,
getTable,
getWrapperBody,
getLastCell,
queryPaginationBar,
};
};

it('should render the component', () => {
const { getTable } = setup();

expect(getTable()).toBeInTheDocument();
});

it('should render the rows', () => {
setup();

expect(screen.getByText('Alpha')).toBeInTheDocument();
expect(screen.getByText('Beta')).toBeInTheDocument();
expect(screen.getByText('Gamma')).toBeInTheDocument();
});

it('should close the bottom borders when pagination is disabled', () => {
const { getWrapperBody, getLastCell, queryPaginationBar } = setup();

expect(queryPaginationBar()).not.toBeInTheDocument();
expect(getWrapperBody()).toHaveClass('rounded-lg');
expect(getLastCell()).toHaveClass('rounded-br-lg');
});

it('should close the bottom borders when pagination is enabled but the bar is hidden for few items', () => {
const { getWrapperBody, getLastCell, queryPaginationBar } = setup({
totalItems: data.length,
});

expect(queryPaginationBar()).not.toBeInTheDocument();
expect(getWrapperBody()).toHaveClass('rounded-lg');
expect(getLastCell()).toHaveClass('rounded-br-lg');
});

it('should let the pagination bar close the bottom borders when it is visible', () => {
const { getWrapperBody, getLastCell, queryPaginationBar } = setup({
totalItems: 25,
});

expect(queryPaginationBar()).toBeInTheDocument();
expect(getWrapperBody()).not.toHaveClass('rounded-lg');
expect(getLastCell()).not.toHaveClass('rounded-br-lg');
});

it("should doesn't have violations", async () => {
const { component } = setup();

const results = await axe(component);

expect(results).toHaveNoViolations();
});
});
8 changes: 7 additions & 1 deletion lib/components/VirtualizedTable/VirtualizedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ const VirtualizedTableInner = <TData extends RowData>({
showDotPagination,
showFormPagination,
].some(Boolean),
[],
[
showPaginationProp,
showTotalItems,
showDropdownPagination,
showDotPagination,
showFormPagination,
],
);

return (
Expand Down
11 changes: 7 additions & 4 deletions lib/components/VirtualizedTable/components/Body/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Skeleton } from '@/components/VirtualizedTable/components/Skeleton/Skel
import { cn } from '@/utils';

import { useTableContext } from '../../contexts';
import { isPaginationBarVisible } from '../../utils';
import { RowData, RowDataWithMeta } from '../../VirtualizedTable.types';
import { ExpandableRow } from '../ExpandableRow/ExpandableRow';

Expand All @@ -18,6 +19,7 @@ export const Body = <TData extends RowData = RowData>({
const {
table,
pageSize,
totalItems = -Infinity,
tableFetching,
enableExpandedRow,
classNameExpandedRow,
Expand All @@ -36,6 +38,7 @@ export const Body = <TData extends RowData = RowData>({
}

const rows = table.getRowModel().rows ?? [];
const hasPaginationBar = showPagination && isPaginationBarVisible(totalItems);

if (rows.length === 0 && emptyState) {
const colSpan = table.getVisibleLeafColumns().length;
Expand Down Expand Up @@ -156,14 +159,14 @@ export const Body = <TData extends RowData = RowData>({
rowIndex === rows.length - 1 &&
firstDataColumnIndex !== null &&
columnIndex === firstDataColumnIndex &&
!showPagination &&
!hasPaginationBar &&
!isExpanded,
'rounded-br-lg':
rowIndex === rows.length - 1 &&
columnIndex === columns.length - 1 &&
!showPagination &&
!hasPaginationBar &&
!isExpanded,
'dark:[tr:last-child_&]:border-b': !showPagination,
'dark:[tr:last-child_&]:border-b': !hasPaginationBar,
},
)}
data-expanded={isExpanded ? true : undefined}
Expand All @@ -183,7 +186,7 @@ export const Body = <TData extends RowData = RowData>({
colSpan={columns.length}
id={id}
isExpanded={!!isExpanded}
isLastRow={rowIndex === rows.length - 1 && !showPagination}
isLastRow={rowIndex === rows.length - 1 && !hasPaginationBar}
>
{meta.expandedRow ?? renderExpandedRow?.(original)}
</ExpandableRow>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react';

import { DEFAULT_PAGE_SIZE } from '../../constants';
import { useTableContext } from '../../contexts';
import { isPaginationBarVisible } from '../../utils';

import { DotPaginate } from '../DotPaginate/DotPaginate';
import { DropdownPaginate } from '../DropdownPaginate/DropdownPaginate';
Expand All @@ -25,7 +25,7 @@ export const Pagination: FC<Props> = ({
isFirstLoad,
} = useTableContext();

if (totalItems <= DEFAULT_PAGE_SIZE || tableFetching || isLoading) {
if (!isPaginationBarVisible(totalItems) || tableFetching || isLoading) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import { FC } from 'react';

import { Props } from './WrapperBody.types';
import { useTableContext } from '../../contexts';
import { isPaginationBarVisible } from '../../utils';

export const WrapperBody: FC<Props> = ({
children,
classNameWrapperTable,
isLoading,
showPagination,
}) => {
const { tableFetching } = useTableContext();
const { tableFetching, totalItems = -Infinity } = useTableContext();

return (
<div
className={cn(
'shadow rounded-t-lg overflow-hidden',
{
'rounded-lg': !showPagination || tableFetching || isLoading,
'rounded-lg':
!showPagination ||
!isPaginationBarVisible(totalItems) ||
tableFetching ||
isLoading,
},
classNameWrapperTable,
)}
Expand Down
41 changes: 41 additions & 0 deletions lib/components/VirtualizedTable/stories/Dark.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,47 @@ export const Default: Story = {
},
};

export const PaginationWithFewItems: Story = {
render: () => {
const id = useId();
const [pokemons, setPokemons] = useState<Pokemon[]>([]);

useEffect(() => {
const init = async () => {
const result = await getPokemons({ page: 1, pageSize: 5 });

setPokemons(result.results);
};

init();
}, []);

useEffect(() => {
document.body.setAttribute('data-theme', 'dark');
document.body.classList.add('bg-metal-900');

return () => {
document.body.removeAttribute('data-theme');
document.body.classList.remove('bg-metal-900');
};
}, []);

return (
<QueryClientProvider client={queryClient}>
<VirtualizedTableComponent<Pokemon>
id={id}
ariaLabel="List of pokemons"
classNameWrapperTable="overflow-visible"
data={pokemons}
columns={columns}
showPagination={true}
totalItems={pokemons.length}
/>
</QueryClientProvider>
);
},
};

type PokemonWithMeta = Pokemon & {
meta?: { expandedRow?: ReactNode };
};
Expand Down
31 changes: 31 additions & 0 deletions lib/components/VirtualizedTable/stories/Light.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,37 @@ export const Default: Story = {
},
};

export const PaginationWithFewItems: Story = {
render: () => {
const id = useId();
const [pokemons, setPokemons] = useState<Pokemon[]>([]);

useEffect(() => {
const init = async () => {
const result = await getPokemons({ page: 1, pageSize: 5 });

setPokemons(result.results);
};

init();
}, []);

return (
<QueryClientProvider client={queryClient}>
<VirtualizedTableComponent<Pokemon>
id={id}
ariaLabel="List of pokemons"
classNameWrapperTable="overflow-visible"
data={pokemons}
columns={columns}
showPagination={true}
totalItems={pokemons.length}
/>
</QueryClientProvider>
);
},
};

type PokemonWithMeta = Pokemon & {
meta?: { expandedRow?: ReactNode };
};
Expand Down
1 change: 1 addition & 0 deletions lib/components/VirtualizedTable/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './pagination';
5 changes: 5 additions & 0 deletions lib/components/VirtualizedTable/utils/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DEFAULT_PAGE_SIZE } from '../constants';

export const isPaginationBarVisible = (totalItems: number) => {
return totalItems > DEFAULT_PAGE_SIZE;
};
Loading