Skip to content

Commit

Permalink
fix(filters): filters with != (not empty) should return non-blanks (
Browse files Browse the repository at this point in the history
…#1570)

- note that `!=` is "not equal" and is not equivalent to `<>` which is "not contains". So only the `!= ` will return non-blanks while `<> ` will often return no data when all data have white spaces.
- partially fixes a problem highlighted in issue #1569 on how to return non-blanks rows for a local JSON dataset
  • Loading branch information
ghiscoding committed Jun 12, 2024
1 parent 91ef50b commit 9837ef1
Show file tree
Hide file tree
Showing 6 changed files with 805 additions and 667 deletions.
7 changes: 5 additions & 2 deletions docs/column-functionalities/filters/input-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ Examples:
- `<02/28/17` => lower than date `02/28/17`
- `2001-01-01..2002-02-22` => range between 2001-01-01 and 2002-02-22
- String type
- `<>John` (anything except the sub-string `John`)
- `<>John` => not containing the sub-string `John`
- `!=John` => not equal to the text `John` (note that this is **not** equivalent to `<>`)
- `John*` => starts with the sub-string `John`
- `*Doe` => ends with the sub-string `Doe`
- `ab..ef` => anything included between "af" and "ef"
- refer to ASCII table for each character assigned number
- refer to the ASCII table for each character assigned index
- `!= ` => get defined only data and exclude any `undefined`, `null` or empty string `''`
- notice the empty string in the search value `' '`

Note that you could also do the same kind of functionality by using the Compound Filter.

Expand Down
4 changes: 3 additions & 1 deletion examples/vite-demo-vanilla-bundle/src/examples/example11.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,9 @@ export default class Example11 {
cost: (i % 33 === 0) ? null : Math.round(Math.random() * 10000) / 100,
completed: (randomFinish < new Date()),
product: { id: this.mockProducts()[randomItemId]?.id, itemName: this.mockProducts()[randomItemId]?.itemName, },
countryOfOrigin: (i % 2) ? { code: 'CA', name: 'Canada' } : { code: 'US', name: 'United States' },
countryOfOrigin: i % 33
? (i % 2) ? { code: 'CA', name: 'Canada' } : { code: 'US', name: 'United States' }
: null
};

if (!(i % 8)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,35 @@ describe('executeStringFilterCondition method', () => {
expect(output).toBe(true);
});

it('should return False when search term is a substring of the cell value and the operator is "<>" (not contains)', () => {
it('should return False when search term is a substring of the cell value and the operator is "<>" (not contains substring)', () => {
const searchTerms = ['bost'];
const options = { dataKey: '', operator: '<>', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption;
const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms));
expect(output).toBe(false);
});

it('should return True when search term is a substring of the cell value and the operator is "!=" (not contains) because "!=" compares agains the entire string', () => {
it('should return True when search term is a substring of the cell value and the operator is "!=" (not equal text) because "!=" compares agains the entire string', () => {
const searchTerms = ['bost'];
const options = { dataKey: '', operator: '!=', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption;
const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms));
expect(output).toBe(true);
});

it('should exclude anything undefined when search term is empty string value and the operator is "!=" (not equal text) because "!=" compares agains the entire string', () => {
const searchTerms = [''];
const options1 = { dataKey: '', operator: '!=', cellValue: null, fieldType: FieldType.string, searchTerms } as FilterConditionOption;
const options2 = { dataKey: '', operator: '!=', cellValue: '', fieldType: FieldType.string, searchTerms } as FilterConditionOption;
const options3 = { dataKey: '', operator: '!=', cellValue: 'hello world', fieldType: FieldType.string, searchTerms } as FilterConditionOption;

const output1 = executeStringFilterCondition(options1, getFilterParsedText(searchTerms));
const output2 = executeStringFilterCondition(options2, getFilterParsedText(searchTerms));
const output3 = executeStringFilterCondition(options3, getFilterParsedText(searchTerms));

expect(output1).toBe(false);
expect(output2).toBe(false);
expect(output3).toBe(true);
});

it('should return True when input value provided starts with same substring and the operator is empty string', () => {
const searchTerms = ['abb'];
const options = { dataKey: '', operator: '', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption;
Expand Down Expand Up @@ -171,17 +186,17 @@ describe('executeStringFilterCondition method', () => {
const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms));
expect(output).toBe(false);
});

it('should return True when input value contains accent is searchTerms value does not contain accent when "ignoreAccentOnStringFilterAndSort" is set in grid options', () => {
const searchTerms = ['jose'];
const options = { dataKey: '', operator: 'EQ', cellValue: 'José', fieldType: FieldType.string, ignoreAccentOnStringFilterAndSort: true} as FilterConditionOption;
const options = { dataKey: '', operator: 'EQ', cellValue: 'José', fieldType: FieldType.string, ignoreAccentOnStringFilterAndSort: true } as FilterConditionOption;
const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms));
expect(output).toBe(true);
});

it('should return False when input value contains accent is searchTerms value does not contain accent when "ignoreAccentOnStringFilterAndSort" is not set in grid options', () => {
const searchTerms = ['jose'];
const options = { dataKey: '', operator: 'EQ', cellValue: 'José', fieldType: FieldType.string, ignoreAccentOnStringFilterAndSort: false} as FilterConditionOption;
const options = { dataKey: '', operator: 'EQ', cellValue: 'José', fieldType: FieldType.string, ignoreAccentOnStringFilterAndSort: false } as FilterConditionOption;
const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms));
expect(output).toBe(false);
});
Expand Down
15 changes: 15 additions & 0 deletions packages/common/src/services/__tests__/filter.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,21 @@ describe('FilterService', () => {
expect(output).toBe(true);
});

it('should return False, since firstname is filled, when input value from datacontext contains an operator "<>" and its value is an empty string', () => {
const searchTerms = ['<> '];
const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column;
jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]);

service.init(gridStub);
const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string };
const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter);
const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, 'text');
const columnFilters = { firstname: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } } as ColumnFilters;
const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters });

expect(output).toBe(false);
});

it('should return False when input value from datacontext contains an operator >= and its value is greater than 10 substring but "autoParseInputFilterOperator" is set to false', () => {
const searchTerms = ['>=10'];
const mockColumn1 = { id: 'age', field: 'age', filterable: true, autoParseInputFilterOperator: false } as Column;
Expand Down
6 changes: 5 additions & 1 deletion packages/common/src/services/filter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,11 @@ export class FilterService {
// if search value has a regex match we will only keep the value without the operator
// in this case we need to overwrite the returned search values to truncate operator from the string search
if (Array.isArray(matches) && matches.length >= 1 && (Array.isArray(searchValues) && searchValues.length === 1)) {
searchValues[0] = searchTerm;
// string starts with a whitespace we'll trim only the first whitespace char
// e.g. " " becomes "" and " slick grid " becomes "slick grid " (notice last whitespace is kept)
searchValues[0] = (searchTerm.length > 0 && searchTerm.substring(0, 1) === ' ')
? searchTerm.substring(1)
: searchTerm;
}

return {
Expand Down
Loading

0 comments on commit 9837ef1

Please sign in to comment.