diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts index df2eebdae..a8eab153b 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts @@ -1,4 +1,4 @@ -import { FieldType, FilterConditionOption } from '../../models/index'; +import { FieldType, FilterConditionOption, OperatorType, SearchTerm } from '../../models/index'; import { executeDateFilterCondition, getFilterParsedDates } from '../dateFilterCondition'; import { executeFilterConditionTest, getParsedSearchTermsByFieldType } from '../filterConditionProcesses'; @@ -98,6 +98,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; @@ -140,14 +154,14 @@ describe('dateIsoFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { const searchTerms = [undefined]; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '' } as FilterConditionOption; - const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms as unknown as SearchTerm[], FieldType.dateIso) as any[]); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { const searchTerms = [undefined]; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25' } as FilterConditionOption; - const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms as unknown as SearchTerm[], FieldType.dateIso) as any[]); expect(output).toBe(false); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts index eb0032f7e..68e3dd008 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts @@ -1,4 +1,4 @@ -import { FieldType, FilterConditionOption } from '../../models/index'; +import { FieldType, FilterConditionOption, OperatorType, SearchTerm } from '../../models/index'; import { executeFilterConditionTest } from '../filterConditionProcesses'; import { executeNumberFilterCondition, getFilterParsedNumbers } from '../numberFilterCondition'; @@ -20,12 +20,12 @@ describe('executeNumberFilterCondition method', () => { it('should return True when first searchTerm is undefined provided neither an operator when executing "executeFilterConditionTest" method', () => { const searchTerms = [undefined]; const options = { dataKey: '', cellValue: 0, fieldType: FieldType.number } as FilterConditionOption; - const output = executeFilterConditionTest(options, searchTerms); + const output = executeFilterConditionTest(options, searchTerms as unknown as SearchTerm[]); expect(output).toBe(true); }); it('should return False when any cell value is provided without any search terms', () => { - const searchTerms = []; + const searchTerms: SearchTerm[] = []; const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.number } as FilterConditionOption; const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); @@ -115,6 +115,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/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts index dd6b049e3..5e65cd5aa 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts @@ -27,7 +27,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 && 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/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts index ce904b2aa..28055e634 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts @@ -12,7 +12,11 @@ export const executeNumberFilterCondition: FilterCondition = ((options: FilterCo } if (searchValue1 !== undefined && searchValue2 !== undefined) { - const isInclusive = (options && options.operator) === OperatorType.rangeInclusive; + let operator = options && 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); diff --git a/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts b/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts index 1844422c5..c8389cce4 100644 --- a/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts +++ b/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts @@ -1,11 +1,15 @@ import { FieldType } from './fieldType.enum'; import { OperatorString } from './operatorString'; +import { OperatorType } from './operatorType.enum'; import { SearchTerm } from './searchTerm.type'; 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/src/app/modules/angular-slickgrid/services/__tests__/extension.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/extension.service.spec.ts index 93d8036c1..c4db86b8b 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/extension.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/extension.service.spec.ts @@ -180,7 +180,7 @@ describe('ExtensionService', () => { expect(gridSpy).toHaveBeenCalled(); expect(getAddonSpy).toHaveBeenCalled(); expect(extSpy).toHaveBeenCalled(); - expect(output.instance).toEqual(instance); + expect(output!.instance).toEqual(instance); expect(output).toEqual({ name: ExtensionName.gridMenu, addon: instanceMock, instance: instanceMock, class: extensionGridMenuStub } as ExtensionModel); }); }); @@ -259,7 +259,7 @@ describe('ExtensionService', () => { expect(gridSpy).toHaveBeenCalled(); expect(getAddonSpy).toHaveBeenCalled(); expect(extSpy).toHaveBeenCalled(); - expect(output.instance).toEqual(instance); + expect(output!.instance).toEqual(instance); expect(output).toEqual({ name: ExtensionName.gridMenu, addon: instanceMock, instance: instanceMock, class: extensionGridMenuStub } as ExtensionModel); }); @@ -448,7 +448,7 @@ describe('ExtensionService', () => { }); describe('createExtensionsBeforeGridCreation method', () => { - let instanceMock; + let instanceMock: any; beforeEach(() => { instanceMock = { onColumnsChanged: () => { } }; @@ -703,36 +703,6 @@ describe('ExtensionService', () => { expect(setColumnsSpy).toHaveBeenCalledWith(columnsMock); }); - it('should re-register the Header Button when enable and method is called with new column definition collection provided as argument', () => { - const instanceMock = { onColumnsChanged: jest.fn() }; - const extensionMock = { name: ExtensionName.headerButton, addon: null, instance: null, class: null } as ExtensionModel; - const expectedExtension = { name: ExtensionName.headerButton, instance: instanceMock as unknown, class: null } as ExtensionModel; - const gridOptionsMock = { enableHeaderButton: true } as GridOption; - const columnsMock = [ - { id: 'field1', field: 'field1', nameKey: 'HELLO' }, - { id: 'field2', field: 'field2', nameKey: 'WORLD' } - ] as Column[]; - - jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); - jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub); - const spyGetExt = jest.spyOn(service, 'getExtensionByName').mockReturnValue(extensionMock); - const spyGmDispose = jest.spyOn(extensionHeaderButtonStub, 'dispose'); - const spyGmRegister = jest.spyOn(extensionHeaderButtonStub, 'register').mockReturnValue(instanceMock); - const spyAllCols = jest.spyOn(SharedService.prototype, 'allColumns', 'set'); - const setColumnsSpy = jest.spyOn(gridStub, 'setColumns'); - - service.renderColumnHeaders(columnsMock); - - expect(expectedExtension).toEqual(expectedExtension); - expect(spyGetExt).toHaveBeenCalled(); - expect(expectedExtension).toEqual(expectedExtension); - expect(spyGetExt).toHaveBeenCalled(); - expect(spyGmDispose).toHaveBeenCalled(); - expect(spyGmRegister).toHaveBeenCalled(); - expect(spyAllCols).toHaveBeenCalledWith(columnsMock); - expect(setColumnsSpy).toHaveBeenCalledWith(columnsMock); - }); - it('should re-register the Header Menu when enable and method is called with new column definition collection provided as argument', () => { const instanceMock = { onColumnsChanged: jest.fn() }; const extensionMock = { name: ExtensionName.headerMenu, addon: null, instance: null, class: null } as ExtensionModel; @@ -767,7 +737,7 @@ describe('ExtensionService', () => { describe('without ngx-translate', () => { beforeEach(() => { - translate = null; + (translate as any) = null; service = new ExtensionService( // extensions extensionStub as unknown as AutoTooltipExtension, diff --git a/src/app/modules/angular-slickgrid/services/extension.service.ts b/src/app/modules/angular-slickgrid/services/extension.service.ts index e05fd7e4e..c998bd9d5 100644 --- a/src/app/modules/angular-slickgrid/services/extension.service.ts +++ b/src/app/modules/angular-slickgrid/services/extension.service.ts @@ -429,11 +429,6 @@ export class ExtensionService { this.recreateExternalAddon(this.gridMenuExtension, ExtensionName.gridMenu); } - // recreate the Header Button when enabled - if (this.sharedService.gridOptions.enableHeaderButton) { - this.recreateExternalAddon(this.headerButtonExtension, ExtensionName.headerButton); - } - // recreate the Header Menu when enabled if (this.sharedService.gridOptions.enableHeaderMenu) { this.recreateExternalAddon(this.headerMenuExtension, ExtensionName.headerMenu); diff --git a/src/app/modules/angular-slickgrid/services/filter.service.ts b/src/app/modules/angular-slickgrid/services/filter.service.ts index 25e33af38..995ab124f 100644 --- a/src/app/modules/angular-slickgrid/services/filter.service.ts +++ b/src/app/modules/angular-slickgrid/services/filter.service.ts @@ -400,7 +400,8 @@ export class FilterService { searchTerms: searchValues || [], operator: operator as OperatorString, searchInputLastChar: inputLastChar, - filterSearchType: columnDef.filterSearchType + filterSearchType: columnDef.filterSearchType, + defaultFilterRangeOperator: this._gridOptions.defaultFilterRangeOperator, } as FilterConditionOption; } @@ -489,6 +490,7 @@ export class FilterService { operator: operator as OperatorString, searchInputLastChar: columnFilter.searchInputLastChar, filterSearchType: columnDef.filterSearchType, + defaultFilterRangeOperator: this._gridOptions.defaultFilterRangeOperator, } as FilterConditionOption; } diff --git a/test/cypress/integration/example12.spec.js b/test/cypress/integration/example12.spec.js index f0963bba0..28092ad0f 100644 --- a/test/cypress/integration/example12.spec.js +++ b/test/cypress/integration/example12.spec.js @@ -90,10 +90,9 @@ describe('Example 12: Localization (i18n)', () => { .find('.slick-custom-footer') .find('.right-footer') .should($span => { - const now = new Date(); + const dateTime = moment().format('YYYY-MM-DD, hh:mm a'); const text = removeExtraSpaces($span.text()); // remove all white spaces - const dateFormatted = moment(now).format('YYYY-MM-DD, hh:mm a'); - expect(text).to.eq(`Dernière mise à jour ${dateFormatted} | 1500 de 1500 éléments`); + expect(text).to.eq(`Dernière mise à jour ${dateTime} | 1500 de 1500 éléments`); }); }); @@ -270,14 +269,14 @@ describe('Example 12: Localization (i18n)', () => { it('should have some metrics shown in the grid footer', () => { const now = new Date(); - const dateFormatted = moment(now).format('YYYY-MM-DD, hh:mm a'); cy.get('#slickGridContainer-grid12') .find('.slick-custom-footer') .find('.right-footer') .should($span => { + const dateTime = moment().format('YYYY-MM-DD, hh:mm a'); const text = removeExtraSpaces($span.text()); // remove all white spaces - expect(text).to.eq(`Last Update ${dateFormatted} | 447 of 1500 items`); + expect(text).to.eq(`Last Update ${dateTime} | 447 of 1500 items`); }); });