diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts index a39fa7c9f..87139b43b 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example12.ts @@ -181,6 +181,31 @@ export class Example12 { massUpdate: true, minValue: 0, maxValue: 100, }, }, + // { + // id: 'percentComplete2', name: '% Complete', field: 'analysis.percentComplete', minWidth: 100, + // type: FieldType.number, + // sortable: true, filterable: true, columnGroup: 'Analysis', + // // filter: { model: Filters.compoundSlider, operator: '>=' }, + // formatter: Formatters.complex, + // exportCustomFormatter: Formatters.complex, // without the Editing cell Formatter + // editor: { + // model: Editors.singleSelect, + // serializeComplexValueFormat: 'flat', // if we keep "object" as the default it will apply { value: 2, label: 2 } which is not what we want in this case + // collection: Array.from(Array(101).keys()).map(k => ({ value: k, label: k })), + // collectionOptions: { + // addCustomFirstEntry: { value: '', label: '--none--' } + // }, + // collectionOverride: (_collectionInput, args) => { + // const originalCollection = args.originalCollections || []; + // const duration = args?.dataContext?.duration ?? args?.compositeEditorOptions?.formValues?.duration; + // if (duration === 10) { + // return originalCollection.filter(itemCollection => +itemCollection.value !== 1); + // } + // return originalCollection; + // }, + // massUpdate: true, minValue: 0, maxValue: 100, + // }, + // }, { id: 'complexity', name: 'Complexity', field: 'complexity', minWidth: 100, type: FieldType.number, diff --git a/packages/common/src/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts b/packages/common/src/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts index 84215a658..fe758f7f4 100644 --- a/packages/common/src/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts +++ b/packages/common/src/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts @@ -1,4 +1,4 @@ -import { FieldType, SearchTerm } from '../../enums/index'; +import { FieldType, OperatorType } from '../../enums/index'; import { FilterConditionOption } from '../../interfaces/index'; import { executeDateFilterCondition, getFilterParsedDates } from '../dateFilterCondition'; import { executeFilterConditionTest, getParsedSearchTermsByFieldType } from '../filterConditionProcesses'; @@ -99,6 +99,20 @@ describe('dateIsoFilterCondition method', () => { }); describe('date range', () => { + 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 = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeInclusive, cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + 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 = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeExclusive, cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + it('should return True when input value is in the range of search terms', () => { const searchTerms = ['1993-12-01..1993-12-31']; const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; diff --git a/packages/common/src/filter-conditions/__tests__/numberFilterCondition.spec.ts b/packages/common/src/filter-conditions/__tests__/numberFilterCondition.spec.ts index 0496602bd..cb160bcef 100644 --- a/packages/common/src/filter-conditions/__tests__/numberFilterCondition.spec.ts +++ b/packages/common/src/filter-conditions/__tests__/numberFilterCondition.spec.ts @@ -1,4 +1,4 @@ -import { FieldType } from '../../enums/index'; +import { FieldType, OperatorType } from '../../enums/index'; import { FilterConditionOption } from '../../interfaces/index'; import { executeFilterConditionTest } from '../filterConditionProcesses'; import { executeNumberFilterCondition, getFilterParsedNumbers } from '../numberFilterCondition'; @@ -116,6 +116,20 @@ describe('executeNumberFilterCondition method', () => { 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 = ['1..5']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeInclusive, cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(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 = ['1..5']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeExclusive, cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(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 = ['1..5']; const options = { dataKey: '', operator: 'EQ', cellValue: '15', fieldType: FieldType.number, searchTerms } as FilterConditionOption; diff --git a/packages/common/src/filter-conditions/dateFilterCondition.ts b/packages/common/src/filter-conditions/dateFilterCondition.ts index 57cf1832d..b88baa167 100644 --- a/packages/common/src/filter-conditions/dateFilterCondition.ts +++ b/packages/common/src/filter-conditions/dateFilterCondition.ts @@ -28,7 +28,11 @@ export function executeDateFilterCondition(options: FilterConditionOption, parse // having 2 search dates, we assume that it's a date range filtering and we'll compare against both dates if (searchDate1 && searchDate2) { - const isInclusive = options.operator && options.operator === OperatorType.rangeInclusive; + let operator = options?.operator ?? options.defaultFilterRangeOperator; + if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) { + operator = options.defaultFilterRangeOperator; + } + const isInclusive = operator === OperatorType.rangeInclusive; const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), dateCellTimestamp, searchDate1.valueOf()); const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), dateCellTimestamp, searchDate2.valueOf()); return (resultCondition1 && resultCondition2); diff --git a/packages/common/src/filter-conditions/numberFilterCondition.ts b/packages/common/src/filter-conditions/numberFilterCondition.ts index 7c642aae5..ad09a3e83 100644 --- a/packages/common/src/filter-conditions/numberFilterCondition.ts +++ b/packages/common/src/filter-conditions/numberFilterCondition.ts @@ -13,7 +13,11 @@ export const executeNumberFilterCondition: FilterCondition = ((options: FilterCo } if (searchValue1 !== undefined && searchValue2 !== undefined) { - const isInclusive = options?.operator === OperatorType.rangeInclusive; + let operator = options?.operator ?? options.defaultFilterRangeOperator; + if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) { + operator = options.defaultFilterRangeOperator; + } + const isInclusive = operator === OperatorType.rangeInclusive; const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), cellValue, +searchValue1); const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), cellValue, +searchValue2); return (resultCondition1 && resultCondition2); @@ -31,7 +35,6 @@ export function getFilterParsedNumbers(inputSearchTerms: SearchTerm[] | undefine const parsedSearchValues: number[] = []; 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 = parseFloat(Array.isArray(searchValues) ? searchValues[0] as string : ''); diff --git a/packages/common/src/interfaces/filterConditionOption.interface.ts b/packages/common/src/interfaces/filterConditionOption.interface.ts index efdb7c3a0..ec2c322d1 100644 --- a/packages/common/src/interfaces/filterConditionOption.interface.ts +++ b/packages/common/src/interfaces/filterConditionOption.interface.ts @@ -1,9 +1,12 @@ -import { FieldType, OperatorString, SearchTerm } from '../enums/index'; +import { FieldType, OperatorString, OperatorType, SearchTerm } from '../enums/index'; export interface FilterConditionOption { /** optional object data key */ dataKey?: string; + /** pull the grid option default filter in case the "operator" provided is not a range operator or is simply undefined */ + defaultFilterRangeOperator: OperatorType | OperatorString; + /** filter operator */ operator: OperatorString; diff --git a/packages/common/src/services/filter.service.ts b/packages/common/src/services/filter.service.ts index fd64c79ea..9a61ee174 100644 --- a/packages/common/src/services/filter.service.ts +++ b/packages/common/src/services/filter.service.ts @@ -389,7 +389,8 @@ export class FilterService { searchTerms: searchValues || [], operator: operator as OperatorString, searchInputLastChar: inputLastChar, - filterSearchType: columnDef.filterSearchType + filterSearchType: columnDef.filterSearchType, + defaultFilterRangeOperator: this._gridOptions.defaultFilterRangeOperator, } as FilterConditionOption; } @@ -476,6 +477,7 @@ export class FilterService { operator: operator as OperatorString, searchInputLastChar: columnFilter.searchInputLastChar, filterSearchType: columnDef.filterSearchType, + defaultFilterRangeOperator: this._gridOptions.defaultFilterRangeOperator, } as FilterConditionOption; } 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 8406280db..d1e3ca67e 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