diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example13.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example13.ts index 172ed7f81..49e78247f 100644 --- a/examples/webpack-demo-vanilla-bundle/src/examples/example13.ts +++ b/examples/webpack-demo-vanilla-bundle/src/examples/example13.ts @@ -1,6 +1,8 @@ import { BindingEventService, Column, + Editors, + FieldType, GridOption, } from '@slickgrid-universal/common'; import { ExcelExportService } from '@slickgrid-universal/excel-export'; @@ -59,6 +61,9 @@ export class Example13 { enableAutoResize: true, enableHeaderButton: true, enableHeaderMenu: false, + autoCommitEdit: true, + autoEdit: true, + editable: true, autoResize: { container: '.demo-container', }, @@ -124,9 +129,11 @@ export class Example13 { id: i, name: 'Column ' + String.fromCharCode('A'.charCodeAt(0) + i), field: i + '', - width: i === 0 ? 70 : 100, // have the 2 first columns wider + width: i === 0 ? 70 : 100, // make the first 2 columns wider filterable: true, sortable: true, + type: FieldType.number, + editor: { model: Editors.integer }, formatter: (_row, _cell, value, columnDef) => { if (gridNo === 1 && columns1WithHighlightingById[columnDef.id] && value < 0) { return `
${value}
`; diff --git a/package.json b/package.json index ab02b3d07..0b2efce7f 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "rimraf": "^3.0.2", "rxjs": "^7.5.7", "servor": "^4.0.2", - "slickgrid": "^3.0.3", + "slickgrid": "^3.0.4", "sortablejs": "^1.15.0", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", diff --git a/packages/common/package.json b/packages/common/package.json index 17e14b9d8..ac1433c88 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -82,7 +82,7 @@ "jquery": "^3.6.3", "moment-mini": "^2.29.4", "multiple-select-modified": "^1.3.17", - "slickgrid": "^3.0.3", + "slickgrid": "^3.0.4", "sortablejs": "^1.15.0", "un-flatten-tree": "^2.0.12" }, diff --git a/packages/common/src/services/__tests__/gridEvent.service.spec.ts b/packages/common/src/services/__tests__/gridEvent.service.spec.ts index 7f900b4e5..4ee50de5b 100644 --- a/packages/common/src/services/__tests__/gridEvent.service.spec.ts +++ b/packages/common/src/services/__tests__/gridEvent.service.spec.ts @@ -179,46 +179,22 @@ describe('GridEventService', () => { }); }); - it('should execute the column "onCellClick" callback method and "setActiveCell" cell navigation is enabled but column is not editable', () => { + it('should execute the column "onCellClick" callback method and "setActiveCell" only when enableExcelCopyBuffer is enabled', () => { gridStub.getOptions = jest.fn(); - jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableCellNavigation: true, editable: false }); + jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableCellNavigation: true, enableExcelCopyBuffer: true }); const spyActive = jest.spyOn(gridStub, 'setActiveCell'); const spyGetCols = jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn]); const spyGetData = jest.spyOn(gridStub, 'getDataItem').mockReturnValue(mockRowData); const spyOnChange = jest.spyOn(mockColumn, 'onCellClick'); service.bindOnClick(gridStub); - gridStub.onClick.notify({ cell: 0, row: 0, grid: gridStub }, new Slick.EventData(), gridStub); + gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, new Slick.EventData(), gridStub); - expect(spyActive).toHaveBeenCalled(); + expect(spyActive).toHaveBeenCalledWith(2, 0); expect(spyGetCols).toHaveBeenCalled(); expect(spyGetData).toHaveBeenCalled(); expect(spyOnChange).toHaveBeenCalledWith(expect.anything(), { - row: 0, - cell: 0, - dataView: dataViewStub, - grid: gridStub, - columnDef: mockColumn, - dataContext: mockRowData - }); - }); - - it('should execute the column "onCellClick" callback method and "setActiveCell" when cell is editable and autoCommitEdit', () => { - gridStub.getOptions = jest.fn(); - jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableCellNavigation: true, editable: true, autoCommitEdit: true }); - const spyActive = jest.spyOn(gridStub, 'setActiveCell'); - const spyGetCols = jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn]); - const spyGetData = jest.spyOn(gridStub, 'getDataItem').mockReturnValue(mockRowData); - const spyOnChange = jest.spyOn(mockColumn, 'onCellClick'); - - service.bindOnClick(gridStub); - gridStub.onClick.notify({ cell: 0, row: 0, grid: gridStub }, new Slick.EventData(), gridStub); - - expect(spyActive).toHaveBeenCalled(); - expect(spyGetCols).toHaveBeenCalled(); - expect(spyGetData).toHaveBeenCalled(); - expect(spyOnChange).toHaveBeenCalledWith(expect.anything(), { - row: 0, + row: 2, cell: 0, dataView: dataViewStub, grid: gridStub, diff --git a/packages/common/src/services/gridEvent.service.ts b/packages/common/src/services/gridEvent.service.ts index 2de066357..d063c934e 100644 --- a/packages/common/src/services/gridEvent.service.ts +++ b/packages/common/src/services/gridEvent.service.ts @@ -28,7 +28,7 @@ export class GridEventService { /* OnCellChange Event */ bindOnBeforeEditCell(grid: SlickGrid) { - const dataView = grid?.getData && grid.getData() as SlickDataView; + const dataView = grid?.getData?.() as SlickDataView; // subscribe to this Slickgrid event of onBeforeEditCell this._eventHandler.subscribe(grid.onBeforeEditCell, (e, args) => { @@ -57,7 +57,7 @@ export class GridEventService { /* OnCellChange Event */ bindOnCellChange(grid: SlickGrid) { - const dataView = grid?.getData && grid.getData() as SlickDataView; + const dataView = grid?.getData?.() as SlickDataView; // subscribe to this Slickgrid event of onCellChange this._eventHandler.subscribe(grid.onCellChange, (e, args) => { @@ -86,22 +86,19 @@ export class GridEventService { /* OnClick Event */ bindOnClick(grid: SlickGrid) { - const dataView = grid?.getData && grid.getData() as SlickDataView; + const dataView = grid?.getData?.() as SlickDataView; this._eventHandler.subscribe(grid.onClick, (e, args) => { if (!e || !args || !grid || args.cell === undefined || !grid.getColumns || !grid.getDataItem) { return; } - const column: Column = grid && grid.getColumns && grid.getColumns()[args.cell]; - const gridOptions: GridOption = grid && grid.getOptions && grid.getOptions() || {}; - - // only when the grid option "autoCommitEdit" is enabled, we will make the cell active (in focus) when clicked - // setting the cell as active as a side effect and if "autoCommitEdit" is set to false then the Editors won't save correctly - if (gridOptions.enableCellNavigation && (!gridOptions.editable || (gridOptions.editable && gridOptions.autoCommitEdit))) { - try { - grid.setActiveCell(args.row, args.cell, false, false, true); - // eslint-disable-next-line @typescript-eslint/no-shadow, no-empty - } catch(e) {} + const column: Column = grid.getColumns?.()[args.cell]; + const gridOptions: GridOption = grid.getOptions?.() || {}; + + // when using Excel copy buffer to copy cell ranges, the cell loses its focus after the copy execution + // so we need to reapply the focus on the active cell that the user clicked + if (gridOptions.enableCellNavigation && gridOptions.enableExcelCopyBuffer) { + grid.setActiveCell(args.row, args.cell); } // if the column definition has a onCellClick property (a callback function), then run it diff --git a/packages/vanilla-bundle/package.json b/packages/vanilla-bundle/package.json index 3e33d3e7a..336c2ae95 100644 --- a/packages/vanilla-bundle/package.json +++ b/packages/vanilla-bundle/package.json @@ -66,7 +66,7 @@ "dequal": "^2.0.3", "flatpickr": "^4.6.13", "jquery": "^3.6.3", - "slickgrid": "^3.0.3", + "slickgrid": "^3.0.4", "sortablejs": "^1.15.0", "whatwg-fetch": "^3.6.2" }, diff --git a/packages/vanilla-force-bundle/webpack.config.js b/packages/vanilla-force-bundle/webpack.config.js index e5fcd83ef..329a60a44 100644 --- a/packages/vanilla-force-bundle/webpack.config.js +++ b/packages/vanilla-force-bundle/webpack.config.js @@ -41,9 +41,5 @@ module.exports = ({ production } = {}) => ({ options: { loader: 'ts', target: 'es2018' } }, ], - }, - watchOptions: { - ignored: '**/node_modules', - poll: 1000, // Check for changes every second } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f64546ba..25f3ec333 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,7 +38,7 @@ importers: rimraf: ^3.0.2 rxjs: ^7.5.7 servor: ^4.0.2 - slickgrid: ^3.0.3 + slickgrid: ^3.0.4 sortablejs: ^1.15.0 ts-jest: ^29.0.5 ts-node: ^10.9.1 @@ -73,7 +73,7 @@ importers: rimraf: 3.0.2 rxjs: 7.5.7 servor: 4.0.2 - slickgrid: 3.0.3 + slickgrid: 3.0.4 sortablejs: 1.15.0 ts-jest: 29.0.5_jzhsp3ae3eifadqc4vuxu2hrie ts-node: 10.9.1_4bewfcp2iebiwuold25d6rgcsy @@ -205,7 +205,7 @@ importers: postcss-cli: ^10.1.0 rimraf: ^3.0.2 sass: ^1.58.0 - slickgrid: ^3.0.3 + slickgrid: ^3.0.4 sortablejs: ^1.15.0 un-flatten-tree: ^2.0.12 dependencies: @@ -218,7 +218,7 @@ importers: jquery: 3.6.3 moment-mini: 2.29.4 multiple-select-modified: 1.3.17 - slickgrid: 3.0.3 + slickgrid: 3.0.4 sortablejs: 1.15.0 un-flatten-tree: 2.0.12 devDependencies: @@ -459,7 +459,7 @@ importers: jquery: ^3.6.3 npm-run-all2: ^6.0.4 rimraf: ^3.0.2 - slickgrid: ^3.0.3 + slickgrid: ^3.0.4 sortablejs: ^1.15.0 whatwg-fetch: ^3.6.2 dependencies: @@ -473,7 +473,7 @@ importers: dequal: 2.0.3 flatpickr: 4.6.13 jquery: 3.6.3 - slickgrid: 3.0.3 + slickgrid: 3.0.4 sortablejs: 1.15.0 whatwg-fetch: 3.6.2 devDependencies: @@ -9602,8 +9602,8 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true - /slickgrid/3.0.3: - resolution: {integrity: sha512-9NlWDTHftNs3+Ta62cF6rV9Vo4PBHCYuBVxb5yHxZ62CiuqtMvKusktEOt1O1RLC1J+lg5v4Qs7LccJ6T3CAJQ==} + /slickgrid/3.0.4: + resolution: {integrity: sha512-0CY57mxZWJ5Y9XI29ogC4kIMQmyOIHSbmCefOnFFvVUsaI0yqKHfOC84TtGwCScxQfYWFM4AhvWBrae3ff3YZA==} dependencies: jquery: 3.6.3 sortablejs: 1.15.0 diff --git a/test/cypress/e2e/example11.cy.ts b/test/cypress/e2e/example11.cy.ts index abfcce639..c5ac54293 100644 --- a/test/cypress/e2e/example11.cy.ts +++ b/test/cypress/e2e/example11.cy.ts @@ -21,18 +21,17 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { cy.get('h3').should('contain', 'Example 11 - Batch Editing'); }); + it('should click on "Clear Local Storage" and expect to be back to original grid with all the columns', () => { + cy.get('[data-test="clear-storage-btn"]') + .click(); - it('should have exact Column Titles in the grid', () => { cy.get('.grid11') .find('.slick-header-columns') .children() .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); }); - it('should click on "Clear Local Storage" and expect to be back to original grid with all the columns', () => { - cy.get('[data-test="clear-storage-btn"]') - .click(); - + it('should have exact Column Titles in the grid', () => { cy.get('.grid11') .find('.slick-header-columns') .children() @@ -56,36 +55,41 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { .should('contain', '0 day') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-duration').type('1').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).click().type('1{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', '1 day') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-duration').type('2').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).click().type('2{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).should('contain', '2 days') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-duration').type('3').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(2)`).click().type('3{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(2)`).should('contain', '3 days') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-duration').type('{esc}'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(2)`).click().type('{esc}'); cy.get('.editor-duration').should('not.exist'); }); it('should be able to change "Title" values of row indexes 1-3', () => { // change title cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'TASK 1').click(); - cy.get('.editor-title').type('task 1111').type('{enter}', { force: true }); + cy.get('.editor-title').type('task 1111').type('{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'TASK 1111') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-title').type('task 2222').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).click() + .type('task 2222{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).should('contain', 'TASK 2222') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-title').type('task 3333').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(1)`).click() + .type('task 3333').type('{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(1)`).should('contain', 'TASK 3333') .should('have.css', 'background-color').and('eq', UNSAVED_RGB_COLOR); - cy.get('.editor-title').type('{esc}'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(1)`).click() + .type('{esc}'); cy.get('.editor-title').should('not.exist'); cy.get('.slick-viewport.slick-viewport-top.slick-viewport-left') diff --git a/test/cypress/e2e/example12.cy.ts b/test/cypress/e2e/example12.cy.ts index 745de1f89..a58ac3f48 100644 --- a/test/cypress/e2e/example12.cy.ts +++ b/test/cypress/e2e/example12.cy.ts @@ -1,6 +1,6 @@ import { changeTimezone, zeroPadding } from '../plugins/utilities'; -describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { +describe('Example 12 - Composite Editor Modal', { retries: 0 }, () => { const fullPreTitles = ['', 'Common Factor', 'Analysis', 'Period', 'Item', '']; const fullTitles = ['', ' Title', 'Duration', 'Cost', '% Complete', 'Complexity', 'Start', 'Completed', 'Finish', 'Product', 'Country of Origin', 'Action']; @@ -43,20 +43,20 @@ describe('Example 12 - Composite Editor Modal', { retries: 1 }, () => { it('should be able to change "Duration" values of first 4 rows', () => { // change duration cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'days').click(); - cy.get('.editor-duration').type('0').type('{enter}', { force: true }); + cy.get('.editor-duration').type('0{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`) .should('contain', '0 day') .get('.editing-field') .should('have.css', 'border') .and('contain', `solid ${UNSAVED_RGB_COLOR}`); - cy.get('.editor-duration').type('1').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).click().type('1{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', '1 day') .get('.editing-field') .should('have.css', 'border') .and('contain', `solid ${UNSAVED_RGB_COLOR}`); - cy.get('.editor-duration').type('2').type('{enter}', { force: true }); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).click().type('2{enter}'); cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).should('contain', '2 days') .get('.editing-field') .should('have.css', 'border')