@patternfly/react-data-view 6.4.0 → 6.5.0-prerelease.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.d.ts +2 -0
- package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.js +3 -2
- package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.test.d.ts +1 -1
- package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.test.js +8 -2
- package/dist/cjs/DataViewTable/DataViewTable.d.ts +2 -1
- package/dist/cjs/DataViewTableBasic/DataViewTableBasic.d.ts +11 -0
- package/dist/cjs/DataViewTableBasic/DataViewTableBasic.js +46 -6
- package/dist/cjs/DataViewTableBasic/DataViewTableBasic.test.js +47 -9
- package/dist/cjs/DataViewTextFilter/DataViewTextFilter.d.ts +4 -0
- package/dist/cjs/DataViewTextFilter/DataViewTextFilter.js +31 -2
- package/dist/cjs/DataViewTextFilter/DataViewTextFilter.test.d.ts +1 -1
- package/dist/cjs/DataViewTextFilter/DataViewTextFilter.test.js +88 -0
- package/dist/cjs/DataViewTh/DataViewTh.d.ts +4 -4
- package/dist/cjs/DataViewTh/DataViewTh.js +8 -1
- package/dist/cjs/DataViewTh/index.d.ts +2 -0
- package/dist/cjs/DataViewTh/index.js +23 -0
- package/dist/cjs/DataViewToolbar/DataViewToolbar.js +13 -1
- package/dist/cjs/DataViewTreeFilter/DataViewTreeFilter.d.ts +28 -0
- package/dist/cjs/DataViewTreeFilter/DataViewTreeFilter.js +230 -0
- package/dist/cjs/DataViewTreeFilter/DataViewTreeFilter.test.d.ts +1 -0
- package/dist/cjs/DataViewTreeFilter/DataViewTreeFilter.test.js +176 -0
- package/dist/cjs/DataViewTreeFilter/index.d.ts +2 -0
- package/dist/cjs/DataViewTreeFilter/index.js +23 -0
- package/dist/cjs/Hooks/selection.d.ts +8 -8
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.js +10 -1
- package/dist/dynamic/DataViewTh/package.json +1 -0
- package/dist/dynamic/DataViewTreeFilter/package.json +1 -0
- package/dist/dynamic-modules.json +62 -0
- package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.d.ts +2 -0
- package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.js +3 -2
- package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.test.d.ts +1 -1
- package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.test.js +9 -3
- package/dist/esm/DataViewTable/DataViewTable.d.ts +2 -1
- package/dist/esm/DataViewTableBasic/DataViewTableBasic.d.ts +11 -0
- package/dist/esm/DataViewTableBasic/DataViewTableBasic.js +48 -8
- package/dist/esm/DataViewTableBasic/DataViewTableBasic.test.js +45 -10
- package/dist/esm/DataViewTextFilter/DataViewTextFilter.d.ts +4 -0
- package/dist/esm/DataViewTextFilter/DataViewTextFilter.js +31 -2
- package/dist/esm/DataViewTextFilter/DataViewTextFilter.test.d.ts +1 -1
- package/dist/esm/DataViewTextFilter/DataViewTextFilter.test.js +90 -2
- package/dist/esm/DataViewTh/DataViewTh.d.ts +4 -4
- package/dist/esm/DataViewTh/DataViewTh.js +8 -1
- package/dist/esm/DataViewTh/index.d.ts +2 -0
- package/dist/esm/DataViewTh/index.js +2 -0
- package/dist/esm/DataViewToolbar/DataViewToolbar.js +13 -1
- package/dist/esm/DataViewTreeFilter/DataViewTreeFilter.d.ts +28 -0
- package/dist/esm/DataViewTreeFilter/DataViewTreeFilter.js +226 -0
- package/dist/esm/DataViewTreeFilter/DataViewTreeFilter.test.d.ts +1 -0
- package/dist/esm/DataViewTreeFilter/DataViewTreeFilter.test.js +171 -0
- package/dist/esm/DataViewTreeFilter/index.d.ts +2 -0
- package/dist/esm/DataViewTreeFilter/index.js +2 -0
- package/dist/esm/Hooks/selection.d.ts +8 -8
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/generate-fed-package-json.js +18 -0
- package/generate-index.js +2 -2
- package/package.json +6 -6
- package/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md +10 -4
- package/patternfly-docs/content/extensions/data-view/examples/DataView/PredefinedLayoutFullExample.tsx +2 -1
- package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableExpandableExample.tsx +108 -0
- package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableInteractiveExample.tsx +148 -0
- package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableStickyExample.tsx +90 -0
- package/patternfly-docs/content/extensions/data-view/examples/Table/Table.md +63 -2
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/FiltersExample.tsx +3 -2
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/PaginationExample.tsx +1 -1
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md +9 -2
- package/patternfly-docs/content/extensions/data-view/examples/Toolbar/TreeFilterExample.tsx +248 -0
- package/patternfly-docs/patternfly-docs.config.js +4 -1
- package/src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx +16 -7
- package/src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx +5 -1
- package/src/DataViewTable/DataViewTable.tsx +3 -1
- package/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap +7 -7
- package/src/DataViewTableBasic/DataViewTableBasic.test.tsx +54 -12
- package/src/DataViewTableBasic/DataViewTableBasic.tsx +101 -10
- package/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap +10 -10
- package/src/DataViewTextFilter/DataViewTextFilter.test.tsx +140 -1
- package/src/DataViewTextFilter/DataViewTextFilter.tsx +63 -22
- package/src/DataViewTh/DataViewTh.tsx +15 -7
- package/src/DataViewTh/index.ts +2 -0
- package/src/DataViewToolbar/DataViewToolbar.tsx +17 -2
- package/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap +288 -280
- package/src/DataViewTreeFilter/DataViewTreeFilter.test.tsx +233 -0
- package/src/DataViewTreeFilter/DataViewTreeFilter.tsx +365 -0
- package/src/DataViewTreeFilter/__snapshots__/DataViewTreeFilter.test.tsx.snap +199 -0
- package/src/DataViewTreeFilter/index.ts +2 -0
- package/src/Hooks/selection.ts +8 -8
- package/src/index.ts +9 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import DataViewCheckboxFilter from './DataViewCheckboxFilter';
|
|
4
4
|
import DataViewToolbar from '../DataViewToolbar';
|
|
5
|
+
import '@testing-library/jest-dom';
|
|
5
6
|
describe('DataViewCheckboxFilter component', () => {
|
|
6
7
|
const defaultProps = {
|
|
7
8
|
filterId: 'test-checkbox-filter',
|
|
@@ -10,11 +11,16 @@ describe('DataViewCheckboxFilter component', () => {
|
|
|
10
11
|
options: [
|
|
11
12
|
{ label: 'Workspace one', value: 'workspace-one' },
|
|
12
13
|
{ label: 'Workspace two', value: 'workspace-two' },
|
|
13
|
-
{ label: 'Workspace three', value: 'workspace-three' }
|
|
14
|
-
]
|
|
14
|
+
{ label: 'Workspace three', value: 'workspace-three' }
|
|
15
|
+
]
|
|
15
16
|
};
|
|
16
17
|
it('should render correctly', () => {
|
|
17
18
|
const { container } = render(_jsx(DataViewToolbar, { filters: _jsx(DataViewCheckboxFilter, Object.assign({}, defaultProps)) }));
|
|
18
19
|
expect(container).toMatchSnapshot();
|
|
19
20
|
});
|
|
21
|
+
it('should use chipTitle for the filter chip category when provided', () => {
|
|
22
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewCheckboxFilter, Object.assign({}, defaultProps, { chipTitle: "Short name" })) }));
|
|
23
|
+
expect(screen.getByText('Short name')).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByText('Test Checkbox Filter')).toBeInTheDocument();
|
|
25
|
+
});
|
|
20
26
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { FC, ReactNode } from 'react';
|
|
2
2
|
import { TdProps, ThProps, TrProps } from '@patternfly/react-table';
|
|
3
3
|
import { DataViewTableTreeProps } from '../DataViewTableTree';
|
|
4
|
-
import { DataViewTableBasicProps } from '../DataViewTableBasic';
|
|
4
|
+
import { DataViewTableBasicProps, ExpandableContent } from '../DataViewTableBasic';
|
|
5
5
|
import { DataViewThResizableProps } from '../DataViewTh/DataViewTh';
|
|
6
|
+
export type { ExpandableContent };
|
|
6
7
|
export type DataViewTh = ReactNode | {
|
|
7
8
|
/** Table head cell node */
|
|
8
9
|
cell: ReactNode;
|
|
@@ -2,12 +2,19 @@ import { FC } from 'react';
|
|
|
2
2
|
import { TableProps } from '@patternfly/react-table';
|
|
3
3
|
import { DataViewTh, DataViewTr } from '../DataViewTable';
|
|
4
4
|
import { DataViewState } from '../DataView/DataView';
|
|
5
|
+
export interface ExpandableContent {
|
|
6
|
+
rowId: number;
|
|
7
|
+
columnId: number;
|
|
8
|
+
content: React.ReactNode;
|
|
9
|
+
}
|
|
5
10
|
/** extends TableProps */
|
|
6
11
|
export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | 'rows'> {
|
|
7
12
|
/** Columns definition */
|
|
8
13
|
columns: DataViewTh[];
|
|
9
14
|
/** Current page rows */
|
|
10
15
|
rows: DataViewTr[];
|
|
16
|
+
/** Expanded rows content */
|
|
17
|
+
expandedRows?: ExpandableContent[];
|
|
11
18
|
/** Table head states to be displayed when active */
|
|
12
19
|
headStates?: Partial<Record<DataViewState | string, React.ReactNode>>;
|
|
13
20
|
/** Table body states to be displayed when active */
|
|
@@ -16,6 +23,10 @@ export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | '
|
|
|
16
23
|
ouiaId?: string;
|
|
17
24
|
/** @hide Indicates if the table is resizable */
|
|
18
25
|
hasResizableColumns?: boolean;
|
|
26
|
+
/** Toggles expandable */
|
|
27
|
+
isExpandable?: boolean;
|
|
28
|
+
/** Toggles sticky columns and header */
|
|
29
|
+
isSticky?: boolean;
|
|
19
30
|
}
|
|
20
31
|
export declare const DataViewTableBasic: FC<DataViewTableBasicProps>;
|
|
21
32
|
export default DataViewTableBasic;
|
|
@@ -10,20 +10,32 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useMemo } from 'react';
|
|
14
|
-
import { Table, Tbody, Td, Tr, } from '@patternfly/react-table';
|
|
13
|
+
import { useMemo, useState, useRef } from 'react';
|
|
14
|
+
import { ExpandableRowContent, InnerScrollContainer, OuterScrollContainer, Table, Tbody, Td, Tr, } from '@patternfly/react-table';
|
|
15
15
|
import { useInternalContext } from '../InternalContext';
|
|
16
16
|
import { DataViewTableHead } from '../DataViewTableHead';
|
|
17
17
|
import { isDataViewTdObject, isDataViewTrObject } from '../DataViewTable';
|
|
18
18
|
export const DataViewTableBasic = (_a) => {
|
|
19
|
-
var { columns, rows, ouiaId = 'DataViewTableBasic', headStates, bodyStates, hasResizableColumns } = _a, props = __rest(_a, ["columns", "rows", "ouiaId", "headStates", "bodyStates", "hasResizableColumns"]);
|
|
19
|
+
var { columns, rows, expandedRows, ouiaId = 'DataViewTableBasic', headStates, bodyStates, hasResizableColumns, isExpandable = false, isSticky = false } = _a, props = __rest(_a, ["columns", "rows", "expandedRows", "ouiaId", "headStates", "bodyStates", "hasResizableColumns", "isExpandable", "isSticky"]);
|
|
20
20
|
const { selection, activeState, isSelectable } = useInternalContext();
|
|
21
21
|
const { onSelect, isSelected, isSelectDisabled } = selection !== null && selection !== void 0 ? selection : {};
|
|
22
22
|
const activeHeadState = useMemo(() => activeState ? headStates === null || headStates === void 0 ? void 0 : headStates[activeState] : undefined, [activeState, headStates]);
|
|
23
23
|
const activeBodyState = useMemo(() => activeState ? bodyStates === null || bodyStates === void 0 ? void 0 : bodyStates[activeState] : undefined, [activeState, bodyStates]);
|
|
24
|
+
const [expandedRowsState, setExpandedRowsState] = useState({});
|
|
25
|
+
const [expandedColumnIndex, setExpandedColumnIndex] = useState({});
|
|
26
|
+
const tableRef = useRef(null);
|
|
27
|
+
const needsSeparateTbody = isExpandable;
|
|
24
28
|
const renderedRows = useMemo(() => rows.map((row, rowIndex) => {
|
|
25
29
|
const rowIsObject = isDataViewTrObject(row);
|
|
26
|
-
|
|
30
|
+
const isRowExpanded = expandedRowsState[rowIndex] || false;
|
|
31
|
+
const expandedColIndex = expandedColumnIndex[rowIndex];
|
|
32
|
+
// Get the first cell to extract the row ID
|
|
33
|
+
const rowData = rowIsObject ? row.row : row;
|
|
34
|
+
const firstCell = rowData[0];
|
|
35
|
+
const rowId = isDataViewTdObject(firstCell) ? firstCell.id : undefined;
|
|
36
|
+
// Find all expandable contents for this row
|
|
37
|
+
const rowExpandableContents = isExpandable ? expandedRows === null || expandedRows === void 0 ? void 0 : expandedRows.filter((content) => content.rowId === rowId) : [];
|
|
38
|
+
const rowContent = (_jsxs(Tr, Object.assign({ ouiaId: `${ouiaId}-tr-${rowIndex}` }, (rowIsObject && (row === null || row === void 0 ? void 0 : row.props)), { isContentExpanded: isRowExpanded, isControlRow: true, children: [isSelectable && (_jsx(Td, { select: {
|
|
27
39
|
rowIndex,
|
|
28
40
|
onSelect: (_event, isSelecting) => {
|
|
29
41
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(isSelecting, rowIsObject ? row : [row]);
|
|
@@ -33,9 +45,37 @@ export const DataViewTableBasic = (_a) => {
|
|
|
33
45
|
} }, `select-${rowIndex}`)), (rowIsObject ? row.row : row).map((cell, colIndex) => {
|
|
34
46
|
var _a;
|
|
35
47
|
const cellIsObject = isDataViewTdObject(cell);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
const cellExpandableContent = isExpandable ? expandedRows === null || expandedRows === void 0 ? void 0 : expandedRows.find((content) => content.rowId === rowId && content.columnId === colIndex) : undefined;
|
|
49
|
+
return (_jsx(Td, Object.assign({}, (cellIsObject && ((_a = cell === null || cell === void 0 ? void 0 : cell.props) !== null && _a !== void 0 ? _a : {})), (cellExpandableContent != null && {
|
|
50
|
+
compoundExpand: {
|
|
51
|
+
isExpanded: isRowExpanded && expandedColIndex === colIndex,
|
|
52
|
+
expandId: `expandable-${rowIndex}`,
|
|
53
|
+
onToggle: () => {
|
|
54
|
+
setExpandedRowsState(prev => {
|
|
55
|
+
const isSameColumn = expandedColIndex === colIndex;
|
|
56
|
+
const wasExpanded = prev[rowIndex];
|
|
57
|
+
return Object.assign(Object.assign({}, prev), { [rowIndex]: isSameColumn ? !wasExpanded : true });
|
|
58
|
+
});
|
|
59
|
+
setExpandedColumnIndex(prev => (Object.assign(Object.assign({}, prev), { [rowIndex]: colIndex })));
|
|
60
|
+
},
|
|
61
|
+
rowIndex,
|
|
62
|
+
columnIndex: colIndex
|
|
63
|
+
}
|
|
64
|
+
}), { "data-ouia-component-id": `${ouiaId}-td-${rowIndex}-${colIndex}`, children: cellIsObject ? cell.cell : cell }), colIndex));
|
|
65
|
+
})] }), needsSeparateTbody ? undefined : rowIndex));
|
|
66
|
+
if (needsSeparateTbody) {
|
|
67
|
+
return (_jsxs(Tbody, { isExpanded: isRowExpanded, children: [rowContent, rowExpandableContents === null || rowExpandableContents === void 0 ? void 0 : rowExpandableContents.map((expandableContent) => (_jsx(Tr, { isExpanded: isRowExpanded && expandedColIndex === expandableContent.columnId, children: _jsx(Td, { colSpan: rowData.length + (isSelectable ? 1 : 0), "data-expanded-column-index": expandableContent.columnId, children: _jsx(ExpandableRowContent, { children: expandableContent.content }) }) }, `expand-${rowIndex}-${expandableContent.columnId}`)))] }, rowIndex));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return rowContent;
|
|
71
|
+
}
|
|
72
|
+
}), [rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId, expandedRowsState, expandedColumnIndex, expandedRows, isExpandable, needsSeparateTbody]);
|
|
73
|
+
const bodyContent = activeBodyState || (needsSeparateTbody ? renderedRows : _jsx(Tbody, { children: renderedRows }));
|
|
74
|
+
if (isSticky) {
|
|
75
|
+
return (_jsx(OuterScrollContainer, { children: _jsx(InnerScrollContainer, { children: _jsxs(Table, Object.assign({ ref: tableRef, "aria-label": "Data table", ouiaId: ouiaId, isExpandable: isExpandable, hasAnimations: true }, props, { isStickyHeader: true, children: [activeHeadState || _jsx(DataViewTableHead, { columns: columns, ouiaId: ouiaId, hasResizableColumns: hasResizableColumns }), bodyContent] })) }) }));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return (_jsxs(Table, Object.assign({ ref: tableRef, "aria-label": "Data table", ouiaId: ouiaId, isExpandable: isExpandable, hasAnimations: true }, props, { children: [activeHeadState || _jsx(DataViewTableHead, { columns: columns, ouiaId: ouiaId, hasResizableColumns: hasResizableColumns }), bodyContent] })));
|
|
79
|
+
}
|
|
40
80
|
};
|
|
41
81
|
export default DataViewTableBasic;
|
|
@@ -1,19 +1,36 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
11
|
+
import { render, screen } from '@testing-library/react';
|
|
12
|
+
import userEvent from '@testing-library/user-event';
|
|
3
13
|
import { DataView } from '../DataView';
|
|
4
14
|
import { DataViewTableBasic } from './DataViewTableBasic';
|
|
5
15
|
const repositories = [
|
|
6
|
-
{ name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' },
|
|
7
|
-
{ name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' },
|
|
8
|
-
{ name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' },
|
|
9
|
-
{ name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' },
|
|
10
|
-
{ name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' },
|
|
11
|
-
{ name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' }
|
|
16
|
+
{ id: 1, name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' },
|
|
17
|
+
{ id: 2, name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' },
|
|
18
|
+
{ id: 3, name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' },
|
|
19
|
+
{ id: 4, name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' },
|
|
20
|
+
{ id: 5, name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' },
|
|
21
|
+
{ id: 6, name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' }
|
|
12
22
|
];
|
|
13
|
-
const rows = repositories.map(
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
const rows = repositories.map(({ id, name, branches, prs, workspaces, lastCommit }) => [
|
|
24
|
+
{ id, cell: name },
|
|
25
|
+
branches,
|
|
26
|
+
prs,
|
|
27
|
+
workspaces,
|
|
28
|
+
lastCommit
|
|
29
|
+
]);
|
|
16
30
|
const columns = ['Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit'];
|
|
31
|
+
const expandableContents = [
|
|
32
|
+
{ rowId: 1, columnId: 1, content: _jsx("div", { children: "Branch details for Repository one" }) },
|
|
33
|
+
];
|
|
17
34
|
const ouiaId = 'TableExample';
|
|
18
35
|
describe('DataViewTable component', () => {
|
|
19
36
|
test('should render correctly', () => {
|
|
@@ -32,4 +49,22 @@ describe('DataViewTable component', () => {
|
|
|
32
49
|
const { container } = render(_jsx(DataView, { activeState: "loading", children: _jsx(DataViewTableBasic, { "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { loading: "Data is loading" }, rows: [] }) }));
|
|
33
50
|
expect(container).toMatchSnapshot();
|
|
34
51
|
});
|
|
52
|
+
test('when isExpandable cell should be clickable and expandable', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
const user = userEvent.setup();
|
|
55
|
+
render(_jsx(DataViewTableBasic, { "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, rows: rows, isExpandable: true, expandedRows: expandableContents }));
|
|
56
|
+
// Initially, expandable content is rendered but should be hidden (not visible)
|
|
57
|
+
const initialBranchContent = screen.getByText('Branch details for Repository one');
|
|
58
|
+
expect((_a = initialBranchContent.closest('tr')) === null || _a === void 0 ? void 0 : _a.classList.contains('pf-m-expanded')).toBeFalsy();
|
|
59
|
+
// Find the first expandable button by ID
|
|
60
|
+
const branchExpandButton = document.getElementById('expandable-0-0-1');
|
|
61
|
+
expect(branchExpandButton).toBeTruthy();
|
|
62
|
+
// Verify the button is in the cell with "Branch one" text
|
|
63
|
+
expect((_b = branchExpandButton === null || branchExpandButton === void 0 ? void 0 : branchExpandButton.closest('td')) === null || _b === void 0 ? void 0 : _b.textContent).toContain('Branch one');
|
|
64
|
+
// Click the expand button for Branches column
|
|
65
|
+
yield user.click(branchExpandButton);
|
|
66
|
+
// After clicking, the expandable content should be visible
|
|
67
|
+
const branchContent = screen.getByText('Branch details for Repository one');
|
|
68
|
+
expect((_c = branchContent.closest('tr')) === null || _c === void 0 ? void 0 : _c.classList.contains('pf-m-expanded')).toBeTruthy();
|
|
69
|
+
}));
|
|
35
70
|
});
|
|
@@ -8,6 +8,8 @@ export interface DataViewTextFilterProps extends SearchInputProps {
|
|
|
8
8
|
value?: string;
|
|
9
9
|
/** Filter title displayed in the toolbar */
|
|
10
10
|
title: string;
|
|
11
|
+
/** Label for the applied filter chip / category; defaults to title */
|
|
12
|
+
chipTitle?: string;
|
|
11
13
|
/** Callback for when the input value changes */
|
|
12
14
|
onChange?: (event: React.FormEvent<HTMLInputElement> | undefined, value: string) => void;
|
|
13
15
|
/** Controls visibility of the filter in the toolbar */
|
|
@@ -16,6 +18,8 @@ export interface DataViewTextFilterProps extends SearchInputProps {
|
|
|
16
18
|
trimValue?: boolean;
|
|
17
19
|
/** Custom OUIA ID */
|
|
18
20
|
ouiaId?: string;
|
|
21
|
+
/** Enable keyboard shortcut (/) to focus the filter. Defaults to true. */
|
|
22
|
+
enableShortcut?: boolean;
|
|
19
23
|
}
|
|
20
24
|
export declare const DataViewTextFilter: FC<DataViewTextFilterProps>;
|
|
21
25
|
export default DataViewTextFilter;
|
|
@@ -10,9 +10,38 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { useEffect } from 'react';
|
|
13
14
|
import { SearchInput, ToolbarFilter } from '@patternfly/react-core';
|
|
14
15
|
export const DataViewTextFilter = (_a) => {
|
|
15
|
-
var { filterId, title, value = '', onChange, onClear = () => onChange === null || onChange === void 0 ? void 0 : onChange(undefined, ''), showToolbarItem, trimValue = true, ouiaId = 'DataViewTextFilter' } = _a, props = __rest(_a, ["filterId", "title", "value", "onChange", "onClear", "showToolbarItem", "trimValue", "ouiaId"]);
|
|
16
|
-
|
|
16
|
+
var { filterId, title, chipTitle, value = '', onChange, onClear = () => onChange === null || onChange === void 0 ? void 0 : onChange(undefined, ''), showToolbarItem, trimValue = true, ouiaId = 'DataViewTextFilter', enableShortcut = true } = _a, props = __rest(_a, ["filterId", "title", "chipTitle", "value", "onChange", "onClear", "showToolbarItem", "trimValue", "ouiaId", "enableShortcut"]);
|
|
17
|
+
const categoryName = chipTitle !== null && chipTitle !== void 0 ? chipTitle : title;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!enableShortcut) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const handleKeyDown = (event) => {
|
|
23
|
+
// Only handle "/" key when not typing in an input, textarea, or contenteditable element
|
|
24
|
+
if (event.key === '/' && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
25
|
+
const target = event.target;
|
|
26
|
+
const isInputElement = target.tagName === 'INPUT' ||
|
|
27
|
+
target.tagName === 'TEXTAREA' ||
|
|
28
|
+
target.isContentEditable;
|
|
29
|
+
// Only focus if the filter is visible and we're not already in an input field
|
|
30
|
+
if (showToolbarItem && !isInputElement) {
|
|
31
|
+
// Find the input element by its ID (searchInputId prop)
|
|
32
|
+
const inputElement = document.getElementById(filterId);
|
|
33
|
+
if (inputElement) {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
inputElement.focus();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
41
|
+
return () => {
|
|
42
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
43
|
+
};
|
|
44
|
+
}, [showToolbarItem, filterId, enableShortcut]);
|
|
45
|
+
return (_jsx(ToolbarFilter, { "data-ouia-component-id": ouiaId, labels: value.length > 0 ? [{ key: categoryName, node: value }] : [], deleteLabel: () => onChange === null || onChange === void 0 ? void 0 : onChange(undefined, ''), categoryName: categoryName, showToolbarItem: showToolbarItem, children: _jsx(SearchInput, Object.assign({ searchInputId: filterId, value: value, onChange: (e, inputValue) => onChange === null || onChange === void 0 ? void 0 : onChange(e, trimValue ? inputValue.trim() : inputValue), onClear: onClear, placeholder: `Filter by ${title}`, "aria-label": `${title !== null && title !== void 0 ? title : filterId} filter`, "data-ouia-component-id": `${ouiaId}-input` }, props)) }, ouiaId));
|
|
17
46
|
};
|
|
18
47
|
export default DataViewTextFilter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
3
4
|
import DataViewTextFilter from './DataViewTextFilter';
|
|
4
5
|
import DataViewToolbar from '../DataViewToolbar';
|
|
5
6
|
describe('DataViewTextFilter component', () => {
|
|
@@ -14,4 +15,91 @@ describe('DataViewTextFilter component', () => {
|
|
|
14
15
|
const { container } = render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps)) }));
|
|
15
16
|
expect(container).toMatchSnapshot();
|
|
16
17
|
});
|
|
18
|
+
it('should use chipTitle for the filter chip category when provided', () => {
|
|
19
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { chipTitle: "Short name" })) }));
|
|
20
|
+
expect(screen.getByText('Short name')).toBeInTheDocument();
|
|
21
|
+
expect(screen.getByPlaceholderText('Filter by Test Filter')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
it('should focus the search input when "/" key is pressed and filter is visible', () => {
|
|
24
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { showToolbarItem: true })) }));
|
|
25
|
+
const input = document.getElementById('test-filter');
|
|
26
|
+
expect(input).toBeInTheDocument();
|
|
27
|
+
// Simulate pressing "/" key by creating and dispatching a KeyboardEvent
|
|
28
|
+
const keyEvent = new KeyboardEvent('keydown', {
|
|
29
|
+
key: '/',
|
|
30
|
+
code: 'Slash',
|
|
31
|
+
bubbles: true,
|
|
32
|
+
cancelable: true,
|
|
33
|
+
});
|
|
34
|
+
window.dispatchEvent(keyEvent);
|
|
35
|
+
// Check that the input has focus
|
|
36
|
+
expect(document.activeElement).toBe(input);
|
|
37
|
+
});
|
|
38
|
+
it('should not focus the search input when "/" key is pressed if filter is not visible', () => {
|
|
39
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { showToolbarItem: false })) }));
|
|
40
|
+
const input = document.getElementById('test-filter');
|
|
41
|
+
// Simulate pressing "/" key
|
|
42
|
+
const keyEvent = new KeyboardEvent('keydown', {
|
|
43
|
+
key: '/',
|
|
44
|
+
code: 'Slash',
|
|
45
|
+
bubbles: true,
|
|
46
|
+
cancelable: true,
|
|
47
|
+
});
|
|
48
|
+
window.dispatchEvent(keyEvent);
|
|
49
|
+
if (input) {
|
|
50
|
+
expect(document.activeElement).not.toBe(input);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it('should not focus the search input when "/" key is pressed while typing in another input', () => {
|
|
54
|
+
const { container } = render(_jsxs("div", { children: [_jsx("input", { "data-testid": "other-input" }), _jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { showToolbarItem: true })) })] }));
|
|
55
|
+
const otherInput = container.querySelector('[data-testid="other-input"]');
|
|
56
|
+
// Focus the other input first
|
|
57
|
+
otherInput.focus();
|
|
58
|
+
expect(document.activeElement).toBe(otherInput);
|
|
59
|
+
// Simulate pressing "/" key while focused on the other input
|
|
60
|
+
// The event target should be the input element
|
|
61
|
+
const keyEvent = new KeyboardEvent('keydown', {
|
|
62
|
+
key: '/',
|
|
63
|
+
code: 'Slash',
|
|
64
|
+
bubbles: true,
|
|
65
|
+
cancelable: true,
|
|
66
|
+
});
|
|
67
|
+
Object.defineProperty(keyEvent, 'target', {
|
|
68
|
+
value: otherInput,
|
|
69
|
+
enumerable: true,
|
|
70
|
+
});
|
|
71
|
+
window.dispatchEvent(keyEvent);
|
|
72
|
+
// The search input should not be focused since we're already in an input field
|
|
73
|
+
expect(document.activeElement).toBe(otherInput);
|
|
74
|
+
});
|
|
75
|
+
it('should not focus the search input when enableShortcut is false', () => {
|
|
76
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { showToolbarItem: true, enableShortcut: false })) }));
|
|
77
|
+
const input = document.getElementById('test-filter');
|
|
78
|
+
expect(input).toBeInTheDocument();
|
|
79
|
+
// Simulate pressing "/" key
|
|
80
|
+
const keyEvent = new KeyboardEvent('keydown', {
|
|
81
|
+
key: '/',
|
|
82
|
+
code: 'Slash',
|
|
83
|
+
bubbles: true,
|
|
84
|
+
cancelable: true,
|
|
85
|
+
});
|
|
86
|
+
window.dispatchEvent(keyEvent);
|
|
87
|
+
// The input should not be focused since the shortcut is disabled
|
|
88
|
+
expect(document.activeElement).not.toBe(input);
|
|
89
|
+
});
|
|
90
|
+
it('should focus the search input when enableShortcut is true (default)', () => {
|
|
91
|
+
render(_jsx(DataViewToolbar, { filters: _jsx(DataViewTextFilter, Object.assign({}, defaultProps, { showToolbarItem: true })) }));
|
|
92
|
+
const input = document.getElementById('test-filter');
|
|
93
|
+
expect(input).toBeInTheDocument();
|
|
94
|
+
// Simulate pressing "/" key
|
|
95
|
+
const keyEvent = new KeyboardEvent('keydown', {
|
|
96
|
+
key: '/',
|
|
97
|
+
code: 'Slash',
|
|
98
|
+
bubbles: true,
|
|
99
|
+
cancelable: true,
|
|
100
|
+
});
|
|
101
|
+
window.dispatchEvent(keyEvent);
|
|
102
|
+
// The input should be focused since the shortcut is enabled by default
|
|
103
|
+
expect(document.activeElement).toBe(input);
|
|
104
|
+
});
|
|
17
105
|
});
|
|
@@ -5,13 +5,13 @@ export interface DataViewThResizableProps {
|
|
|
5
5
|
isResizable?: boolean;
|
|
6
6
|
/** Callback after the column is resized. Returns the triggering event, the column id passed in via cell props, and the new width of the column. */
|
|
7
7
|
onResize?: (event: ReactMouseEvent | MouseEvent | ReactKeyboardEvent | KeyboardEvent | TouchEvent, id: string | number | undefined, width: number) => void;
|
|
8
|
-
/**
|
|
8
|
+
/** Starting width in pixels of the column */
|
|
9
9
|
width?: number;
|
|
10
|
-
/** Minimum width of the column */
|
|
10
|
+
/** Minimum resize width in pixels of the column */
|
|
11
11
|
minWidth?: number;
|
|
12
|
-
/** Increment for keyboard navigation */
|
|
12
|
+
/** Increment in pixels for keyboard navigation */
|
|
13
13
|
increment?: number;
|
|
14
|
-
/** Increment for keyboard navigation while shift is held */
|
|
14
|
+
/** Increment in pixels for keyboard navigation while shift is held */
|
|
15
15
|
shiftIncrement?: number;
|
|
16
16
|
/** Provides an accessible name for the resizable column via a human readable string. */
|
|
17
17
|
resizeButtonAriaLabel?: string;
|
|
@@ -210,6 +210,13 @@ export const DataViewTh = (_a) => {
|
|
|
210
210
|
onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, newSize);
|
|
211
211
|
};
|
|
212
212
|
const resizableContent = (_jsxs(Fragment, { children: [_jsx("div", { "aria-live": "polite", className: "pf-v6-screen-reader", children: screenReaderText }), _jsx(Button, { ref: resizeButtonRef, variant: "plain", hasNoPadding: true, icon: _jsx(ResizeIcon, {}), onMouseDown: handleMousedown, onKeyDown: handleKeys, onTouchStart: handleTouchStart, "aria-label": resizeButtonAriaLabel, className: classes.dataViewResizableButton })] }));
|
|
213
|
-
|
|
213
|
+
const classNames = [];
|
|
214
|
+
if (thProps === null || thProps === void 0 ? void 0 : thProps.className) {
|
|
215
|
+
classNames.push(thProps.className);
|
|
216
|
+
}
|
|
217
|
+
if (dataViewThClassName) {
|
|
218
|
+
classNames.push(dataViewThClassName);
|
|
219
|
+
}
|
|
220
|
+
return (_jsx(Th, Object.assign({ modifier: "truncate" }, thProps, props, { ref: thRef, style: width > 0 ? Object.assign(Object.assign({}, thProps === null || thProps === void 0 ? void 0 : thProps.style), { minWidth: width }) : thProps === null || thProps === void 0 ? void 0 : thProps.style, className: classNames.length > 0 ? classNames.join(' ') : undefined }, (isResizable && { additionalContent: resizableContent }), { children: content })));
|
|
214
221
|
};
|
|
215
222
|
export default DataViewTh;
|
|
@@ -12,9 +12,21 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { useRef } from 'react';
|
|
14
14
|
import { Button, Toolbar, ToolbarContent, ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core';
|
|
15
|
+
import { createUseStyles } from 'react-jss';
|
|
16
|
+
const useStyles = createUseStyles({
|
|
17
|
+
dataViewToolbarPagination: {
|
|
18
|
+
flexBasis: '100%',
|
|
19
|
+
width: '100%'
|
|
20
|
+
},
|
|
21
|
+
dataViewToolbarPaginationWrapper: {
|
|
22
|
+
flexBasis: '100%',
|
|
23
|
+
width: '100%'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
15
26
|
export const DataViewToolbar = (_a) => {
|
|
16
27
|
var { className, ouiaId = 'DataViewToolbar', bulkSelect, actions, toggleGroup, pagination, filters, customLabelGroupContent, clearAllFilters, children } = _a, props = __rest(_a, ["className", "ouiaId", "bulkSelect", "actions", "toggleGroup", "pagination", "filters", "customLabelGroupContent", "clearAllFilters", "children"]);
|
|
28
|
+
const classes = useStyles();
|
|
17
29
|
const defaultClearFilters = useRef(_jsx(ToolbarItem, { children: _jsx(Button, { ouiaId: `${ouiaId}-clear-all-filters`, variant: "link", onClick: clearAllFilters, isInline: true, children: "Clear filters" }) }));
|
|
18
|
-
return (_jsx(Toolbar, Object.assign({ ouiaId: ouiaId, className: className, customLabelGroupContent: customLabelGroupContent !== null && customLabelGroupContent !== void 0 ? customLabelGroupContent : defaultClearFilters.current }, props, { children: _jsxs(ToolbarContent, { children: [bulkSelect && (_jsx(ToolbarItem, { "data-ouia-component-id": `${ouiaId}-bulk-select`, children: bulkSelect })), filters && (_jsx(ToolbarItem, { children: filters })), actions && (_jsx(ToolbarItem, { children: actions })), toggleGroup && (_jsx(ToolbarItem, { children: toggleGroup })), pagination && (_jsx(ToolbarItem, { variant: ToolbarItemVariant.pagination, "data-ouia-component-id": `${ouiaId}-pagination`, children: pagination })), children] }) })));
|
|
30
|
+
return (_jsx(Toolbar, Object.assign({ ouiaId: ouiaId, className: className, customLabelGroupContent: customLabelGroupContent !== null && customLabelGroupContent !== void 0 ? customLabelGroupContent : defaultClearFilters.current }, props, { children: _jsxs(ToolbarContent, { children: [bulkSelect && (_jsx(ToolbarItem, { "data-ouia-component-id": `${ouiaId}-bulk-select`, children: bulkSelect })), filters && (_jsx(ToolbarItem, { children: filters })), actions && (_jsx(ToolbarItem, { children: actions })), toggleGroup && (_jsx(ToolbarItem, { children: toggleGroup })), pagination && (_jsx(ToolbarItem, { variant: ToolbarItemVariant.pagination, "data-ouia-component-id": `${ouiaId}-pagination`, className: classes.dataViewToolbarPagination, children: _jsx("div", { className: classes.dataViewToolbarPaginationWrapper, children: pagination }) })), children] }) })));
|
|
19
31
|
};
|
|
20
32
|
export default DataViewToolbar;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ToolbarFilterProps, TreeViewDataItem } from '@patternfly/react-core';
|
|
2
|
+
import React, { FC } from 'react';
|
|
3
|
+
export interface DataViewTreeFilterProps {
|
|
4
|
+
/** Unique key for the filter attribute */
|
|
5
|
+
filterId: string;
|
|
6
|
+
/** Array of current filter values */
|
|
7
|
+
value?: string[];
|
|
8
|
+
/** Filter title displayed in the toolbar */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Label for the applied filter chip / category; defaults to title */
|
|
11
|
+
chipTitle?: string;
|
|
12
|
+
/** Callback for when the selection changes */
|
|
13
|
+
onChange?: (event?: React.MouseEvent, values?: string[]) => void;
|
|
14
|
+
/** Controls visibility of the filter in the toolbar */
|
|
15
|
+
showToolbarItem?: ToolbarFilterProps['showToolbarItem'];
|
|
16
|
+
/** Custom OUIA ID */
|
|
17
|
+
ouiaId?: string;
|
|
18
|
+
/** Hierarchical data items for the tree structure */
|
|
19
|
+
items?: TreeViewDataItem[];
|
|
20
|
+
/** When true, expands all tree nodes by default */
|
|
21
|
+
defaultExpanded?: boolean;
|
|
22
|
+
/** Callback for when tree items are selected/deselected, provides all currently selected nodes */
|
|
23
|
+
onSelect?: (selectedItems: TreeViewDataItem[]) => void;
|
|
24
|
+
/** Array of pre-selected item id's to be checked on initial render */
|
|
25
|
+
defaultSelected?: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare const DataViewTreeFilter: FC<DataViewTreeFilterProps>;
|
|
28
|
+
export default DataViewTreeFilter;
|