gregnb / mui-datatables

Datatables for React using Material-UI
MIT License
2.71k stars 932 forks source link

Bugs with filter #1066

Open jlubeck opened 4 years ago

jlubeck commented 4 years ago

My filter has a few bugs which I'm not sure if they are bad configurations or bugs.

First let me show you what is happening:

filterbug

Current Behavior

There are couple of different things

1) After clicking on a filter it doesn't stay on the box. The box gets a blank space. Although I do see a pill. 2) After clicking the reset buttons, nothing happens 3) After clicking the X on the pill, the filter is not removed and a white screen is shown. 4) It would be nice to close the filter window after choosing a filter automatically. Is that possible?

Steps to Reproduce (for bugs)

Here is my view:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { bindActionCreators } from 'redux';
import { Link } from 'react-router-dom';
import Money from './Money';
import { CircularProgress, Typography } from '@material-ui/core';
import MUIDataTable from 'mui-datatables';

import { getMids } from '../actions/mids';

class MidsIndex extends Component {
    constructor(props){
        super(props);
        this.state = { pending:undefined,
            page: 0,
            count: 1,
            pageSize: 20,
            filters: [],
            filterList: [],
            data: [["Loading Data..."]],
            isLoading: false 
        };
    }

    componentDidMount(){
        this.setState({ pending:this.props.pending });
        this.getData();
    }

    componentWillReceiveProps(props){
        this.setState({ pending:props.pending });
    }

    getData = () => {
        this.setState({ isLoading: true });
        this.xhrRequest(this.state).then(res => {
          this.setState({ data: res.data, isLoading: false, count: res.total });
        });
      }

      xhrRequest = (query) => {
        return new Promise((resolve, reject) => {
            query.pending = this.state.pending
            this.props.getMids(query).then((response) => {
                let data = response && response.data
                if(data){
                    let results = data.results ? data.results : []
                    let totalCount = data.total_count
                    resolve({
                        data: results,
                        page: query.page,
                        total: totalCount
                    })
                } else {
                    resolve({
                        data: [],
                        page: 0,
                        total: 0
                    })          
                }
            })
        })  
      }

    formatFieldWithRow(field, row) {
        switch(field){
            case 'name':
                return (
                    <Link to={`/mids/${row[0]}`}>{row[1]}</Link>
                );
            case 'amount':
                return (
                    row[4] === null ? <p>-</p> : <Money value={row[4]} />
                );
            case 'status':
                var $status = ''
                switch(row[5]){
                    case 1:
                    $status = 'New'
                    break;
                    case 2:
                    $status = 'Submitted'
                    break;
                    case 3:
                    $status = 'Pending Review'
                    break;
                    case 4:
                    $status = 'Live'
                    break;
                    case 5:
                    $status = 'Declined'
                    break;
                    case 6:
                    $status = 'Closed'
                    break;
                    default:
                    break;          
                }
                return (
                    $status
                );
            default:
                break;
        }
    }

    statusStringToID(status){
        switch(status){
            case 'New':
                return 1
            case 'Submitted':
                return 2
            case 'Pending Review':
                return 3
            case 'Live':
                return 4
            case 'Declined':
                return 5
            case 'Closed':
                return 6
            default:
                return 0
            }
    }

