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

Commit c1d617b

Browse files
author
Shammamah Hossain
authored
Add spaces to regex for relational operators. (#612)
* Add spaces to regex for relational operators. * Add and adjust tests for relational operators. Since gt is now interpreted as a string, it was removed from the invalid filters test. * Add spaces to remaining relational operators. * Add test for symbol-based operators that aren't followed by a space. * Update src/dash-table/syntax-tree/lexeme/relational.ts Co-Authored-By: Marc-André Rivet <[email protected]> * Add space to regex for notEqual. * Add support for incomplete queries. * Add CHANGELOG entry. * Add unit tests. * Update tests/cypress/tests/unit/query_syntactic_tree_test.ts Co-Authored-By: Marc-André Rivet <[email protected]> * Trim filter values. * Ensure that trailing space does not get captured. * Revert "Trim filter values." This reverts commit aae4907. * Add regexpMatch parameter for relational operators. * Replace non-capturing group with positive lookahead. * Update relational.ts
1 parent eafc6d3 commit c1d617b

File tree

4 files changed

+65
-16
lines changed

4 files changed

+65
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2727
- Include input box for user to navigate directly to a page
2828

2929
### Fixed
30+
3031
[#460](https://github.com/plotly/dash-table/issues/460)
3132
- The `datestartswith` relational operator now supports number comparison
3233
- Fixed a bug where the implicit operator for columns was `equal` instead of the expected default for the column type
3334

3435
[#546](https://github.com/plotly/dash-table/issues/546)
3536
- Visible columns are used correctly for both header and data rows
3637

38+
[#563](https://github.com/plotly/dash-table/issues/563)
39+
- Fixed a bug where any string beginning with a relational operator was being interpreted as that operator being applied to the rest of the string (e.g., "lens" was interpreted as "<=ns")
40+
3741
[#591](https://github.com/plotly/dash-table/issues/591)
3842
- Fixed row and column selection when multiple tables are present
3943

src/dash-table/syntax-tree/lexeme/relational.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ export const contains: IUnboundedLexeme = R.merge({
5757
op.toString().indexOf(exp.toString()) !== -1
5858
),
5959
subType: RelationalOperator.Contains,
60-
regexp: /^(contains)/i
60+
regexp: /^((contains)(?=\s|$))/i,
61+
regexpMatch: 1
6162
}, LEXEME_BASE);
6263

6364
export const equal: IUnboundedLexeme = R.merge({
@@ -67,19 +68,22 @@ export const equal: IUnboundedLexeme = R.merge({
6768
op === exp
6869
),
6970
subType: RelationalOperator.Equal,
70-
regexp: /^(=|eq)/i
71+
regexp: /^(=|(eq)(?=\s|$))/i,
72+
regexpMatch: 1
7173
}, LEXEME_BASE);
7274

7375
export const greaterOrEqual: IUnboundedLexeme = R.merge({
7476
evaluate: relationalEvaluator(([op, exp]) => op >= exp),
7577
subType: RelationalOperator.GreaterOrEqual,
76-
regexp: /^(>=|ge)/i
78+
regexp: /^(>=|(ge)(?=\s|$))/i,
79+
regexpMatch: 1
7780
}, LEXEME_BASE);
7881

7982
export const greaterThan: IUnboundedLexeme = R.merge({
8083
evaluate: relationalEvaluator(([op, exp]) => op > exp),
8184
subType: RelationalOperator.GreaterThan,
82-
regexp: /^(>|gt)/i
85+
regexp: /^(>|(gt)(?=\s|$))/i,
86+
regexpMatch: 1
8387
}, LEXEME_BASE);
8488

8589
const DATE_OPTIONS: IDateValidation = {
@@ -100,23 +104,27 @@ export const dateStartsWith: IUnboundedLexeme = R.merge({
100104
normalizedOp.indexOf(normalizedExp) === 0;
101105
}),
102106
subType: RelationalOperator.DateStartsWith,
103-
regexp: /^(datestartswith)/i
107+
regexp: /^((datestartswith)(?=\s|$))/i,
108+
regexpMatch: 1
104109
}, LEXEME_BASE);
105110

106111
export const lessOrEqual: IUnboundedLexeme = R.merge({
107112
evaluate: relationalEvaluator(([op, exp]) => op <= exp),
108113
subType: RelationalOperator.LessOrEqual,
109-
regexp: /^(<=|le)/i
114+
regexp: /^(<=|(le)(?=\s|$))/i,
115+
regexpMatch: 1
110116
}, LEXEME_BASE);
111117

112118
export const lessThan: IUnboundedLexeme = R.merge({
113119
evaluate: relationalEvaluator(([op, exp]) => op < exp),
114120
subType: RelationalOperator.LessThan,
115-
regexp: /^(<|lt)/i
121+
regexp: /^(<|(lt)(?=\s|$))/i,
122+
regexpMatch: 1
116123
}, LEXEME_BASE);
117124

118125
export const notEqual: IUnboundedLexeme = R.merge({
119126
evaluate: relationalEvaluator(([op, exp]) => op !== exp),
120127
subType: RelationalOperator.NotEqual,
121-
regexp: /^(!=|ne)/i
122-
}, LEXEME_BASE);
128+
regexp: /^(!=|(ne)(?=\s|$))/i,
129+
regexpMatch: 1
130+
}, LEXEME_BASE);

tests/cypress/tests/standalone/filtering_test.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ describe('filter', () => {
7171
.within(() => cy.get('.dash-cell-value')
7272
.then($el => cell_1 = $el[0].innerHTML));
7373

74-
DashTable.getFilterById('ccc').click();
75-
DOM.focused.type(`gt`);
7674
DashTable.getFilterById('ddd').click();
7775
DOM.focused.type('"20 a000');
7876
DashTable.getFilterById('eee').click();
@@ -85,12 +83,10 @@ describe('filter', () => {
8583
DashTable.getCellById(1, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', cell_1));
8684

8785
DashTable.getFilterById('bbb').within(() => cy.get('input').should('have.value', '! !"'));
88-
DashTable.getFilterById('ccc').within(() => cy.get('input').should('have.value', 'gt'));
8986
DashTable.getFilterById('ddd').within(() => cy.get('input').should('have.value', '"20 a000'));
9087
DashTable.getFilterById('eee').within(() => cy.get('input').should('have.value', 'is prime2'));
9188

9289
DashTable.getFilterById('bbb').should('have.class', 'invalid');
93-
DashTable.getFilterById('ccc').should('have.class', 'invalid');
9490
DashTable.getFilterById('ddd').should('have.class', 'invalid');
9591
DashTable.getFilterById('eee').should('have.class', 'invalid');
9692
});
@@ -113,14 +109,43 @@ describe('filter', () => {
113109
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '100'));
114110
});
115111

112+
it('does not use text-based relational operators unless they are followed by a space', () => {
113+
DashTable.getCellById(2, 'ccc').click();
114+
DOM.focused.type(`le5${Key.Enter}`);
115+
116+
DashTable.getFilterById('ccc').click();
117+
DOM.focused.type(`le5${Key.Enter}`);
118+
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', 'le5'));
119+
DashTable.getCellById(0, 'rows').within(() => cy.get('.dash-cell-value').should('have.html', '3'));
120+
121+
cy.get('.clear-filters').click();
122+
123+
DashTable.getFilterById('ccc').click();
124+
DOM.focused.type(`le 5${Key.Enter}`);
125+
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '1'));
126+
DashTable.getCellById(1, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '2'));
127+
DashTable.getCellById(2, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '4'));
128+
DashTable.getCellById(3, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '5'));
129+
});
130+
131+
it('uses symbol relational operators that are not followed by a space', () => {
132+
DashTable.getFilterById('ccc').click();
133+
DOM.focused.type(`<=5${Key.Enter}`);
134+
DashTable.getCellById(0, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '1'));
135+
DashTable.getCellById(1, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '2'));
136+
DashTable.getCellById(2, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '3'));
137+
DashTable.getCellById(3, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '4'));
138+
DashTable.getCellById(4, 'ccc').within(() => cy.get('.dash-cell-value').should('have.html', '5'));
139+
});
140+
116141
it('typing invalid followed by valid query fragment does not reset invalid', () => {
117142
DashTable.getFilterById('ccc').click();
118-
DOM.focused.type(`gt`);
143+
DOM.focused.type(`is prime2`);
119144
DashTable.getFilterById('ddd').click();
120145
DOM.focused.type('lt 20000');
121146
DashTable.getFilterById('eee').click();
122147

123-
DashTable.getFilterById('ccc').within(() => cy.get('input').should('have.value', 'gt'));
148+
DashTable.getFilterById('ccc').within(() => cy.get('input').should('have.value', 'is prime2'));
124149
DashTable.getFilterById('ddd').within(() => cy.get('input').should('have.value', 'lt 20000'));
125150
});
126151

tests/cypress/tests/unit/query_syntactic_tree_test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,5 +589,17 @@ describe('Query Syntax Tree', () => {
589589
expect(tree.evaluate({ a: 'abc v' })).to.equal(true);
590590
expect(tree.evaluate({ a: 'abc w' })).to.equal(false);
591591
});
592+
593+
it('correctly interprets text-based with no spaces as invalid', () => {
594+
const tree = new QuerySyntaxTree('{a} le5');
595+
expect(tree.isValid).to.equal(false);
596+
});
597+
598+
it('correctly interprets non-text-based with no spaces as valid', () => {
599+
const tree = new QuerySyntaxTree('{a}<=5');
600+
expect(tree.isValid).to.equal(true);
601+
expect(tree.evaluate({ a: 4 })).to.equal(true);
602+
603+
});
592604
});
593-
});
605+
});

0 commit comments

Comments
 (0)