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
135 changes: 134 additions & 1 deletion apps/vr-tests-react-components/src/stories/Table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,43 @@ import {
TableCellActions,
TableProps,
TableRowProps,
useTableColumnSizing_unstable,
useTableFeatures,
TableColumnDefinition,
createTableColumn,
} from '@fluentui/react-table';
import { Button } from '@fluentui/react-button';
import { storiesOf } from '@storybook/react';
import { Steps, StoryWright } from 'storywright';

const items = [
type FileCell = {
label: string;
icon: JSX.Element;
};

type LastUpdatedCell = {
label: string;
timestamp: number;
};

type LastUpdateCell = {
label: string;
icon: JSX.Element;
};

type AuthorCell = {
label: string;
status: PresenceBadgeStatus;
};

type Item = {
file: FileCell;
author: AuthorCell;
lastUpdated: LastUpdatedCell;
lastUpdate: LastUpdateCell;
};

const items: Item[] = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
Expand Down Expand Up @@ -74,6 +105,25 @@ const columns = [
{ columnKey: 'lastUpdate', label: 'Last update' },
];

const columnsDef: TableColumnDefinition<Item>[] = [
createTableColumn<Item>({
columnId: 'file',
renderHeaderCell: () => <>File</>,
}),
createTableColumn<Item>({
columnId: 'author',
renderHeaderCell: () => <>Author</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdated',
renderHeaderCell: () => <>Last updated</>,
}),
createTableColumn<Item>({
columnId: 'lastUpdate',
renderHeaderCell: () => <>Last update</>,
}),
];

interface SharedVrTestArgs {
noNativeElements: TableProps['noNativeElements'];
selectedRowAppearance?: TableRowProps['appearance'];
Expand Down Expand Up @@ -634,6 +684,85 @@ const Truncate: React.FC<SharedVrTestArgs & { truncate?: boolean }> = ({ noNativ
</Table>
);

const ResizableColumns: React.FC<SharedVrTestArgs & { scrollToEnd?: boolean }> = ({
noNativeElements,
scrollToEnd,
}) => {
const [columnSizingOptions] = React.useState({
file: {
idealWidth: 300,
minWidth: 300,
},
});

const { columnSizing_unstable: columnSizing, tableRef } = useTableFeatures(
{
columns: columnsDef,
items,
},
[
useTableColumnSizing_unstable({
columnSizingOptions,
}),
],
);
return (
<div style={{ width: '500px', overflow: 'auto', border: '1px dashed black' }}>
<Table noNativeElements={noNativeElements} ref={tableRef} {...columnSizing.getTableProps()}>
<TableHeader>
<TableRow>
{columns.map(column => (
<TableHeaderCell key={column.columnKey} {...columnSizing.getTableHeaderCellProps(column.columnKey)}>
{column.label}
</TableHeaderCell>
))}
</TableRow>
</TableHeader>
<TableBody>
{items.map((item, i) => (
<TableRow key={item.file.label} className={`row-${i}`}>
<TableCell {...columnSizing.getTableCellProps('file')}>
<TableCellLayout truncate media={item.file.icon}>
{item.file.label}
<TableCellActions>
<Button icon={<EditRegular />} appearance="subtle" />
<Button icon={<MoreHorizontalRegular />} appearance="subtle" />
</TableCellActions>
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing.getTableCellProps('author')}>
<TableCellLayout
truncate
media={
<Avatar name={item.author.label} badge={{ status: item.author.status as PresenceBadgeStatus }} />
}
>
{item.author.label}
</TableCellLayout>
</TableCell>
<TableCell {...columnSizing.getTableCellProps('lastUpdated')}>
<TableCellLayout truncate>{item.lastUpdated.label}</TableCellLayout>
</TableCell>
<TableCell {...columnSizing.getTableCellProps('lastUpdate')}>
<TableCellLayout truncate media={item.lastUpdate.icon}>
{item.lastUpdate.label}
</TableCellLayout>
</TableCell>
<div
ref={el => {
if (el && scrollToEnd) {
el.scrollIntoView();
}
}}
/>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
};

([true, false] as const).forEach(noNativeElements => {
const layoutName = noNativeElements ? 'flex' : 'table';
storiesOf(`Table layout ${layoutName} - cell actions`, module)
Expand Down Expand Up @@ -760,4 +889,8 @@ const Truncate: React.FC<SharedVrTestArgs & { truncate?: boolean }> = ({ noNativ
.addStory('default (disabled)', () => <Truncate noNativeElements={noNativeElements} />)
.addStory('false', () => <Truncate noNativeElements={noNativeElements} truncate={false} />)
.addStory('true', () => <Truncate noNativeElements={noNativeElements} truncate={true} />);

storiesOf(`Table layout ${layoutName} - resizable columns`, module)
.addStory('default', () => <ResizableColumns noNativeElements={noNativeElements} />)
.addStory('end', () => <ResizableColumns noNativeElements={noNativeElements} scrollToEnd />);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: Improve visuals when Table/DataGrid overflows it's parent",
"packageName": "@fluentui/react-table",
"email": "jirivyhnalek@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const useDataGrid_unstable = (props: DataGridProps, ref: React.Ref<HTMLEl
...(focusMode === 'composite' && compositeTabsterAttribute),
...props,
onKeyDown,
...(resizableColumns ? tableState.columnSizing_unstable.getTableProps(props) : {}),
},
useMergedRefs(ref, tableState.tableRef, innerRef),
);
Expand Down
4 changes: 3 additions & 1 deletion packages/react-components/react-table/src/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { SortDirection } from '../components/Table/Table.types';
import { SortDirection, TableProps } from '../components/Table/Table.types';
import { TableHeaderCellProps } from '../components/TableHeaderCell/TableHeaderCell.types';
import { SelectionMode } from '@fluentui/react-utilities';

Expand Down Expand Up @@ -175,6 +175,7 @@ export interface ColumnWidthState {
padding: number;
}

export type ColumnSizingTableProps = Partial<TableProps>;
export type ColumnSizingTableHeaderCellProps = Pick<TableHeaderCellProps, 'style' | 'aside'>;
export type ColumnSizingTableCellProps = Pick<TableHeaderCellProps, 'style'>;

Expand All @@ -184,6 +185,7 @@ export interface TableColumnSizingState {
getOnMouseDown: (columnId: TableColumnId) => (e: React.MouseEvent | React.TouchEvent) => void;
setColumnWidth: (columnId: TableColumnId, newSize: number) => void;
getColumnWidths: () => ColumnWidthState[];
getTableProps: (props?: Partial<TableProps>) => ColumnSizingTableProps;
getTableHeaderCellProps: (columnId: TableColumnId) => ColumnSizingTableHeaderCellProps;
getTableCellProps: (columnId: TableColumnId) => ColumnSizingTableCellProps;
enableKeyboardMode: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('useTableColumnSizing', () => {
"getOnMouseDown": [MockFunction],
"getTableCellProps": [Function],
"getTableHeaderCellProps": [Function],
"getTableProps": [Function],
"setColumnWidth": [Function],
}
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const defaultColumnSizingState: TableColumnSizingState = {
getColumnWidths: () => [],
getOnMouseDown: () => () => null,
setColumnWidth: () => null,
getTableProps: () => ({}),
getTableHeaderCellProps: () => ({ style: {}, columnId: '' }),
getTableCellProps: () => ({ style: {}, columnId: '' }),
enableKeyboardMode: () => () => null,
Expand Down Expand Up @@ -75,16 +76,27 @@ function useTableColumnSizingState<TItem>(
setColumnWidth: (columnId: TableColumnId, w: number) =>
columnResizeState.setColumnWidth(undefined, { columnId, width: w }),
getColumnWidths: columnResizeState.getColumns,
getTableProps: (props = {}) => {
return {
...props,
style: {
minWidth: 'fit-content',
...(props.style || {}),
},
};
},
getTableHeaderCellProps: (columnId: TableColumnId) => {
const col = columnResizeState.getColumnById(columnId);
const isLastColumn = columns[columns.length - 1]?.columnId === columnId;

const aside = (
const aside = isLastColumn ? null : (
<TableResizeHandle
onMouseDown={mouseHandler.getOnMouseDown(columnId)}
onTouchStart={mouseHandler.getOnMouseDown(columnId)}
{...getKeyboardResizingProps(columnId, col?.width || 0)}
/>
);

return col
? {
style: getColumnStyles(col),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,47 +178,49 @@ export const ResizableColumns = () => {
const refMap = React.useRef<Record<string, HTMLElement | null>>({});

return (
<DataGrid
items={items}
columns={columns}
sortable
getRowId={item => item.file.label}
selectionMode="multiselect"
resizableColumns
columnSizingOptions={columnSizingOptions}
>
<DataGridHeader>
<DataGridRow selectionCell={{ 'aria-label': 'Select all rows' }}>
{({ renderHeaderCell, columnId }, dataGrid) =>
dataGrid.resizableColumns ? (
<Menu openOnContext>
<MenuTrigger>
<DataGridHeaderCell ref={el => (refMap.current[columnId] = el)}>
{renderHeaderCell()}
</DataGridHeaderCell>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={dataGrid.columnSizing_unstable.enableKeyboardMode(columnId)}>
Keyboard Column Resizing
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
) : (
<DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
)
}
</DataGridRow>
</DataGridHeader>
<DataGridBody<Item>>
{({ item, rowId }) => (
<DataGridRow<Item> key={rowId} selectionCell={{ 'aria-label': 'Select row' }}>
{({ renderCell }) => <DataGridCell>{renderCell(item)}</DataGridCell>}
<div style={{ overflowX: 'auto' }}>
<DataGrid
items={items}
columns={columns}
sortable
getRowId={item => item.file.label}
selectionMode="multiselect"
resizableColumns
columnSizingOptions={columnSizingOptions}
>
<DataGridHeader>
<DataGridRow selectionCell={{ 'aria-label': 'Select all rows' }}>
{({ renderHeaderCell, columnId }, dataGrid) =>
dataGrid.resizableColumns ? (
<Menu openOnContext>
<MenuTrigger>
<DataGridHeaderCell ref={el => (refMap.current[columnId] = el)}>
{renderHeaderCell()}
</DataGridHeaderCell>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={dataGrid.columnSizing_unstable.enableKeyboardMode(columnId)}>
Keyboard Column Resizing
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
) : (
<DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
)
}
</DataGridRow>
)}
</DataGridBody>
</DataGrid>
</DataGridHeader>
<DataGridBody<Item>>
{({ item, rowId }) => (
<DataGridRow<Item> key={rowId} selectionCell={{ 'aria-label': 'Select row' }}>
{({ renderCell }) => <DataGridCell>{renderCell(item)}</DataGridCell>}
</DataGridRow>
)}
</DataGridBody>
</DataGrid>
</div>
);
};

Expand Down
Loading