GriddleGriddle / Griddle

Simple Grid Component written in React
http://griddlegriddle.github.io/Griddle/
MIT License
2.5k stars 378 forks source link

Custom Header as CheckBox to Select all rows in table. #822

Open neerajmangal opened 6 years ago

neerajmangal commented 6 years ago

Griddle version

1.13.1

Expected Behavior

I have a customHeaderComponent which is a check box and CustomColumnComponent which has a checkBox for each row in table.

I want to select Header checkbox to select all rows in the table.

screen shot 2018-07-20 at 8 17 44 pm

Actual Behavior

Could not figure out a way to achieve it. Can someone please help on this? Also how I can restrict users to select more than one row

screen shot 2018-07-20 at 8 14 45 pm
dahlbyk commented 6 years ago

Please share what you have tried, or some sort of starter code so we can point you in the right direction!

Also how I can restrict users to select more than one row

Not sure what you mean by this?

neerajmangal commented 6 years ago

Thanks @dahlbyk for taking a look.
This is I have been trying. I am trying to create a common component for TableView using griddle which other component in my application will use. Below is the CustomTableView I have created.

const rowDataSelector = (state, { griddleKey }) => {
    return state
        .get('data')
        .find(rowMap => rowMap.get('griddleKey') === griddleKey)
        .toJSON();
};

/** use griddle's connect function only*/
const enhancedWithRowData = utils.connect((state, props) => {
    return {
        // rowData will be available into customComponents
        rowData: rowDataSelector(state, props)
    };
});

const getAllRowsData = (state, props) => {
    return state.get('data').toJSON();
};

const enhanceedWithTableData = utils.connect((state, props) =>{
    return {
      allRowsData: getAllRowsData(state, props)
    };
 });

/* This seems to be a stateless function but as we are using "ref" in Griddle
*  It will throw error if converted to pure/smart component.
*/
class CustomTableView extends React.Component {
  constructor(props) {
      super(props);
      /*Default fallback to CustomFilter in case Platform/Tool
       specific Filter is not provided.*/
      this.customFilters = props.tblFilters ? props.tblFilters : CustomFilter;
  }
  render() {
    return (
    <Griddle
      ref="Griddle"
      useCustomFilterComponent
      /** This storeKey is very important here, without providing it
       * Custom Component will not be able to connect to global store.   
       * https://github.com/GriddleGriddle/Griddle/issues/626
       * https://github.com/GriddleGriddle/Griddle/issues/647 
       */
      storeKey="griddle"
      customFilterComponent={this.customFilters}
      styleConfig={styleConfig}
      data={this.props.data}
      showSettings={false}
      sortProperties={this.props.sortProperties}
      pageProperties={{
        currentPage: 1,
        pageSize: 10,
        recordCount: 10,
        useGriddleStyles: false
      }}
      components={
          /* Make sure your custom filter component pass setFilter to children*/
          { Filter: (filter) =>
                  this.props.tblFilters ? <this.props.tblFilters {...this.props} filter={filter.setFilter}/>
                      : <CustomFilter {...this.props} filterRef={filter.setFilter}/>,
              Layout: Layout
          } //SettingsToggle: CustomFilter //TableContainer: CustomTableComponent
      }
      plugins={[plugins.LocalPlugin]}
    >
      <RowDefinition>
        {this.props.columns.map((column, i) => {
            let columnComponent;
            let CustomHeader;
            if (typeof column.CustomComponent !== 'undefined') {
                columnComponent = enhancedWithRowData(column.CustomComponent);
            } 
            if (typeof column.CustomHeader !== 'undefined') {
              CustomHeader = enhanceedWithTableData(column.CustomHeader);
            } 
            return(
              <ColumnDefinition
                key={i}
                id={column.key}
                title={column.title}
                width={column.width}
                customComponent={columnComponent}
                customHeadingComponent={CustomHeader}
                //sortable={column.sortable}
                {...this.props}
              />
            );
          }
        )}
      </RowDefinition>
    </Griddle>
    );
  }
}

CustomTableView.propTypes = {
    /* Table Data to be rendered*/
    data: PropTypes.array.isRequired,
    /* Table Columns to be rendered and mapped to data*/
    columns: PropTypes.array.isRequired,
    /* Any shortProperties for pagination*/
    sortProperties: PropTypes.array,
    /* Custom filters for table data*/
    tblFilters: PropTypes.func
};

export default CustomTableView;

The Table Columns are defined like this.

