Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
129 changes: 123 additions & 6 deletions packages/@react-spectrum/table/docs/TableView.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default Layout;

import docs from 'docs:@react-spectrum/table';
import tableTypes from 'docs:@react-types/table/src/index.d.ts';
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
import {HeaderInfo, PropTable, PageDescription, VersionBadge} from '@react-spectrum/docs';
import {Keyboard} from '@react-spectrum/text';
import packageData from '@react-spectrum/table/package.json';

Expand Down Expand Up @@ -560,6 +560,118 @@ function AsyncSortTable() {
}
```

## Column Resizing <VersionBadge version="beta" style={{marginLeft: 4, verticalAlign: 'bottom'}} />

TableView supports resizable columns, allowing users to dynamically adjust the width of a column. To designate that a Column is resizable, provide it with the `allowsResizing` prop. This will render a draggable
resizer handle that becomes visible on hover. Keyboard, touch, and screen reader users can start resizing by interacting with the target column's header and selecting the "Resize column" option
from the dropdown.

### Width values
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the width configuration isn't really specific to column resizing. Wonder if people will miss it because it is a sub-heading?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep this section here and restore the old width values section we had down in visual options then

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, it was more of a question for everyone

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd worry about people missing how to define widths if it was under resizing only. Can we invert it and have resizing be a sub header?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, it would be good to preserve the old section and keep the resizing as the main header. I feel like most people who are looking for column resizing will look specifically for "Resizing" as a header and keeping the old section as will also keep the page layout familiar to old users (no confusion as to if that visual option has disappeared and old links will still work)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I would like width to be it's own heading/sub-heading and it could be referenced from the resizing section that seems ok


Columns support four different width props: `defaultWidth`, `width`, `minWidth`, and `maxWidth`. Each of these props accepts fixed values or percentages, with `width` and `defaultWidth` accepting [fractional](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout#the_fr_unit)
(`fr`) units as well. An initial, uncontrolled width can be provided to a Column using the `defaultWidth` prop. This allows the column width to freely shrink and grow in relation to other column widths. Alternatively, a controlled value can be provided
by the `width` prop. Providing a non-fractional value `width` to a Column will make it unaffected by other columns' widths on resize. `minWidth` and `maxWidth` allows you to restrict a Column's
width to a minimum and maximum value respectively. TableView prioritizes columns with defined widths and divides the remaining space evenly amongst the other columns.

The example below illustrates how each of the aforementioned column width properties affects their respective column's resize behavior.

```tsx example
<TableView
aria-label="TableView with resizable columns"
maxWidth={320}
height={200} >
<TableHeader>
{/*- begin highlight -*/}
<Column key="file" allowsResizing maxWidth={500}>File Name</Column>
<Column key="size" width={80}>Size</Column>
<Column key="date" allowsResizing minWidth={100}>Date Modified</Column>
{/*- end highlight -*/}
</TableHeader>
<TableBody>
<Row>
<Cell>2022-Roadmap-Proposal-Revision-012822-Copy(2)</Cell>
<Cell>214 KB</Cell>
<Cell>November 27, 2022 at 4:56PM</Cell>
</Row>
<Row>
<Cell>62259692_p0_master1200</Cell>
<Cell>120 KB</Cell>
<Cell>January 27, 2021 at 1:56AM</Cell>
</Row>
</TableBody>
</TableView>
```

### Resize events

TableView accepts an `onResize` prop which is triggered whenever a column resizer is moved by the user. This can be used in combination with the `width` prop to update a Column's width in a controlled fashion. TableView also accepts
an `onResizeEnd` props, which is triggered when the user finishes a column resize operation. Both events receive a Map object containing the widths of every column in the TableView.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
an `onResizeEnd` props, which is triggered when the user finishes a column resize operation. Both events receive a Map object containing the widths of every column in the TableView.
an `onResizeEnd` prop, which is triggered when the user finishes a column resize operation. Both events receive a Map object containing the widths of every column in the TableView.


The example below uses `onResize` to update each of the TableView's controlled column widths. It also saves the finalized column widths to `localStorage` in `onResizeEnd`, allowing the TableView's state to be preserved between
page loads and refreshes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should mention somewhere in here that a resize event locks all columns to the left of the resizing column to a pixel width, so it's not just one column that changes at a time

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, IMO this feels a bit specific and potentially confusing the reader. Do you feel that this might be a common question a user of column resize might ask?

```tsx example
let items = [
{id: '1', file: '2022-Roadmap-Proposal-Revision-012822-Copy(2)', size: '214 KB', date: 'November 27, 2022 at 4:56PM'},
{id: '2', file: '62259692_p0_master1200', size: '120 KB', date: 'January 27, 2021 at 1:56AM'}
];

