Skip to content

Commit 13f6fb1

Browse files
authored
docs(Table): add virtualization example (#6813)
1 parent 185f0d8 commit 13f6fb1

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

.storybook/preview-head.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@
109109
padding-block: 0.125rem !important;
110110
}
111111

112+
/*ui5-table: height for virtualized table according to content-density mode*/
113+
.tableHeightContentDensity {
114+
height: 396px;
115+
}
116+
.ui5-content-density-compact .tableHeightContentDensity {
117+
height: 288px;
118+
}
119+
112120
/* TODO remove this workaround as soon as https://github.com/storybookjs/storybook/issues/20497 is fixed */
113121
.docs-story > div > div[scale] {
114122
min-height: 20px;

packages/main/src/webComponents/Table/Table.mdx

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const GrowingTable = () => {
113113
<summary>Show code</summary>
114114

115115
```jsx
116-
function TableWithRowSelection() {
116+
function TableWithRowSelection(props) {
117117
const [mode, setMode] = useState(TableSelectionMode.Multiple);
118118
return (
119119
<>
@@ -129,7 +129,7 @@ function TableWithRowSelection() {
129129
))}
130130
</SegmentedButton>
131131
<Table
132-
{...otherProps}
132+
{...props}
133133
headerRow={
134134
<TableHeaderRow sticky>
135135
<TableHeaderCell width={'200px'} minWidth={'200px'}>
@@ -193,6 +193,74 @@ function TableWithRowSelection() {
193193

194194
</details>
195195

196+
## Table with virtualized rows
197+
198+
`Table` with virtualization feature (`<TableVirtualizer {...props) />`):
199+
200+
<Canvas of={ComponentStories.VirtualizedTableRows} />
201+
202+
<details>
203+
204+
<summary>Show code</summary>
205+
206+
```tsx
207+
// enrich data with `position` (if not already available)
208+
const dataLargeWithPosition = dataLarge.map((item, index) => ({
209+
...item,
210+
position: index
211+
}));
212+
213+
function VirtualizedTable(props) {
214+
const [data, setData] = useState(dataLargeWithPosition.slice(0, 9));
215+
216+
const handleRangeChange: TableVirtualizerPropTypes['onRangeChange'] = (e) => {
217+
const { first, last } = e.detail;
218+
219+
// render two rows before and after the visible area of the table body container
220+
const overscanCountStart = Math.max(first - 2, 0);
221+
const overscanCountEnd = Math.min(last + 2, dataLargeWithPosition.length);
222+
setData(dataLargeWithPosition.slice(overscanCountStart, overscanCountEnd));
223+
};
224+
return (
225+
<Table
226+
{...props}
227+
// headerRow + 8 visible rows:
228+
// 9 * 44px = 396px (content-density: Cozy)
229+
// 9 * 32px = 288px (content-density: Compact)
230+
style={{ height: '396px' }}
231+
headerRow={
232+
<TableHeaderRow sticky>
233+
<TableHeaderCell>Name</TableHeaderCell>
234+
<TableHeaderCell>Age</TableHeaderCell>
235+
<TableHeaderCell>Friend Name</TableHeaderCell>
236+
<TableHeaderCell>Friend Age</TableHeaderCell>
237+
</TableHeaderRow>
238+
}
239+
features={<TableVirtualizer rowCount={500} rowHeight={44} onRangeChange={handleRangeChange} />}
240+
>
241+
{data.map((row) => (
242+
<TableRow key={row.position} position={row.position}>
243+
<TableCell>
244+
<span>{row.name}</span>
245+
</TableCell>
246+
<TableCell>
247+
<span>{row.age}</span>
248+
</TableCell>
249+
<TableCell>
250+
<span>{row.friend.name}</span>
251+
</TableCell>
252+
<TableCell>
253+
<span>{row.friend.age}</span>
254+
</TableCell>
255+
</TableRow>
256+
))}
257+
</Table>
258+
);
259+
}
260+
```
261+
262+
</details>
263+
196264
<Markdown>{SubcomponentsSection}</Markdown>
197265

198266
## TableHeaderRow

packages/main/src/webComponents/Table/Table.stories.tsx

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
import dataLarge from '@sb/mockData/Friends500.json';
12
import type { Meta, StoryObj } from '@storybook/react';
23
import TableGrowingMode from '@ui5/webcomponents/dist/types/TableGrowingMode.js';
34
import TableSelectionMode from '@ui5/webcomponents/dist/types/TableSelectionMode.js';
5+
import type { TableVirtualizerPropTypes } from '@ui5/webcomponents-react';
46
import { SegmentedButton, SegmentedButtonItem } from '@ui5/webcomponents-react';
5-
import { useState } from 'react';
7+
import { useEffect, useState } from 'react';
68
import { TableCell } from '../TableCell/index.js';
79
import { TableGrowing } from '../TableGrowing/index.js';
810
import { TableHeaderCell } from '../TableHeaderCell/index.js';
911
import { TableHeaderRow } from '../TableHeaderRow/index.js';
1012
import { TableRow } from '../TableRow/index.js';
1113
import { TableSelection } from '../TableSelection/index.js';
14+
import { TableVirtualizer } from '../TableVirtualizer/index.js';
1215
import { Table } from './index.js';
1316

1417
const meta = {
@@ -182,3 +185,72 @@ export const WithSelection: Story = {
182185
);
183186
}
184187
};
188+
189+
const dataLargeWithPosition = dataLarge.map((item, index) => ({ ...item, position: index }));
190+
191+
export const VirtualizedTableRows: Story = {
192+
args: { className: 'tableHeightContentDensity' },
193+
render(args) {
194+
const [data, setData] = useState(dataLargeWithPosition.slice(0, 9));
195+
const [isCozy, setIsCozy] = useState(true);
196+
197+
const handleRangeChange: TableVirtualizerPropTypes['onRangeChange'] = (e) => {
198+
const { first, last } = e.detail;
199+
200+
// overscanCount = 2
201+
const overscanCountStart = Math.max(first - 2, 0);
202+
const overscanCountEnd = Math.min(last + 2, dataLargeWithPosition.length);
203+
setData(dataLargeWithPosition.slice(overscanCountStart, overscanCountEnd));
204+
};
205+
206+
// adjust row height according to content-density mode (only for demo purposes)
207+
useEffect(() => {
208+
const body = document.body;
209+
if (!body) return;
210+
211+
const observer = new MutationObserver((mutationsList) => {
212+
mutationsList.forEach((mutation) => {
213+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
214+
setIsCozy(!body.classList.contains('ui5-content-density-compact'));
215+
}
216+
});
217+
});
218+
observer.observe(body, { attributes: true, attributeFilter: ['class'] });
219+
return () => {
220+
observer.disconnect();
221+
};
222+
}, []);
223+
224+
return (
225+
<Table
226+
{...args}
227+
headerRow={
228+
<TableHeaderRow sticky>
229+
<TableHeaderCell>Name</TableHeaderCell>
230+
<TableHeaderCell>Age</TableHeaderCell>
231+
<TableHeaderCell>Friend Name</TableHeaderCell>
232+
<TableHeaderCell>Friend Age</TableHeaderCell>
233+
</TableHeaderRow>
234+
}
235+
features={<TableVirtualizer rowCount={500} rowHeight={isCozy ? 44 : 32} onRangeChange={handleRangeChange} />}
236+
>
237+
{data.map((row) => (
238+
<TableRow key={row.position} position={row.position}>
239+
<TableCell>
240+
<span>{row.name}</span>
241+
</TableCell>
242+
<TableCell>
243+
<span>{row.age}</span>
244+
</TableCell>
245+
<TableCell>
246+
<span>{row.friend.name}</span>
247+
</TableCell>
248+
<TableCell>
249+
<span>{row.friend.age}</span>
250+
</TableCell>
251+
</TableRow>
252+
))}
253+
</Table>
254+
);
255+
}
256+
};

0 commit comments

Comments
 (0)