Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(editor): simplify input editor & it should call save on input focusout/blur #1497

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/common/src/editors/__tests__/floatEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('FloatEditor', () => {
editor = new FloatEditor(editorArguments);
const editorElm = divContainer.querySelector('input.editor-text.editor-price') as HTMLInputElement;

expect(editorElm.ariaLabel).toBe('Price Number Editor');
expect(editorElm.ariaLabel).toBe('Price Input Editor');
});

it('should initialize the editor and focus on the element after a small delay', () => {
Expand Down
18 changes: 18 additions & 0 deletions packages/common/src/editors/__tests__/inputEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,24 @@ describe('InputEditor (TextEditor)', () => {
expect(spyCommit).toHaveBeenCalled();
expect(spySave).toHaveBeenCalled();
});

it('should call "getEditorLock" and "save" methods when "hasAutoCommitEdit" is enabled and the event "blur" is triggered', () => {
mockItemData = { id: 1, title: 'task', isActive: true };
gridOptionMock.autoCommitEdit = true;
const spyCommit = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');

editor = new InputEditor(editorArguments, 'text');
editor.loadValue(mockItemData);
editor.setValue('task 21');
const spySave = jest.spyOn(editor, 'save');
const editorElm = editor.editorDomElement;

editorElm.dispatchEvent(new (window.window as any).Event('blur'));
jest.runAllTimers(); // fast-forward timer

expect(spyCommit).toHaveBeenCalled();
expect(spySave).toHaveBeenCalled();
});
});

describe('validate method', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('IntegerEditor', () => {
editor = new IntegerEditor(editorArguments);
const editorElm = divContainer.querySelector('input.editor-text.editor-price') as HTMLInputElement;

expect(editorElm.ariaLabel).toBe('Price Slider Editor');
expect(editorElm.ariaLabel).toBe('Price Input Editor');
});

it('should initialize the editor and focus on the element after a small delay', () => {
Expand Down
83 changes: 0 additions & 83 deletions packages/common/src/editors/floatEditor.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,13 @@
import { createDomElement, toSentenceCase } from '@slickgrid-universal/utils';

import type { EditorArguments, EditorValidationResult } from '../interfaces/index';
import { floatValidator } from '../editorValidators/floatValidator';
import { InputEditor } from './inputEditor';
import { getDescendantProperty } from '../services/utilities';

const DEFAULT_DECIMAL_PLACES = 0;

export class FloatEditor extends InputEditor {
constructor(protected readonly args: EditorArguments) {
super(args, 'number');
}

/** Initialize the Editor */
init() {
if (this.columnDef && this.columnEditor && this.args) {
const columnId = this.columnDef?.id ?? '';
const compositeEditorOptions = this.args.compositeEditorOptions;

this._input = createDomElement('input', {
type: 'number', autocomplete: 'off', ariaAutoComplete: 'none',
ariaLabel: this.columnEditor?.ariaLabel ?? `${toSentenceCase(columnId + '')} Number Editor`,
className: `editor-text editor-${columnId}`,
placeholder: this.columnEditor?.placeholder ?? '',
title: this.columnEditor?.title ?? '',
step: `${(this.columnEditor.valueStep !== undefined) ? this.columnEditor.valueStep : this.getInputDecimalSteps()}`,
});
const cellContainer = this.args.container;
if (cellContainer && typeof cellContainer.appendChild === 'function') {
cellContainer.appendChild(this._input);
}

this._bindEventService.bind(this._input, 'focus', () => this._input?.select());
this._bindEventService.bind(this._input, 'keydown', ((event: KeyboardEvent) => {
this._lastInputKeyEvent = event;
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
event.stopImmediatePropagation();
}
}) as EventListener);

// the lib does not get the focus out event for some reason
// so register it here
if (this.hasAutoCommitEdit && !compositeEditorOptions) {
this._bindEventService.bind(this._input, 'focusout', () => {
this._isValueTouched = true;
this.save();
});
}

if (compositeEditorOptions) {
this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener);
this._bindEventService.bind(this._input, 'wheel', this.handleOnMouseWheel.bind(this) as EventListener, { passive: true });
}
}
}

getDecimalPlaces(): number {
// returns the number of fixed decimal places or null
let rtn = this.columnEditor?.decimal ?? this.columnEditor?.params?.decimalPlaces ?? undefined;

if (rtn === undefined) {
rtn = DEFAULT_DECIMAL_PLACES;
}
return (!rtn && rtn !== 0 ? null : rtn);
}

getInputDecimalSteps(): string {
const decimals = this.getDecimalPlaces();
let zeroString = '';
for (let i = 1; i < decimals; i++) {
zeroString += '0';
}

if (decimals > 0) {
return `0.${zeroString}1`;
}
return '1';
}

loadValue(item: any) {
const fieldName = this.columnDef && this.columnDef.field;

Expand Down Expand Up @@ -137,17 +67,4 @@ export class FloatEditor extends InputEditor {
validator: this.validator,
});
}

// --
// protected functions
// ------------------

/** When the input value changes (this will cover the input spinner arrows on the right) */
protected handleOnMouseWheel(event: KeyboardEvent) {
this._isValueTouched = true;
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
this.handleChangeOnCompositeEditor(event, compositeEditorOptions);
}
}
}
56 changes: 50 additions & 6 deletions packages/common/src/editors/inputEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { getDescendantProperty } from '../services/utilities';
import { textValidator } from '../editorValidators/textValidator';
import { SlickEventData, type SlickGrid } from '../core/index';

const DEFAULT_DECIMAL_PLACES = 0;

/*
* An example of a 'detached' editor.
* KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
Expand Down Expand Up @@ -92,13 +94,18 @@ export class InputEditor implements Editor {
const compositeEditorOptions = this.args.compositeEditorOptions;

this._input = createDomElement('input', {
type: this._inputType || 'text',
autocomplete: 'off', ariaAutoComplete: 'none',
type: this._inputType || 'text', autocomplete: 'off', ariaAutoComplete: 'none',
ariaLabel: this.columnEditor?.ariaLabel ?? `${toSentenceCase(columnId + '')} Input Editor`,
className: `editor-text editor-${columnId}`,
placeholder: this.columnEditor?.placeholder ?? '',
title: this.columnEditor?.title ?? '',
className: `editor-text editor-${columnId}`,
});

// add "step" attribute when editor type is integer/float
if (this.inputType === 'number') {
this._input.step = `${(this.columnEditor.valueStep !== undefined) ? this.columnEditor.valueStep : this.getInputDecimalSteps()}`;
}

const cellContainer = this.args.container;
if (cellContainer && typeof cellContainer.appendChild === 'function') {
cellContainer.appendChild(this._input);
Expand All @@ -113,17 +120,21 @@ export class InputEditor implements Editor {
}
}) as EventListener);

// the lib does not get the focus out event for some reason
// so register it here
// listen to focusout or blur to automatically call a save
if (this.hasAutoCommitEdit && !compositeEditorOptions) {
this._bindEventService.bind(this._input, 'focusout', () => {
this._bindEventService.bind(this._input, ['focusout', 'blur'], () => {
this._isValueTouched = true;
this.save();
});
}

if (compositeEditorOptions) {
this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener);

// add an extra mousewheel listener when editor type is integer/float
if (this.inputType === 'number') {
this._bindEventService.bind(this._input, 'wheel', this.handleOnMouseWheel.bind(this) as EventListener, { passive: true });
}
}
}

Expand Down Expand Up @@ -157,6 +168,30 @@ export class InputEditor implements Editor {
this._input?.focus();
}

getDecimalPlaces(): number {
// returns the number of fixed decimal places or null
let rtn = this.columnEditor?.decimal ?? this.columnEditor?.params?.decimalPlaces ?? undefined;

if (rtn === undefined) {
rtn = DEFAULT_DECIMAL_PLACES;
}
return (!rtn && rtn !== 0 ? null : rtn);
}

/** when editor is a float input editor, we'll want to know how many decimals to show */
getInputDecimalSteps(): string {
const decimals = this.getDecimalPlaces();
let zeroString = '';
for (let i = 1; i < decimals; i++) {
zeroString += '0';
}

if (decimals > 0) {
return `0.${zeroString}1`;
}
return '1';
}

show() {
const isCompositeEditor = !!this.args?.compositeEditorOptions;
if (isCompositeEditor) {
Expand Down Expand Up @@ -338,4 +373,13 @@ export class InputEditor implements Editor {
this._timer = setTimeout(() => this.handleChangeOnCompositeEditor(event, compositeEditorOptions), typingDelay);
}
}

/** When the input value changes (this will cover the input spinner arrows on the right) */
protected handleOnMouseWheel(event: KeyboardEvent) {
this._isValueTouched = true;
const compositeEditorOptions = this.args.compositeEditorOptions;
if (compositeEditorOptions) {
this.handleChangeOnCompositeEditor(event, compositeEditorOptions);
}
}
}
45 changes: 0 additions & 45 deletions packages/common/src/editors/integerEditor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { createDomElement, toSentenceCase } from '@slickgrid-universal/utils';

import type { EditorArguments, EditorValidationResult } from '../interfaces/index';
import { integerValidator } from '../editorValidators/integerValidator';
import { InputEditor } from './inputEditor';
Expand All @@ -10,49 +8,6 @@ export class IntegerEditor extends InputEditor {
super(args, 'number');
}

/** Initialize the Editor */
init() {
if (this.columnDef && this.columnEditor && this.args) {
const columnId = this.columnDef?.id ?? '';
const compositeEditorOptions = this.args.compositeEditorOptions;

this._input = createDomElement('input', {
type: 'number', autocomplete: 'off', ariaAutoComplete: 'none',
ariaLabel: this.columnEditor?.ariaLabel ?? `${toSentenceCase(columnId + '')} Slider Editor`,
placeholder: this.columnEditor?.placeholder ?? '',
title: this.columnEditor?.title ?? '',
step: `${(this.columnEditor.valueStep !== undefined) ? this.columnEditor.valueStep : '1'}`,
className: `editor-text editor-${columnId}`,
});
const cellContainer = this.args.container;
if (cellContainer && typeof cellContainer.appendChild === 'function') {
cellContainer.appendChild(this._input);
}

this._bindEventService.bind(this._input, 'focus', () => this._input?.select());
this._bindEventService.bind(this._input, 'keydown', ((event: KeyboardEvent) => {
this._lastInputKeyEvent = event;
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
event.stopImmediatePropagation();
}
}) as EventListener);

// the lib does not get the focus out event for some reason
// so register it here
if (this.hasAutoCommitEdit && !compositeEditorOptions) {
this._bindEventService.bind(this._input, 'focusout', () => {
this._isValueTouched = true;
this.save();
});
}

if (compositeEditorOptions) {
this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener);
this._bindEventService.bind(this._input, 'wheel', this.handleOnMouseWheel.bind(this) as EventListener, { passive: true });
}
}
}

loadValue(item: any) {
const fieldName = this.columnDef && this.columnDef.field;

Expand Down
Loading