Selection

The TreeList enables the user to select a single row or multiple rows and provides checkbox and row-click selection options.

Getting Started

To implement row selection:

  1. Use the onRowClick event.
  2. Set the selectedField option.

As a result, the TreeList allows you to set the currently clicked item as selected. The selectedField option represents a field inside the data collection which determines the rows that will render as selected.

import React from 'react';
import ReactDOM from 'react-dom';
import {
    TreeList, mapTree, extendDataItem
} from '@progress/kendo-react-treelist';
import employees from './data';

const subItemsField = 'employees';
const expandField = 'expanded';
const selectField = 'selected';

class App extends React.Component {
    state = {
        data: employees.slice(),
        expanded: [1, 2, 32],
        selected: []
    }

    handleRowClick = (e) => {
        const selected = e.dataItem.selected ?
            this.state.selected.filter(x => x !== e.dataItem.id) :
            [...this.state.selected, e.dataItem.id];

        this.setState({ selected });
    }

    onExpandChange = (e) => {
        this.setState({
            expanded: e.value ?
                this.state.expanded.filter(id => id !== e.dataItem.id) :
                [...this.state.expanded, e.dataItem.id]
        });
    }
    updateFields = (dataArr) => {
        const { expanded, selected } = this.state;
        return mapTree(dataArr, subItemsField, (item) =>
            extendDataItem(item, subItemsField, {
                expanded: expanded.includes(item.id),
                selected: selected.includes(item.id)
            })
        );
    }
    render() {
        return (
            <TreeList
                style={{ maxHeight: '510px', overflow: 'auto' }}
                data={this.updateFields(this.state.data)}
                selectedField={selectField}
                onRowClick={this.handleRowClick}
                expandField={expandField}
                subItemsField={subItemsField}
                onExpandChange={this.onExpandChange}
                columns={[
                    { field: 'name', title: 'Name', expandable: true, width: 280 },
                    { field: 'position', title: 'Position', width: 230 },
                    { field: 'timeInPosition', title: 'Year(s) in Position', width: 150 },
                    { field: 'hireDate', title: 'Hire Date', format: '{0:d}', width: 150 },
                    { field: 'fullTime', title: 'Full Time', width: 150 }
                ]}
            />
        );
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Customizing the Selection

The TreeList provides both checkbox and row-click selection options which can be applied to single or multiple records.

The checkbox selection enables the user to select a TreeList item by checking a checkbox and implements a master checkbox in the header that selects and deselects all items.

To configure the checkbox selection:

  1. Set a selection column by setting the field property value of the column to be the same as the selectedField value of the TreeList.
  2. Handle the TreeListSelectionChangeEvent, TreeListHeaderSelectionChangeEvent, and TreeListRowClickEvent events, and store the selected state of the rows in the state of the application.
  3. In the render method of the application, set the selected state for each TreeList data item to the its selectedField field (that is, the updateFields function in the following example).

The following example demonstrates how to implement a multiple records selection both on row click and with checkboxes.

import React from 'react';
import ReactDOM from 'react-dom';
import {
    TreeList, mapTree, extendDataItem,
    TreeListSelectionCell, TreeListHeaderSelectionCell
} from '@progress/kendo-react-treelist';
import employees from './data';

const subItemsField = 'employees';
const expandField = 'expanded';
const selectField = 'selected';

class App extends React.Component {
    state = {
        data: employees.slice(),
        expanded: [1, 2, 32],
        selected: []
    }

    handleRowClick = (e) => {
        const selected = e.dataItem.selected ?
            this.state.selected.filter(x => x !== e.dataItem.id) :
            [...this.state.selected, e.dataItem.id];

        this.setState({ selected });
    }

    onExpandChange = (e) => {
        this.setState({
            expanded: e.value ?
                this.state.expanded.filter(id => id !== e.dataItem.id) :
                [...this.state.expanded, e.dataItem.id]
        });
    }
    updateFields = (dataArr) => {
        const { expanded, selected } = this.state;
        return mapTree(dataArr, subItemsField, (item) =>
            extendDataItem(item, subItemsField, {
                expanded: expanded.includes(item.id),
                selected: selected.includes(item.id)
            })
        );
    }
    onSelectionChange = (event) => {
        this.changeSelection(event.dataItem);
    }

    changeSelection = (dataItem) => {
        const selected = dataItem.selected ?
            this.state.selected.filter(x => x !== dataItem.id) :
            [ ...this.state.selected, dataItem.id ];

        this.setState({ selected });
    }

    onRowClick = (event) => {
        this.changeSelection(event.dataItem);
    }

    onHeaderSelectionChange = (event) => {
        const checked = event.syntheticEvent.target.checked;
        const selected = [];
        if (checked) {
            mapTree(this.state.data, subItemsField, (item) => {
                selected.push(item.id);
                return item;
            });
        }

        this.setState({ selected });
    }

    headerSelectionValue = () => {
        let allSelected = true;
        const selected = this.state.selected;
        mapTree(this.state.data, subItemsField, (item) => {
            allSelected = allSelected && selected.includes(item.id);
            return item;
        });
        return allSelected;
    }

    render() {
        return (
            <TreeList
                style={{ maxHeight: '510px', overflow: 'auto' }}
                data={this.updateFields(this.state.data)}
                selectedField={selectField}
                onSelectionChange={this.onSelectionChange}
                onHeaderSelectionChange={this.onHeaderSelectionChange}
                onRowClick={this.onRowClick}
                expandField={expandField}
                subItemsField={subItemsField}
                onExpandChange={this.onExpandChange}
                columns={[
                    {
                        field: 'selected',
                        width: '7%',
                        headerSelectionValue: this.headerSelectionValue(),
                        cell: TreeListSelectionCell,
                        headerCell: TreeListHeaderSelectionCell
                    },
                    { field: 'name', title: 'Name', expandable: true,  width: '31%' },
                    { field: 'position', title: 'Position',  width: '31%' },
                    { field: 'hireDate', title: 'Hire Date', format: '{0:d}',  width: '31%' },
                ]}
            />
        );
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);
 /