Skip to content

Commit

Permalink
fix(filters): skipCompoundOperatorFilterWithNullInput skip empty stri…
Browse files Browse the repository at this point in the history
…ng (#1566)

* fix(filters): skipCompoundOperatorFilterWithNullInput skip empty string
  • Loading branch information
ghiscoding committed Jun 10, 2024
1 parent ce0d7c0 commit 4d69bc0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 8 deletions.
3 changes: 2 additions & 1 deletion examples/vite-demo-vanilla-bundle/src/examples/example12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ export default class Example12 {
this.toggleBodyBackground();
}
}
}
},
skipCompoundOperatorFilterWithNullInput: true
};
}

Expand Down
34 changes: 34 additions & 0 deletions packages/common/src/filters/__tests__/compoundDateFilter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,40 @@ describe('CompoundDateFilter', () => {
expect(spyCallback).not.toHaveBeenCalled();
});

it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as True and value is undefined', () => {
mockColumn.filter!.operator = '>';
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = true;
const callbackSpy = jest.spyOn(filterArguments, 'callback');

filter.init(filterArguments);
filter.setValues(['']);
const filterInputElm = divContainer.querySelector('.search-filter.filter-finish input.date-picker') as HTMLInputElement;
const filterSelectElm = divContainer.querySelector('.search-filter.filter-finish select') as HTMLInputElement;

filterInputElm.value = undefined as any;
filterSelectElm.value = '<=';
filterSelectElm.dispatchEvent(new Event('change'));

expect(callbackSpy).not.toHaveBeenCalled();
});

it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as True and value is empty string', () => {
mockColumn.filter!.operator = '>';
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = true;
const callbackSpy = jest.spyOn(filterArguments, 'callback');

filter.init(filterArguments);
filter.setValues(['']);
const filterInputElm = divContainer.querySelector('.search-filter.filter-finish input.date-picker') as HTMLInputElement;
const filterSelectElm = divContainer.querySelector('.search-filter.filter-finish select') as HTMLInputElement;

filterInputElm.value = '';
filterSelectElm.value = '<=';
filterSelectElm.dispatchEvent(new Event('change'));

expect(callbackSpy).not.toHaveBeenCalled();
});

it('should change operator dropdown without a date entered and expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as False', () => {
mockColumn.filter!.operator = '>';
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe('CompoundInputFilter', () => {
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['9'], shouldTriggerQuery: true });
});

it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as True', () => {
it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as True and value is undefined', () => {
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = true;
mockColumn.type = FieldType.number;
const callbackSpy = jest.spyOn(filterArguments, 'callback');
Expand All @@ -190,6 +190,38 @@ describe('CompoundInputFilter', () => {
expect(callbackSpy).not.toHaveBeenCalled();
});

it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as True and value is empty string', () => {
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = true;
mockColumn.type = FieldType.number;
const callbackSpy = jest.spyOn(filterArguments, 'callback');

filter.init(filterArguments);
filter.setValues(['']);
const filterSelectElm = divContainer.querySelector('.search-filter.filter-duration select') as HTMLInputElement;

filterSelectElm.value = '<=';
filterSelectElm.dispatchEvent(new Event('change'));

expect(callbackSpy).not.toHaveBeenCalled();
});

it('should change operator dropdown without a value entered and expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" but value was changed from set to unset', () => {
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = true;
mockColumn.type = FieldType.number;
const callbackSpy = jest.spyOn(filterArguments, 'callback');

filter.init(filterArguments);
const filterSelectElm = divContainer.querySelector('.search-filter.filter-duration select') as HTMLInputElement;
filter.setValues(['abc']);
filterSelectElm.dispatchEvent(new Event('change'));

filter.setValues(['']);
filterSelectElm.value = '<=';
filterSelectElm.dispatchEvent(new Event('change'));

expect(callbackSpy).toHaveBeenCalled();
});

it('should change operator dropdown without a value entered and not expect the callback to be called when "skipCompoundOperatorFilterWithNullInput" is defined as False', () => {
mockColumn.filter!.skipCompoundOperatorFilterWithNullInput = false;
mockColumn.type = FieldType.number;
Expand Down
6 changes: 3 additions & 3 deletions packages/common/src/filters/dateFilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BindingEventService } from '@slickgrid-universal/binding';
import { createDomElement, emptyElement, extend, } from '@slickgrid-universal/utils';
import { createDomElement, emptyElement, extend, isDefined, } from '@slickgrid-universal/utils';
import { format, parse } from '@formkit/tempo';
import { VanillaCalendar, type IOptions } from 'vanilla-calendar-picker';

Expand Down Expand Up @@ -495,8 +495,8 @@ export class DateFilter implements Filter {
this._currentValue ? this._filterElm.classList.add('filled') : this._filterElm.classList.remove('filled');

// when changing compound operator, we don't want to trigger the filter callback unless the date input is also provided
const skipCompoundOperatorFilterWithNullInput = this.columnFilter.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput === undefined;
if (!skipCompoundOperatorFilterWithNullInput || this._currentDateOrDates !== undefined) {
const skipNullInput = this.columnFilter.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput === undefined;
if (!skipNullInput || (skipNullInput && isDefined(this._currentDateOrDates))) {
this.callback(e, { columnDef: this.columnDef, searchTerms: (this._currentValue ? [this._currentValue] : null), operator: selectedOperator || '', shouldTriggerQuery: this._shouldTriggerQuery });
}
}
Expand Down
10 changes: 7 additions & 3 deletions packages/common/src/filters/inputFilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BindingEventService } from '@slickgrid-universal/binding';
import { createDomElement, emptyElement, toSentenceCase } from '@slickgrid-universal/utils';
import { createDomElement, emptyElement, isDefined, toSentenceCase } from '@slickgrid-universal/utils';

import type {
Column,
Expand All @@ -25,6 +25,7 @@ export class InputFilter implements Filter {
protected _cellContainerElm!: HTMLDivElement;
protected _filterContainerElm!: HTMLDivElement;
protected _filterInputElm!: HTMLInputElement;
protected _lastSearchValue?: number | string;
protected _selectOperatorElm?: HTMLSelectElement;
inputFilterType: 'single' | 'compound' = 'single';
grid!: SlickGrid;
Expand Down Expand Up @@ -335,15 +336,18 @@ export class InputFilter implements Filter {
const callbackArgs = { columnDef: this.columnDef, operator: selectedOperator, searchTerms: (value ? [value] : null), shouldTriggerQuery: this._shouldTriggerQuery };
const typingDelay = (eventType === 'keyup' && (event as KeyboardEvent)?.key !== 'Enter') ? this._debounceTypingDelay : 0;

const skipCompoundOperatorFilterWithNullInput = this.columnFilter.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput;
if (this.inputFilterType === 'single' || !skipCompoundOperatorFilterWithNullInput || this._currentValue !== undefined) {
const skipNullInput = this.columnFilter.skipCompoundOperatorFilterWithNullInput ?? this.gridOptions.skipCompoundOperatorFilterWithNullInput;
const hasSkipNullValChanged = (skipNullInput && isDefined(this._currentValue)) || (this._currentValue === '' && isDefined(this._lastSearchValue));

if (this.inputFilterType === 'single' || !skipNullInput || hasSkipNullValChanged) {
if (typingDelay > 0) {
clearTimeout(this._timer as NodeJS.Timeout);
this._timer = setTimeout(() => this.callback(event, callbackArgs), typingDelay);
} else {
this.callback(event, callbackArgs);
}
}
this._lastSearchValue = value;
}

// reset both flags for next use
Expand Down

0 comments on commit 4d69bc0

Please sign in to comment.