export const ServerlessTableDataColumns = [
    {
        title: "Select",
        key: "IsSelected",
        width: 20,
        sortable: false,
        CustomComponent: RowSelection,
        CustomHeader: SelectAllRow
    },
    {
        title: 'Function Name',
        key: 'functionName',
        width: 100,
        sortable:true
    },
    {
        title: 'Runtime',
        key: 'runtime',
        minWidth: 50,
        sortable: true,
        divider: true
    },
    {
        title: 'Namespace',
        key: 'namespace',
        minWidth: 50,
        sortable: true
    },
    {
        title: 'Web Action',
        key: 'webAction',
        minWidth: 20,
        sortable: true,
        CustomComponent: ToggleWebAction
    },
    {
        title: 'Shared',
        key: 'shared',
        minWidth: 20,
        sortable: true,
        CustomComponent: togglePublish
    },
    {
        title: 'Version',
        key: 'version',
        minWidth: 20,
        sortable: true
    },
    {
        title: 'Delete',
        key: 'delete',
        minWidth: 20,
        sortable: false,
        CustomComponent: DeleteFunction
    },
    {
        title: 'More',
        key: 'more',
        minWidth: 20,
        sortable: false,
        CustomComponent: ExtendedActions
    }
];

As you can see first colum is having customHeadingComponent as CustomHeader: SelectAllRow and customComponent as columnComponent:RowSelection I passed in to .

// SelectAllRow CustomHeader Component for header checkBox.

export const SelectAllRow = (props) => {
    function onSelectAllRow(e) {
        console.log("All rows selected", e);
        /*
        if (props.allRowsData !== null) {
            props.allRowsData.forEach(
                (data, index) => {
                    let RowData = {
                        value: e,
                        griddleKey: index,
                        rowData: data
                    };
                    RowSelection(RowData);
                }
            );
        }
        */
    }
    return (
        <div style={{ paddingTop: "4px" }}>
            <Checkbox
                label=""
                onChange={onSelectAllRow}
            />
        </div>);
};

In SelectAllRow i am not able to figure out how I can pass the onChange event to all table rows. Please let me know how I can do this or is there anything I am missing. Let me know if more information is needed, i will provide that.

Regarding restriction on user to select more than one row, I mean to say is there any property I can pass to Griddle to not allow multiple row selection at a time (i.e user can select only one row at a time or all rows by header checkbox).

Thanks.

dahlbyk commented 6 years ago

OK, based on your commented code in onSelectAllRow I think you need to shift how you're thinking about selection. You're not going to be able to make changes to props.allRowsData and expect the app to update accordingly; you need to approach this as a state change. Since you have a CustomTableView wrapper around your <Griddle />, you could have your which-row(s)-selected state live there (passed down into RowSelection and SelectAllRow). Or it might be easier to use Griddle's internal Redux store, similar to PositionPlugin.

neerajmangal commented 6 years ago

Thanks @dahlbyk , I moved to the first approach and make it working somewhat. Here are the changes I made in RowSelection and SelectAllRows. Basically I have a state enabled in my global store and based on that update the state of all checkboxes in RowSelection.

class RowSelection extends Component {
    constructor(props) {
        super(props);
        this.props = props;
        this.onRowSelection = this.onRowSelection.bind(this);
        this.state = {
            isSelected: this.props.isAllChecked
        };   
    }

    onRowSelection(e) {
        console.log("value, propsValue, GriddleKey, rowData",
         e, this.props.vale, this.props.griddleKey, this.props.rowData);
        this.setState({
            isSelected: e
        });
        /*
        this.dispatch(updateWebActionStatus(this.props.cellProperties.userDetails.ldapId, 
            this.props.cellProperties.token,
            this.props.rowData, !this.props.value));
            */
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
        this.setState({
            isSelected: nextProps.isAllChecked
        });
    }

    render() {
        return (
            <Checkbox
                label=""
                value={this.state.isSelected}
                checked={this.state.isSelected}
                onChange={this.onRowSelection}
            />
        );
    }
}

const mapStateToProps = (state) => {
    return {
        isAllChecked: state.platforms.serverless.isAllChecked
    };
};

export default connect(mapStateToProps)(RowSelection);
class SelectAllRow extends Component {
    constructor(props) {
        super(props);
        this.props = props;
        this.dispatch = this.props.cellProperties.dispatch;
        this.onAllRowSelection = this.onAllRowSelection.bind(this);
        this.state = {
            isSelected: this.props.isAllChecked
        };   
    }

    onAllRowSelection(e) {
        this.dispatch(selectAll(e)); 
        this.setState({
            isSelected: e
        });
        /*
        this.dispatch(updateWebActionStatus(this.props.cellProperties.userDetails.ldapId, 
            this.props.cellProperties.token,
            this.props.rowData, !this.props.value));
            */
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
        this.setState({
            isSelected: nextProps.isAllChecked
        });
    }

    render() {
        return (
            <Checkbox
                label=""
                value={this.state.isSelected}
                checked={this.state.isSelected}
                onChange={this.onAllRowSelection}
            />
        );
    }
}

const mapStateToProps = (state) => {
    return {
        isAllChecked: state.platforms.serverless.isAllChecked
    };
};

export default connect(mapStateToProps)(SelectAllRow);

With this I am able to see all checkboxes selected when header checkbox is selected. I am now looking for better solution to update the internal Griddle State for more features for this checkbox flow and will check position plugin for reference to do that.

Thanks for the guidance. I will post once I am done with that.