Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ad99460
feat(DataView): add support for resizable columns
kmcfaul Sep 19, 2025
e974b13
fix spread props & lint, shortcut useeffects for non-resize cols, moc…
kmcfaul Sep 19, 2025
89fef7d
resolve lint spacing
kmcfaul Sep 19, 2025
6455bb8
refactor props to single interface, add aria label to button, add shi…
kmcfaul Sep 22, 2025
ebb15c2
update keyboard interaction
kmcfaul Sep 23, 2025
b020101
remove preventDefault for non arrow keys
kmcfaul Sep 23, 2025
e21ec67
add useState to .md
kmcfaul Sep 23, 2025
37674b8
add other missing function
kmcfaul Sep 23, 2025
5a7b0e0
updated example to properly update srtext
kmcfaul Sep 23, 2025
b4e33cd
remove console log
kmcfaul Sep 23, 2025
88466f9
remove unused helper call in example
kmcfaul Sep 23, 2025
0d23398
move onResize for keyboard
kmcfaul Sep 23, 2025
e4bbb26
round srtext width
kmcfaul Sep 23, 2025
4025dfa
update srtext to be in state
kmcfaul Sep 23, 2025
7f27aaa
clean up width logic and srtext
kmcfaul Sep 23, 2025
1ddd1e4
update sr prop name
kmcfaul Sep 23, 2025
3f51e93
update some prop desc, remove default aria label and add warning if n…
kmcfaul Sep 26, 2025
c4c313d
update example docs
kmcfaul Sep 26, 2025
9c1e385
update prop desc for resizableProps
kmcfaul Sep 26, 2025
f89d922
chore: style resize button
mcoker Sep 26, 2025
b4507af
fix callback width returning 0, add id to callback, add aria label to…
kmcfaul Sep 26, 2025
41f26d2
fix lint
kmcfaul Sep 26, 2025
f4a5d2e
remove resize from sort column
kmcfaul Sep 26, 2025
ea9f17b
fix typo in docs
kmcfaul Sep 26, 2025
9c0eb6b
chore: use grabbing cursor on resize
mcoker Sep 29, 2025
df5499f
update to main prerelease versions, add back resize to sort column in…
kmcfaul Sep 29, 2025
9117942
remove span
kmcfaul Sep 29, 2025
facc679
update snap from pulling in new versions
kmcfaul Sep 29, 2025
6f2e993
disabled array-bracket-spacing rule to match prettier config, fix lin…
kmcfaul Sep 29, 2025
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
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@typescript-eslint/unified-signatures": "error",
"@typescript-eslint/no-var-requires": "off",
"arrow-body-style": "error",
"array-bracket-spacing": ["error", "always"],
"array-bracket-spacing": "off",
"camelcase": [
"error",
{
Expand Down
11 changes: 9 additions & 2 deletions config/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ global.MutationObserver = class {
observe(element, initObject) {}
};

global.IntersectionObserver = class {
constructor(callback, options) {}
disconnect() {}
observe(element) {}
unobserve(element) {}
};

jest.mock('react', () => ({
...jest.requireActual('react'),
useLayoutEffect: jest.requireActual('react').useEffect,
useLayoutEffect: jest.requireActual('react').useEffect
}));
Element.prototype.scrollTo = () => {};
Element.prototype.scrollTo = () => {};
317 changes: 232 additions & 85 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions packages/module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
},
"dependencies": {
"@patternfly/react-component-groups": "^6.1.0",
"@patternfly/react-core": "^6.0.0",
"@patternfly/react-icons": "^6.0.0",
"@patternfly/react-table": "^6.0.0",
"@patternfly/react-core": "6.4.0-prerelease.1",
"@patternfly/react-icons": "6.4.0-prerelease.1",
"@patternfly/react-table": "6.4.0-prerelease.2",
"clsx": "^2.1.1",
"react-jss": "^10.10.0"
},
Expand All @@ -44,8 +44,8 @@
},
"devDependencies": {
"@patternfly/documentation-framework": "^6.5.20",
"@patternfly/patternfly": "^6.0.0",
"@patternfly/react-code-editor": "^6.0.0",
"@patternfly/patternfly": "6.4.0-prerelease.1",
"@patternfly/react-code-editor": "6.4.0-prerelease.1",
"@patternfly/patternfly-a11y": "^5.1.0",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { FunctionComponent } from 'react';
import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { Button } from '@patternfly/react-core';
import { ActionsColumn } from '@patternfly/react-table';

interface Repository {
id: number;
name: string;
branches: string | null;
prs: string | null;
workspaces: string;
lastCommit: string;
}

const repositories: Repository[] = [
{
id: 1,
name: 'Repository one',
branches: 'Branch one',
prs: 'Pull request one',
workspaces: 'Workspace one',
lastCommit: 'Timestamp one'
},
{
id: 2,
name: 'Repository two',
branches: 'Branch two',
prs: 'Pull request two',
workspaces: 'Workspace two',
lastCommit: 'Timestamp two'
},
{
id: 3,
name: 'Repository three',
branches: 'Branch three',
prs: 'Pull request three',
workspaces: 'Workspace three',
lastCommit: 'Timestamp three'
},
{
id: 4,
name: 'Repository four',
branches: 'Branch four',
prs: 'Pull request four',
workspaces: 'Workspace four',
lastCommit: 'Timestamp four'
},
{
id: 5,
name: 'Repository five',
branches: 'Branch five',
prs: 'Pull request five',
workspaces: 'Workspace five',
lastCommit: 'Timestamp five'
},
{
id: 6,
name: 'Repository six',
branches: 'Branch six',
prs: 'Pull request six',
workspaces: 'Workspace six',
lastCommit: 'Timestamp six'
}
];

const rowActions = [
{
title: 'Some action',
onClick: () => console.log('clicked on Some action') // eslint-disable-line no-console
},
{
title: <div>Another action</div>,
onClick: () => console.log('clicked on Another action') // eslint-disable-line no-console
},
{
isSeparator: true
},
{
title: 'Third action',
onClick: () => console.log('clicked on Third action') // eslint-disable-line no-console
}
];

// you can also pass props to Tr by returning { row: DataViewTd[], props: TrProps } }
const rows: DataViewTr[] = repositories.map(({ id, name, branches, prs, workspaces, lastCommit }) => [
{ id, cell: workspaces, props: { favorites: { isFavorited: true } } },
{
cell: (
<Button href="#" variant="link" isInline>
{name}
</Button>
)
},
branches,
prs,
workspaces,
lastCommit,
{ cell: <ActionsColumn items={rowActions} />, props: { isActionCell: true } }
]);

const ouiaId = 'TableExample';

export const ResizableColumnsExample: FunctionComponent = () => {
const onResize = (
_e: React.MouseEvent | MouseEvent | React.KeyboardEvent | KeyboardEvent | TouchEvent,
id: string | number | undefined,
width: number
) => {
// eslint-disable-next-line no-console
console.log(`resized column id: ${id} width to: ${width.toFixed(0)}px`);
};

const columns: DataViewTh[] = [
null,
'Repositories',
{
cell: 'Branches',
resizableProps: {
isResizable: true,
onResize,
resizeButtonAriaLabel: 'Resize repositories column'
},
props: { id: 'repositories' }
},
{
cell: 'Pull requests',
resizableProps: {
isResizable: true,
onResize,
resizeButtonAriaLabel: 'Resize pull requests column'
},
props: { info: { tooltip: 'More information' }, id: 'pull-requests' }
},
{
cell: 'This is a really long title',
resizableProps: {
isResizable: true,
onResize,
resizeButtonAriaLabel: 'Resize this is a really long title column'
},
props: { info: { tooltip: 'More information' }, id: 'this-is-a-really-long-title' }
},
{
cell: 'Last commit',
resizableProps: {
isResizable: true,
onResize,
resizeButtonAriaLabel: 'Resize last commit column'
},
props: { sort: { sortBy: {}, columnIndex: 4 }, id: 'last-commit' }
}
];

return <DataViewTable isResizable aria-label="Repositories table" ouiaId={ouiaId} columns={columns} rows={rows} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ source: react
# If you use typescript, the name of the interface to display props for
# These are found through the sourceProps function provided in patternfly-docs.source.js
sortValue: 3
propComponents: ['DataViewTableBasic', 'DataViewTableTree', 'DataViewTrTree', 'DataViewTrObject']
propComponents:
[
'DataViewTableBasic',
'DataViewTableTree',
'DataViewTrTree',
'DataViewTrObject',
'DataViewTh',
'DataViewThResizableProps'
]
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
---
import { FunctionComponent, useMemo } from 'react';

import { FunctionComponent, useMemo, useState } from 'react';
import { BrowserRouter, useSearchParams } from 'react-router-dom';
import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter } from '@patternfly/react-core';
import { CubesIcon, FolderIcon, FolderOpenIcon, LeafIcon, ExclamationCircleIcon } from '@patternfly/react-icons';
Expand All @@ -28,7 +37,9 @@ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynami
The **data view table** component renders your data into columns and rows within a [PatternFly table](/components/table) component. You can easily customize and configure the table with these additional [data view components and props](/extensions/data-view/table#props).

## Configuring rows and columns

To define rows and columns for your table, use these props:

- `columns`: Defines the column heads of the table. Each item in the array can be a `ReactNode` for simple heads, or an object with the following properties:
- `cell`: Content to display in the column head.
- `props` (optional): (`ThProps`) to pass to the `<Th>` component, such as `width`, `sort`, and other table head cell properties.
Expand All @@ -42,20 +53,38 @@ It is also possible to disable row selection using the `isSelectDisabled` functi
If you want to have all expandable nodes open on initial load pass the `expandAll` prop to the DataViewTable component

### Table example

```js file="./DataViewTableExample.tsx"

```

### Resizable columns

To allow a column to resize, add `isResizable` to the `DataViewTable` element, and pass `resizableProps` to each applicable header cell. The `resizableProps` object consists of the following fields:

- `isResizable` - indicates that the column is resizable
- `resizeButtonAriaLabel` - an accessible name for the resizable column's resize button. This must be passed in if the column is resizable.
- `onResize` - a callback that will return the source event and the new width of the column
- `width` - a default width value for a column
- `minWidth` - the minimum width a column may shrink to
- `increment` - how many pixels the column will move left or right for keyboard navigation
- `shiftIncrement` - how many pixels the column will move left or right while shift is held for keyboard navigation
- `screenReaderText` - text that will be announced when a column is resized

```js file="./DataViewTableResizableColumnsExample.tsx"

```

## Tree table

A tree table includes expandable rows and custom icons for leaf and parent nodes.
A tree table includes expandable rows and custom icons for leaf and parent nodes.
To enable a tree table, pass the `isTreeTable` flag to the `<DataViewTable>` component.


Tree table rows have to be defined with following keys:
- `row`: Defines the content for each cell in the row.
- `id`: Unique identifier for the row that's used for matching selected items.
- `children` (optional): Defines the children rows.

- `row`: Defines the content for each cell in the row.
- `id`: Unique identifier for the row that's used for matching selected items.
- `children` (optional): Defines the children rows.

To update a row's icon to reflect its expansion state, pass `collapsedIcon`, `expandedIcon`, and `leafIcon` to `<DataViewTable>`.

Expand All @@ -68,17 +97,21 @@ To disable row selection, pass the `isSelectDisabled` function to `selection` pr
```

## Sorting

The following example demonstrates how to enable sorting functionality within a data view. This implementation supports dynamic sorting by column and persists the sort state in the page's URL via [React Router](https://reactrouter.com/).

### Sorting example

```js file="./SortingExample.tsx"

```

### Sorting state

The `useDataViewSort` hook manages the sorting state of a data view and provides an easy way to handle sorting logic, such as synchronization with URL parameters and the definition of default sorting behavior.

**Initial values:**

- `initialSort` object to set default `sortBy` and `direction` values:
- `sortBy`: Key of the initial column to sort.
- `direction`: Default sorting direction (`asc` or `desc`).
Expand All @@ -88,44 +121,47 @@ The `useDataViewSort` hook manages the sorting state of a data view and provides
- Customizable parameter names for the URL:
- `sortByParam`: Name of the URL parameter for the column key.
- `directionParam`: Name of the URL parameter for the sorting direction.
The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.
The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.

**Return values:**

- `sortBy`: Key of the column currently being sorted.
- `direction`: Current sorting direction (`asc` or `desc`).
- `onSort`: Function to handle sorting changes programmatically or via user interaction.

## States

The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.
The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.

### Empty
When there is no data to render in the data view, you can instead display an empty state.

You can create your empty state by passing a [PatternFly empty state](/components/empty-state) to the `empty` key of `headStates` or `bodyStates`.
When there is no data to render in the data view, you can instead display an empty state.

You can create your empty state by passing a [PatternFly empty state](/components/empty-state) to the `empty` key of `headStates` or `bodyStates`.

```js file="./DataViewTableEmptyExample.tsx"

```

### Error

When there is a data connection or retrieval error, you can display an error state.

The error state will be displayed when the data view `activeState` value is `error`.

You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.
You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.

```js file="./DataViewTableErrorExample.tsx"

```

### Loading

To indicate that data is loading, you can display a loading state.

The loading state will be displayed when the data view `activeState` value is `loading`.

You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.

You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.

```js file="./DataViewTableLoadingExample.tsx"

Expand Down
4 changes: 2 additions & 2 deletions packages/module/patternfly-docs/generated/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ module.exports = {
'/extensions/data-view/table/react': {
id: "Table",
title: "Data view table",
toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
examples: ["Table example","Tree table example","Sorting example","Empty","Error","Loading"],
toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"},{"text":"Resizable columns"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
examples: ["Table example","Resizable columns","Tree table example","Sorting example","Empty","Error","Loading"],
section: "extensions",
subsection: "Data view",
source: "react",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ exports[`DataViewCheckboxFilter component should render correctly 1`] = `
class="pf-v6-c-label__actions"
>
<button
aria-disabled="false"
aria-label="Close Workspace one"
class="pf-v6-c-button pf-m-plain pf-m-no-padding"
data-ouia-component-id="OUIA-Generated-Button-plain-1"
Expand Down Expand Up @@ -176,7 +175,6 @@ exports[`DataViewCheckboxFilter component should render correctly 1`] = `
class="pf-v6-c-toolbar__item"
>
<button
aria-disabled="false"
class="pf-v6-c-button pf-m-link pf-m-inline"
data-ouia-component-id="DataViewToolbar-clear-all-filters"
data-ouia-component-type="PF6/Button"
Expand Down
Loading
Loading