    render() {
        const columns = [
            { label:"Gateway ID", name: 'id', options: { filter: false } }, 
            { label:"Gateway Alias", name: 'name', options: { filter: false, customBodyRender: (value, tableMeta, updateValue) => { 
                return this.formatFieldWithRow('name', tableMeta.rowData) 
            } } }, 
            { label:"Corporation", name: 'corporation', options: { filter: false }  }, 
            { label:"CRM", name:'crm', options: { filter: false }  },
            { label:"MTD", name:'mtd', options: { filter: false, customBodyRender: (value, tableMeta, updateValue) => { 
                return this.formatFieldWithRow('amount', tableMeta.rowData) 
            } } }, 
            { label:"Status", name:'status', options: { customBodyRender: (value, tableMeta, updateValue) => { 
                return this.formatFieldWithRow('status', tableMeta.rowData) 
            }, filterOptions: {
                names: ['New', 'Submitted', 'Pending Review', 'Live', 'Declined', 'Closed']
            }, filterList: this.state.filters,
            } }
        ];
        const { data, page, count, pageSize, isLoading , filterList} = this.state;

        const options = {
          filter: true,
          filterType: 'dropdown',
          responsive: 'stacked',
          serverSide: true,
          count: count,
          page: page,
          rowsPerPage: pageSize,
          serverSideFilterList: filterList,
          onTableChange: (action, tableState) => {

            // a developer could react to change on an action basis or
            // examine the state as a whole and do whatever they want

            switch (action) {
              case 'changePage':
                this.changePage(tableState.page);
                break;
                case 'filterChange':
                    var filters = []
                    tableState.columns.map((column, index) => {
                        if(tableState.filterList[index].length > 0){
                            var filter = { 
                                column: {
                                    field: column.name
                                },
                                value: tableState.filterList[index].map(this.statusStringToID)
                            }
                            filters.push(filter)
                        }
                    })
                this.setState({ filters: filters, filterList: tableState.filterList }, () => {
                    this.getData();
                }) 
                break;
                default:
                break;
            }
          }
        };

        return (
            <div>
                <div className="row">
                    <div className="col-lg-12">
                        <div className="panel panel-default">
                            <div className="panel-heading">
                                <h3 className="panel-title pull-left">Mids { this.state.pending ? '(showing pending only)' : ''} </h3>
                                <div className="clearfix"></div>
                            </div>
                            <div className="panel-body">
                            <MUIDataTable title={<Typography variant="title">
                            MIDs
                            {isLoading && <CircularProgress size={24} style={{marginLeft: 15, position: 'relative', top: 4}} />}
                            </Typography>
                            } data={data} columns={columns} options={options} />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

function mapStateToProps(state){
    return { mids: state.mids };
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({ getMids } , dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(MidsIndex));

Your Environment

Tech Version
Material-UI 4.6.1
MUI-datatables 2.12.4
React 16.8.6
browser Chrome 78
etc
gabrielliwerant commented 4 years ago

It's difficult to be sure what exactly may be happening, as there's a lot of code, but my guess is that serverside is what's tripping you up. Serverside functionality right now is all or nothing, so it's opting you into serverside filters which work differently from client side filters in a couple of ways. Have a look at the serverside-filters example for some ideas.

gabrielliwerant commented 4 years ago

You can also test if this is the main culprit by testing your filters with serverside set to false.

patorjk commented 4 years ago

I think I see what's going on here - the table creates an internal array-of-arrays object called filterList from the individual filterList arrays on each column. This "master" filterList is used as the state for the filter dialog. In your example you're saving this master filterList to your state, but you're not correctly setting the individual filterLists for each column. Instead you're setting the master filterList on one column.

Have a closer look at the the server-side filters example (https://github.com/gregnb/mui-datatables/blob/master/examples/serverside-filters/index.js) and how the filterList props for each column are set.

patorjk commented 4 years ago

Also, I just tried out the server-side example and this issue will present itself there too but for a different reason. The server-side example returns the same bit of data regardless of what filters you choose, so if, for example, you filter on the location "Baltimore" you'll get this issue but only because the return data doesn't have "Baltimore" in it and "Baltimore" isn't one of the choices in the dropdown list.

So this will still be a problem in the above example if you don't get any results back. One way around this is to set the filterOptions (ex: https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js).

jlubeck commented 4 years ago

Hey @patorjk thanks a bunch for troubleshooting my issue. As soon as I get a minute I'm going to double check with your comments and circle back here. Unfortunately this is for a side project of mine, so it might take me a while... But thank you so much!