From b46eb5ebdb177e7d0d6c93cb6df541cedc7eb5d1 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Sun, 30 May 2021 22:47:45 -0400 Subject: [PATCH] feat(filters): convert jQuery to native element on more filters --- .../common/src/filters/compoundInputFilter.ts | 8 +- .../src/filters/compoundSliderFilter.ts | 221 +++++++++--------- .../common/src/filters/filterUtilities.ts | 9 - packages/common/src/filters/sliderFilter.ts | 177 +++++++------- test/cypress/integration/example05.spec.js | 2 +- 5 files changed, 210 insertions(+), 207 deletions(-) diff --git a/packages/common/src/filters/compoundInputFilter.ts b/packages/common/src/filters/compoundInputFilter.ts index c9660df2e..668f18b39 100644 --- a/packages/common/src/filters/compoundInputFilter.ts +++ b/packages/common/src/filters/compoundInputFilter.ts @@ -38,12 +38,12 @@ export class CompoundInputFilter implements Filter { /** Getter for the Grid Options pulled through the Grid Object */ protected get gridOptions(): GridOption { - return (this.grid && this.grid.getOptions) ? this.grid.getOptions() : {}; + return this.grid?.getOptions?.() ?? {}; } /** Getter for the Column Filter */ get columnFilter(): ColumnFilter { - return this.columnDef && this.columnDef.filter || {}; + return this.columnDef?.filter ?? {}; } /** Getter to know what would be the default operator when none is specified */ @@ -117,7 +117,7 @@ export class CompoundInputFilter implements Filter { this._clearFilterTriggered = true; this._shouldTriggerQuery = shouldTriggerQuery; this.searchTerms = []; - this._selectOperatorElm.value = ''; + this._selectOperatorElm.selectedIndex = 0; this._filterInputElm.value = ''; this.onTriggerEvent(undefined); } @@ -128,8 +128,8 @@ export class CompoundInputFilter implements Filter { */ destroy() { this._bindEventService.unbindAll(); - this._filterElm?.remove?.(); this._selectOperatorElm?.remove?.(); + this._filterElm?.remove?.(); } /** Set value(s) on the DOM element */ diff --git a/packages/common/src/filters/compoundSliderFilter.ts b/packages/common/src/filters/compoundSliderFilter.ts index d4ddae836..ed52cfea7 100644 --- a/packages/common/src/filters/compoundSliderFilter.ts +++ b/packages/common/src/filters/compoundSliderFilter.ts @@ -11,8 +11,9 @@ import { } from '../interfaces/index'; import { Constants } from '../constants'; import { OperatorString, OperatorType, SearchTerm } from '../enums/index'; -import { buildSelectOperatorHtmlString } from './filterUtilities'; -import { getTranslationPrefix, mapOperatorToShorthandDesignation } from '../services/utilities'; +import { buildSelectOperator } from './filterUtilities'; +import { emptyElement, getTranslationPrefix, mapOperatorToShorthandDesignation } from '../services/utilities'; +import { BindingEventService } from '../services/bindingEvent.service'; import { TranslaterService } from '../services/translater.service'; const DEFAULT_MIN_VALUE = 0; @@ -20,22 +21,26 @@ const DEFAULT_MAX_VALUE = 100; const DEFAULT_STEP = 1; export class CompoundSliderFilter implements Filter { + protected _bindEventService: BindingEventService; protected _clearFilterTriggered = false; protected _currentValue?: number; protected _shouldTriggerQuery = true; protected _elementRangeInputId = ''; protected _elementRangeOutputId = ''; protected _operator?: OperatorType | OperatorString; - protected $containerInputGroupElm: any; - protected $filterElm: any; - protected $filterInputElm: any; - protected $selectOperatorElm: any; + protected containerInputGroupElm?: HTMLDivElement; + protected filterElm!: HTMLDivElement; + protected filterInputElm!: HTMLInputElement; + protected filterNumberElm?: HTMLSpanElement; + protected selectOperatorElm!: HTMLSelectElement; grid!: SlickGrid; searchTerms: SearchTerm[] = []; columnDef!: Column; callback!: FilterCallback; - constructor(protected readonly translaterService: TranslaterService) { } + constructor(protected readonly translaterService: TranslaterService) { + this._bindEventService = new BindingEventService(); + } /** Getter for the Filter Operator */ get columnFilter(): ColumnFilter { @@ -59,7 +64,7 @@ export class CompoundSliderFilter implements Filter { /** Getter for the Grid Options pulled through the Grid Object */ protected get gridOptions(): GridOption { - return (this.grid && this.grid.getOptions) ? this.grid.getOptions() : {}; + return this.grid?.getOptions?.() ?? {}; } /** Getter for the single Locale texts provided by the user in main file or else use default English locales via the Constants */ @@ -88,7 +93,7 @@ export class CompoundSliderFilter implements Filter { this.callback = args.callback; this.columnDef = args.columnDef; this.operator = args.operator || ''; - this.searchTerms = (args.hasOwnProperty('searchTerms') ? args.searchTerms : []) || []; + this.searchTerms = args?.searchTerms ?? []; // define the input & slider number IDs this._elementRangeInputId = `rangeInput_${this.columnDef.field}`; @@ -99,29 +104,17 @@ export class CompoundSliderFilter implements Filter { // step 1, create the DOM Element of the filter which contain the compound Operator+Input // and initialize it if searchTerm is filled - this.$filterElm = this.createDomElement(searchTerm); + this.filterElm = this.createDomElement(searchTerm); - // step 3, subscribe to the input change event and run the callback when that happens + // step 2, subscribe to the input change event and run the callback when that happens // also add/remove "filled" class for styling purposes - this.$filterInputElm.change((e: any) => { - this.onTriggerEvent(e); - }); - this.$selectOperatorElm.change((e: any) => { - this.onTriggerEvent(e); - }); + this._bindEventService.bind(this.filterInputElm, 'change', this.onTriggerEvent.bind(this)); + this._bindEventService.bind(this.selectOperatorElm, 'change', this.onTriggerEvent.bind(this)); // if user chose to display the slider number on the right side, then update it every time it changes // we need to use both "input" and "change" event to be all cross-browser if (!this.filterParams.hideSliderNumber) { - this.$filterInputElm.on('input change', (e: { target: HTMLInputElement }) => { - const value = e && e.target && e.target.value; - if (value && document) { - const elements = document.getElementsByClassName(this._elementRangeOutputId || ''); - if (elements && elements.length > 0 && elements[0].innerHTML) { - elements[0].innerHTML = value; - } - } - }); + this._bindEventService.bind(this.filterInputElm, ['input', 'change'], this.handleInputChange.bind(this)); } } @@ -129,19 +122,19 @@ export class CompoundSliderFilter implements Filter { * Clear the filter value */ clear(shouldTriggerQuery = true) { - if (this.$filterElm && this.$selectOperatorElm) { + if (this.filterElm && this.selectOperatorElm) { this._clearFilterTriggered = true; this._shouldTriggerQuery = shouldTriggerQuery; this.searchTerms = []; - const clearedValue = this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : DEFAULT_MIN_VALUE; + const clearedValue = this.filterParams?.sliderStartValue ?? DEFAULT_MIN_VALUE; this._currentValue = +clearedValue; - this.$selectOperatorElm.val(0); - this.$filterInputElm.val(clearedValue); - if (!this.filterParams.hideSliderNumber) { - this.$containerInputGroupElm.children('div.input-group-addon.input-group-append').children().last().html(clearedValue); + this.selectOperatorElm.selectedIndex = 0; + this.filterInputElm.value = clearedValue; + if (this.filterNumberElm) { + this.filterNumberElm.textContent = clearedValue; } this.onTriggerEvent(undefined); - this.$filterElm.removeClass('filled'); + this.filterElm.classList.remove('filled'); } } @@ -149,13 +142,10 @@ export class CompoundSliderFilter implements Filter { * destroy the filter */ destroy() { - if (this.$filterInputElm) { - this.$filterInputElm.off('input change').remove(); - this.$selectOperatorElm.off('change').remove(); - } - this.$filterInputElm = null; - this.$filterElm = null; - this.$selectOperatorElm = null; + this._bindEventService.unbindAll(); + this.selectOperatorElm?.remove?.(); + emptyElement(this.filterElm); + this.filterElm?.remove?.(); } /** @@ -170,14 +160,16 @@ export class CompoundSliderFilter implements Filter { setValues(values: SearchTerm | SearchTerm[], operator?: OperatorType | OperatorString) { const newValue = Array.isArray(values) ? values[0] : values; this._currentValue = +newValue; - this.$filterInputElm.val(newValue); - this.$containerInputGroupElm.children('div.input-group-addon.input-group-append').children().last().html(newValue); + this.filterInputElm.value = `${newValue ?? ''}`; + if (this.filterNumberElm) { + this.filterNumberElm.textContent = `${newValue ?? ''}`; + } // set the operator, in the DOM as well, when defined this.operator = operator || this.defaultOperator; - if (operator && this.$selectOperatorElm) { + if (operator && this.selectOperatorElm) { const operatorShorthand = mapOperatorToShorthandDesignation(this.operator); - this.$selectOperatorElm.val(operatorShorthand); + this.selectOperatorElm.value = operatorShorthand; } } @@ -185,26 +177,6 @@ export class CompoundSliderFilter implements Filter { // protected functions // ------------------ - /** Build HTML Template for the input range (slider) */ - protected buildTemplateHtmlString() { - const minValue = this.filterProperties.hasOwnProperty('minValue') ? this.filterProperties.minValue : DEFAULT_MIN_VALUE; - const maxValue = this.filterProperties.hasOwnProperty('maxValue') ? this.filterProperties.maxValue : DEFAULT_MAX_VALUE; - const defaultValue = this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : minValue; - const step = this.filterProperties.hasOwnProperty('valueStep') ? this.filterProperties.valueStep : DEFAULT_STEP; - - return ``; - } - - /** Build HTML Template for the text (number) that is shown appended to the slider */ - protected buildTemplateSliderTextHtmlString() { - const minValue = this.filterProperties.hasOwnProperty('minValue') ? this.filterProperties.minValue : DEFAULT_MIN_VALUE; - const defaultValue = this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : minValue; - - return `
${defaultValue}
`; - } - /** Get the available operator option values to populate the operator select dropdown list */ protected getOperatorOptionValues(): OperatorDetail[] { if (this.columnFilter?.compoundOperatorList) { @@ -234,12 +206,15 @@ export class CompoundSliderFilter implements Filter { /** * Create the DOM element */ - protected createDomElement(searchTerm?: SearchTerm) { + protected createDomElement(searchTerm?: SearchTerm): HTMLDivElement { const columnId = this.columnDef?.id ?? ''; - const minValue = (this.filterProperties.hasOwnProperty('minValue') && this.filterProperties.minValue) ? this.filterProperties.minValue : DEFAULT_MIN_VALUE; - const startValue = +(this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : minValue); - const $headerElm = this.grid.getHeaderRowColumn(this.columnDef.id); - $($headerElm).empty(); + const minValue = this.filterProperties?.minValue ?? DEFAULT_MIN_VALUE; + const maxValue = this.filterProperties?.maxValue ?? DEFAULT_MAX_VALUE; + const defaultValue = this.filterParams?.sliderStartValue ?? minValue; + const step = this.filterProperties?.valueStep ?? DEFAULT_STEP; + const startValue = +(this.filterParams?.sliderStartValue ?? minValue); + const headerElm = this.grid.getHeaderRowColumn(this.columnDef.id); + emptyElement(headerElm); let searchTermInput = (searchTerm || '0') as string; if (+searchTermInput < minValue) { @@ -250,66 +225,102 @@ export class CompoundSliderFilter implements Filter { } this._currentValue = +searchTermInput; - // create the DOM Select dropdown for the Operator - const selectOperatorHtmlString = buildSelectOperatorHtmlString(this.getOperatorOptionValues()); - this.$selectOperatorElm = $(selectOperatorHtmlString); - this.$filterInputElm = $(this.buildTemplateHtmlString()); - const $filterContainerElm = $(`
`); - this.$containerInputGroupElm = $(`
`); - const $operatorInputGroupAddon = $(``); - - /* the DOM element final structure will be -
-
- + /* + Full DOM Element Template:: +
+
+ + + + +
+ 0 +
- -
0
*/ - $operatorInputGroupAddon.append(this.$selectOperatorElm); - this.$containerInputGroupElm.append($operatorInputGroupAddon); - this.$containerInputGroupElm.append(this.$filterInputElm); + + // create the DOM Select dropdown for the Operator + this.selectOperatorElm = buildSelectOperator(this.getOperatorOptionValues()); + + const spanPrependElm = document.createElement('span'); + spanPrependElm.className = 'input-group-addon input-group-prepend operator'; + spanPrependElm.appendChild(this.selectOperatorElm); + + // create the DOM element + this.filterInputElm = document.createElement('input'); + this.filterInputElm.type = 'range'; + this.filterInputElm.className = `form-control slider-filter-input range compound-slider ${this._elementRangeInputId}`; + this.filterInputElm.defaultValue = defaultValue; + this.filterInputElm.value = searchTermInput; + this.filterInputElm.min = `${minValue}`; + this.filterInputElm.max = `${maxValue}`; + this.filterInputElm.step = `${step}`; + this.filterInputElm.name = this._elementRangeInputId; + + const divContainerFilterElm = document.createElement('div'); + divContainerFilterElm.className = `form-group search-filter slider-container filter-${columnId}`; + + // this.containerInputGroupElm = $(`
`); + this.containerInputGroupElm = document.createElement('div'); + this.containerInputGroupElm.className = `input-group search-filter filter-${columnId}`; + this.containerInputGroupElm.appendChild(spanPrependElm); + this.containerInputGroupElm.appendChild(this.filterInputElm); + divContainerFilterElm.appendChild(this.containerInputGroupElm); + if (!this.filterParams.hideSliderNumber) { - const $sliderTextInputAppendAddon = $(this.buildTemplateSliderTextHtmlString()); - $sliderTextInputAppendAddon.children().html(searchTermInput); - this.$containerInputGroupElm.append($sliderTextInputAppendAddon); - } + this.containerInputGroupElm.classList.add('input-group'); + this.filterInputElm.value = searchTermInput; - // create the DOM element & add an ID and filter class - $filterContainerElm.append(this.$containerInputGroupElm); + const divGroupAppendElm = document.createElement('div'); + divGroupAppendElm.className = 'input-group-addon input-group-append slider-value'; + + this.filterNumberElm = document.createElement('span'); + this.filterNumberElm.className = `input-group-text ${this._elementRangeOutputId}`; + this.filterNumberElm.textContent = searchTermInput; + divGroupAppendElm.appendChild(this.filterNumberElm); + this.containerInputGroupElm.appendChild(divGroupAppendElm); + } - this.$filterInputElm.val(searchTermInput); - this.$filterInputElm.data('columnId', columnId); + // this.filterNumberElm.html(searchTermInput); + divContainerFilterElm.dataset.columnid = `${columnId}`; if (this.operator) { const operatorShorthand = mapOperatorToShorthandDesignation(this.operator); - this.$selectOperatorElm.val(operatorShorthand); + this.selectOperatorElm.value = operatorShorthand; } // if there's a search term, we will add the "filled" class for styling purposes - if (searchTerm !== '') { - $filterContainerElm.addClass('filled'); + if (searchTerm) { + divContainerFilterElm.classList.add('filled'); } // append the new DOM element to the header row - if ($filterContainerElm && typeof $filterContainerElm.appendTo === 'function') { - $filterContainerElm.appendTo($headerElm); - } + headerElm.appendChild(divContainerFilterElm); + + return divContainerFilterElm; + } - return $filterContainerElm; + protected handleInputChange(event: Event) { + const value = (event?.target as HTMLInputElement).value; + if (value !== undefined && value !== null) { + const element = document.querySelector(`.${this._elementRangeOutputId || ''}`); + if (element?.textContent) { + element.textContent = value; + } + } } protected onTriggerEvent(e: Event | undefined) { - const value = this.$filterInputElm.val(); + const value = this.filterInputElm.value; this._currentValue = +value; if (this._clearFilterTriggered) { - this.$filterElm.removeClass('filled'); + this.filterElm.classList.remove('filled'); this.callback(e, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, shouldTriggerQuery: this._shouldTriggerQuery }); } else { - this.$filterElm.addClass('filled'); - const selectedOperator = this.$selectOperatorElm.find('option:selected').val(); + this.filterElm.classList.add('filled'); + const selectedOperator = this.selectOperatorElm.value as OperatorString; this.callback(e, { columnDef: this.columnDef, searchTerms: (value ? [value || '0'] : null), operator: selectedOperator || '', shouldTriggerQuery: this._shouldTriggerQuery }); } diff --git a/packages/common/src/filters/filterUtilities.ts b/packages/common/src/filters/filterUtilities.ts index 1cb386714..65705e231 100644 --- a/packages/common/src/filters/filterUtilities.ts +++ b/packages/common/src/filters/filterUtilities.ts @@ -3,15 +3,6 @@ import { Column } from '../interfaces/index'; import { Observable, RxJsFacade, Subject, Subscription } from '../services/rxjsFacade'; import { castObservableToPromise, getDescendantProperty, htmlEncodedStringWithPadding } from '../services/utilities'; -export function buildSelectOperatorHtmlString(optionValues: Array<{ operator: OperatorString, description: string }>) { - let optionValueString = ''; - optionValues.forEach(option => { - optionValueString += ``; - }); - - return ``; -} - /** * Create and return a select dropdown HTML element with a list of Operators with descriptions * @param {Array} optionValues - list of operators and their descriptions diff --git a/packages/common/src/filters/sliderFilter.ts b/packages/common/src/filters/sliderFilter.ts index 6d706fc9c..38a3e83a3 100644 --- a/packages/common/src/filters/sliderFilter.ts +++ b/packages/common/src/filters/sliderFilter.ts @@ -7,28 +7,35 @@ import { FilterCallback, SlickGrid, } from './../interfaces/index'; +import { emptyElement } from '../services/utilities'; +import { BindingEventService } from '../services/bindingEvent.service'; const DEFAULT_MIN_VALUE = 0; const DEFAULT_MAX_VALUE = 100; const DEFAULT_STEP = 1; export class SliderFilter implements Filter { + protected _bindEventService: BindingEventService; protected _clearFilterTriggered = false; protected _currentValue?: number; protected _shouldTriggerQuery = true; protected _elementRangeInputId = ''; protected _elementRangeOutputId = ''; - protected $filterElm: any; - protected $filterInputElm: any; - protected $filterNumberElm: any; + protected filterElm!: HTMLDivElement; + protected filterInputElm!: HTMLInputElement; + protected filterNumberElm?: HTMLSpanElement; grid!: SlickGrid; searchTerms: SearchTerm[] = []; columnDef!: Column; callback!: FilterCallback; + constructor() { + this._bindEventService = new BindingEventService(); + } + /** Getter for the Column Filter */ get columnFilter(): ColumnFilter { - return this.columnDef && this.columnDef.filter || {}; + return this.columnDef?.filter ?? {}; } /** Getter to know what would be the default operator when none is specified */ @@ -38,12 +45,12 @@ export class SliderFilter implements Filter { /** Getter for the Filter Generic Params */ protected get filterParams(): any { - return this.columnDef && this.columnDef.filter && this.columnDef.filter.params || {}; + return this.columnDef?.filter?.params ?? {}; } /** Getter for the `filter` properties */ protected get filterProperties(): ColumnFilter { - return this.columnDef && this.columnDef.filter || {}; + return this.columnDef?.filter ?? {}; } /** Getter for the current Operator */ @@ -77,28 +84,17 @@ export class SliderFilter implements Filter { // filter input can only have 1 search term, so we will use the 1st array index if it exist const searchTerm = (Array.isArray(this.searchTerms) && this.searchTerms.length >= 0) ? this.searchTerms[0] : ''; - // step 1, create HTML string template - const filterTemplate = this.buildTemplateHtmlString(); - - // step 2, create the DOM Element of the filter & initialize it if searchTerm is filled - this.$filterElm = this.createDomElement(filterTemplate, searchTerm); + // step 1, create the DOM Element of the filter & initialize it if searchTerm is filled + this.filterElm = this.createDomElement(searchTerm); - // step 3, subscribe to the change event and run the callback when that happens + // step 2, subscribe to the change event and run the callback when that happens // also add/remove "filled" class for styling purposes - this.$filterInputElm.change(this.handleOnChange.bind(this)); + this._bindEventService.bind(this.filterInputElm, 'change', this.handleOnChange.bind(this)); // if user chose to display the slider number on the right side, then update it every time it changes // we need to use both "input" and "change" event to be all cross-browser if (!this.filterParams.hideSliderNumber) { - this.$filterInputElm.on('input change', (e: { target: HTMLInputElement }) => { - const value = e && e.target && e.target.value; - if (value !== undefined && value !== null && document) { - const elements = document.getElementsByClassName(this._elementRangeOutputId || ''); - if (elements && elements.length > 0 && elements[0].innerHTML) { - elements[0].innerHTML = value; - } - } - }); + this._bindEventService.bind(this.filterInputElm, ['input', 'change'], this.handleInputChange.bind(this)); } } @@ -106,15 +102,17 @@ export class SliderFilter implements Filter { * Clear the filter value */ clear(shouldTriggerQuery = true) { - if (this.$filterElm) { + if (this.filterElm) { this._clearFilterTriggered = true; this._shouldTriggerQuery = shouldTriggerQuery; this.searchTerms = []; - const clearedValue = this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : DEFAULT_MIN_VALUE; + const clearedValue = this.filterParams?.sliderStartValue ?? DEFAULT_MIN_VALUE; this._currentValue = +clearedValue; - this.$filterInputElm.val(clearedValue); - this.$filterNumberElm.html(clearedValue); - this.$filterInputElm.trigger('change'); + this.filterInputElm.value = clearedValue; + if (this.filterNumberElm) { + this.filterNumberElm.textContent = clearedValue; + } + this.filterInputElm.dispatchEvent(new Event('change')); } } @@ -122,11 +120,9 @@ export class SliderFilter implements Filter { * destroy the filter */ destroy() { - if (this.$filterInputElm) { - this.$filterInputElm.off('input change').remove(); - } - this.$filterInputElm = null; - this.$filterElm = null; + this._bindEventService.unbindAll(); + emptyElement(this.filterElm); + this.filterElm?.remove?.(); } /** @@ -140,11 +136,13 @@ export class SliderFilter implements Filter { /** Set value(s) on the DOM element */ setValues(values: SearchTerm | SearchTerm[], operator?: OperatorType | OperatorString) { if (Array.isArray(values)) { - this.$filterInputElm.val(`${values[0]}`); - this.$filterNumberElm.html(`${values[0]}`); + this.filterInputElm.value = `${values[0]}`; + if (this.filterNumberElm) { + this.filterNumberElm.textContent = `${values[0]}`; + } this._currentValue = +values[0]; } else if (values) { - this.$filterInputElm.val(values); + this.filterInputElm.value = `${values ?? ''}`; this._currentValue = +values; } @@ -157,51 +155,20 @@ export class SliderFilter implements Filter { // ------------------ /** - * Create the HTML template as a string - */ - protected buildTemplateHtmlString() { - const columnId = this.columnDef?.id ?? ''; - const minValue = this.filterProperties.hasOwnProperty('minValue') ? this.filterProperties.minValue : DEFAULT_MIN_VALUE; - const maxValue = this.filterProperties.hasOwnProperty('maxValue') ? this.filterProperties.maxValue : DEFAULT_MAX_VALUE; - const defaultValue = this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : minValue; - const step = this.filterProperties.hasOwnProperty('valueStep') ? this.filterProperties.valueStep : DEFAULT_STEP; - - if (this.filterParams.hideSliderNumber) { - return ` -
- -
`; - } - - return ` -
- -
- ${defaultValue} -
-
`; - } - - /** - * From the html template string, create a DOM element - * @param filterTemplate string + * Create the Filter DOM element * @param searchTerm optional preset search terms */ - protected createDomElement(filterTemplate: string, searchTerm?: SearchTerm) { - const columnId = this.columnDef && this.columnDef.id; - const minValue = (this.filterProperties.hasOwnProperty('minValue') && this.filterProperties.minValue !== undefined) ? this.filterProperties.minValue : DEFAULT_MIN_VALUE; - const startValue = +(this.filterParams.hasOwnProperty('sliderStartValue') ? this.filterParams.sliderStartValue : minValue); - const $headerElm = this.grid.getHeaderRowColumn(columnId); - $($headerElm).empty(); + protected createDomElement(searchTerm?: SearchTerm) { + const columnId = this.columnDef?.id ?? ''; + const minValue = this.filterProperties?.minValue ?? DEFAULT_MIN_VALUE; + const maxValue = this.filterProperties?.maxValue ?? DEFAULT_MAX_VALUE; + const defaultValue = this.filterParams?.sliderStartValue ?? minValue; + const step = this.filterProperties?.valueStep ?? DEFAULT_STEP; + const startValue = +(this.filterParams?.sliderStartValue ?? minValue); + const headerElm = this.grid.getHeaderRowColumn(columnId); + emptyElement(headerElm); // create the DOM element & add an ID and filter class - const $filterElm = $(filterTemplate); let searchTermInput = (searchTerm || '0') as string; if (+searchTermInput < minValue) { searchTermInput = `${minValue}`; @@ -211,23 +178,57 @@ export class SliderFilter implements Filter { } this._currentValue = +searchTermInput; - this.$filterInputElm = $filterElm.children('input'); - this.$filterNumberElm = $filterElm.children('div.input-group-addon.input-group-append').children(); - this.$filterInputElm.val(searchTermInput); - this.$filterNumberElm.html(searchTermInput); - $filterElm.data('columnId', columnId); + // create the DOM element + this.filterInputElm = document.createElement('input'); + this.filterInputElm.type = 'range'; + this.filterInputElm.className = `form-control slider-filter-input range ${this._elementRangeInputId}`; + this.filterInputElm.defaultValue = defaultValue; + this.filterInputElm.value = searchTermInput; + this.filterInputElm.min = `${minValue}`; + this.filterInputElm.max = `${maxValue}`; + this.filterInputElm.step = `${step}`; + this.filterInputElm.name = this._elementRangeInputId; + + const divContainerFilterElm = document.createElement('div'); + divContainerFilterElm.className = `search-filter slider-container filter-${columnId}`; + divContainerFilterElm.appendChild(this.filterInputElm); + + if (!this.filterParams.hideSliderNumber) { + divContainerFilterElm.classList.add('input-group'); + this.filterInputElm.value = searchTermInput; + + const divGroupAppendElm = document.createElement('div'); + divGroupAppendElm.className = 'input-group-addon input-group-append slider-value'; + + this.filterNumberElm = document.createElement('span'); + this.filterNumberElm.className = `input-group-text ${this._elementRangeOutputId}`; + this.filterNumberElm.textContent = searchTermInput; + divGroupAppendElm.appendChild(this.filterNumberElm); + divContainerFilterElm.appendChild(divGroupAppendElm); + } + + // this.filterNumberElm.html(searchTermInput); + divContainerFilterElm.dataset.columnid = `${columnId}`; // if there's a search term, we will add the "filled" class for styling purposes if (searchTerm) { - $filterElm.addClass('filled'); + divContainerFilterElm.classList.add('filled'); } // append the new DOM element to the header row - if ($filterElm && typeof $filterElm.appendTo === 'function') { - $filterElm.appendTo($headerElm); - } + headerElm.appendChild(divContainerFilterElm); - return $filterElm; + return divContainerFilterElm; + } + + protected handleInputChange(event: Event) { + const value = (event?.target as HTMLInputElement).value; + if (value !== undefined && value !== null) { + const element = document.querySelector(`.${this._elementRangeOutputId || ''}`); + if (element?.textContent) { + element.textContent = value; + } + } } protected handleOnChange(e: any) { @@ -235,10 +236,10 @@ export class SliderFilter implements Filter { this._currentValue = +value; if (this._clearFilterTriggered) { - this.$filterElm.removeClass('filled'); + this.filterElm.classList.remove('filled'); this.callback(e, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, searchTerms: [], shouldTriggerQuery: this._shouldTriggerQuery }); } else { - this.$filterElm.addClass('filled'); + this.filterElm.classList.add('filled'); this.callback(e, { columnDef: this.columnDef, operator: this.operator, searchTerms: [value || '0'], shouldTriggerQuery: this._shouldTriggerQuery }); } // reset both flags for next use diff --git a/test/cypress/integration/example05.spec.js b/test/cypress/integration/example05.spec.js index 73a6a1c5c..0eb44edb6 100644 --- a/test/cypress/integration/example05.spec.js +++ b/test/cypress/integration/example05.spec.js @@ -143,7 +143,7 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' it('should no longer have filters and it should show the full item count in the footer', () => { cy.get('.search-filter.filter-percentComplete .operator .form-control') - .should('have.value', null); + .should('have.value', ''); cy.get('.rangeInput_percentComplete') .invoke('val')