Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New filtering behavior + default filter definition #270

Closed
wants to merge 1 commit into from

Conversation

dana2208
Copy link
Contributor

Hello,

With this PR, we are able to define for each column header if it is filterable and what type of filter it is. I wrote 4 built-in filters:

  1. Text filter
  2. Select filter
  3. Number filter
  4. Date filter

Moreover there is a custom filter that can be any React component you write.
In the UI, filters are part of the column header (and not a separate table as now).

A default value can be passed so that table is filtered as soon as it renders, which is also a new feature.

More info on each filter type:

Text filter:

It renders an input type="text".
Add to the TableHeaderColumn filter={{type: "TextFilter"}}.

It accepts the following properties:

  • delay: the filter event is fired after this delay has elapsed from the last change in the input field. If it is not provided, then default delay is 500 ms.
  • placeholder: if nothing is provided, then the default "Enter [columnName]..." is displayed.
  • defaultValue: a default filter value when the table is rendered first time.

Select filter:

It renders a select input field.
Add to the TableHeaderColumn filter={{type: "SelectFilter", options: {...}}}.

It accepts the following properties:

  • placeholder: if nothing is provided, then the default "Select [columnName]..." is displayed.
  • defaultValue: a default filter value when the table is rendered first time.
  • options: an object where the properties will be the value of the option tags and the values of each property will be the displayed option to the user.
    For example qualityType = {0: good, 1: bad} will display a select input field with 2 options: good with value=0 and bad with value=1.

Number filter:

It may render in 2 different layouts, it depends whether property options is provided or not.
If it is provided, it means that filterable number values are restricted to this list and therefore a select input field is rendered. Elsewhere an input type="number" is displayed.
In both cases, it will be always rendered together with a select input field that contains the legal list of comparators, i.e. =, >, >=, <, <=, !=.
Add to the TableHeaderColumn filter={{type: "NumberFilter"}} or filter={{type: "NumberFilter", options: [...]}}.

It accepts the following properties:

  • delay: the filter event is fired after this delay has elapsed from the last change in the input field. This props take effect only if props options is not passed, i.e. the delay has effect only on the input type="number". If it is not provided, then default delay is 500 ms.
  • placeholder: if nothing is provided, then the default "Select [columnName]..." is displayed if options props is provided and "Enter [columnName]..." elsewhere.
  • defaultValue: a default filter value when the table is rendered first time. This is an object that has 2 properties: comparator and number. comparator value must be in the list of the legal comparators.
  • options: if provided, a select input field is rendered and not an input type="number". This is an array of numbers.
  • numberComparators: the comparators available to the user. If not provided then the list of comparators is =, >, >=, <, <=, !=. It is an array of strings. If provided, it must be in the list of legal comparators.

Date filter:

It renders an input type="date" field.
Add to the TableHeaderColumn filter={{type: "DateFilter"}}.

It accepts the following properties:

  • defaultValue: a default filter value when the table is rendered first time. It is a Date object.

Custom filter:

Any React component.
Add to the TableHeaderColumn filter={{type: "CustomFilter", getElement: getCustomFilterFunc}}

It has the following properties:

  • getElement: this is a required property. It is a function that returns the React element that will filter the table. Signature of this function is:
    1. the filter handler callback that you will need to call in your React element when you will fire the filter event.
    2. an object containing any other data you want to pass to your React element, if any.
  • customFilterParameters: any other data you want to pass to your React element, if any.

You can also choose to write your own filtering logic.
For this, when calling the filter handler in your React element, you need to pass an object containing 2 properties:
1. callback: the function that will actually do the filtering logic. This function takes as input parameter the targeted value (i.e. the value in the cell) and it must return a boolean (true if the row is filtered and false if the row will be in the result set). It may also take as a second input parameters the callbackParameters parameter (see below).
2. callbackParameters: any extra data you need in the function discussed above.

If you don't need any custom filtering logic (i.e. when you implement the custom filter in order to have a different UI only and not a different filtering logic), then simply pass to the filter handler the value and it will behave like a Text filter.
If you want it to behave like a Select filter, pass 2 parameters: the value and "SelectFilter",
If you want it to behave like a Number filter, pass 2 parameters: an object in this format {number: 2, comparator: ">"}, and "NumberFilter".
If you want it to behave like a Date filter, pass 2 parameters: the value and "DateFilter".

This PR is related to issue #249.
You can compare here between your master and my branch.
All the examples are under Examples > Column Filter.

Thank you!

@dana2208
Copy link
Contributor Author

I updated styling only in react-bootstrap-table.css.
Should I update also in react-bootstrap-table-all.css?

@dana2208
Copy link
Contributor Author

In issue #249 you asked me for some css changes.
In my point of view it looks ok now and if we put on the same line then the filter fields will e too small.
What do you think?

@AllenFang
Copy link
Owner

Should I update also in react-bootstrap-table-all.css?

yes, you should.

