diff --git a/packages/common/package.json b/packages/common/package.json index 0d36e327b..202dfd58d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -68,7 +68,7 @@ "jquery": "^3.5.1", "jquery-ui-dist": "^1.12.1", "moment-mini": "^2.24.0", - "multiple-select-modified": "^1.3.10", + "multiple-select-modified": "^1.3.11", "slickgrid": "^2.4.33" }, "devDependencies": { diff --git a/packages/common/src/filter-conditions/__tests__/filterConditionProcesses.spec.ts b/packages/common/src/filter-conditions/__tests__/filterConditionProcesses.spec.ts index b199cddee..0d43743a7 100644 --- a/packages/common/src/filter-conditions/__tests__/filterConditionProcesses.spec.ts +++ b/packages/common/src/filter-conditions/__tests__/filterConditionProcesses.spec.ts @@ -44,6 +44,6 @@ describe('getParsedSearchTermsByFieldType method', () => { const input = 'world'; const result = getParsedSearchTermsByFieldType([input], 'string'); - expect(result).toBe(input); + expect(result).toEqual([input]); }); }); \ No newline at end of file diff --git a/packages/common/src/filter-conditions/__tests__/stringFilterCondition.spec.ts b/packages/common/src/filter-conditions/__tests__/stringFilterCondition.spec.ts index fdde0662c..d06b736ab 100644 --- a/packages/common/src/filter-conditions/__tests__/stringFilterCondition.spec.ts +++ b/packages/common/src/filter-conditions/__tests__/stringFilterCondition.spec.ts @@ -129,4 +129,46 @@ describe('executeStringFilterCondition method', () => { const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); + + it('should return True when input value is in the range of search terms using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'c', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return True when input value is on the inclusive limit range of search terms using 2 dots (..) notation AND no operator provided except a defaultFilterRangeOperator is rangeInclusive', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeInclusive, cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return False when input value is on the inclusive limit range of search terms using 2 dots (..) notation AND no operator provided except a defaultFilterRangeOperator is rangeExclusive', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeExclusive, cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); + + it('should return False when input value is not in the range of search terms using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'g', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); + + it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive" using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive" using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); }); diff --git a/packages/common/src/filter-conditions/filterConditionProcesses.ts b/packages/common/src/filter-conditions/filterConditionProcesses.ts index 4d566c32a..2347fb188 100644 --- a/packages/common/src/filter-conditions/filterConditionProcesses.ts +++ b/packages/common/src/filter-conditions/filterConditionProcesses.ts @@ -39,7 +39,7 @@ export const executeFilterConditionTest: FilterCondition = ((options: FilterCond case 'text': default: // the parsedSearchTerms should be single value (result came from getFilterParsedText() method) - return executeStringFilterCondition(options, parsedSearchTerms as SearchTerm); + return executeStringFilterCondition(options, (parsedSearchTerms || []) as string[]); } }) as FilterCondition; @@ -68,7 +68,7 @@ export function getParsedSearchTermsByFieldType(inputSearchTerms: SearchTerm[] | parsedSearchValues = getFilterParsedObjectResult(inputSearchTerms); break; case 'text': - parsedSearchValues = getFilterParsedText(inputSearchTerms); + parsedSearchValues = getFilterParsedText(inputSearchTerms) as SearchTerm[]; break; } return parsedSearchValues; diff --git a/packages/common/src/filter-conditions/stringFilterCondition.ts b/packages/common/src/filter-conditions/stringFilterCondition.ts index 133f01281..479e80438 100644 --- a/packages/common/src/filter-conditions/stringFilterCondition.ts +++ b/packages/common/src/filter-conditions/stringFilterCondition.ts @@ -1,10 +1,12 @@ -import { OperatorType, SearchTerm } from '../enums/index'; +import { OperatorString, OperatorType, SearchTerm } from '../enums/index'; import { FilterCondition, FilterConditionOption } from '../interfaces/index'; import { testFilterCondition } from './filterUtilities'; /** Execute filter condition check on each cell */ -export const executeStringFilterCondition: FilterCondition = ((options: FilterConditionOption, parsedSearchValue: string | undefined) => { - if (parsedSearchValue === undefined && !options.operator) { +export const executeStringFilterCondition: FilterCondition = ((options: FilterConditionOption, parsedSearchValues: string[]) => { + let [searchValue1, searchValue2] = parsedSearchValues; + + if (searchValue1 === undefined && !options.operator) { return true; } @@ -13,28 +15,64 @@ export const executeStringFilterCondition: FilterCondition = ((options: FilterCo // make both the cell value and search value lower for case insensitive comparison const cellValue = options.cellValue.toLowerCase(); - if (typeof parsedSearchValue === 'string') { - parsedSearchValue = parsedSearchValue.toLowerCase(); + if (typeof searchValue1 === 'string') { + searchValue1 = searchValue1.toLowerCase(); + } + if (typeof searchValue2 === 'string') { + searchValue2 = searchValue2.toLowerCase(); } - if (options.operator === '*' || options.operator === OperatorType.endsWith) { - return cellValue.endsWith(parsedSearchValue); - } else if ((options.operator === '' && options.searchInputLastChar === '*') || options.operator === OperatorType.startsWith) { - return cellValue.startsWith(parsedSearchValue); - } else if (options.operator === '' || options.operator === OperatorType.contains) { - return (cellValue.indexOf(parsedSearchValue) > -1); - } else if (options.operator === '<>' || options.operator === OperatorType.notContains) { - return (cellValue.indexOf(parsedSearchValue) === -1); + if (searchValue1 !== undefined && searchValue2 !== undefined) { + let operator = options?.operator ?? options.defaultFilterRangeOperator; + if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) { + operator = options.defaultFilterRangeOperator; + } + const isInclusive = operator === OperatorType.rangeInclusive; + const searchResult1 = testStringCondition((isInclusive ? '>=' : '>'), cellValue, searchValue1, options.searchInputLastChar); + const searchResult2 = testStringCondition((isInclusive ? '<=' : '<'), cellValue, searchValue2, options.searchInputLastChar); + return searchResult1 && searchResult2; } - return testFilterCondition(options.operator || '==', cellValue, parsedSearchValue); + const searchResult1 = testStringCondition(options.operator, cellValue, searchValue1, options.searchInputLastChar); + return searchResult1; }) as FilterCondition; /** * From our search filter value(s), get the parsed value(s). * This is called only once per filter before running the actual filter condition check on each cell */ -export function getFilterParsedText(inputSearchTerms: SearchTerm[] | undefined): SearchTerm { - let parsedSearchValue = (Array.isArray(inputSearchTerms) && inputSearchTerms.length > 0) ? inputSearchTerms[0] : ''; - parsedSearchValue = parsedSearchValue === undefined || parsedSearchValue === null ? '' : `${parsedSearchValue}`; // make sure it's a string - return parsedSearchValue; +export function getFilterParsedText(inputSearchTerms: SearchTerm[] | undefined): SearchTerm[] { + const defaultSearchTerm = ''; // when nothing is provided, we'll default to 0 + const searchTerms = Array.isArray(inputSearchTerms) && inputSearchTerms || [defaultSearchTerm]; + const parsedSearchValues: string[] = []; + let searchValue1; + let searchValue2; + if (searchTerms.length === 2 || (typeof searchTerms[0] === 'string' && (searchTerms[0] as string).indexOf('..') > 0)) { + const searchValues = (searchTerms.length === 2) ? searchTerms : (searchTerms[0] as string).split('..'); + searchValue1 = `${Array.isArray(searchValues) ? searchValues[0] : ''}`; + searchValue2 = `${Array.isArray(searchValues) ? searchValues[1] : ''}`; + } else { + const parsedSearchValue = (Array.isArray(inputSearchTerms) && inputSearchTerms.length > 0) ? inputSearchTerms[0] : ''; + searchValue1 = parsedSearchValue === undefined || parsedSearchValue === null ? '' : `${parsedSearchValue}`; // make sure it's a string + } + + if (searchValue1 !== undefined && searchValue2 !== undefined) { + parsedSearchValues.push(searchValue1 as string, searchValue2 as string); + } else if (searchValue1 !== undefined) { + parsedSearchValues.push(searchValue1 as string); + } + return parsedSearchValues; } + +/** Execute the filter string test condition, returns a boolean */ +function testStringCondition(operator: OperatorType | OperatorString, cellValue: string, searchValue: string, searchInputLastChar?: string): boolean { + if (operator === '*' || operator === OperatorType.endsWith) { + return cellValue.endsWith(searchValue); + } else if ((operator === '' && searchInputLastChar === '*') || operator === OperatorType.startsWith) { + return cellValue.startsWith(searchValue); + } else if (operator === '' || operator === OperatorType.contains) { + return (cellValue.indexOf(searchValue) > -1); + } else if (operator === '<>' || operator === OperatorType.notContains) { + return (cellValue.indexOf(searchValue) === -1); + } + return testFilterCondition(operator || '==', cellValue, searchValue); +} \ No newline at end of file diff --git a/packages/common/src/services/__tests__/filter.service.spec.ts b/packages/common/src/services/__tests__/filter.service.spec.ts index 1cf0cd3f1..71f15da13 100644 --- a/packages/common/src/services/__tests__/filter.service.spec.ts +++ b/packages/common/src/services/__tests__/filter.service.spec.ts @@ -322,7 +322,7 @@ describe('FilterService', () => { }); it('should execute the search callback normally when a input change event is triggered and searchTerms are defined', () => { - const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; + const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; const spySearchChange = jest.spyOn(service.onSearchChange as any, 'notify'); const spyEmit = jest.spyOn(service, 'emitFilterChanged'); @@ -340,14 +340,14 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['John'], - parsedSearchTerms: 'John', + parsedSearchTerms: ['John'], grid: gridStub }, expect.anything()); expect(spyEmit).toHaveBeenCalledWith('local'); }); it('should execute the callback normally when a input change event is triggered and the searchTerm comes from this event.target', () => { - const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; + const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; const spySearchChange = jest.spyOn(service.onSearchChange as any, 'notify'); service.init(gridStub); @@ -367,7 +367,7 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['John'], - parsedSearchTerms: 'John', + parsedSearchTerms: ['John'], grid: gridStub }, expect.anything()); }); @@ -441,7 +441,7 @@ describe('FilterService', () => { describe('clearFilterByColumnId method', () => { it('should clear the filter by passing a column id as argument on a backend grid', () => { - const filterExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string }; + const filterExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string }; const newEvent = new CustomEvent(`mouseup`); const spyClear = jest.spyOn(service.getFiltersMetadata()[0], 'clear'); const spyFilterChange = jest.spyOn(service, 'onBackendFilterChange'); @@ -460,8 +460,8 @@ describe('FilterService', () => { }); it('should not call "onBackendFilterChange" method when the filter is previously empty', () => { - const filterFirstExpectation = { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; - const filterLastExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string }; + const filterFirstExpectation = { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; + const filterLastExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string }; const newEvent = new Event('mouseup'); const spyClear = jest.spyOn(service.getFiltersMetadata()[2], 'clear'); const spyFilterChange = jest.spyOn(service, 'onBackendFilterChange'); @@ -550,7 +550,7 @@ describe('FilterService', () => { expect(spyClear).toHaveBeenCalled(); expect(filterCountBefore).toBe(2); expect(filterCountAfter).toBe(1); - expect(service.getColumnFilters()).toEqual({ lastName: { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string } }); + expect(service.getColumnFilters()).toEqual({ lastName: { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string } }); expect(spyEmitter).toHaveBeenCalledWith('local'); }); }); @@ -573,7 +573,7 @@ describe('FilterService', () => { const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); - expect(columnFilters.firstName['parsedSearchTerms']).toBe('John'); + expect(columnFilters.firstName['parsedSearchTerms']).toEqual(['John']); expect(output).toBe(true); }); @@ -836,7 +836,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John Doe'], parsedSearchTerms: 'John Doe', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John Doe'], parsedSearchTerms: ['John Doe'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -848,7 +848,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); @@ -860,7 +860,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); @@ -1078,7 +1078,7 @@ describe('FilterService', () => { expect(emitSpy).toHaveBeenCalledWith('local'); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); }); @@ -1096,7 +1096,7 @@ describe('FilterService', () => { expect(emitSpy).not.toHaveBeenCalled(); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); }); @@ -1123,7 +1123,7 @@ describe('FilterService', () => { expect(clearSpy).toHaveBeenCalledWith(false); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); expect(clearSpy).toHaveBeenCalledWith(false); @@ -1152,7 +1152,7 @@ describe('FilterService', () => { expect(mockRefreshBackendDataset).not.toHaveBeenCalled(); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); expect(clearSpy).toHaveBeenCalledWith(false); @@ -1474,7 +1474,7 @@ describe('FilterService', () => { gridStub.onHeaderRowCellRendered.notify(mockArgs1 as any, new Slick.EventData(), gridStub); gridStub.onHeaderRowCellRendered.notify(mockArgs2 as any, new Slick.EventData(), gridStub); - const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: 'map', type: FieldType.string } }; + const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: ['map'], type: FieldType.string } }; service.updateFilters([{ columnId: 'file', operator: '', searchTerms: ['map'] }], true, true, true); const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); @@ -1498,7 +1498,7 @@ describe('FilterService', () => { gridStub.onHeaderRowCellRendered.notify(mockArgs1 as any, new Slick.EventData(), gridStub); gridStub.onHeaderRowCellRendered.notify(mockArgs2 as any, new Slick.EventData(), gridStub); - const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: 'map', type: FieldType.string } }; + const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: ['map'], type: FieldType.string } }; service.updateFilters([{ columnId: 'file', operator: '', searchTerms: ['map'] }], true, true, true); const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); @@ -1528,7 +1528,7 @@ describe('FilterService', () => { expect(pubSubSpy).toHaveBeenCalledWith(`onFilterChanged`, [{ columnId: 'file', operator: 'Contains', searchTerms: ['unknown'] }]); expect(output).toBe(false); - expect(preFilterSpy).toHaveBeenCalledWith(dataset, { ...columnFilters, file: { ...columnFilters.file, operator: 'Contains', parsedSearchTerms: 'unknown', type: 'string' } }); // it will use Contains by default + expect(preFilterSpy).toHaveBeenCalledWith(dataset, { ...columnFilters, file: { ...columnFilters.file, operator: 'Contains', parsedSearchTerms: ['unknown'], type: 'string' } }); // it will use Contains by default expect(preFilterSpy).toHaveReturnedWith([]); }); }); diff --git a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip index 0a025b0d5..9dacc07c5 100644 Binary files a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip and b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip differ diff --git a/test/cypress/integration/example11.spec.js b/test/cypress/integration/example11.spec.js index fec10b231..78a03e679 100644 --- a/test/cypress/integration/example11.spec.js +++ b/test/cypress/integration/example11.spec.js @@ -552,4 +552,13 @@ describe('Example 11 - Batch Editing', () => { cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(7)`).find('.checkmark-icon').should('have.length', 1); }); + + it('should be able to filter "Country of Origin" with a text range filter "b..e" and expect to see only Canada showing up', () => { + cy.get('input.search-filter.filter-countryOfOrigin').type('b..e'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(9)`).should('contain', 'Canada'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(9)`).should('contain', 'Canada'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(9)`).should('contain', 'Canada'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(9)`).should('contain', 'Canada'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 4}px"] > .slick-cell:nth(9)`).should('contain', 'Canada'); + }); }); diff --git a/yarn.lock b/yarn.lock index bc21ca8fd..1d3061c12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8148,10 +8148,10 @@ multimatch@^3.0.0: arrify "^1.0.1" minimatch "^3.0.4" -multiple-select-modified@^1.3.10: - version "1.3.10" - resolved "https://registry.yarnpkg.com/multiple-select-modified/-/multiple-select-modified-1.3.10.tgz#753e8c40f2e60f91b2cc36f0d874d532aeae53c5" - integrity sha512-CVbw9hIYa8VyBYpk9cv2+6jTKt/zFKILlWgy7x0C/jUlNrQk8+X7RW2XUMAYX2Oguyk287tD3YOhrLmc1jXqaw== +multiple-select-modified@^1.3.11: + version "1.3.11" + resolved "https://registry.yarnpkg.com/multiple-select-modified/-/multiple-select-modified-1.3.11.tgz#dedd1eac1229cbc210f11f39fb1a003f6d064d56" + integrity sha512-ljf3XEQjePY5QN7g3sYAhhBOHmSr1JSQuqQEVrNbFr6KVQ+G4MVh4NFhAF3OC/KwnareShXG/b+44MsVq0lqSg== dependencies: jquery "^3.5.1"