diff --git a/docs/column-functionalities/editors/date-editor-(vanilla-calendar).md b/docs/column-functionalities/editors/date-editor-(vanilla-calendar).md index 13a44b171..a3baca0f5 100644 --- a/docs/column-functionalities/editors/date-editor-(vanilla-calendar).md +++ b/docs/column-functionalities/editors/date-editor-(vanilla-calendar).md @@ -39,6 +39,11 @@ prepareGrid() { } ``` +On top of the default ones provided by Vanilla-Calendar there are also: + +* hideClearButton (default: false): which toggles the visiblity of the clear button +* allowInput (default: false): which determines whether you can directly enter a date in the input element + #### Grid Option `defaultEditorOptions You could also define certain options as a global level (for the entire grid or even all grids) by taking advantage of the `defaultEditorOptions` Grid Option. Note that they are set via the editor type as a key name (`autocompleter`, `date`, ...) and then the content is the same as `editorOptions` (also note that each key is already typed with the correct editor option interface), for example diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example11.ts b/examples/vite-demo-vanilla-bundle/src/examples/example11.ts index 48cb806d9..6ce2fba27 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example11.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example11.ts @@ -175,7 +175,7 @@ export default class Example11 { type: FieldType.date, outputType: FieldType.dateIso, filterable: true, filter: { model: Filters.compoundDate }, - editor: { model: Editors.date, massUpdate: true }, + editor: { model: Editors.date, massUpdate: true, editorOptions: { allowInput: true } }, }, { id: 'finish', name: 'Finish', field: 'finish', sortable: true, minWidth: 80, diff --git a/packages/common/src/editors/__tests__/dateEditor.spec.ts b/packages/common/src/editors/__tests__/dateEditor.spec.ts index 1651467ac..48a33f4ba 100644 --- a/packages/common/src/editors/__tests__/dateEditor.spec.ts +++ b/packages/common/src/editors/__tests__/dateEditor.spec.ts @@ -141,6 +141,34 @@ describe('DateEditor', () => { expect(showSpy).toHaveBeenCalled(); }); + it('should initialize the editor and add a keydown event listener that early exists by default', () => { + editor = new DateEditor(editorArguments); + + const event = new KeyboardEvent('keydown', { key: 'Enter' }); + editor.editorDomElement.dispatchEvent(event); + + expect(editor.columnEditor.editorOptions?.allowEdit).toBeFalsy(); + expect(editor.isValueTouched()).toBeFalsy(); + }); + + it('should stop propagation on allowEdit when hitting left or right arrow keys', () => { + editor = new DateEditor({ ...editorArguments, + column: { ...editorArguments.column, + editor: { ...editorArguments.column.editor, + editorOptions: { ...editorArguments.column?.editor?.editorOptions, allowEdit: true } + }}}); + + let event = new KeyboardEvent('keydown', { key: 'ArrowLeft' }); + let propagationSpy = vi.spyOn(event, 'stopImmediatePropagation'); + editor.editorDomElement.dispatchEvent(event); + expect(propagationSpy).toHaveBeenCalled(); + + event = new KeyboardEvent('keydown', { key: 'ArrowRight' }); + propagationSpy = vi.spyOn(event, 'stopImmediatePropagation'); + editor.editorDomElement.dispatchEvent(event); + expect(propagationSpy).toHaveBeenCalled(); + }); + it('should have a placeholder when defined in its column definition', () => { const testValue = 'test placeholder'; mockColumn.editor!.placeholder = testValue; @@ -267,6 +295,23 @@ describe('DateEditor', () => { expect(editor.isValueTouched()).toBe(true); }); + it('should return True when the last key was enter and alwaysSaveOnEnterKey is active', () => { + mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; + + editor = new DateEditor({...editorArguments, + column: { ...mockColumn, editor: { ...editorArguments.column.editor, alwaysSaveOnEnterKey: true, + editorOptions: { ...editorArguments.column.editor?.editorOptions, allowEdit: true} + } } + }); + const event = new KeyboardEvent('keydown', { key: 'Enter' }); + vi.runAllTimers(); + + editor.editorDomElement.dispatchEvent(event); + + expect(editor.isValueChanged()).toBe(true); + + }); + it('should return True when date is reset by the clear date button', () => { mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; diff --git a/packages/common/src/editors/dateEditor.ts b/packages/common/src/editors/dateEditor.ts index 163485227..4749bd27e 100644 --- a/packages/common/src/editors/dateEditor.ts +++ b/packages/common/src/editors/dateEditor.ts @@ -36,6 +36,7 @@ export class DateEditor implements Editor { protected _lastTriggeredByClearDate = false; protected _originalDate?: string; protected _pickerMergedOptions!: IOptions; + protected _lastInputKeyEvent?: KeyboardEvent; calendarInstance?: VanillaCalendar; defaultDate?: string; hasTimePicker = false; @@ -185,7 +186,7 @@ export class DateEditor implements Editor { title: this.columnEditor && this.columnEditor.title || '', className: inputCssClasses.replace(/\./g, ' '), dataset: { input: '', defaultdate: this.defaultDate }, - readOnly: true, + readOnly: this.columnEditor.editorOptions?.allowEdit === true ? true : false, }, this._editorInputGroupElm ); @@ -202,6 +203,18 @@ export class DateEditor implements Editor { }); } + this._bindEventService.bind(this._inputElm, 'keydown', ((event: KeyboardEvent) => { + if (this.columnEditor.editorOptions?.allowEdit !== true) { + return; + } + + this._isValueTouched = true; + this._lastInputKeyEvent = event; + if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + event.stopImmediatePropagation(); + } + }) as EventListener); + queueMicrotask(() => { this.calendarInstance = new VanillaCalendar(this._inputElm, this._pickerMergedOptions); this.calendarInstance.init(); @@ -349,6 +362,12 @@ export class DateEditor implements Editor { let isChanged = false; const elmDateStr = this.getValue(); + const lastEventKey = this._lastInputKeyEvent?.key; + if (this.columnEditor.editorOptions?.allowEdit === true && + this.columnEditor?.alwaysSaveOnEnterKey && lastEventKey === 'Enter') { + return true; + } + if (this.columnDef) { isChanged = this._lastTriggeredByClearDate || (!(elmDateStr === '' && this._originalDate === '')) && (elmDateStr !== this._originalDate); } diff --git a/packages/common/src/interfaces/vanillaCalendarOption.interface.ts b/packages/common/src/interfaces/vanillaCalendarOption.interface.ts index ebc1a2b30..1d9d4afbd 100644 --- a/packages/common/src/interfaces/vanillaCalendarOption.interface.ts +++ b/packages/common/src/interfaces/vanillaCalendarOption.interface.ts @@ -12,4 +12,7 @@ export interface VanillaCalendarOption extends IPartialSettings { /** defaults to false, do we want to hide the clear date button? */ hideClearButton?: boolean; + + /** defaults to false, should keyboard entries be allowed in input field? */ + allowInput?: boolean; } diff --git a/test/cypress/e2e/example11.cy.ts b/test/cypress/e2e/example11.cy.ts index 1a544c136..4bb5fccfd 100644 --- a/test/cypress/e2e/example11.cy.ts +++ b/test/cypress/e2e/example11.cy.ts @@ -1087,4 +1087,20 @@ describe('Example 11 - Batch Editing', () => { .click({ force: true }); }); }); + + describe('with Date Editor', () => { + it('should input values directly in input of datepicker', () => { + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(5)`) + .click(); + + cy.get(`.input-group-editor`) + .focus() + .type('{backspace}'.repeat(10)) + .type('1970-01-01') + .type('{enter}'); + + cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(5)`) + .should('contain', '1970-01-01'); + }); + }); });