In my point of view it looks ok now and if we put on the same line then the filter fields will e too small.
What do you think?

ok, follow your solution

if you've completed this PR, let me know, I'll merge it. 🚢
Thanks your contribution :)

@dana2208
Copy link
Contributor Author

Hi @AllenFang ,

I hope you had great time during your holidays!
I updated react-bootstrap-table-all.css.
You can merge.

Thanks!

@AllenFang
Copy link
Owner

@dana2208, thanks :)
I'll spend some time to review this awesome new feature 💯

@AllenFang
Copy link
Owner

@dana2208, I've reviewed your code quickly at first seeing. a good and cleaner separated module design, use Filter as mediator to separate the filtering between filter and table by event emitter, finally use the origin handleFilterData function to filter data!! awesome!!

If you are in Taiwan, it's necessary shout you a meal 🎉
Anyway, you did a great job! Your name will be recorded as an important contributor on README.md on next version :)

@dana2208
Copy link
Contributor Author

Hi,

Thank you very much for the compliment! I am glad you like it!
And thanks for the invitation even if I can't make it to Taiwan in the next few years :)
It is an honor to be an important contributor to your very good repository!

@AllenFang
Copy link
Owner

Release on v1.5.1

@AllenFang AllenFang closed this Feb 17, 2016
@Kurtas
Copy link
Contributor

Kurtas commented Feb 21, 2016

@dana2208 Really great, thanks! I'm play with this today and I found one problem.
When I use NumberFilter with defaultValue options then I see in console this error
TypeError: this.refs.pagination is undefined and the app crashed (in dev mode)

Do you have any idea?

Here is code example

render() {
const adFilter = aDomain >= 0 && devicesList.size > 0 ? {type: 'NumberFilter', defaultValue: {comparator: '=', number: Number(aDomain)}} :  {type: 'NumberFilter'};
....
<BootstrapTable data={devicesList.toArray()}>
<TableHeaderColumn dataField="ad" dataSort={true} filter={adFilter} width={"110"}>Ad. Domain</TableHeaderColumn>

@dana2208
Copy link
Contributor Author

@Kurtas hello,

I need you to provide me with an example that reproduces the issue.
It is something that is related to pagination and in your example you don't even use pagination.
Your example looks like mine (under Column Filter) and there this error does not occur.
Are you sure that if you remove the default value from the Number filter you do not get this error?

Thanks

@Kurtas
Copy link
Contributor

Kurtas commented Feb 22, 2016

I removed all attributes from BoostrapTable because of space, sorry.
Here is full config what I using ....

            <BootstrapTable data={devicesList.toArray()}
                            dataSort={true}
                            condensed={true}
                            hover={true}
                            options={options}
                            pagination={true}
                            search={false}
                            exportCSV={true}
                            csvFileName="devices.csv"
                            striped={true}
                            >

Yes, I'm sure when I didn't use defaultValue for filtering then there wasn't this error
I also found solution for me, I have to set default value in componetWillUpdate method like that

  componentWillUpdate() {
    const {location: {query: {aDomainID}}} = this.props;
    this.adFilter = aDomainID >= 0 ? {type: 'NumberFilter', defaultValue: {comparator: '=', number: Number(aDomainID)}} :  {type: 'NumberFilter'};
  }

PS: I'm using https://github.com/este/este full dev stack, but I think that it doesn't have any relation.

@dana2208
Copy link
Contributor Author

Hi @Kurtas and @AllenFang,

The issue is the following one:
When a filter is defined with a default value, the in the specific filter element in the componentDidMount function I call the filterHandler callback which ends in calling the handleFilterData function in BootstrapTable. In this function it accesses refs of the BootstrapTable that does not exist yet since its own componentDidMount was not called yet
Here is what we can find in React documentation about componentDidMount:

At this point in the lifecycle, you can access any refs to your children (e.g., to access the underlying DOM representation). The componentDidMount() method of child components is invoked before that of parent components.

And from the changelog for React v0.13:

ref resolution order has changed slightly such that a ref to a component is available immediately after its componentDidMount method is called; this change should be observable only if your component calls a parent component's callback within your componentDidMount, which is an anti-pattern and should be avoided regardless.

I think the problem is that the state of the number of page is on the PaginationList and not on BootstrapTable.
In my point of view, all the state changes should be handled in the Bootstrap table and render function should pass those states to PaginationList through props.
Instead the state of number of pages is handled by the Pagination list and that's why we need to access this.refs.pagination to get the page size.

I can make a fix by using setTimeout in componentDidMount of the different filters so that I hope componentDidMount of the Bootstrap table already happened but I dislike this patch.

What do you think @AllenFang?

@AllenFang
Copy link
Owner

I agree we should make all states on the root component. in the past, I've moved selection row key from src/TableBody.js to src/BootstrapTable.js. I'll open another issues for fixing this bug and avoid to discuss in here.

@AllenFang
Copy link
Owner

@Kurtas @dana2208, Fixed on v1.5.3. Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants