Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
describe('Table Selectable Expandable Test', () => {
it('Navigate to demo section', () => {
cy.visit('http://localhost:3000/');
cy.get('#table-selectable-expandable-demo-nav-item-link').click();
cy.url().should('eq', 'http://localhost:3000/table-selectable-expandable-demo-nav-link');
});

it('Verify table string', () => {
cy.get('caption').contains('Selectable expandable Table');
});

it('Check number of rows', () => {
cy.get('.pf-c-table')
.find('tr')
.should('have.length', 5);
});

it('Checks that initially checked inputs carry checked property', () => {
cy.get('.pf-c-table')
.find('[name="checkrow0"]')
.should('have.prop', 'checked');
});

it('Checks that check all checkbox is indeterminate', () => {
cy.get('.pf-c-table')
.find('[name="check-all"]')
.should('have.prop', 'indeterminate', true);
});
});
5 changes: 5 additions & 0 deletions packages/react-integration/demo-app-ts/src/Demos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,11 @@ export const Demos: DemoInterface[] = [
name: 'Table Selectable Demo',
componentType: Examples.TableSelectableDemo
},
{
id: 'table-selectable-expandable-demo',
name: 'Table Selectable Expandable Demo',
componentType: Examples.TableSelectableExpandableDemo
},
{
id: 'table-simple-actions-demo',
name: 'Table Simple Actions Demo',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import { Table, TableHeader, TableBody, TableProps, headerCol, ICell, IRow, expandable } from '@patternfly/react-table';
import { Checkbox } from '@patternfly/react-core';

interface TableState {
columns: (ICell | string)[];
rows: IRow[];
canSelectAll: boolean;
}

export class TableSelectableExpandableDemo extends React.Component<TableProps, TableState> {
static displayName = 'TableSelectableExpandableDemo';
constructor(props: TableProps) {
super(props);
this.state = {
columns: [
{ title: 'Repositories', cellTransforms: [headerCol()], cellFormatters: [expandable] },
'Branches',
{ title: 'Pull requests' }
],
rows: [
{
cells: ['row0 - one', 'row0 - two', 'row0 - three'],
selected: true,
showSelect: true,
isOpen: false
},
{
cells: ['row1 - one', 'row1 - two', 'row1 - three'],
selected: false,
parent: 0
},
{
cells: ['row2 - one', 'row2 - two', 'row2 - three'],
selected: false,
isOpen: false
},
{
cells: ['row3 - one', 'row3 - two', 'row3 - three'],
selected: false,
parent: 2
}
],
canSelectAll: true
};
this.onSelect = this.onSelect.bind(this);
this.onCollapse = this.onCollapse.bind(this);
}

onCollapse(event: React.FormEvent, rowKey: number, isOpen: boolean) {
const { rows } = this.state;
rows[rowKey].isOpen = isOpen;
this.setState({
rows
});
}

onSelect(event: React.FormEvent, isSelected: boolean, rowId: number) {
let rows: IRow[];
if (rowId === -1) {
rows = this.state.rows.map(oneRow => {
oneRow.selected = isSelected;
return oneRow;
});
} else {
rows = [...this.state.rows];
rows[rowId].selected = isSelected;
}
this.setState({
rows
});
}

toggleSelect = (checked: boolean) => {
this.setState({
canSelectAll: checked
});
};

componentDidMount() {
window.scrollTo(0, 0);
}

render() {
const { columns, rows } = this.state;

return (
<div>
<Table
caption="Selectable expandable Table"
onCollapse={this.onCollapse}
onSelect={this.onSelect}
cells={columns}
rows={rows}
canSelectAll={this.state.canSelectAll}
>
<TableHeader />
<TableBody />
</Table>
<Checkbox
label="canSelectAll"
isChecked={this.state.canSelectAll}
onChange={this.toggleSelect}
aria-label="toggle select all checkbox"
id="toggle-select-all"
name="toggle-select-all"
/>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export * from './TableDemo/TableHeadersWrappableDemo';
export * from './TableDemo/TableRowWrapperDemo';
export * from './TableDemo/TableRowClickDemo';
export * from './TableDemo/TableSelectableDemo';
export * from './TableDemo/TableSelectableExpandableDemo';
export * from './TableDemo/TableSimpleActionsDemo';
export * from './TableDemo/TableSimpleDemo';
export * from './TableDemo/TableSortableDemo';
Expand Down
50 changes: 37 additions & 13 deletions packages/react-table/src/components/Table/SelectColumn.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
import * as React from 'react';
import { SelectedRowsAmount } from './Table';

export interface SelectColumnProps {
name?: string;
'data-selectedrowsamount'?: SelectedRowsAmount;
children?: React.ReactNode;
className?: string;
onSelect?: (event: React.FormEvent<HTMLInputElement>) => void;
}

export const SelectColumn: React.FunctionComponent<SelectColumnProps> = ({
children = null as React.ReactNode,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
className,
onSelect = null as (event: React.FormEvent<HTMLInputElement>) => void,
...props
}: SelectColumnProps) => (
<React.Fragment>
<input {...props} type="checkbox" onChange={onSelect} />
{children}
</React.Fragment>
);
SelectColumn.displayName = 'SelectColumn';
export class SelectColumn extends React.Component<SelectColumnProps> {
static displayName = 'SelectColumn';
static defaultProps: SelectColumnProps = {
children: null as React.ReactNode,
onSelect: null as (event: React.FormEvent<HTMLInputElement>) => void
};

ref: React.RefObject<HTMLInputElement> = React.createRef();

setCheckStatus() {
if (this.props.name === 'check-all') {
this.ref.current.indeterminate = this.props['data-selectedrowsamount'] === SelectedRowsAmount.some;
this.ref.current.checked = this.props['data-selectedrowsamount'] === SelectedRowsAmount.all;
}
}

componentDidMount() {
this.setCheckStatus();
}

componentDidUpdate() {
this.setCheckStatus();
}

render() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { children, onSelect, className, ...props } = this.props;
return (
<React.Fragment>
<input {...props} type="checkbox" onChange={onSelect} ref={this.ref} />
{children}
</React.Fragment>
);
}
}
5 changes: 3 additions & 2 deletions packages/react-table/src/components/Table/Table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
truncate,
breakWord,
fitContent,
ICell
ICell,
SelectedRowsAmount
} from './index';
import { rows, columns, editableRows, editableColumns, actions } from '../../test-helpers/data-sets';
import { ColumnsType } from './base';
Expand Down Expand Up @@ -278,7 +279,7 @@ test('Selectable table with selected expandable row', () => {
</Table>
);

expect(view.find('input[name="check-all"]').prop('checked')).toEqual(true);
expect(view.find('input[name="check-all"]').prop('data-selectedrowsamount')).toEqual(SelectedRowsAmount.all);
});

test('Empty state table', () => {
Expand Down
22 changes: 17 additions & 5 deletions packages/react-table/src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export enum TableVariant {
compact = 'compact'
}

export enum SelectedRowsAmount {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you think we could assign strings to represent these enum values? I think using the enum indices is really elegant but makes it a little difficult to debug when comparing against the resulting markup. In other words I thought data-rowsamount="1" meant there was a single row selected instead of it referring to "some".

all = 'all',
some = 'some',
none = 'none'
}

export type RowEditType = 'save' | 'cancel' | 'edit';

export interface RowErrors {
Expand Down Expand Up @@ -103,7 +109,7 @@ export interface IColumn {
contentId?: string;
dropdownPosition?: DropdownPosition;
dropdownDirection?: DropdownDirection;
allRowsSelected?: boolean;
selectedRowsAmount?: SelectedRowsAmount;
};
}

Expand Down Expand Up @@ -333,11 +339,17 @@ export class Table extends React.Component<TableProps & OUIAProps, {}> {

isSelected = (row: IRow) => row.selected === true;

areAllRowsSelected = (rows: IRow[]) => {
howManyRowsSelected = (rows: IRow[]) => {
if (rows === undefined || rows.length === 0) {
return false;
return SelectedRowsAmount.none;
}
return rows.every(row => this.isSelected(row) || (row.hasOwnProperty('parent') && !row.showSelect));
const selectableRows = rows.filter(row => !(row.hasOwnProperty('parent') && !row.showSelect));
const count = selectableRows.filter(row => this.isSelected(row)).length;
return count === 0
? SelectedRowsAmount.none
: count === selectableRows.length
? SelectedRowsAmount.all
: SelectedRowsAmount.some;
};

componentDidMount() {
Expand Down Expand Up @@ -394,7 +406,7 @@ export class Table extends React.Component<TableProps & OUIAProps, {}> {
onSort,
onSelect,
canSelectAll,
allRowsSelected: onSelect ? this.areAllRowsSelected(rows as IRow[]) : false,
selectedRowsAmount: onSelect ? this.howManyRowsSelected(rows as IRow[]) : SelectedRowsAmount.none,
actions,
actionResolver,
areActionsDisabled,
Expand Down
Loading