Skip to content

Commit bbf24c9

Browse files
feat(Table): add virtualization (#2489)
* feat: virtulization on table poc * chore: virtulized selectable demo * chore: minor changes * chore: update table styling and virtulization poc * chore: add toolbar * chore: styling fix * fix: height of table * chore: remove hasPadding from tableBody * chore: scroll styling in case of not proper space is their , in virtual table div * chore: added isVirtualized prop * chore: added dynamic width and height * chore: table hover actions fixes * fix: row bottom not visible with rowHeight * fix: distance between header height and table height * fix: gap between table cells * chore: added isSelectable todo * chore: add isVirtualized prop * chore: remove unused classes * chore: remove comments * chore: code clean up * fix: old table styling * chore: table height fix * fix: table bg on hover , (but hover actions are not working now ;_;) * fix: table hover actions :tick * fix: inconsistent size of checkbox header * chore: added examples * chore: update story * chore: pagination check and prop fix * chore: throw error * chore: added error * fix: make table response * chore: update docs & components and types * chore: fixed ts and lint errors in code * fix: more ts error and ignore few files now * chore: ignore table test * chore: update test * fix: ts error * fix: more ts error * chore: update virtuazlied table api * chore: added table body styles * refactor: styles for table and table body * chore: code clean up * chore: added test & updated ref type * chore: added changeset * fix: lint error * chore: cleanup * chore: update snap * chore: removed ununsed code * chore: unused code * chore: move updates * chore: update snap * chore: update table * chore: self review changes * chore: removed getTableRowSelector * chore: minior review changes * chore: table api changes * chore: make rowHeight internal * chore: more review changes * chore: update docs * chore: added more stories * chore: update style type , added isDisbaled and fix double background color in hover actions * fix: build error * chore: update tests * fix: update table virtualized * chore: update snap * chore: lint fix * chore: update default page size * chore: rename VirtulizedWrapper to TableVirtualizedWrapper * fix: headerHeight , rowHeight by default * chore: remove rowHeight * chore: able to pass height * chore: update ref * chore: exmaple updates * chore: update api docs * feat: move isVirtualized internally * fix: lint change * chore: update lint * chore: lint fix * chore: remove boxref * chore: update table.web.test * chore: resolve generics * chore: move tabledata to context * chore: more ts changes * chore: update body ts type * chore: more changes * chore: remove unsed file * chore: change types * chore: more values * chore: review changes * chore: remove basic table example * chore: add component id * chore: remove extra stories * refactor: isVirtualized code * chore: update snaps * chore: change to local table node * chore: update jsdocs * chore: review changes --------- Co-authored-by: saurabhdaware <[email protected]>
1 parent a694ac7 commit bbf24c9

File tree

18 files changed

+2470
-1859
lines changed

18 files changed

+2470
-1859
lines changed

.changeset/quiet-students-allow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@razorpay/blade': minor
3+
---
4+
5+
feat(blade): add support for table virtualization

packages/blade/codemods/aicodemod/knowledge/Table.md

+126
Original file line numberDiff line numberDiff line change
@@ -594,3 +594,129 @@ const TableComponent: StoryFn<typeof Table> = ({ ...args }) => {
594594
);
595595
};
596596
```
597+
598+
# Virtualized Table
599+
600+
Virtaulized table is a table component that renders only the visible rows and columns. This is useful when you have a large dataset and you want to render only the visible rows and columns to improve the performance of the table.
601+
602+
Out implementation of virtualized table is an wrapper on top of react-table-library 's implementation. It provides a simple API to create a virtualized table.
603+
604+
## Props
605+
606+
most of props are same as Table component. we have added following table component.
607+
608+
609+
but their is a change in children prop of Table component. In virtualized table we need to pass a component named TableVirtulized that takes TableHeader, TableBody components.
610+
VirtualizedTable is a wrapper on top of react-table-library's [Virtualized](https://github.com/table-library/react-table-library/blob/master/src/virtualized/Virtualized.tsx) component. It provides a simple API to create a virtualized table.
611+
612+
```ts
613+
type VirtualizedWrapperProps<Item> = {
614+
/**
615+
* * @example
616+
* <TableComponent
617+
* data={data}
618+
* isVirtualized
619+
* rowDensity="compact"
620+
* selectionType="multiple"
621+
* height="700px"
622+
* toolbar={
623+
* <TableToolbar>
624+
* <TableToolbarActions>
625+
* <Button variant="secondary" marginRight="spacing.2">
626+
* Export
627+
* </Button>
628+
* <Button>Payout</Button>
629+
* </TableToolbarActions>
630+
* </TableToolbar>
631+
* }
632+
* >
633+
* {(tableData) => (
634+
* <TableVirtualizedWrapper tableData={tableData}>
635+
* <TableHeader>
636+
* <TableHeaderRow>
637+
* <TableHeaderCell>ID</TableHeaderCell>
638+
* <TableHeaderCell>Amount</TableHeaderCell>
639+
* <TableHeaderCell>Account</TableHeaderCell>
640+
* <TableHeaderCell>Date</TableHeaderCell>
641+
* <TableHeaderCell>Method</TableHeaderCell>
642+
* <TableHeaderCell>Status</TableHeaderCell>
643+
* </TableHeaderRow>
644+
* </TableHeader>
645+
* <TableBody<Item>>
646+
* {(tableItem, index) => (
647+
* <TableRow
648+
* key={index}
649+
* item={tableItem}
650+
* hoverActions={
651+
* <>
652+
* <IconButton
653+
* accessibilityLabel="Copy"
654+
* isHighlighted
655+
* icon={CopyIcon}
656+
* onClick={() => console.log('copy', tableItem)}
657+
* />
658+
* <IconButton
659+
* accessibilityLabel="Delete"
660+
* isHighlighted
661+
* icon={TrashIcon}
662+
* onClick={() => console.log('delete', tableItem)}
663+
* />
664+
* </>
665+
* }
666+
* >
667+
* <TableCell>
668+
* <Code size="medium">{tableItem.paymentId}</Code>
669+
* </TableCell>
670+
* <TableCell>
671+
* <Amount value={tableItem.amount} />
672+
* </TableCell>
673+
* <TableCell>{tableItem.account}</TableCell>
674+
* <TableCell>
675+
* {tableItem.date?.toLocaleDateString('en-IN', {
676+
* year: 'numeric',
677+
* month: '2-digit',
678+
* day: '2-digit',
679+
* })}
680+
* </TableCell>
681+
* <TableCell>{tableItem.method}</TableCell>
682+
* <TableCell>
683+
* <Badge
684+
* size="medium"
685+
* color={
686+
* tableItem.status === 'Completed'
687+
* ? 'positive'
688+
* : tableItem.status === 'Pending'
689+
* ? 'notice'
690+
* : tableItem.status === 'Failed'
691+
* ? 'negative'
692+
* : 'default'
693+
* }
694+
* >
695+
* {tableItem.status}
696+
* </Badge>
697+
* </TableCell>
698+
* </TableRow>
699+
* )}
700+
* </TableBody>
701+
* </TableVirtualizedWrapper>
702+
* )}
703+
* </TableComponent>
704+
*
705+
**/
706+
/**
707+
/**
708+
* The tableData prop is an array of objects.
709+
*/
710+
tableData: TableNode<Item>[];
711+
/**
712+
* headerHeight is the height of the header
713+
**/
714+
headerHeight?: number;
715+
/**
716+
* rowHeight is the height of each row, it can be a fixed number or a function that returns a number
717+
**/
718+
rowHeight?: (item: TableLibraryTableNode, index: number) => number;
719+
children: React.ReactNode;
720+
children: React.ReactNode;
721+
};
722+
```

packages/blade/src/components/SpotlightPopoverTour/docs/TourDocs.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//ignore file for ts check
12
import React from 'react';
23
import { BasicExample } from './examples';
34
import StoryPageWrapper from '~utils/storybook/StoryPageWrapper';

packages/blade/src/components/Table/Table.web.tsx

+57-21
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
TablePaginationType,
2929
TableHeaderRowProps,
3030
} from './types';
31+
import { getTableBodyStyles } from './commonStyles';
3132
import { makeBorderSize, makeMotionTime } from '~utils';
3233
import { getComponentId, isValidAllowedChildren } from '~utils/isValidAllowedChildren';
3334
import { throwBladeError } from '~utils/logger';
@@ -87,21 +88,42 @@ const getTableHeaderCellCount = (children: (data: []) => React.ReactElement): nu
8788
return 0;
8889
};
8990

90-
const StyledReactTable = styled(ReactTable)<{ $styledProps?: { height?: BoxProps['height'] } }>(
91-
({ $styledProps }) => {
92-
const { theme } = useTheme();
93-
const styledPropsCSSObject = getBaseBoxStyles({
94-
theme,
95-
height: $styledProps?.height,
96-
});
97-
98-
return {
99-
'&&&': {
100-
...styledPropsCSSObject,
101-
},
102-
};
103-
},
104-
);
91+
const StyledReactTable = styled(ReactTable)<{
92+
$styledProps?: {
93+
height?: BoxProps['height'];
94+
width?: BoxProps['width'];
95+
isVirtualized?: boolean;
96+
isSelectable?: boolean;
97+
showStripedRows?: boolean;
98+
};
99+
}>(({ $styledProps }) => {
100+
const { theme } = useTheme();
101+
const styledPropsCSSObject = getBaseBoxStyles({
102+
theme,
103+
height: $styledProps?.height,
104+
...($styledProps?.isVirtualized && {
105+
width: '100%',
106+
}),
107+
});
108+
const $isSelectable = $styledProps?.isSelectable;
109+
const $showStripedRows = $styledProps?.showStripedRows;
110+
return {
111+
'&&&': {
112+
...styledPropsCSSObject,
113+
overflow: `${$styledProps?.isVirtualized ? 'unset' : 'auto'} !important`,
114+
},
115+
...($styledProps?.isVirtualized
116+
? getTableBodyStyles({
117+
isVirtualized: $styledProps?.isVirtualized,
118+
theme,
119+
height: $styledProps?.height,
120+
width: '100%',
121+
isSelectable: $isSelectable,
122+
showStripedRows: $showStripedRows,
123+
})
124+
: null),
125+
};
126+
});
105127

106128
const RefreshWrapper = styled(BaseBox)<{
107129
isRefreshSpinnerVisible: boolean;
@@ -156,8 +178,10 @@ const _Table = <Item,>({
156178
undefined,
157179
);
158180
const [hasHoverActions, setHasHoverActions] = React.useState(false);
181+
const tableRootComponent = children([]);
182+
const isVirtualized = getComponentId(tableRootComponent) === ComponentIds.VirtualizedTable;
159183
// Need to make header is sticky if first column is sticky otherwise the first header cell will not be sticky
160-
const shouldHeaderBeSticky = isHeaderSticky ?? isFirstColumnSticky;
184+
const shouldHeaderBeSticky = isVirtualized ?? isHeaderSticky ?? isFirstColumnSticky;
161185
const backgroundColor = tableBackgroundColor;
162186

163187
const isMobile = useIsMobile();
@@ -265,7 +289,7 @@ const _Table = <Item,>({
265289
}, [data.nodes]);
266290

267291
// Selection Logic
268-
const onSelectChange: MiddlewareFunction = (action, state): void => {
292+
const onSelectChange: MiddlewareFunction = (_, state): void => {
269293
const selectedIds: Identifier[] = state.id ? [state.id] : state.ids ?? [];
270294
setSelectedRows(selectedIds);
271295
onSelectionChange?.({
@@ -323,7 +347,7 @@ const _Table = <Item,>({
323347
);
324348

325349
// Sort Logic
326-
const handleSortChange: MiddlewareFunction = (action, state) => {
350+
const handleSortChange: MiddlewareFunction = (_, state) => {
327351
onSortChange?.({
328352
sortKey: state.sortKey,
329353
isSortReversed: state.reverse,
@@ -341,7 +365,7 @@ const _Table = <Item,>({
341365
},
342366
);
343367

344-
const currentSortedState: TableContextType['currentSortedState'] = useMemo(() => {
368+
const currentSortedState: TableContextType<Item>['currentSortedState'] = useMemo(() => {
345369
return {
346370
sortKey: sort.state.sortKey,
347371
isSortReversed: sort.state.reverse,
@@ -409,7 +433,7 @@ const _Table = <Item,>({
409433
}
410434

411435
// Table Context
412-
const tableContext: TableContextType = useMemo(
436+
const tableContext: TableContextType<Item> = useMemo(
413437
() => ({
414438
selectionType,
415439
selectedRows,
@@ -434,6 +458,10 @@ const _Table = <Item,>({
434458
showBorderedCells,
435459
hasHoverActions,
436460
setHasHoverActions,
461+
columnCount,
462+
gridTemplateColumns,
463+
isVirtualized,
464+
tableData: data.nodes,
437465
}),
438466
[
439467
selectionType,
@@ -442,8 +470,10 @@ const _Table = <Item,>({
442470
toggleRowSelectionById,
443471
toggleAllRowsSelection,
444472
deselectAllRows,
473+
gridTemplateColumns,
445474
rowDensity,
446475
toggleSort,
476+
columnCount,
447477
currentSortedState,
448478
setPaginationPage,
449479
setPaginationRowSize,
@@ -459,6 +489,8 @@ const _Table = <Item,>({
459489
showBorderedCells,
460490
hasHoverActions,
461491
setHasHoverActions,
492+
isVirtualized,
493+
data,
462494
],
463495
);
464496

@@ -484,6 +516,7 @@ const _Table = <Item,>({
484516
position="relative"
485517
{...getStyledProps(rest)}
486518
{...metaAttribute({ name: MetaConstants.Table })}
519+
width={isVirtualized ? `100%` : undefined}
487520
>
488521
{isRefreshSpinnerMounted && (
489522
<RefreshWrapper
@@ -513,6 +546,10 @@ const _Table = <Item,>({
513546
sort={sortFunctions ? sort : null}
514547
$styledProps={{
515548
height,
549+
width: isVirtualized ? `100%` : undefined,
550+
isVirtualized,
551+
isSelectable: selectionType !== 'none',
552+
showStripedRows,
516553
}}
517554
pagination={hasPagination ? paginationConfig : null}
518555
{...makeAccessible({ multiSelectable: selectionType === 'multiple' })}
@@ -527,7 +564,6 @@ const _Table = <Item,>({
527564
</TableContext.Provider>
528565
);
529566
};
530-
531567
const Table = assignWithoutSideEffects(_Table, {
532568
componentId: ComponentIds.Table,
533569
});

packages/blade/src/components/Table/TableBody.native.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React from 'react';
44
import type { TableBodyProps, TableCellProps, TableRowProps } from './types';
55
import { Text } from '~components/Typography';
66

7-
const TableBody = (props: TableBodyProps): React.ReactElement => {
7+
const TableBody = <Item,>(props: TableBodyProps<Item>): React.ReactElement => {
88
return <Text>Table Component is not available for Native mobile apps.</Text>;
99
};
1010

0 commit comments

Comments
 (0)