let columnsData = [
{name: 'File Name', id: 'file', width: '1fr'},
{name: 'Size', id: 'size', width: 80},
{name: 'Date', id: 'date', width: 100}
];

function ResizableTable() {
/*- begin highlight -*/
let [columns, setColumns] = React.useState(() => {
let localStorageWidths = localStorage.getItem('RSPWidths');
if (localStorageWidths) {
let widths = JSON.parse(localStorageWidths);
return columnsData.map(col => ({...col, width: widths[col.id]}));
} else {
return columnsData;
}
});

let onResize = (widths) => {
setColumns(columns => columns.map(col => ({...col, width: widths.get(col.id)})));
};

let onResizeEnd = (widths) => {
localStorage.setItem('RSPWidths', JSON.stringify(Object.fromEntries(widths)));
};
/*- end highlight -*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*- end highlight -*/
/*- end highlight -*/

return (
<Flex direction="column">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the extra flex for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah derp, hold over from when the example had pre elements to show what widths were being provided to the event handlers, thanks for catching

<TableView
/*- begin highlight -*/
onResize={onResize}
onResizeEnd={onResizeEnd}
/*- end highlight -*/
aria-label="TableView with controlled, resizable columns saved in local storage"
maxWidth={320}
height={200} >
<TableHeader columns={columns}>
{(column) => {
const {name, id, width} = column;
return <Column allowsResizing key={id} width={width}>{name}</Column>;
}}
</TableHeader>
<TableBody items={items}>
{(item) => (
<Row key={item.id}>{(key) => <Cell>{item[key]}</Cell>}</Row>
)}
</TableBody>
</TableView>
</Flex>
);
}

<ResizableTable />
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These examples are too wide on mobile and mess up scrolling

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Also got rid of one of the columns since the table's columns were almost all the same due to the width restraint, I think the three columns should show how the widths work a bit better without immediately making the TableView scrollable right away



## Props

### TableView props
Expand Down Expand Up @@ -643,35 +755,40 @@ function AsyncSortTable() {
```

### Column widths
Columns support three different width props: `minWidth`, `width`, and `maxWidth`. Each of these props accepts fixed values or percentages. TableView prioritizes columns with defined widths and divides the remaining space evenly amongst the other columns.
See [above](#width-values) for additional details on the width props and the values they support.

```tsx example
<TableView aria-label="Example table for column widths">
<TableView aria-label="Example table for column widths" maxWidth={320}>
<TableHeader>
<Column maxWidth={300} align="start">Name</Column>
<Column width={80}>Type</Column>
<Column minWidth={100} align="end">Size</Column>
<Column defaultWidth="1fr" align="start">Name</Column>
<Column maxWidth={80}>Type</Column>
<Column width={80}>Size</Column>
<Column minWidth={100} align="end">Date Modified</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>2021406_Proposal</Cell>
<Cell>PDF</Cell>
<Cell>86 KB</Cell>
<Cell>April 12</Cell>
</Row>
<Row>
<Cell>Budget Template</Cell>
<Cell>XLS</Cell>
<Cell>120 KB</Cell>
<Cell>November 27</Cell>
</Row>
<Row>
<Cell>Onboarding</Cell>
<Cell>PPT</Cell>
<Cell>472 KB</Cell>
<Cell>January 7</Cell>
</Row>
<Row>
<Cell>Welcome</Cell>
<Cell>TXT</Cell>
<Cell>24 KB</Cell>
<Cell>February 11</Cell>
</Row>
</TableBody>
</TableView>
Expand Down
2 changes: 1 addition & 1 deletion packages/dev/docs/src/PropTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const GROUPS = {
'padding', 'paddingTop', 'paddingLeft', 'paddingRight', 'paddingBottom', 'paddingStart', 'paddingEnd', 'paddingX', 'paddingY'
],
Sizing: [
'width', 'minWidth', 'maxWidth', 'height', 'minHeight', 'maxHeight'
'width', 'minWidth', 'maxWidth', 'height', 'minHeight', 'maxHeight', 'defaultWidth'
],
Background: [
'background', 'backgroundColor', 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat',
Expand Down