Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit e144584

Browse files
Issue 370 - Clearing filtering_settings clears the by-column filter UI (#372)
1 parent f3e5e3f commit e144584

File tree

12 files changed

+137
-52
lines changed

12 files changed

+137
-52
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212
- `style_header_conditional`
1313

1414
### Fixed
15+
[#347](https://github.com/plotly/dash-core/issues/347)
16+
- Fixed table behavior when `filtering_settings` is updated through a callback
17+
1518
[#322](https://github.com/plotly/dash-core/issues/322)
1619
- Added LICENSE file to Python distributable
1720

demo/App.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
import * as R from 'ramda';
33
import React, {Component} from 'react';
44
import { DataTable } from 'dash-table';
5+
import Environment from 'core/environment';
56
import { memoizeOne } from 'core/memoizer';
67
import Logger from 'core/Logger';
7-
import AppMode from './AppMode';
8+
import AppState, { AppMode } from './AppMode';
89

910
import './style.less';
1011

1112
class App extends Component {
1213
constructor() {
1314
super();
1415

15-
this.state = AppMode;
16+
this.state = AppState;
1617

1718
const setProps = memoizeOne(() => {
1819
return newProps => {
@@ -28,11 +29,30 @@ class App extends Component {
2829
});
2930
}
3031

32+
renderMode() {
33+
const mode = Environment.searchParams.get('mode');
34+
35+
if (mode === AppMode.Filtering) {
36+
return (<button
37+
className='clear-filters'
38+
onClick={() => {
39+
const tableProps = R.clone(this.state.tableProps);
40+
tableProps.filtering_settings = '';
41+
42+
this.setState({ tableProps });
43+
}}
44+
>Clear Filter</button>);
45+
}
46+
}
47+
3148
render() {
32-
return (<DataTable
33-
setProps={this.setProps}
34-
{...this.state.tableProps}
35-
/>);
49+
return (<div>
50+
{this.renderMode()}
51+
<DataTable
52+
setProps={this.setProps}
53+
{...this.state.tableProps}
54+
/>
55+
</div>);
3656
}
3757
}
3858

demo/AppMode.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { TooltipSyntax } from 'dash-table/tooltips/props';
1616
export enum AppMode {
1717
Date = 'date',
1818
Default = 'default',
19+
Filtering = 'filtering',
1920
FixedTooltips = 'fixed,tooltips',
2021
FixedVirtualized = 'fixed,virtualized',
2122
ReadOnly = 'readonly',
@@ -31,6 +32,11 @@ export const ReadWriteModes = [
3132
AppMode.Virtualized
3233
];
3334

35+
export const BasicModes = [
36+
...ReadWriteModes,
37+
AppMode.ReadOnly
38+
];
39+
3440
function getBaseTableProps(mock: IDataMock) {
3541
return {
3642
id: 'table',
@@ -209,6 +215,13 @@ function getDateState() {
209215
return state;
210216
}
211217

218+
function getFilteringState() {
219+
const state = getDefaultState();
220+
state.tableProps.filtering = true;
221+
222+
return state;
223+
}
224+
212225
function getVirtualizedState() {
213226
const mock = generateMockData(5000);
214227

@@ -253,6 +266,8 @@ function getState() {
253266
switch (mode) {
254267
case AppMode.Date:
255268
return getDateState();
269+
case AppMode.Filtering:
270+
return getFilteringState();
256271
case AppMode.FixedTooltips:
257272
return getFixedTooltipsState();
258273
case AppMode.FixedVirtualized:

src/core/components/IsolatedInput/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ export default class IsolatedInput extends PureComponent<IProps, IState> {
3838
return this.props as PropsWithDefaults;
3939
}
4040

41+
componentWillReceiveProps(nextProps: IProps) {
42+
const { value: nextValue } = nextProps;
43+
44+
if (this.state.value !== nextValue) {
45+
this.setState({
46+
value: nextValue
47+
});
48+
}
49+
}
50+
4151
constructor(props: PropsWithDefaults) {
4252
super(props);
4353

src/core/syntax-tree/index.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,16 @@ export default class SyntaxTree {
1818
}
1919

2020
evaluate = (target: any) => {
21-
if (!this.isValid || !this.tree) {
21+
if (!this.isValid) {
2222
const msg = `unable to evaluate target: syntax tree is invalid for query=${this.query}`;
2323

2424
Logger.error(msg);
2525
throw new Error(msg);
2626
}
2727

28-
const evaluate = this.tree.lexeme.evaluate;
29-
30-
return evaluate ?
31-
evaluate(target, this.tree) :
32-
false;
28+
return this.tree && this.tree.lexeme && this.tree.lexeme.evaluate ?
29+
this.tree.lexeme.evaluate(target, this.tree) :
30+
true;
3331
}
3432

3533
filter = (targets: any[]) => {

src/core/syntax-tree/syntaxer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ export default (lexerResult: ILexerResult): ISyntaxerResult => {
6464
return { valid: false, error: `lexer -- ${lexerResult.error}` };
6565
}
6666

67+
if (lexerResult.lexemes.length === 0) {
68+
return { valid: true };
69+
}
70+
6771
try {
6872
return { tree: parser(lexemes), valid: true };
6973
} catch (error) {

src/dash-table/components/Filter/Column.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ interface IColumnFilterProps {
1414
value?: string;
1515
}
1616

17-
interface IColumnFilterState {
18-
value?: string;
19-
}
20-
21-
export default class ColumnFilter extends PureComponent<IColumnFilterProps, IColumnFilterState> {
17+
export default class ColumnFilter extends PureComponent<IColumnFilterProps> {
2218
constructor(props: IColumnFilterProps) {
2319
super(props);
2420

@@ -27,16 +23,6 @@ export default class ColumnFilter extends PureComponent<IColumnFilterProps, ICol
2723
};
2824
}
2925

30-
componentWillReceiveProps(nextProps: IColumnFilterProps) {
31-
const { value: nextValue } = nextProps;
32-
33-
if (this.state.value !== nextValue) {
34-
this.setState({
35-
value: nextValue
36-
});
37-
}
38-
}
39-
4026
private submit = (value: string | undefined) => {
4127
const { setFilter } = this.props;
4228

src/dash-table/components/FilterFactory.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,12 @@ export default class FilterFactory {
130130
}
131131

132132
const { tree } = syntaxerResult;
133-
const toCheck: (ISyntaxTree | undefined)[] = [tree];
133+
if (!tree) {
134+
this.ops.clear();
135+
return;
136+
}
134137

138+
const toCheck: (ISyntaxTree | undefined)[] = [tree];
135139
while (toCheck.length) {
136140
const item = toCheck.pop();
137141
if (!item) {

tests/cypress/tests/standalone/filtering_test.ts

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,76 @@ import Key from 'cypress/Key';
55
import { AppMode } from 'demo/AppMode';
66

77
describe(`filter`, () => {
8-
beforeEach(() => {
9-
cy.visit(`http://localhost:8080?mode=${AppMode.ColumnsInSpace}`);
10-
DashTable.toggleScroll(false);
8+
describe(`special characters`, () => {
9+
beforeEach(() => {
10+
cy.visit(`http://localhost:8080?mode=${AppMode.ColumnsInSpace}`);
11+
DashTable.toggleScroll(false);
12+
});
13+
14+
it('can filter on special column id', () => {
15+
DashTable.getFilterById('c cc').click();
16+
DOM.focused.type(`gt num(90)${Key.Enter}`);
17+
18+
DashTable.getFilterById('d:dd').click();
19+
DOM.focused.type(`lt num(12500)${Key.Enter}`);
20+
21+
DashTable.getFilterById('e-ee').click();
22+
DOM.focused.type(`is prime${Key.Enter}`);
23+
24+
DashTable.getFilterById('f_ff').click();
25+
DOM.focused.type(`le num(106)${Key.Enter}`);
26+
27+
DashTable.getFilterById('g.gg').click();
28+
DOM.focused.type(`gt num(1000)${Key.Enter}`);
29+
30+
DashTable.getFilterById('b+bb').click();
31+
DOM.focused.type(`eq "Wet"${Key.Enter}`);
32+
33+
DashTable.getCellById(0, 'rows').within(() => cy.get('.dash-cell-value').should('have.html', '101'));
34+
DashTable.getCellById(1, 'rows').should('not.exist');
35+
});
1136
});
1237

13-
it('can filter on special column id', () => {
14-
DashTable.getFilterById('c cc').click();
15-
DOM.focused.type(`gt num(90)${Key.Enter}`);
38+
describe('reset', () => {
39+
beforeEach(() => {
40+
cy.visit(`http://localhost:8080?mode=${AppMode.Filtering}`);
41+
DashTable.toggleScroll(false);
42+
});
43+
44+
it('updates results and filter fields', () => {
45+
let cell_0;
46+
let cell_1;
47+
48+
DashTable.getCellById(0, 'ccc')
49+
.within(() => cy.get('.dash-cell-value')
50+
.then($el => cell_0 = $el[0].innerHTML));
1651

17-
DashTable.getFilterById('d:dd').click();
18-
DOM.focused.type(`lt num(12500)${Key.Enter}`);
52+
DashTable.getCellById(1, 'ccc')
53+
.within(() => cy.get('.dash-cell-value')
54+
.then($el => cell_1 = $el[0].innerHTML));
1955

20-
DashTable.getFilterById('e-ee').click();
21-
DOM.focused.type(`is prime${Key.Enter}`);
56+
DashTable.getFilterById('ccc').click();
57+
DOM.focused.type(`gt num(100)`);
58+
DashTable.getFilterById('ddd').click();
59+
DOM.focused.type('lt num(20000)');
60+
DashTable.getFilterById('eee').click();
61+
DOM.focused.type('is prime');
62+
DashTable.getFilterById('bbb').click();
63+
DOM.focused.type(`eq "Wet"`);
64+
DashTable.getFilterById('ccc').click();
2265

23-
DashTable.getFilterById('f_ff').click();
24-
DOM.focused.type(`le num(106)${Key.Enter}`);
66+
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '101'));
67+
DashTable.getCellById(1, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '109'));
2568

26-
DashTable.getFilterById('g.gg').click();
27-
DOM.focused.type(`gt num(1000)${Key.Enter}`);
69+
cy.get('.clear-filters').click();
2870

29-
DashTable.getFilterById('b+bb').click();
30-
DOM.focused.type(`eq "Wet"${Key.Enter}`);
71+
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', cell_0));
72+
DashTable.getCellById(1, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', cell_1));
3173

32-
DashTable.getCellById(0, 'rows').within(() => cy.get('.dash-cell-value').should('have.html', '101'));
33-
DashTable.getCellById(1, 'rows').should('not.exist');
74+
DashTable.getFilterById('bbb').within(() => cy.get('input').should('have.value', ''));
75+
DashTable.getFilterById('ccc').within(() => cy.get('input').should('have.value', ''));
76+
DashTable.getFilterById('ddd').within(() => cy.get('input').should('have.value', ''));
77+
DashTable.getFilterById('eee').within(() => cy.get('input').should('have.value', ''));
78+
});
3479
});
3580
});

tests/cypress/tests/standalone/navigation_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import DashTable from 'cypress/DashTable';
22
import DOM from 'cypress/DOM';
33
import Key from 'cypress/Key';
44

5-
import { AppMode } from 'demo/AppMode';
5+
import { BasicModes } from 'demo/AppMode';
66

7-
Object.values(AppMode).forEach(mode => {
7+
Object.values(BasicModes).forEach(mode => {
88
describe(`navigate, mode=${mode}`, () => {
99
beforeEach(() => {
1010
cy.visit(`http://localhost:8080?mode=${mode}`);

tests/cypress/tests/standalone/select_row_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import DashTable from 'cypress/DashTable';
22

3-
import { AppMode } from 'demo/AppMode';
3+
import { BasicModes } from 'demo/AppMode';
44

5-
Object.values(AppMode).forEach(mode => {
5+
Object.values(BasicModes).forEach(mode => {
66
describe(`select row, mode=${mode}`, () => {
77
beforeEach(() => {
88
cy.visit(`http://localhost:8080?mode=${mode}`);

tests/cypress/tests/standalone/selection_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import DashTable from 'cypress/DashTable';
22
import DOM from 'cypress/DOM';
33
import Key from 'cypress/Key';
44

5-
import { AppMode } from 'demo/AppMode';
5+
import { BasicModes } from 'demo/AppMode';
66

7-
Object.values(AppMode).forEach(mode => {
7+
Object.values(BasicModes).forEach(mode => {
88
describe(`select, mode=${mode}`, () => {
99
beforeEach(() => {
1010
cy.visit(`http://localhost:8080?mode=${mode}`);

0 commit comments

Comments
 (0)