diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 370e5ab44d7..02b8967c8b5 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -109,6 +109,14 @@ padding-block: 0.125rem !important; } + /*ui5-table: height for virtualized table according to content-density mode*/ + .tableHeightContentDensity { + height: 396px; + } + .ui5-content-density-compact .tableHeightContentDensity { + height: 288px; + } + /* TODO remove this workaround as soon as https://github.com/storybookjs/storybook/issues/20497 is fixed */ .docs-story > div > div[scale] { min-height: 20px; diff --git a/packages/main/src/webComponents/Table/Table.mdx b/packages/main/src/webComponents/Table/Table.mdx index a6b44db89da..f5d4a67c07f 100644 --- a/packages/main/src/webComponents/Table/Table.mdx +++ b/packages/main/src/webComponents/Table/Table.mdx @@ -113,7 +113,7 @@ const GrowingTable = () => { Show code ```jsx -function TableWithRowSelection() { +function TableWithRowSelection(props) { const [mode, setMode] = useState(TableSelectionMode.Multiple); return ( <> @@ -129,7 +129,7 @@ function TableWithRowSelection() { ))} @@ -193,6 +193,74 @@ function TableWithRowSelection() { +## Table with virtualized rows + +`Table` with virtualization feature (``): + + + +
+ + Show code + +```tsx +// enrich data with `position` (if not already available) +const dataLargeWithPosition = dataLarge.map((item, index) => ({ + ...item, + position: index +})); + +function VirtualizedTable(props) { + const [data, setData] = useState(dataLargeWithPosition.slice(0, 9)); + + const handleRangeChange: TableVirtualizerPropTypes['onRangeChange'] = (e) => { + const { first, last } = e.detail; + + // render two rows before and after the visible area of the table body container + const overscanCountStart = Math.max(first - 2, 0); + const overscanCountEnd = Math.min(last + 2, dataLargeWithPosition.length); + setData(dataLargeWithPosition.slice(overscanCountStart, overscanCountEnd)); + }; + return ( +
+ Name + Age + Friend Name + Friend Age + + } + features={} + > + {data.map((row) => ( + + + {row.name} + + + {row.age} + + + {row.friend.name} + + + {row.friend.age} + + + ))} +
+ ); +} +``` + + + {SubcomponentsSection} ## TableHeaderRow diff --git a/packages/main/src/webComponents/Table/Table.stories.tsx b/packages/main/src/webComponents/Table/Table.stories.tsx index 1053c58871f..5703e5aa020 100644 --- a/packages/main/src/webComponents/Table/Table.stories.tsx +++ b/packages/main/src/webComponents/Table/Table.stories.tsx @@ -1,14 +1,17 @@ +import dataLarge from '@sb/mockData/Friends500.json'; import type { Meta, StoryObj } from '@storybook/react'; import TableGrowingMode from '@ui5/webcomponents/dist/types/TableGrowingMode.js'; import TableSelectionMode from '@ui5/webcomponents/dist/types/TableSelectionMode.js'; +import type { TableVirtualizerPropTypes } from '@ui5/webcomponents-react'; import { SegmentedButton, SegmentedButtonItem } from '@ui5/webcomponents-react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { TableCell } from '../TableCell/index.js'; import { TableGrowing } from '../TableGrowing/index.js'; import { TableHeaderCell } from '../TableHeaderCell/index.js'; import { TableHeaderRow } from '../TableHeaderRow/index.js'; import { TableRow } from '../TableRow/index.js'; import { TableSelection } from '../TableSelection/index.js'; +import { TableVirtualizer } from '../TableVirtualizer/index.js'; import { Table } from './index.js'; const meta = { @@ -182,3 +185,72 @@ export const WithSelection: Story = { ); } }; + +const dataLargeWithPosition = dataLarge.map((item, index) => ({ ...item, position: index })); + +export const VirtualizedTableRows: Story = { + args: { className: 'tableHeightContentDensity' }, + render(args) { + const [data, setData] = useState(dataLargeWithPosition.slice(0, 9)); + const [isCozy, setIsCozy] = useState(true); + + const handleRangeChange: TableVirtualizerPropTypes['onRangeChange'] = (e) => { + const { first, last } = e.detail; + + // overscanCount = 2 + const overscanCountStart = Math.max(first - 2, 0); + const overscanCountEnd = Math.min(last + 2, dataLargeWithPosition.length); + setData(dataLargeWithPosition.slice(overscanCountStart, overscanCountEnd)); + }; + + // adjust row height according to content-density mode (only for demo purposes) + useEffect(() => { + const body = document.body; + if (!body) return; + + const observer = new MutationObserver((mutationsList) => { + mutationsList.forEach((mutation) => { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + setIsCozy(!body.classList.contains('ui5-content-density-compact')); + } + }); + }); + observer.observe(body, { attributes: true, attributeFilter: ['class'] }); + return () => { + observer.disconnect(); + }; + }, []); + + return ( + + Name + Age + Friend Name + Friend Age + + } + features={} + > + {data.map((row) => ( + + + {row.name} + + + {row.age} + + + {row.friend.name} + + + {row.friend.age} + + + ))} +
+ ); + } +};