diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts index bb9486d57..4feb51354 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example11.ts @@ -156,7 +156,7 @@ export class Example11 { }, { id: 'percentComplete', name: '% Complete', field: 'percentComplete', type: FieldType.number, minWidth: 80, - editor: { model: Editors.slider, massUpdate: true, minValue: 0, maxValue: 100, }, + editor: { model: Editors.slider, massUpdate: true, minValue: 0, maxValue: 100, params: { hideSliderNumber: true } }, sortable: true, filterable: true, filter: { model: Filters.slider, operator: '>=' }, }, diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example16.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example16.ts index be697964f..897a80274 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example16.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example16.ts @@ -7,6 +7,7 @@ import { Formatters, GridOption, OperatorType, + SliderOption, } from '@slickgrid-universal/common'; import { SlickCustomTooltip } from '@slickgrid-universal/custom-tooltip-plugin'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; @@ -148,7 +149,7 @@ export class Example16 { model: Editors.slider, minValue: 0, maxValue: 100, - // params: { hideSliderNumber: true }, + editorOptions: { enableSliderTrackColoring: true, hideSliderNumber: true } as SliderOption, }, exportWithFormatter: false, formatter: Formatters.percentCompleteBar, diff --git a/packages/common/src/editors/__tests__/sliderEditor.spec.ts b/packages/common/src/editors/__tests__/sliderEditor.spec.ts index a4ad7379d..cc01d3fac 100644 --- a/packages/common/src/editors/__tests__/sliderEditor.spec.ts +++ b/packages/common/src/editors/__tests__/sliderEditor.spec.ts @@ -35,12 +35,14 @@ const gridStub = { describe('SliderEditor', () => { let divContainer: HTMLDivElement; + let consoleSpy: any; let editor: SliderEditor; let editorArguments: EditorArguments; let mockColumn: Column; let mockItemData: any; beforeEach(() => { + consoleSpy = jest.spyOn(global.console, 'warn').mockReturnValue(); divContainer = document.createElement('div'); divContainer.innerHTML = template; document.body.appendChild(divContainer); @@ -96,7 +98,7 @@ describe('SliderEditor', () => { editor = new SliderEditor(editorArguments); const editorElm = divContainer.querySelector('.slider-editor input.editor-price') as HTMLInputElement; - expect(editorElm.getAttribute('aria-label')).toBe('Price Slider Editor'); + expect(editorElm.ariaLabel).toBe('Price Slider Editor'); }); it('should have a title (tooltip) when defined in its column definition', () => { @@ -460,6 +462,52 @@ describe('SliderEditor', () => { expect(validation).toEqual({ valid: false, msg: 'Please enter a valid number between 10 and 99' }); }); }); + + it('should create the input editor with option defined in editor params and expect deprecated console warning', () => { + (mockColumn.internalColumnEditor as ColumnEditor).params = { sliderStartValue: 5, enableSliderTrackColoring: true }; + mockItemData = { id: 1, price: 80, isActive: true }; + editor = new SliderEditor(editorArguments); + editor.loadValue(mockItemData); + editor.setValue(45); + + const editorElm = divContainer.querySelector('.slider-editor input.editor-price') as HTMLInputElement; + editorElm.dispatchEvent(new Event('change')); + + expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All editor.params were moved, and deprecated, to "editorOptions" as SliderOption for better typing support.'); + }); + + it('should enableSliderTrackColoring and trigger a change event and expect slider track to have background color', () => { + (mockColumn.internalColumnEditor as ColumnEditor).editorOptions = { sliderStartValue: 5, enableSliderTrackColoring: true }; + mockItemData = { id: 1, price: 80, isActive: true }; + editor = new SliderEditor(editorArguments); + editor.loadValue(mockItemData); + editor.setValue(45); + + const editorElm = divContainer.querySelector('.slider-editor input.editor-price') as HTMLInputElement; + editorElm.dispatchEvent(new Event('change')); + + expect(editor.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', () => { + (mockColumn.internalColumnEditor as ColumnEditor).editorOptions = { sliderStartValue: 5, enableSliderTrackColoring: true }; + editor = new SliderEditor(editorArguments); + + const editorElm = divContainer.querySelector('.slider-editor input.editor-price') as HTMLInputElement; + editorElm.dispatchEvent(new Event('change')); + + const sliderInputs = divContainer.querySelectorAll('.slider-editor-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(); + }); }); describe('with Composite Editor', () => { diff --git a/packages/common/src/editors/sliderEditor.ts b/packages/common/src/editors/sliderEditor.ts index 7f980392b..f8e759523 100644 --- a/packages/common/src/editors/sliderEditor.ts +++ b/packages/common/src/editors/sliderEditor.ts @@ -1,7 +1,7 @@ import { setDeepValue, toSentenceCase } from '@slickgrid-universal/utils'; import { Constants } from '../constants'; -import { Column, ColumnEditor, CompositeEditorOption, Editor, EditorArguments, EditorValidator, EditorValidationResult, GridOption, SlickGrid, SlickNamespace } from '../interfaces/index'; +import { Column, ColumnEditor, CompositeEditorOption, Editor, EditorArguments, EditorValidator, EditorValidationResult, GridOption, SlickGrid, SlickNamespace, SliderOption, CurrentSliderOption } from '../interfaces/index'; import { getDescendantProperty } from '../services/utilities'; import { sliderValidator } from '../editorValidators/sliderValidator'; import { BindingEventService } from '../services/bindingEvent.service'; @@ -17,13 +17,13 @@ declare const Slick: SlickNamespace; export class SliderEditor implements Editor { protected _bindEventService: BindingEventService; protected _defaultValue = 0; - protected _elementRangeInputId = ''; - protected _elementRangeOutputId = ''; + protected _isValueTouched = false; + protected _originalValue?: number | string; protected _editorElm!: HTMLDivElement; protected _inputElm!: HTMLInputElement; - protected _isValueTouched = false; - originalValue?: number | string; - sliderNumberElm: HTMLSpanElement | null = null; + protected _sliderOptions!: CurrentSliderOption; + protected _sliderTrackElm!: HTMLDivElement; + protected _sliderNumberElm: HTMLSpanElement | null = null; /** is the Editor disabled? */ disabled = false; @@ -73,11 +73,16 @@ export class SliderEditor implements Editor { return this.gridOptions.autoCommitEdit ?? false; } - /** Getter for the Editor Generic Params */ + /** @deprecated Getter for the Editor Generic Params */ protected get editorParams(): any { return this.columnEditor.params || {}; } + /** Getter for the current Slider Options */ + get sliderOptions(): CurrentSliderOption | undefined { + return this._sliderOptions; + } + /** Get the Validator function, can be passed in Editor property or Column Definition */ get validator(): EditorValidator | undefined { return this.columnEditor?.validator ?? this.columnDef?.validator; @@ -88,15 +93,11 @@ export class SliderEditor implements Editor { if (container && this.columnDef) { // define the input & slider number IDs - const itemId = this.args?.item?.id ?? ''; - this._elementRangeInputId = `rangeInput_${this.columnDef.id}_${itemId}`; - this._elementRangeOutputId = `rangeOutput_${this.columnDef.id}_${itemId}`; const compositeEditorOptions = this.args.compositeEditorOptions; // create HTML string template this._editorElm = this.buildDomElement(); this._inputElm = this._editorElm.querySelector('input') as HTMLInputElement; - this.sliderNumberElm = this._editorElm.querySelector(`span.input-group-text.${this._elementRangeOutputId}`); if (!compositeEditorOptions) { this.focus(); @@ -104,7 +105,8 @@ export class SliderEditor implements Editor { // watch on change event container.appendChild(this._editorElm); - this._bindEventService.bind(this._editorElm, ['change', 'mouseup', 'touchend'], this.handleChangeEvent.bind(this)); + this._bindEventService.bind(this._sliderTrackElm, ['click', 'mouseup'], this.sliderTrackClicked.bind(this) as EventListener); + this._bindEventService.bind(this._editorElm, ['change', 'mouseup', 'touchend'], this.handleChangeEvent.bind(this) as EventListener); // 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 @@ -114,14 +116,16 @@ export class SliderEditor implements Editor { cancel() { if (this._inputElm) { - this._inputElm.value = `${this.originalValue}`; + this._inputElm.value = `${this._originalValue}`; } this.args.cancelChanges(); } destroy() { this._bindEventService.unbindAll(); - this._inputElm?.remove?.(); + this._inputElm?.remove(); + this._editorElm?.remove(); + this._sliderTrackElm?.remove(); } disable(isDisabled = true) { @@ -165,8 +169,8 @@ export class SliderEditor implements Editor { if (this._inputElm) { this._inputElm.value = `${value}`; } - if (this.sliderNumberElm) { - this.sliderNumberElm.textContent = `${value}`; + if (this._sliderNumberElm) { + this._sliderNumberElm.textContent = `${value}`; } if (isApplyingValue) { @@ -202,7 +206,7 @@ export class SliderEditor implements Editor { isValueChanged(): boolean { const elmValue = this._inputElm?.value ?? ''; - return (!(elmValue === '' && this.originalValue === undefined)) && (+elmValue !== this.originalValue); + return (!(elmValue === '' && this._originalValue === undefined)) && (+elmValue !== this._originalValue); } isValueTouched(): boolean { @@ -212,7 +216,6 @@ export class SliderEditor implements Editor { loadValue(item: any) { const fieldName = this.columnDef?.field ?? ''; - if (item && fieldName !== undefined) { // is the field a complex object, "address.streetNumber" const isComplexObject = fieldName?.indexOf('.') > 0; @@ -221,15 +224,16 @@ export class SliderEditor implements Editor { if (value === '' || value === null || value === undefined) { value = this._defaultValue; // load default value when item doesn't have any value } - this.originalValue = +value; + this._originalValue = +value; if (this._inputElm) { this._inputElm.value = `${value}`; this._inputElm.title = `${value}`; } - if (this.sliderNumberElm) { - this.sliderNumberElm.textContent = `${value}`; + if (this._sliderNumberElm) { + this._sliderNumberElm.textContent = `${value}`; } } + this.updateTrackFilledColorWhenEnabled(); } /** @@ -237,7 +241,7 @@ export class SliderEditor implements Editor { * when no value is provided it will use the original value to reset (could be useful with Composite Editor Modal with edit/clone) */ reset(value?: number | string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { - const inputValue = value ?? this.originalValue ?? 0; + const inputValue = value ?? this._originalValue ?? 0; if (this._editorElm) { this._editorElm.querySelector('input')!.value = `${inputValue}`; this._editorElm.querySelector('div.input-group-addon.input-group-append')!.textContent = `${inputValue}`; @@ -266,7 +270,7 @@ export class SliderEditor implements Editor { serializeValue() { const elmValue: string = this._inputElm?.value ?? ''; - return elmValue !== '' ? parseInt(elmValue, 10) : this.originalValue; + return elmValue !== '' ? parseInt(elmValue, 10) : this._originalValue; } validate(_targetElm?: any, inputValue?: any): EditorValidationResult { @@ -301,33 +305,39 @@ export class SliderEditor implements Editor { protected buildDomElement(): HTMLDivElement { const columnId = this.columnDef?.id ?? ''; const title = this.columnEditor && this.columnEditor.title || ''; - const minValue = this.columnEditor?.minValue ?? Constants.SLIDER_DEFAULT_MIN_VALUE; - const maxValue = this.columnEditor?.maxValue ?? Constants.SLIDER_DEFAULT_MAX_VALUE; - const defaultValue = this.editorParams?.sliderStartValue ?? minValue; - this._defaultValue = defaultValue; + const minValue = +(this.columnEditor?.minValue ?? Constants.SLIDER_DEFAULT_MIN_VALUE); + const maxValue = +(this.columnEditor?.maxValue ?? Constants.SLIDER_DEFAULT_MAX_VALUE); + const step = +(this.columnEditor?.valueStep ?? Constants.SLIDER_DEFAULT_STEP); + const defaultValue = this.getEditorOptionByName('sliderStartValue') ?? minValue; + this._defaultValue = +defaultValue; + this._sliderTrackElm = createDomElement('div', { className: 'slider-track' }); const inputElm = createDomElement('input', { - type: 'range', name: this._elementRangeInputId, title, - defaultValue, value: defaultValue, min: `${minValue}`, max: `${maxValue}`, + type: 'range', title, + defaultValue: `${defaultValue}`, value: `${defaultValue}`, min: `${minValue}`, max: `${maxValue}`, step: `${this.columnEditor?.valueStep ?? Constants.SLIDER_DEFAULT_STEP}`, - className: `form-control slider-editor-input editor-${columnId} range ${this._elementRangeInputId}`, + ariaLabel: this.columnEditor?.ariaLabel ?? `${toSentenceCase(columnId + '')} Slider Editor`, + className: `slider-editor-input editor-${columnId}`, }); - inputElm.setAttribute('aria-label', this.columnEditor?.ariaLabel ?? `${toSentenceCase(columnId + '')} Slider Editor`); const divContainerElm = createDomElement('div', { className: 'slider-container slider-editor' }); - divContainerElm.appendChild(inputElm); + const sliderInputContainerElm = createDomElement('div', { className: 'slider-input-container slider-editor' }); + sliderInputContainerElm.appendChild(this._sliderTrackElm); + sliderInputContainerElm.appendChild(inputElm); + divContainerElm.appendChild(sliderInputContainerElm); - if (!this.editorParams.hideSliderNumber) { + if (!this.getEditorOptionByName('hideSliderNumber')) { divContainerElm.classList.add('input-group'); - //
${defaultValue}
const divGroupAddonElm = createDomElement('div', { className: 'input-group-addon input-group-append slider-value' }); - divGroupAddonElm.appendChild( - createDomElement('span', { className: `input-group-text ${this._elementRangeOutputId}`, textContent: `${defaultValue}` }) - ); + this._sliderNumberElm = createDomElement('span', { className: `input-group-text`, textContent: `${defaultValue}` }); + divGroupAddonElm.appendChild(this._sliderNumberElm); divContainerElm.appendChild(divGroupAddonElm); } + // merge options with optional user's custom options + this._sliderOptions = { minValue, maxValue, step }; + return divContainerElm; } @@ -340,7 +350,22 @@ export class SliderEditor implements Editor { this.disable(isCellEditable === false); } - protected handleChangeEvent(event: Event) { + /** + * Get option from editor.params PR editor.editorOptions + * @deprecated this should be removed when slider editorParams are replaced by editorOptions + */ + protected getEditorOptionByName(optionName: string, defaultValue?: string | number | boolean): T { + let outValue: string | number | boolean | undefined; + if (this.columnEditor.editorOptions?.[optionName as keyof SliderOption] !== undefined) { + outValue = this.columnEditor.editorOptions[optionName as keyof SliderOption]; + } else if (this.editorParams?.[optionName] !== undefined) { + console.warn('[Slickgrid-Universal] All editor.params were moved, and deprecated, to "editorOptions" as SliderOption for better typing support.'); + outValue = this.editorParams?.[optionName]; + } + return outValue as T ?? defaultValue ?? undefined; + } + + protected handleChangeEvent(event: MouseEvent) { this._isValueTouched = true; const compositeEditorOptions = this.args.compositeEditorOptions; @@ -354,11 +379,12 @@ export class SliderEditor implements Editor { protected handleChangeSliderNumber(event: Event) { const value = (event.target)?.value ?? ''; if (value !== '') { - if (!this.editorParams.hideSliderNumber && this.sliderNumberElm) { - this.sliderNumberElm.textContent = value; + if (!this.getEditorOptionByName('hideSliderNumber') && this._sliderNumberElm) { + this._sliderNumberElm.textContent = value; } this._inputElm.title = value; } + this.updateTrackFilledColorWhenEnabled(); } protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) { @@ -384,4 +410,33 @@ export class SliderEditor implements Editor { { ...new Slick.EventData(), ...event } ); } + + protected sliderTrackClicked(e: MouseEvent) { + e.preventDefault(); + const sliderTrackX = e.offsetX; + const sliderTrackWidth = this._sliderTrackElm.offsetWidth; + const trackPercentPosition = (sliderTrackX + 0) * 100 / sliderTrackWidth; + + if (this._inputElm) { + // automatically move to calculated clicked percentage + // dispatch a change event to update its value & number when shown + this._inputElm.value = `${trackPercentPosition}`; + this._inputElm.dispatchEvent(new Event('change')); + } + } + + protected updateTrackFilledColorWhenEnabled() { + if (this.getEditorOptionByName('enableSliderTrackColoring') && this._inputElm) { + const percent1 = 0; + const percent2 = ((+this.getValue() - +this._inputElm.min) / (this.sliderOptions?.maxValue ?? 0 - +this._inputElm.min)) * 100; + const bg = 'linear-gradient(to right, %b %p1, %c %p1, %c %p2, %b %p2)' + .replace(/%b/g, '#eee') + .replace(/%c/g, (this.getEditorOptionByName('sliderTrackFilledColor') ?? 'var(--slick-slider-filter-thumb-color, #86bff8)') as string) + .replace(/%p1/g, `${percent1}%`) + .replace(/%p2/g, `${percent2}%`); + + this._sliderTrackElm.style.background = bg; + this._sliderOptions.sliderTrackBackground = bg; + } + } } diff --git a/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts b/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts index b14073cc9..bb35d3ab2 100644 --- a/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts +++ b/packages/common/src/filters/__tests__/singleSelectFilter.spec.ts @@ -75,7 +75,7 @@ describe('SelectFilter', () => { expect(spyGetHeaderRow).toHaveBeenCalled(); expect(filterCount).toBe(1); expect(filter.isMultipleSelect).toBe(false); - expect(filter.columnDef.filter.emptySearchTermReturnAllValues).toBeUndefined(); + expect(filter.columnDef.filter!.emptySearchTermReturnAllValues).toBeUndefined(); }); it('should create the select filter with empty search term when passed an empty string as a filter argument and not expect "filled" css class either', () => { diff --git a/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts b/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts index 6b11801a5..5251fa8e4 100644 --- a/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts +++ b/packages/common/src/filters/__tests__/sliderRangeFilter.spec.ts @@ -246,7 +246,7 @@ describe('SliderRangeFilter', () => { const filterLowestElm = divContainer.querySelector('.lowest-range-duration') as HTMLInputElement; const filterHighestElm = divContainer.querySelector('.highest-range-duration') as HTMLInputElement; - expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All filter.params were moved, and deprecated, to "filterOptions" as SliderRangeOption for better typing support.'); + expect(consoleSpy).toHaveBeenCalledWith('[Slickgrid-Universal] All filter.params were moved, and deprecated, to "filterOptions" as SliderOption for better typing support.'); expect(filterLowestElm.textContent).toBe('4'); expect(filterHighestElm.textContent).toBe('69'); expect(filter.currentValues).toEqual([4, 69]); diff --git a/packages/common/src/filters/sliderFilter.ts b/packages/common/src/filters/sliderFilter.ts index 81e04f50b..e466531ee 100644 --- a/packages/common/src/filters/sliderFilter.ts +++ b/packages/common/src/filters/sliderFilter.ts @@ -5,6 +5,7 @@ import { OperatorString, OperatorType, SearchTerm, } from '../enums/index'; import { Column, ColumnFilter, + CurrentSliderOption, Filter, FilterArguments, FilterCallback, @@ -13,6 +14,7 @@ import { SlickGrid, SlickNamespace, SliderRangeOption, + SliderType, } from '../interfaces/index'; import { BindingEventService } from '../services/bindingEvent.service'; import { createDomElement, emptyElement } from '../services/domUtilities'; @@ -20,14 +22,6 @@ import { TranslaterService } from '../services/translater.service'; import { mapOperatorToShorthandDesignation } from '../services/utilities'; import { buildSelectOperator, compoundOperatorNumeric } from './filterUtilities'; -interface CurrentSliderOption { - minValue: number; - maxValue: number; - step: number; - sliderTrackBackground?: string; -} -type SliderType = 'single' | 'double' | 'compound'; - declare const Slick: SlickNamespace; const GAP_BETWEEN_SLIDER_HANDLES = 0; const Z_INDEX_MIN_GAP = 20; // gap in Px before we change z-index so that lowest/highest handle doesn't block each other @@ -67,11 +61,6 @@ export class SliderFilter implements Filter { return this.columnDef?.filter?.params ?? {}; } - /** Getter for the Filter Options */ - get filterOptions(): SliderRangeOption | undefined { - return this.columnFilter.filterOptions; - } - /** Getter for the Column Filter */ get columnFilter(): ColumnFilter { return this.columnDef?.filter ?? {}; @@ -181,21 +170,9 @@ export class SliderFilter implements Filter { /** destroy the filter */ destroy() { this._bindEventService.unbindAll(); - } - - /** - * Get option from filter.params PR filter.filterOptions - * @deprecated this should be removed when slider filterParams are replaced by filterOptions - */ - getFilterOptionByName(optionName: string, defaultValue?: string | number | boolean): T { - let outValue: string | number | boolean | undefined; - if (this.filterOptions?.[optionName as keyof SliderRangeOption] !== undefined) { - outValue = this.filterOptions[optionName as keyof SliderRangeOption]; - } else if (this.filterParams?.[optionName] !== undefined) { - console.warn('[Slickgrid-Universal] All filter.params were moved, and deprecated, to "filterOptions" as SliderRangeOption for better typing support.'); - outValue = this.filterParams?.[optionName]; - } - return outValue as T ?? defaultValue ?? undefined; + this._sliderTrackElm?.remove(); + this._sliderLeftElm?.remove(); + this._sliderRightElm?.remove(); } /** @@ -328,14 +305,14 @@ export class SliderFilter implements Filter { const sliderNumberClass = hideSliderNumbers ? '' : 'input-group'; this._divContainerFilterElm = createDomElement('div', { className: `${sliderNumberClass} search-filter slider-container slider-values filter-${columnId}`.trim() }); - this._sliderRangeContainElm.append(this._sliderTrackElm); + this._sliderRangeContainElm.appendChild(this._sliderTrackElm); if (this.sliderType === 'double' && this._sliderLeftElm) { - this._sliderRangeContainElm.append(this._sliderLeftElm); + this._sliderRangeContainElm.appendChild(this._sliderLeftElm); } - this._sliderRangeContainElm.append(this._sliderRightElm); + this._sliderRangeContainElm.appendChild(this._sliderRightElm); if (hideSliderNumbers) { - this._divContainerFilterElm.append(this._sliderRangeContainElm); + this._divContainerFilterElm.appendChild(this._sliderRangeContainElm); } else { let leftDivGroupElm: HTMLDivElement | HTMLSpanElement | undefined; if (this.sliderType === 'compound' && this._selectOperatorElm) { @@ -344,18 +321,18 @@ export class SliderFilter implements Filter { } else if (this.sliderType === 'double') { leftDivGroupElm = createDomElement('div', { className: `input-group-addon input-group-prepend slider-range-value` }); this._leftSliderNumberElm = createDomElement('span', { className: `input-group-text lowest-range-${columnId}`, textContent: `${defaultStartValue}` }); - leftDivGroupElm.append(this._leftSliderNumberElm); + leftDivGroupElm.appendChild(this._leftSliderNumberElm); } const rightDivGroupElm = createDomElement('div', { className: `input-group-addon input-group-append slider-range-value` }); this._rightSliderNumberElm = createDomElement('span', { className: `input-group-text highest-range-${columnId}`, textContent: `${rightDefaultVal}` }); - rightDivGroupElm.append(this._rightSliderNumberElm); + rightDivGroupElm.appendChild(this._rightSliderNumberElm); if (leftDivGroupElm) { - this._divContainerFilterElm.append(leftDivGroupElm); + this._divContainerFilterElm.appendChild(leftDivGroupElm); } - this._divContainerFilterElm.append(this._sliderRangeContainElm); - this._divContainerFilterElm.append(rightDivGroupElm); + this._divContainerFilterElm.appendChild(this._sliderRangeContainElm); + this._divContainerFilterElm.appendChild(rightDivGroupElm); } // if we are preloading searchTerms, we'll keep them for reference @@ -371,8 +348,8 @@ export class SliderFilter implements Filter { } // append the new DOM element to the header row - this._argFilterContainerElm.append(this._divContainerFilterElm); - this.updateTrackFilledColor(); + this._argFilterContainerElm.appendChild(this._divContainerFilterElm); + this.updateTrackFilledColorWhenEnabled(); // attach events this._bindEventService.bind(this._sliderTrackElm, 'click', this.sliderTrackClicked.bind(this) as EventListener); @@ -441,6 +418,21 @@ export class SliderFilter implements Filter { this._sliderRightElm?.classList[addRemoveCmd]('focus'); } + /** + * Get option from filter.params PR filter.filterOptions + * @deprecated this should be removed when slider filterParams are replaced by filterOptions + */ + protected getFilterOptionByName(optionName: string, defaultValue?: string | number | boolean): T { + let outValue: string | number | boolean | undefined; + if (this.columnFilter.filterOptions?.[optionName as keyof SliderRangeOption] !== undefined) { + outValue = this.columnFilter.filterOptions[optionName as keyof SliderRangeOption]; + } else if (this.filterParams?.[optionName] !== undefined) { + console.warn('[Slickgrid-Universal] All filter.params were moved, and deprecated, to "filterOptions" as SliderOption for better typing support.'); + outValue = this.filterParams?.[optionName]; + } + return outValue as T ?? defaultValue ?? undefined; + } + protected slideLeftInputChanged() { const sliderLeftVal = parseInt(this._sliderLeftElm?.value ?? '', 10); const sliderRightVal = parseInt(this._sliderRightElm?.value ?? '', 10); @@ -464,13 +456,12 @@ export class SliderFilter implements Filter { } } - this.updateTrackFilledColor(); + this.updateTrackFilledColorWhenEnabled(); this.changeBothSliderFocuses(true); const hideSliderNumbers = this.getFilterOptionByName('hideSliderNumber') ?? this.getFilterOptionByName('hideSliderNumbers'); if (!hideSliderNumbers && this._leftSliderNumberElm?.textContent) { this._leftSliderNumberElm.textContent = this._sliderLeftElm?.value ?? ''; } - } protected slideRightInputChanged() { @@ -481,7 +472,7 @@ export class SliderFilter implements Filter { this._sliderRightElm.value = String(sliderLeftVal + this.getFilterOptionByName('stopGapBetweenSliderHandles', GAP_BETWEEN_SLIDER_HANDLES)); } - this.updateTrackFilledColor(); + this.updateTrackFilledColorWhenEnabled(); this.changeBothSliderFocuses(true); this._sliderRangeContainElm.title = this.sliderType === 'double' ? `${sliderLeftVal} - ${sliderRightVal}` : `${sliderRightVal}`; @@ -497,8 +488,9 @@ export class SliderFilter implements Filter { const sliderTrackWidth = this._sliderTrackElm.offsetWidth; const trackPercentPosition = (sliderTrackX + 0) * 100 / sliderTrackWidth; - if (this._sliderRightElm && (this.sliderType === 'compound' || this.sliderType === 'single')) { + if (this._sliderRightElm && this.sliderType !== 'double') { // when slider is compound/single, we'll automatically move to calculated clicked percentage + // dispatch a change event to update its value & number when shown this._sliderRightElm.value = `${trackPercentPosition}`; this._sliderRightElm.dispatchEvent(new Event('change')); } else { @@ -515,7 +507,7 @@ export class SliderFilter implements Filter { } } - protected updateTrackFilledColor() { + protected updateTrackFilledColorWhenEnabled() { if (this.getFilterOptionByName('enableSliderTrackColoring') && this._sliderRightElm) { let percent1 = 0; if (this._sliderLeftElm) { diff --git a/packages/common/src/interfaces/columnEditor.interface.ts b/packages/common/src/interfaces/columnEditor.interface.ts index ebfd42d91..b177ab416 100644 --- a/packages/common/src/interfaces/columnEditor.interface.ts +++ b/packages/common/src/interfaces/columnEditor.interface.ts @@ -7,7 +7,6 @@ import { CollectionOverrideArgs, CollectionSortBy, EditorValidator, - MultipleSelectOption, } from './index'; export interface ColumnEditor { @@ -71,7 +70,7 @@ export interface ColumnEditor { * Please note that if you use options that have existed model interfaces, you should cast with "as X", * for example { editorOptions: {maxHeight: 250} as MultipleSelectOption } */ - editorOptions?: MultipleSelectOption | any; + editorOptions?: any; /** * Defaults to false, when set it will render any HTML code instead of removing it (sanitized) diff --git a/packages/common/src/interfaces/columnFilter.interface.ts b/packages/common/src/interfaces/columnFilter.interface.ts index de7ea3d93..4f26cfc72 100644 --- a/packages/common/src/interfaces/columnFilter.interface.ts +++ b/packages/common/src/interfaces/columnFilter.interface.ts @@ -6,7 +6,6 @@ import { CollectionSortBy, Column, Filter, - MultipleSelectOption, OperatorDetail, } from './index'; import { Observable, Subject } from '../services/rxjsFacade'; @@ -91,7 +90,7 @@ export interface ColumnFilter { * Please note that if you use options that have existed model interfaces, you should cast with "as X", * for example { filterOptions: {maxHeight: 250} as MultipleSelectOption } */ - filterOptions?: MultipleSelectOption | any; + filterOptions?: any; /** * Use "params" to pass any type of arguments to your Custom Filter diff --git a/packages/common/src/interfaces/sliderOption.interface.ts b/packages/common/src/interfaces/sliderOption.interface.ts index 44e1bf8f7..7406c35da 100644 --- a/packages/common/src/interfaces/sliderOption.interface.ts +++ b/packages/common/src/interfaces/sliderOption.interface.ts @@ -21,4 +21,13 @@ export interface SliderOption { export interface SliderRangeOption extends Omit { /** Defaults to false, hide the slider numbers shown on the left/right side */ hideSliderNumbers?: boolean; -} \ No newline at end of file +} + +export interface CurrentSliderOption { + minValue: number; + maxValue: number; + step: number; + sliderTrackBackground?: string; +} + +export type SliderType = 'single' | 'double' | 'compound'; \ No newline at end of file diff --git a/packages/common/src/styles/_variables.scss b/packages/common/src/styles/_variables.scss index fcf9e8ec6..2fbbc02c4 100644 --- a/packages/common/src/styles/_variables.scss +++ b/packages/common/src/styles/_variables.scss @@ -657,13 +657,17 @@ $slick-draggable-group-column-icon-margin-left: 4px !default; /* Input Slider Filter (vanilla html) */ $slick-slider-filter-border: 1px solid #ccc !default; +$slick-slider-filter-border-radius: 4px !default; $slick-slider-filter-bgcolor: #eee !default; +$slick-slider-filter-input-bgcolor: #fff !default; $slick-slider-filter-runnable-track-bgcolor: #ddd !default; $slick-slider-filter-runnable-track-cursor: pointer !default; $slick-slider-filter-runnable-track-height: 5px !default; $slick-slider-filter-runnable-track-padding: 0 6px !default; $slick-slider-filter-fill-lower-color: #ddd !default; /* ms only */ $slick-slider-filter-fill-focus-lower-color: #aaa !default; /* ms only */ +$slick-slider-filter-focus-border-color: lighten($slick-primary-color, 10%) !default; +$slick-slider-filter-focus-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px rgba(lighten($slick-primary-color, 3%), .3) !default; $slick-slider-filter-height: $slick-header-input-height !default; $slick-slider-filter-thumb-border-radius: 50% !default; $slick-slider-filter-thumb-cursor: grab !default; @@ -676,11 +680,6 @@ $slick-slider-filter-thumb-border: 2px solid darken($sl $slick-slider-filter-number-padding: 4px 8px !default; $slick-slider-filter-number-font-size: calc(#{$slick-font-size-base-value} - 1px) !default; -/* Input Range Slider Filter */ -$slick-slider-range-filter-border-radius: 4px !default; -$slick-slider-range-focus-border-color: lighten($slick-primary-color, 10%) !default; -$slick-slider-range-focus-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px rgba(lighten($slick-primary-color, 3%), .3) !default; - /* Multiple-Select Filter */ $slick-multiselect-input-filter-border: 1px solid #ccc !default; $slick-multiselect-input-filter-font-family: "Helvetica Neue", Helvetica, Arial !default; diff --git a/packages/common/src/styles/slick-plugins.scss b/packages/common/src/styles/slick-plugins.scss index d5d51f554..f111d9c9d 100644 --- a/packages/common/src/styles/slick-plugins.scss +++ b/packages/common/src/styles/slick-plugins.scss @@ -969,130 +969,168 @@ input.flatpickr.form-control { // Input Slider Filters (with vanilla html) // ---------------------------------------------- -input.slider-editor-input[type=range], -input.slider-filter-input[type=range] { - /*removes default webkit styles*/ - appearance: none; - flex: 1; - height: var(--slick-slider-filter-height, $slick-slider-filter-height); - padding: var(--slick-slider-filter-runnable-track-padding, $slick-slider-filter-runnable-track-padding); +.slider-container { + display: flex; - /* change runnable track color while in focus on all browsers */ - &:focus { - outline: none; - } + input[type=range] { + /*removes default webkit styles*/ + appearance: none; + flex: 1; + height: var(--slick-slider-filter-height, $slick-slider-filter-height); + padding: var(--slick-slider-filter-runnable-track-padding, $slick-slider-filter-runnable-track-padding); - /* WebKit specific (Opera/Chrome/Safari) */ - &::-webkit-slider-runnable-track { - height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); - border: none; - border-radius: 3px; - } - &::-webkit-slider-thumb { - cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); - -webkit-appearance: none; - height: var(--slick-slider-filter-thumb-size, $slick-slider-filter-thumb-size); - width: var(--slick-slider-filter-thumb-size, $slick-slider-filter-thumb-size); - border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); - border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); - background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); - margin-top: -4px; - } + /* change runnable track color while in focus on all browsers */ + &:focus { + outline: none; + } - /* Mozilla Firefox specific */ + /* WebKit specific (Opera/Chrome/Safari) */ + &::-webkit-slider-runnable-track { + height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); + border: none; + border-radius: 3px; + -webkit-appearance: none; + } + &::-webkit-slider-thumb { + cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); + height: var(--slick-slider-filter-thumb-size, $slick-slider-filter-thumb-size); + width: var(--slick-slider-filter-thumb-size, $slick-slider-filter-thumb-size); + border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); + border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); + background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + margin-top: -4px; + pointer-events: auto; + -webkit-appearance: none; + } - /*fix for FF unable to apply focus style bug */ - border: var(--slick-slider-filter-border, $slick-slider-filter-border); + /* Mozilla Firefox specific */ - &::-moz-range-track { - height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); - border: none; - border-radius: 3px; - } - &::-moz-range-thumb { - cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); - height: var(--slick-slider-filter-thumb-height, $slick-slider-filter-thumb-height); - width: var(--slick-slider-filter-thumb-width, $slick-slider-filter-thumb-width); - border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); - border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); - background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); - } + /*fix for FF unable to apply focus style bug */ + border: var(--slick-slider-filter-border, $slick-slider-filter-border); - /*hide the outline behind the border*/ - &:-moz-focusring{ - outline: 1px solid white; - outline-offset: -1px; - } + &::-moz-range-track { + height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); + border: none; + border-radius: 3px; + -moz-appearance: none; + } + &::-moz-range-thumb { + cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); + height: var(--slick-slider-filter-thumb-height, $slick-slider-filter-thumb-height); + width: var(--slick-slider-filter-thumb-width, $slick-slider-filter-thumb-width); + border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); + border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); + background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + pointer-events: auto; + } - /* Microsoft IE specific */ - &::-ms-track { - height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); + /*hide the outline behind the border*/ + &:-moz-focusring{ + outline: 1px solid #fff; + outline-offset: -1px; + } - /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ - background: transparent; + /* Microsoft IE specific */ + &::-ms-track { + height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); - /*leave room for the larger thumb to overflow with a transparent border */ - border-color: transparent; - border-width: 6px 0; + /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ + background: transparent; - /*remove default tick marks*/ - color: transparent; - } - &::-ms-fill-lower { - background: var(--slick-slider-filter-fill-lower-color, $slick-slider-filter-fill-lower-color); - border-radius: 10px; - } - &::-ms-fill-upper { - background: var(--slick-slider-filter-bgcolor, $slick-slider-filter-bgcolor); - border-radius: 10px; - } - &::-ms-thumb { - cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); - height: var(--slick-slider-filter-thumb-height, $slick-slider-filter-thumb-height); - width: var(--slick-slider-filter-thumb-width, $slick-slider-filter-thumb-width); - border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); - border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); - background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); - margin-top: 1px; - } - &::-ms-tooltip { - display: none; - } - &:active::-webkit-slider-thumb { - background-color: var(--slick-slider-filter-thumb-active-bg-color, $slick-slider-filter-thumb-active-bg-color); - border: 2px solid var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); - } - &:active::-moz-range-thumb { - background-color: var(--slick-slider-filter-thumb-active-bg-color, $slick-slider-filter-thumb-active-bg-color); - border: 2px solid var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + /*leave room for the larger thumb to overflow with a transparent border */ + border-color: transparent; + border-width: 6px 0; + + /*remove default tick marks*/ + color: transparent; + appearance: none; + } + &::-ms-fill-lower { + background: var(--slick-slider-filter-fill-lower-color, $slick-slider-filter-fill-lower-color); + border-radius: 10px; + } + &::-ms-fill-upper { + background: var(--slick-slider-filter-bgcolor, $slick-slider-filter-bgcolor); + border-radius: 10px; + } + &::-ms-thumb { + cursor: var(--slick-slider-filter-thumb-cursor, $slick-slider-filter-thumb-cursor); + height: var(--slick-slider-filter-thumb-height, $slick-slider-filter-thumb-height); + width: var(--slick-slider-filter-thumb-width, $slick-slider-filter-thumb-width); + border-radius: var(--slick-slider-filter-thumb-border-radius, $slick-slider-filter-thumb-border-radius); + border: var(--slick-slider-filter-thumb-border, $slick-slider-filter-thumb-border); + background: var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + margin-top: 1px; + pointer-events: auto; + } + &::-ms-tooltip { + display: none; + } + &:active::-webkit-slider-thumb { + background-color: var(--slick-slider-filter-thumb-active-bg-color, $slick-slider-filter-thumb-active-bg-color); + border: 2px solid var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + } + &:active::-moz-range-thumb { + background-color: var(--slick-slider-filter-thumb-active-bg-color, $slick-slider-filter-thumb-active-bg-color); + border: 2px solid var(--slick-slider-filter-thumb-color, $slick-slider-filter-thumb-color); + } } } -.search-filter { +.search-filter, .slider-editor { height: var(--slick-header-input-height, $slick-header-input-height); &::placeholder { color: var(--slick-editor-placeholder-color, $slick-editor-placeholder-color); } - .slider-value { + .input-group-text { + display: flex; + align-items: center; + padding: var(--slick-slider-filter-number-padding, $slick-slider-filter-number-padding); + font-size: var(--slick-slider-filter-number-font-size, $slick-slider-filter-number-font-size); + } + + .slider-value, .slider-values { padding: 0; height: 100%; + } + input.slider-filter-input[type=range] { + padding: var(--slick-slider-filter-runnable-track-padding, $slick-slider-filter-runnable-track-padding); + height: var(--slick-slider-filter-height, $slick-slider-filter-height); + } + + /* Slider Editor */ + input.slider-editor-input[type=range] { + padding: var(--slick-slider-editor-runnable-track-padding, $slick-slider-editor-runnable-track-padding); + height: var(--slick-slider-editor-height, $slick-slider-editor-height); + } + .slider-range-value { + padding: 0; + height: 100%; .input-group-text { padding: var(--slick-slider-filter-number-padding, $slick-slider-filter-number-padding); font-size: var(--slick-slider-filter-number-font-size, $slick-slider-filter-number-font-size); } } -} -input.slider-filter-input[type=range] { - padding: var(--slick-slider-filter-runnable-track-padding, $slick-slider-filter-runnable-track-padding); - height: var(--slick-slider-filter-height, $slick-slider-filter-height); -} -/* Slider Editor */ -input.slider-editor-input[type=range] { - padding: var(--slick-slider-editor-runnable-track-padding, $slick-slider-editor-runnable-track-padding); - height: var(--slick-slider-editor-height, $slick-slider-editor-height); + &.input-group .input-group-addon:first-child + .filter-input input { + border-left: 0; + } + &:not(.input-group) { + .slider-editor, .slider-filter, + .slider-input-container .slider-editor-input, + .slider-input-container .slider-filter-input { + border-radius: var(--slick-slider-filter-border-radius, $slick-slider-filter-border-radius); + } + } + &.input-group > :first-child:not(.input-group-addon), + &.input-group > :first-child:not(.input-group-addon), + &.input-group > :first-child:not(.input-group-addon) .slider-filter-input, + &.input-group > :first-child:not(.input-group-addon) .slider-editor-input { + border-bottom-left-radius: var(--slick-slider-filter-border-radius, $slick-slider-filter-border-radius); + border-top-left-radius: var(--slick-slider-filter-border-radius, $slick-slider-filter-border-radius); + } } .slider-editor { @@ -1107,69 +1145,41 @@ input.slider-editor-input[type=range] { } } -// ---------------------------------------------- -// Input Slider Range Filter (with vanilla html) -// ---------------------------------------------- - -.slider-container { - display: flex; - - .slider-range-value { - padding: 0; - height: 100%; - .input-group-text { - padding: var(--slick-slider-filter-number-padding, $slick-slider-filter-number-padding); - font-size: var(--slick-slider-filter-number-font-size, $slick-slider-filter-number-font-size); - } - } - &:not(.input-group) { - .slider-filter-input { - border-radius: var(--slick-slider-range-filter-border-radius, $slick-slider-range-filter-border-radius); - } - } - &.input-group > :first-child:not(.input-group-addon) .slider-filter-input { - border-bottom-left-radius: var(--slick-slider-range-filter-border-radius, $slick-slider-range-filter-border-radius); - border-top-left-radius: var(--slick-slider-range-filter-border-radius, $slick-slider-range-filter-border-radius); - } -} .slider-input-container { position: relative; width: 100%; + background-color: var(--slick-slider-filter-input-bgcolor, $slick-slider-filter-input-bgcolor); + + &.slider-values { + display: flex; + padding: 0; + } .slider-track { - cursor: var(--slick-slider-filter-runnable-track-cursor, $slick-slider-filter-runnable-track-cursor); - width: calc(100% - 16px); - height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); position: absolute; margin: auto; margin-left: 8px; top: 0; bottom: 0; border-radius: 3px; + cursor: var(--slick-slider-filter-runnable-track-cursor, $slick-slider-filter-runnable-track-cursor); background: var(--slick-slider-filter-runnable-track-bgcolor, $slick-slider-filter-runnable-track-bgcolor); + height: var(--slick-slider-filter-runnable-track-height, $slick-slider-filter-runnable-track-height); + width: calc(100% - 16px); } - input[type="range"] { + input[type=range] { position: absolute; background-color: transparent; pointer-events: none; width: 100%; &.focus { outline: 0; - border-color: var(--slick-slider-range-focus-border-color, $slick-slider-range-focus-border-color); - box-shadow: var(--slick-slider-range-focus-box-shadow, $slick-slider-range-focus-box-shadow); + border-color: var(--slick-slider-filter-focus-border-color, $slick-slider-filter-focus-border-color); + box-shadow: var(--slick-slider-filter-focus-box-shadow, $slick-slider-filter-focus-box-shadow); } } - input[type="range"]::-webkit-slider-runnable-track { - -webkit-appearance: none; - } - input[type="range"]::-moz-range-track { - -moz-appearance: none; - } - input[type="range"]::-ms-track { - appearance: none; - } input[type="range"]::-webkit-slider-thumb { pointer-events: auto; } @@ -1177,16 +1187,10 @@ input.slider-editor-input[type=range] { pointer-events: auto; } input[type="range"]::-ms-thumb { - appearance: none; pointer-events: auto; } } -.slider-input-container.slider-values { - display: flex; - padding: 0; -} - // --------------------------------------------------------- // Row Detail View Plugin // --------------------------------------------------------- diff --git a/test/cypress/e2e/example07.cy.js b/test/cypress/e2e/example07.cy.js index cc16b8467..2afb21e63 100644 --- a/test/cypress/e2e/example07.cy.js +++ b/test/cypress/e2e/example07.cy.js @@ -169,7 +169,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries // change % complete cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(5)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 25).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 25).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(5)`).should('contain', '25'); // change Finish date @@ -287,7 +287,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries // change % complete cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(5)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 50).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 50).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(5)`).should('contain', '50'); // change Finish date @@ -345,6 +345,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get(`[style="top:${GRID_ROW_HEIGHT * 9}px"] > .slick-cell:nth(2)`).should('contain', 'Task 9'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 10}px"] > .slick-cell:nth(2)`).should('contain', 'Task 10'); + cy.wait(1); cy.get('.grid7').find(`[style="top:${GRID_ROW_HEIGHT * 9}px"] > .slick-cell:nth(3)`).click({ force: true }); cy.get('.slick-cell-menu .slick-menu-command-list .slick-menu-title').contains('Commands'); cy.get('.slick-cell-menu .slick-menu-command-list .slick-menu-content').contains('Delete Row'); diff --git a/test/cypress/e2e/example11.cy.js b/test/cypress/e2e/example11.cy.js index ee3bcf59f..e9df6dc1a 100644 --- a/test/cypress/e2e/example11.cy.js +++ b/test/cypress/e2e/example11.cy.js @@ -89,17 +89,17 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { it('should be able to change "% Complete" values of row indexes 2-4', () => { // change % complete cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(4)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 5).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 5).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(4)`).should('contain', '5') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(4)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 6).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 6).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(4)`).should('contain', '6') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); cy.get(`[style="top:${GRID_ROW_HEIGHT * 4}px"] > .slick-cell:nth(4)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 7).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 7).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 4}px"] > .slick-cell:nth(4)`).should('contain', '7') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); diff --git a/test/cypress/e2e/example12.cy.js b/test/cypress/e2e/example12.cy.js index 70277cae8..cdce0960e 100644 --- a/test/cypress/e2e/example12.cy.js +++ b/test/cypress/e2e/example12.cy.js @@ -81,13 +81,13 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { it('should be able to change "% Complete" values of row indexes 2-4', () => { // change % complete cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(4)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 5).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 5).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(4)`).should('contain', '5') .get('.editing-field') .should('have.css', 'border').and('eq', `1px solid ${UNSAVED_RGB_COLOR}`); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(4)`).click(); - cy.get('.slider-editor input[type=range]').as('range').invoke('val', 6).trigger('change'); + cy.get('.slider-editor input[type=range]').as('range').invoke('val', 6).trigger('change', { force: true }); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(4)`).should('contain', '6') .get('.editing-field') .should('have.css', 'border').and('eq', `1px solid ${UNSAVED_RGB_COLOR}`); @@ -236,7 +236,7 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { // .should('have.css', 'border') // .and('eq', `1px solid ${UNSAVED_RGB_COLOR}`); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 5).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 5).trigger('change', { force: true }); cy.get('.item-details-editor-container .input-group-text').contains('5'); cy.get('.item-details-container.editor-percentComplete .modified').should('have.length', 1); @@ -285,8 +285,8 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('.slick-editor-modal-title').contains('Editing - Task 8888 (id: 501)'); cy.get('textarea').contains('Task 8888').type('Task 8899'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change', { force: true }); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change', { force: true }); cy.get('.item-details-container.editor-percentComplete .modified').should('have.length', 1); cy.get('.item-details-editor-container .editor-checkbox').uncheck(); @@ -339,8 +339,8 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('.btn-save').contains('Apply Mass Update').click(); cy.get('.validation-summary').contains('Unfortunately we only accept a minimum of 50% Completion...'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 5).trigger('change'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 51).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 5).trigger('change', { force: true }); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 51).trigger('change', { force: true }); cy.get('.item-details-editor-container .input-group-text').contains('51'); cy.get('.btn-save').contains('Apply Mass Update').click(); @@ -451,7 +451,7 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('.btn-save').contains('Update Selection').click(); cy.get('.validation-summary').contains('Unfortunately we only accept a minimum of 50% Completion...'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 77).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 77).trigger('change', { force: true }); cy.get('.item-details-editor-container .input-group-text').contains('77'); cy.get('.btn-save').contains('Update Selection').click(); @@ -503,7 +503,7 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('[data-test="open-modal-mass-update-btn"]').click(); cy.get('.slick-editor-modal-title').contains('Mass Update All Records'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 100).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 100).trigger('change', { force: true }); cy.get('.item-details-container.editor-percentComplete .modified').should('have.length', 1); cy.get('.item-details-container.editor-completed input.editor-checkbox:checked').should('have.length', 1); @@ -537,8 +537,8 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('.slick-editor-modal-title').contains('Clone - Task 8899'); cy.get('textarea').contains('Task 8899').type('Task 9999'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change', { force: true }); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change', { force: true }); cy.get('.item-details-container.editor-percentComplete .modified').should('have.length', 1); cy.get('.item-details-editor-container .editor-checkbox').uncheck(); @@ -568,8 +568,8 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { cy.get('.slick-editor-modal-title').contains('Clone - Task 8899'); cy.get('textarea').contains('Task 8899').type('Task 9999'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change'); - cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change'); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 7).trigger('change', { force: true }); + cy.get('.item-details-editor-container .slider-editor-input.editor-percentComplete').as('range').invoke('val', 17).trigger('change', { force: true }); cy.get('.item-details-container.editor-percentComplete .modified').should('have.length', 1); cy.get('.item-details-editor-container .editor-checkbox').uncheck();