Skip to content

Commit

Permalink
perf(filters): merge all Slider filters into one class (#791)
Browse files Browse the repository at this point in the history
* perf(filters): merge all Slider filters into one class
  • Loading branch information
ghiscoding authored Nov 4, 2022
1 parent 71c976e commit fc4304b
Show file tree
Hide file tree
Showing 17 changed files with 568 additions and 925 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
GridOption,
Grouping,
GroupTotalFormatters,
SliderOption,
SortComparers,
SortDirectionNumber,
} from '@slickgrid-universal/common';
Expand Down Expand Up @@ -76,7 +77,11 @@ export class Example2 {
id: 'duration', name: 'Duration', field: 'duration',
minWidth: 50, width: 60,
filterable: true,
filter: { model: Filters.slider, operator: '>=' },
filter: {
model: Filters.slider,
operator: '>=',
filterOptions: { hideSliderNumber: true, enableSliderTrackColoring: true, sliderTrackFilledColor: '#9ac49c' } as SliderOption
},
sortable: true,
type: FieldType.number,
groupTotalsFormatter: GroupTotalFormatters.sumTotals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ export class Example7 {
},
{
id: 'percentComplete', nameKey: 'PERCENT_COMPLETE', field: 'percentComplete', type: 'number',
filterable: true, sortable: true, editor: { model: Editors.slider, minValue: 0, maxValue: 100, },
filterable: true, sortable: true,
filter: { model: Filters.compoundSlider, minValue: 0, maxValue: 100, operator: '>=' },
editor: { model: Editors.slider, minValue: 0, maxValue: 100, },
},
{
id: 'start', nameKey: 'START', field: 'start', formatter: Formatters.dateIso,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export class Example14 {
filter: {
model: Filters.sliderRange,
operator: '>=',
// searchTerms: [15, 78],
filterOptions: {
enableSliderTrackColoring: true,
hideSliderNumbers: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class Example16 {
type: FieldType.number,
},
{
id: 'percentComplete', name: '% Complete', field: 'percentComplete', type: FieldType.number,
id: 'percentComplete', name: '% Complete', field: 'percentComplete', type: FieldType.number, minWidth: 130,
editor: {
model: Editors.slider,
minValue: 0,
Expand All @@ -153,7 +153,7 @@ export class Example16 {
exportWithFormatter: false,
formatter: Formatters.percentCompleteBar,
sortable: true, filterable: true,
filter: { model: Filters.slider, operator: '>=' },
filter: { model: Filters.sliderRange, operator: '>=' },
customTooltip: { useRegularTooltip: true, position: 'center' },
},
{
Expand Down
52 changes: 39 additions & 13 deletions packages/common/src/filters/__tests__/compoundSliderFilter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,17 @@ describe('CompoundSliderFilter', () => {

expect(spyGetHeaderRow).toHaveBeenCalled();
expect(filterCount).toBe(1);
expect(filter.currentValue).toBe(0);
});

it('should have an aria-label when creating the filter', () => {
filter.init(filterArguments);
const filterInputElm = divContainer.querySelector('.input-group.search-filter.filter-duration input') as HTMLInputElement;

expect(filterInputElm.getAttribute('aria-label')).toBe('Duration Search Filter');
expect(filterInputElm.ariaLabel).toBe('Duration Search Filter');
});

it('should call "setValues" with "operator" set in the filter arguments and expect that value to be in the callback when triggered', () => {
it('should call "setValues" with "operator" set in the filter arguments and expect that value to be converted to number and in the callback when triggered', () => {
const callbackSpy = jest.spyOn(filterArguments, 'callback');
const rowMouseEnterSpy = jest.spyOn(gridStub.onHeaderRowMouseEnter, 'notify');
const filterArgs = { ...filterArguments, operator: '>', grid: gridStub } as FilterArguments;
Expand All @@ -90,7 +91,7 @@ describe('CompoundSliderFilter', () => {

jest.runAllTimers(); // fast-forward timer

expect(callbackSpy).toHaveBeenLastCalledWith(expect.anything(), { columnDef: mockColumn, operator: '>', searchTerms: ['2'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(expect.anything(), { columnDef: mockColumn, operator: '>', searchTerms: [2], shouldTriggerQuery: true });
expect(rowMouseEnterSpy).toHaveBeenCalledWith({ column: mockColumn, grid: gridStub }, expect.anything());
});

Expand All @@ -105,7 +106,7 @@ describe('CompoundSliderFilter', () => {
const filterFilledElms = divContainer.querySelectorAll('.slider-container.search-filter.filter-duration.filled');

expect(filterFilledElms.length).toBe(1);
expect(callbackSpy).toHaveBeenLastCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['3'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: [3], shouldTriggerQuery: true });
});

it('should trigger an operator change event and expect the callback to be called with the searchTerms and operator defined', () => {
Expand All @@ -118,10 +119,10 @@ describe('CompoundSliderFilter', () => {
filterSelectElm.value = '<=';
filterSelectElm.dispatchEvent(new CustomEvent('change'));

expect(callbackSpy).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['9'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: [9], shouldTriggerQuery: true });
});

it('should be able to call "setValues" with a value and an extra operator and expect it to be set as new operator', () => {
it('should be able to call "setValues" with a value, converted as a number, and an extra operator and expect it to be set as new operator', () => {
const callbackSpy = jest.spyOn(filterArguments, 'callback');

filter.init(filterArguments);
Expand All @@ -130,7 +131,7 @@ describe('CompoundSliderFilter', () => {
const filterSelectElm = divContainer.querySelector('.search-filter.filter-duration select') as HTMLInputElement;
filterSelectElm.dispatchEvent(new CustomEvent('change'));

expect(callbackSpy).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '>=', searchTerms: ['9'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '>=', searchTerms: [9], shouldTriggerQuery: true });
});

it('should be able to call "setValues" and set empty values and the input to not have the "filled" css class', () => {
Expand Down Expand Up @@ -186,7 +187,7 @@ describe('CompoundSliderFilter', () => {

it('should create the input filter with min/max slider values being set by filter "sliderStartValue" and "sliderEndValue" through the filter params', () => {
mockColumn.filter = {
params: {
filterOptions: {
sliderStartValue: 4,
sliderEndValue: 69,
}
Expand All @@ -202,7 +203,7 @@ describe('CompoundSliderFilter', () => {

it('should create the input filter with default search terms range but without showing side numbers when "hideSliderNumber" is set in params', () => {
filterArguments.searchTerms = [3];
mockColumn.filter!.params = { hideSliderNumber: true };
mockColumn.filter!.filterOptions = { hideSliderNumber: true };

filter.init(filterArguments);

Expand All @@ -220,7 +221,7 @@ describe('CompoundSliderFilter', () => {
filter.clear();

expect(filter.getValues()).toBe(0);
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: true });
});

it('should trigger a callback with the clear filter but without querying when when calling the "clear" method with False as argument', () => {
Expand All @@ -231,14 +232,14 @@ describe('CompoundSliderFilter', () => {
filter.clear(false);

expect(filter.getValues()).toBe(0);
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, shouldTriggerQuery: false });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
});

it('should trigger a callback with the clear filter set when calling the "clear" method and expect min slider values being with values of "sliderStartValue" when defined through the filter params', () => {
const filterArgs = { ...filterArguments, operator: '<=', searchTerms: [3], grid: gridStub, } as FilterArguments;
const callbackSpy = jest.spyOn(filterArguments, 'callback');
mockColumn.filter = {
params: {
filterOptions: {
sliderStartValue: 4,
sliderEndValue: 69,
}
Expand All @@ -248,7 +249,32 @@ describe('CompoundSliderFilter', () => {
filter.clear(false);

expect(filter.getValues()).toEqual(4);
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, shouldTriggerQuery: false });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
});

it('should enableSliderTrackColoring and trigger a change event and expect slider track to have background color', () => {
mockColumn.filter = { filterOptions: { enableSliderTrackColoring: true } };
filter.init(filterArguments);
filter.setValues(['80']);
const filterElms = divContainer.querySelectorAll<HTMLInputElement>('.search-filter.slider-container.filter-duration input');
filterElms[0].dispatchEvent(new CustomEvent('change'));

expect(filter.sliderOptions?.sliderTrackBackground).toBe('linear-gradient(to right, #eee 0%, var(--slick-slider-filter-thumb-color, #86bff8) 0%, var(--slick-slider-filter-thumb-color, #86bff8) 80%, #eee 80%)');
});

it('should click on the slider track and expect handle to move to the new position', () => {
filter.init(filterArguments);
const sliderInputs = divContainer.querySelectorAll<HTMLInputElement>('.slider-filter-input');
const sliderTrackElm = divContainer.querySelector('.slider-track') as HTMLDivElement;

const sliderRightChangeSpy = jest.spyOn(sliderInputs[0], 'dispatchEvent');

const clickEvent = new Event('click');
Object.defineProperty(clickEvent, 'offsetX', { writable: true, configurable: true, value: 56 });
Object.defineProperty(sliderTrackElm, 'offsetWidth', { writable: true, configurable: true, value: 75 });
sliderTrackElm.dispatchEvent(clickEvent);

expect(sliderRightChangeSpy).toHaveBeenCalled();
});

it('should create the input filter with all available operators in a select dropdown options as a prepend element', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Filters } from '../filters.index';
import { Column, FilterArguments, GridOption, SlickGrid, SlickNamespace } from '../../interfaces/index';
import { SliderFilter } from '../sliderFilter';
import { SingleSliderFilter } from '../singleSliderFilter';
import { TranslateServiceStub } from '../../../../../test/translateServiceStub';

const containerId = 'demo-container';
declare const Slick: SlickNamespace;
Expand All @@ -23,14 +24,16 @@ const gridStub = {
onHeaderRowMouseEnter: new Slick.Event(),
} as unknown as SlickGrid;

describe('SliderFilter', () => {
describe('SingleSliderFilter', () => {
let translateService: TranslateServiceStub;
let divContainer: HTMLDivElement;
let filter: SliderFilter;
let filter: SingleSliderFilter;
let filterArgs: FilterArguments;
let spyGetHeaderRow;
let mockColumn: Column;

beforeEach(() => {
translateService = new TranslateServiceStub();
divContainer = document.createElement('div');
divContainer.innerHTML = template;
document.body.appendChild(divContainer);
Expand All @@ -44,7 +47,7 @@ describe('SliderFilter', () => {
filterContainerElm: gridStub.getHeaderRowColumn(mockColumn.id)
};

filter = new SliderFilter();
filter = new SingleSliderFilter(translateService);
});

afterEach(() => {
Expand All @@ -61,16 +64,17 @@ describe('SliderFilter', () => {

expect(spyGetHeaderRow).toHaveBeenCalled();
expect(filterCount).toBe(1);
expect(filter.currentValue).toBe(0);
});

it('should have an aria-label when creating the filter', () => {
filter.init(filterArgs);
const filterInputElm = divContainer.querySelector('.search-filter.slider-container.filter-duration input') as HTMLInputElement;

expect(filterInputElm.getAttribute('aria-label')).toBe('Duration Search Filter');
expect(filterInputElm.ariaLabel).toBe('Duration Search Filter');
});

it('should call "setValues" and expect that value to be in the callback when triggered', () => {
it('should call "setValues" and expect that value, converted as a number, to be in the callback when triggered', () => {
const callbackSpy = jest.spyOn(filterArgs, 'callback');
const rowMouseEnterSpy = jest.spyOn(gridStub.onHeaderRowMouseEnter, 'notify');

Expand All @@ -81,11 +85,11 @@ describe('SliderFilter', () => {

jest.runAllTimers(); // fast-forward timer

expect(callbackSpy).toHaveBeenLastCalledWith(new CustomEvent('change'), { columnDef: mockColumn, operator: 'EQ', searchTerms: ['2'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(new CustomEvent('change'), { columnDef: mockColumn, operator: 'EQ', searchTerms: [2], shouldTriggerQuery: true });
expect(rowMouseEnterSpy).toHaveBeenCalledWith({ column: mockColumn, grid: gridStub }, expect.anything());
});

it('should call "setValues" and expect that value, converted as a string, to be in the callback when triggered', () => {
it('should call "setValues" and expect that value, converted as a number, to be in the callback when triggered', () => {
const callbackSpy = jest.spyOn(filterArgs, 'callback');

filter.init(filterArgs);
Expand All @@ -98,7 +102,7 @@ describe('SliderFilter', () => {
const filterFilledElms = divContainer.querySelectorAll('.search-filter.slider-container.filter-duration.filled');

expect(filterFilledElms.length).toBe(1);
expect(callbackSpy).toHaveBeenLastCalledWith(new CustomEvent('change'), { columnDef: mockColumn, operator: 'EQ', searchTerms: ['13'], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(new CustomEvent('change'), { columnDef: mockColumn, operator: 'EQ', searchTerms: [3], shouldTriggerQuery: true });
});

it('should be able to call "setValues" and set empty values and the input to not have the "filled" css class', () => {
Expand Down Expand Up @@ -154,7 +158,7 @@ describe('SliderFilter', () => {

it('should create the input filter with min/max slider values being set by filter "sliderStartValue" and "sliderEndValue" through the filter params', () => {
mockColumn.filter = {
params: {
filterOptions: {
sliderStartValue: 4,
sliderEndValue: 69,
}
Expand All @@ -170,7 +174,7 @@ describe('SliderFilter', () => {

it('should create the input filter with default search terms range but without showing side numbers when "hideSliderNumber" is set in params', () => {
filterArgs.searchTerms = [3];
mockColumn.filter!.params = { hideSliderNumber: true };
mockColumn.filter!.filterOptions = { hideSliderNumber: true };

filter.init(filterArgs);

Expand All @@ -188,7 +192,7 @@ describe('SliderFilter', () => {
filter.clear();

expect(filter.getValues()).toBe(0);
expect(callbackSpy).toHaveBeenLastCalledWith(new Event('change'), { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: true });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: true });
});

it('should trigger a callback with the clear filter but without querying when when calling the "clear" method with False as argument', () => {
Expand All @@ -199,13 +203,13 @@ describe('SliderFilter', () => {
filter.clear(false);

expect(filter.getValues()).toBe(0);
expect(callbackSpy).toHaveBeenLastCalledWith(new Event('change'), { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
});

it('should trigger a callback with the clear filter set when calling the "clear" method and expect min slider values being with values of "sliderStartValue" when defined through the filter params', () => {
const callbackSpy = jest.spyOn(filterArgs, 'callback');
mockColumn.filter = {
params: {
filterOptions: {
sliderStartValue: 4,
sliderEndValue: 69,
}
Expand All @@ -215,6 +219,31 @@ describe('SliderFilter', () => {
filter.clear(false);

expect(filter.getValues()).toEqual(4);
expect(callbackSpy).toHaveBeenLastCalledWith(new Event('change'), { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
expect(callbackSpy).toHaveBeenLastCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, searchTerms: [], shouldTriggerQuery: false });
});

it('should enableSliderTrackColoring and trigger a change event and expect slider track to have background color', () => {
mockColumn.filter = { filterOptions: { enableSliderTrackColoring: true } };
filter.init(filterArgs);
filter.setValues(['80']);
const filterElms = divContainer.querySelectorAll<HTMLInputElement>('.search-filter.slider-container.filter-duration input');
filterElms[0].dispatchEvent(new CustomEvent('change'));

expect(filter.sliderOptions?.sliderTrackBackground).toBe('linear-gradient(to right, #eee 0%, var(--slick-slider-filter-thumb-color, #86bff8) 0%, var(--slick-slider-filter-thumb-color, #86bff8) 80%, #eee 80%)');
});

it('should click on the slider track and expect handle to move to the new position', () => {
filter.init(filterArgs);
const sliderInputs = divContainer.querySelectorAll<HTMLInputElement>('.slider-filter-input');
const sliderTrackElm = divContainer.querySelector('.slider-track') as HTMLDivElement;

const sliderRightChangeSpy = jest.spyOn(sliderInputs[0], 'dispatchEvent');

const clickEvent = new Event('click');
Object.defineProperty(clickEvent, 'offsetX', { writable: true, configurable: true, value: 56 });
Object.defineProperty(sliderTrackElm, 'offsetWidth', { writable: true, configurable: true, value: 75 });
sliderTrackElm.dispatchEvent(clickEvent);

expect(sliderRightChangeSpy).toHaveBeenCalled();
});
});
Loading

0 comments on commit fc4304b

Please sign in to comment.