diff --git a/packages/common/src/controls/__tests__/gridMenuControl.spec.ts b/packages/common/src/controls/__tests__/gridMenuControl.spec.ts index eb881a3f7..2af978707 100644 --- a/packages/common/src/controls/__tests__/gridMenuControl.spec.ts +++ b/packages/common/src/controls/__tests__/gridMenuControl.spec.ts @@ -105,6 +105,7 @@ describe('GridMenuControl', () => { toggleFilterCommandKey: 'TOGGLE_FILTER_ROW', togglePreHeaderCommandKey: 'TOGGLE_PRE_HEADER_ROW', }, + customTitleKey: 'COMMANDS', customItems: [], hideClearAllFiltersCommand: false, hideClearFrozenColumnsCommand: true, @@ -205,7 +206,7 @@ describe('GridMenuControl', () => { gridOptionsMock.enableRowSelection = true; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const inputElm = control.menuElement.querySelector('input[type="checkbox"]'); inputElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); @@ -228,7 +229,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.resizeOnShowHeaderRow = true; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const headerRowElm = document.querySelector('.slick-headerrow') as HTMLDivElement; @@ -250,7 +251,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); @@ -269,7 +270,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); @@ -301,7 +302,7 @@ describe('GridMenuControl', () => { gridOptionsMock.frozenColumn = 0; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); const liElmList = control.menuElement.querySelectorAll('li'); @@ -323,7 +324,7 @@ describe('GridMenuControl', () => { gridOptionsMock.frozenColumn = 0; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); const liElmList = control.menuElement.querySelectorAll('li'); @@ -341,9 +342,9 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.contentMinWidth = 200; gridOptionsMock.gridMenu.height = 300; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const gridMenuElm = document.querySelector('.slick-gridmenu') as HTMLDivElement; + const gridMenuElm = document.querySelector('.slick-grid-menu') as HTMLDivElement; expect(gridMenuElm.style.minWidth).toBe('200px'); expect(gridMenuElm.style.height).toBe('300px'); @@ -360,7 +361,7 @@ describe('GridMenuControl', () => { Object.defineProperty(spanEvent, 'target', { writable: true, configurable: true, value: spanBtnElm }); Object.defineProperty(spanBtnElm, 'parentElement', { writable: true, configurable: true, value: buttonElm }); control.showGridMenu(spanEvent, { alignDropSide: 'left' }); - const gridMenuElm = document.querySelector('.slick-gridmenu') as HTMLDivElement; + const gridMenuElm = document.querySelector('.slick-grid-menu') as HTMLDivElement; expect(gridMenuElm.style.display).toBe('block'); }); @@ -374,7 +375,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); const inputForcefitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); @@ -397,7 +398,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -421,7 +422,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); @@ -452,7 +453,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const inputForcefitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const labelSyncElm = control.menuElement.querySelector('label[for=slickgrid_124343-gridmenu-colpicker-forcefit]'); @@ -479,7 +480,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); const labelSyncElm = control.menuElement.querySelector('label[for=slickgrid_124343-gridmenu-colpicker-syncresize]'); @@ -499,7 +500,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.hideSyncResizeButton = false; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -516,7 +517,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.hideSyncResizeButton = false; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -526,8 +527,9 @@ describe('GridMenuControl', () => { expect(inputSyncElm).toBeFalsy(); expect(pubSubSpy).toHaveBeenCalledWith('gridMenu:onBeforeMenuShow', { grid: gridStub, - menu: document.querySelector('.slick-gridmenu'), + menu: document.querySelector('.slick-grid-menu'), allColumns: columnsMock, + columns: columnsMock, visibleColumns: columnsMock }); }); @@ -538,7 +540,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.hideSyncResizeButton = false; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -554,7 +556,7 @@ describe('GridMenuControl', () => { const onAfterSpy = jest.spyOn(gridOptionsMock.gridMenu, 'onAfterMenuShow'); control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); expect(onAfterSpy).toHaveBeenCalled(); @@ -564,8 +566,9 @@ describe('GridMenuControl', () => { expect(control.menuElement.style.display).toBe('none'); expect(pubSubSpy).toHaveBeenCalledWith('gridMenu:onAfterMenuShow', { grid: gridStub, - menu: document.querySelector('.slick-gridmenu'), + menu: document.querySelector('.slick-grid-menu'), allColumns: columnsMock, + columns: columnsMock, visibleColumns: columnsMock }); }); @@ -578,7 +581,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.hideSyncResizeButton = false; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -591,7 +594,7 @@ describe('GridMenuControl', () => { expect(control.menuElement.style.display).toBe('block'); expect(pubSubSpy).toHaveBeenCalledWith('gridMenu:onMenuClose', { grid: gridStub, - menu: document.querySelector('.slick-gridmenu'), + menu: document.querySelector('.slick-grid-menu'), allColumns: columnsMock, visibleColumns: columnsMock }); @@ -605,7 +608,7 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.hideSyncResizeButton = false; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -628,7 +631,7 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const forceFitElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-forcefit'); const inputSyncElm = control.menuElement.querySelector('#slickgrid_124343-gridmenu-colpicker-syncresize'); @@ -656,9 +659,9 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.onCommand = onCommandMock; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); helpCommandElm.dispatchEvent(clickEvent); @@ -682,15 +685,15 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.onCommand = onCommandMock; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); helpCommandElm.dispatchEvent(clickEvent); expect(helpFnMock).not.toHaveBeenCalled(); expect(onCommandMock).not.toHaveBeenCalled(); - expect(helpCommandElm.classList.contains('slick-gridmenu-item-disabled')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item-disabled')).toBeTrue(); }); it('should add a custom Grid Menu item and NOT expect the "action" and "onCommand" callbacks to be called when item "itemUsabilityOverride" callback returns False', () => { @@ -701,9 +704,9 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.onCommand = onCommandMock; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); helpCommandElm.dispatchEvent(clickEvent); @@ -719,9 +722,9 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.onCommand = onCommandMock; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); helpCommandElm.dispatchEvent(clickEvent); @@ -739,20 +742,20 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', hidden: true }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); - expect(helpCommandElm.classList.contains('slick-gridmenu-item-hidden')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item-hidden')).toBeTrue(); }); it('should add a custom Grid Menu item and expect item to NOT be created in the DOM list when "itemVisibilityOverride" callback returns False', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', itemVisibilityOverride: () => false }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); expect(helpCommandElm).toBeFalsy(); }); @@ -761,64 +764,64 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', disabled: true }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); - expect(helpCommandElm.classList.contains('slick-gridmenu-item-disabled')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item-disabled')).toBeTrue(); }); it('should add a custom Grid Menu "divider" item object and expect a divider to be created', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'divider', divider: true }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=divider]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=divider]'); - expect(helpCommandElm.classList.contains('slick-gridmenu-item-divider')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item-divider')).toBeTrue(); }); it('should add a custom Grid Menu "divider" string and expect a divider to be created', () => { gridOptionsMock.gridMenu.customItems = ['divider']; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item'); - expect(helpCommandElm.classList.contains('slick-gridmenu-item-divider')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item-divider')).toBeTrue(); }); it('should add a custom Grid Menu item with "cssClass" and expect all classes to be added to the item in the DOM', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', cssClass: 'text-danger red' }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); - expect(helpCommandElm.classList.contains('slick-gridmenu-item')).toBeTrue(); + expect(helpCommandElm.classList.contains('slick-grid-menu-item')).toBeTrue(); expect(helpCommandElm.classList.contains('text-danger')).toBeTrue(); expect(helpCommandElm.classList.contains('red')).toBeTrue(); - expect(helpCommandElm.className).toBe('slick-gridmenu-item text-danger red'); + expect(helpCommandElm.className).toBe('slick-grid-menu-item text-danger red'); }); it('should add a custom Grid Menu item with "iconCssClass" and expect an icon to be included on the item DOM element', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', iconCssClass: 'mdi mdi-close' }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); - const helpIconElm = helpCommandElm.querySelector('.slick-gridmenu-icon'); - const helpTextElm = helpCommandElm.querySelector('.slick-gridmenu-content'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); + const helpIconElm = helpCommandElm.querySelector('.slick-grid-menu-icon'); + const helpTextElm = helpCommandElm.querySelector('.slick-grid-menu-content'); expect(helpTextElm.textContent).toBe('Help'); - expect(helpIconElm.classList.contains('slick-gridmenu-icon')).toBeTrue(); + expect(helpIconElm.classList.contains('slick-grid-menu-icon')).toBeTrue(); expect(helpIconElm.classList.contains('mdi')).toBeTrue(); expect(helpIconElm.classList.contains('mdi-close')).toBeTrue(); - expect(helpIconElm.className).toBe('slick-gridmenu-icon mdi mdi-close'); + expect(helpIconElm.className).toBe('slick-grid-menu-icon mdi mdi-close'); }); it('should add a custom Grid Menu item with "iconImage" and expect an icon to be included on the item DOM element', () => { @@ -828,12 +831,12 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button') as HTMLButtonElement; + const buttonElm = document.querySelector('.slick-grid-menu-button') as HTMLButtonElement; const buttonImageElm = buttonElm.querySelector('img') as HTMLImageElement; buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); - const helpIconElm = helpCommandElm.querySelector('.slick-gridmenu-icon'); - const helpTextElm = helpCommandElm.querySelector('.slick-gridmenu-content'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); + const helpIconElm = helpCommandElm.querySelector('.slick-grid-menu-icon'); + const helpTextElm = helpCommandElm.querySelector('.slick-grid-menu-content'); expect(buttonImageElm.src).toBe('/images/some-image.png'); expect(helpTextElm.textContent).toBe('Help'); @@ -845,9 +848,9 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', tooltip: 'some tooltip text' }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); expect(helpCommandElm.title).toBe('some tooltip text'); }); @@ -856,15 +859,15 @@ describe('GridMenuControl', () => { gridOptionsMock.gridMenu.customItems = [{ command: 'help', title: 'Help', textCssClass: 'red bold' }]; control.columns = columnsMock; control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); - const helpTextElm = helpCommandElm.querySelector('.slick-gridmenu-content'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); + const helpTextElm = helpCommandElm.querySelector('.slick-grid-menu-content'); expect(helpTextElm.textContent).toBe('Help'); expect(helpTextElm.classList.contains('red')).toBeTrue(); expect(helpTextElm.classList.contains('bold')).toBeTrue(); - expect(helpTextElm.className).toBe('slick-gridmenu-content red bold'); + expect(helpTextElm.className).toBe('slick-grid-menu-content red bold'); }); it('should add a custom Grid Menu item and provide a custom title for the custom items list', () => { @@ -872,18 +875,19 @@ describe('GridMenuControl', () => { control.columns = columnsMock; control.init(); gridOptionsMock.gridMenu.customTitle = 'Custom Title'; + gridOptionsMock.gridMenu.commandTitle = 'Custom Title'; control.updateAllTitles(gridOptionsMock.gridMenu); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const customTitleElm = control.menuElement.querySelector('.slick-gridmenu-custom .title'); - const helpCommandElm = control.menuElement.querySelector('.slick-gridmenu-item[data-command=help]'); - const helpTextElm = helpCommandElm.querySelector('.slick-gridmenu-content'); + const customTitleElm = control.menuElement.querySelector('.slick-grid-menu-command-list .title'); + const helpCommandElm = control.menuElement.querySelector('.slick-grid-menu-item[data-command=help]'); + const helpTextElm = helpCommandElm.querySelector('.slick-grid-menu-content'); expect(customTitleElm.textContent).toBe('Custom Title'); expect(helpTextElm.textContent).toBe('Help'); expect(helpTextElm.classList.contains('red')).toBeTrue(); expect(helpTextElm.classList.contains('bold')).toBeTrue(); - expect(helpTextElm.className).toBe('slick-gridmenu-content red bold'); + expect(helpTextElm.className).toBe('slick-grid-menu-content red bold'); }); it('should be able to recreate the Grid Menu', () => { @@ -1175,8 +1179,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=clear-pinning]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=clear-pinning]').dispatchEvent(clickEvent); // expect(onCommandMock).toHaveBeenCalled(); expect(setColumnsSpy).toHaveBeenCalled(); @@ -1193,8 +1197,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=clear-filter]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=clear-filter]').dispatchEvent(clickEvent); expect(filterSpy).toHaveBeenCalled(); expect(refreshSpy).toHaveBeenCalled(); @@ -1210,8 +1214,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=clear-sorting]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=clear-sorting]').dispatchEvent(clickEvent); expect(sortSpy).toHaveBeenCalled(); expect(refreshSpy).toHaveBeenCalled(); @@ -1225,8 +1229,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-excel]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-excel]').dispatchEvent(clickEvent); expect(consoleErrorSpy).toHaveBeenCalledWith(expect.toInclude('[Slickgrid-Universal] You must register the ExcelExportService to properly use Export to Excel in the Grid Menu.')); }); @@ -1239,8 +1243,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-csv]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-csv]').dispatchEvent(clickEvent); expect(consoleErrorSpy).toHaveBeenCalledWith(expect.toInclude('[Slickgrid-Universal] You must register the TextExportService to properly use Export to File in the Grid Menu.')); }); @@ -1253,8 +1257,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-text-delimited]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-text-delimited]').dispatchEvent(clickEvent); expect(consoleErrorSpy).toHaveBeenCalledWith(expect.toInclude('[Slickgrid-Universal] You must register the TextExportService to properly use Export to File in the Grid Menu.')); }); @@ -1269,8 +1273,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-excel]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-excel]').dispatchEvent(clickEvent); expect(excelExportSpy).toHaveBeenCalled(); }); @@ -1285,8 +1289,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-csv]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-csv]').dispatchEvent(clickEvent); expect(exportSpy).toHaveBeenCalledWith({ delimiter: DelimiterType.comma, format: FileType.csv }); }); @@ -1301,8 +1305,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=export-text-delimited]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=export-text-delimited]').dispatchEvent(clickEvent); expect(exportSpy).toHaveBeenCalledWith({ delimiter: DelimiterType.tab, format: FileType.txt }); }); @@ -1317,8 +1321,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=toggle-filter]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=toggle-filter]').dispatchEvent(clickEvent); expect(setHeaderSpy).toHaveBeenCalledWith(true); expect(scrollSpy).toHaveBeenCalledWith(0); @@ -1326,7 +1330,7 @@ describe('GridMenuControl', () => { copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, hideToggleFilterCommand: false } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=toggle-filter]').dispatchEvent(clickEvent); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=toggle-filter]').dispatchEvent(clickEvent); expect(setHeaderSpy).toHaveBeenCalledWith(false); expect(setColumnSpy).toHaveBeenCalledTimes(1); // same as before, so count won't increase @@ -1340,14 +1344,14 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=toggle-preheader]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=toggle-preheader]').dispatchEvent(clickEvent); expect(gridSpy).toHaveBeenCalledWith(false); copyGridOptionsMock = { ...gridOptionsMock, showPreHeaderPanel: false, hideTogglePreHeaderCommand: false } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=toggle-preheader]').dispatchEvent(clickEvent); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=toggle-preheader]').dispatchEvent(clickEvent); expect(gridSpy).toHaveBeenCalledWith(true); }); @@ -1360,8 +1364,8 @@ describe('GridMenuControl', () => { control.init(); control.columns = columnsMock; const clickEvent = new Event('click', { bubbles: true, cancelable: true, composed: false }); - document.querySelector('.slick-gridmenu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - control.menuElement.querySelector('.slick-gridmenu-item[data-command=refresh-dataset]').dispatchEvent(clickEvent); + document.querySelector('.slick-grid-menu-button').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + control.menuElement.querySelector('.slick-grid-menu-item[data-command=refresh-dataset]').dispatchEvent(clickEvent); expect(refreshSpy).toHaveBeenCalled(); }); @@ -1386,7 +1390,7 @@ describe('GridMenuControl', () => { control.columns = columnsUnorderedMock; control.initEventHandlers(); control.init(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); gridStub.onColumnsReordered.notify({ impactedColumns: columnsUnorderedMock, grid: gridStub }, eventData, gridStub); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); @@ -1423,7 +1427,7 @@ describe('GridMenuControl', () => { control.initEventHandlers(); control.init(); control.translateGridMenu(); - const buttonElm = document.querySelector('.slick-gridmenu-button'); + const buttonElm = document.querySelector('.slick-grid-menu-button'); buttonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); control.menuElement.querySelector('input[type="checkbox"]').dispatchEvent(new Event('click', { bubbles: true })); const labelForcefitElm = control.menuElement.querySelector('label[for=slickgrid_124343-gridmenu-colpicker-forcefit]'); diff --git a/packages/common/src/controls/gridMenu.control.ts b/packages/common/src/controls/gridMenu.control.ts index 1deafbf49..a980de0af 100644 --- a/packages/common/src/controls/gridMenu.control.ts +++ b/packages/common/src/controls/gridMenu.control.ts @@ -7,15 +7,13 @@ import { GridMenuEventWithElementCallbackArgs, GridMenuItem, GridMenuOption, - GridOption, + MenuCommandItem, + MenuOptionItem, SlickEventHandler, - SlickGrid, - SlickNamespace, } from '../interfaces/index'; import { DelimiterType, FileType } from '../enums'; import { ExtensionUtility } from '../extensions/extensionUtility'; -import { emptyElement, getHtmlElementOffset, getTranslationPrefix, hasData, } from '../services'; -import { BindingEventService } from '../services/bindingEvent.service'; +import { emptyElement, getHtmlElementOffset, getTranslationPrefix, } from '../services'; import { ExcelExportService } from '../services/excelExport.service'; import { FilterService } from '../services/filter.service'; import { PubSubService } from '../services/pubSub.service'; @@ -23,9 +21,7 @@ import { SharedService } from '../services/shared.service'; import { SortService } from '../services/sort.service'; import { TextExportService } from '../services/textExport.service'; import { handleColumnPickerItemClick, populateColumnPicker, updateColumnPickerOrder } from '../extensions/extensionCommonUtils'; - -// using external SlickGrid JS libraries -declare const Slick: SlickNamespace; +import { ExtractMenuType, MenuBaseClass, MenuType } from '../plugins/menuBaseClass'; /** * A control to add a Grid Menu with Extra Commands & Column Picker (hambuger menu on top-right of the grid) @@ -40,21 +36,15 @@ declare const Slick: SlickNamespace; * @class GridMenuControl * @constructor */ -export class GridMenuControl { +export class GridMenuControl extends MenuBaseClass { protected _areVisibleColumnDifferent = false; - protected _bindEventService: BindingEventService; protected _columns: Column[] = []; protected _columnCheckboxes: HTMLInputElement[] = []; protected _columnTitleElm!: HTMLDivElement; - protected _customMenuElm!: HTMLDivElement; - protected _customTitleElm?: HTMLDivElement; - protected _eventHandler!: SlickEventHandler; + protected _commandMenuElm!: HTMLDivElement; protected _gridMenuButtonElm!: HTMLButtonElement; - protected _gridUid = ''; - protected _headerElm?: HTMLDivElement | null; protected _isMenuOpen = false; protected _listElm!: HTMLSpanElement; - protected _gridMenuElm!: HTMLDivElement; protected _userOriginalGridMenu!: GridMenu; protected _defaults = { @@ -79,8 +69,9 @@ export class GridMenuControl { protected readonly sharedService: SharedService, protected readonly sortService: SortService, ) { - this._bindEventService = new BindingEventService(); - this._eventHandler = new Slick.EventHandler(); + super(extensionUtility, pubSubService, sharedService); + this._menuCssPrefix = 'slick-grid-menu'; + this._camelPluginName = 'gridMenu'; this._columns = this.sharedService.allColumns ?? []; this._gridUid = this.grid?.getUID?.() ?? ''; @@ -102,22 +93,6 @@ export class GridMenuControl { this._columns = newColumns; } - get eventHandler(): SlickEventHandler { - return this._eventHandler; - } - - get gridOptions(): GridOption { - return this.sharedService.gridOptions ?? {}; - } - - get grid(): SlickGrid { - return this.sharedService.slickGrid; - } - - get menuElement(): HTMLDivElement { - return this._gridMenuElm; - } - initEventHandlers() { // when grid columns are reordered then we also need to update/resync our picker column in the same order const onColumnsReorderedHandler = this.grid.onColumnsReordered; @@ -164,40 +139,36 @@ export class GridMenuControl { /** Dispose (destroy) the SlickGrid 3rd party plugin */ dispose() { this.deleteMenu(); - this._eventHandler.unsubscribeAll(); - this._bindEventService.unbindAll(); - this._listElm?.remove?.(); - this._gridMenuElm?.remove?.(); + super.dispose(); } deleteMenu() { this._bindEventService.unbindAll(); - const gridMenuElm = document.querySelector(`div.slick-gridmenu.${this._gridUid}`); + const gridMenuElm = document.querySelector(`div.slick-grid-menu.${this._gridUid}`); if (gridMenuElm) { - this._gridMenuElm.style.display = 'none'; + gridMenuElm.style.display = 'none'; } this._gridMenuButtonElm?.remove(); - this._gridMenuElm?.remove(); - this._customMenuElm?.remove(); - if (this._headerElm) { - this._headerElm.style.width = '100%'; // put back original width - } + this._menuElm?.remove(); + this._commandMenuElm?.remove(); } createColumnPickerContainer() { - // user could pass a title on top of the columns list - if (this.addonOptions?.columnTitle) { - this._columnTitleElm = document.createElement('div'); - this._columnTitleElm.className = 'title'; - this._columnTitleElm.textContent = this.addonOptions?.columnTitle ?? this._defaults.columnTitle; - this._gridMenuElm.appendChild(this._columnTitleElm); - } + if (this._menuElm) { + // user could pass a title on top of the columns list + if (this.addonOptions?.columnTitle) { + this._columnTitleElm = document.createElement('div'); + this._columnTitleElm.className = 'title'; + this._columnTitleElm.textContent = this.addonOptions?.columnTitle ?? this._defaults.columnTitle; + this._menuElm.appendChild(this._columnTitleElm); + } - this._listElm = document.createElement('span'); - this._listElm.className = 'slick-gridmenu-list'; + this._listElm = document.createElement('span'); + this._listElm.className = 'slick-grid-menu-list'; - // update all columns on any of the column title button click from column picker - this._bindEventService.bind(this._gridMenuElm, 'click', handleColumnPickerItemClick.bind(this) as EventListener); + // update all columns on any of the column title button click from column picker + this._bindEventService.bind(this._menuElm, 'click', handleColumnPickerItemClick.bind(this) as EventListener); + } } createGridMenu() { @@ -205,11 +176,11 @@ export class GridMenuControl { const gridUidSelector = this._gridUid ? `.${this._gridUid}` : ''; const gridMenuWidth = this.addonOptions?.menuWidth ?? this._defaults.menuWidth; const headerSide = (this.gridOptions.hasOwnProperty('frozenColumn') && this.gridOptions.frozenColumn! >= 0) ? 'right' : 'left'; - this._headerElm = document.querySelector(`${gridUidSelector} .slick-header-${headerSide}`); + this._menuElm = document.querySelector(`${gridUidSelector} .slick-header-${headerSide}`) as HTMLDivElement; - if (this._headerElm) { + if (this._menuElm) { // resize the header row to include the hamburger menu icon - this._headerElm.style.width = `calc(100% - ${gridMenuWidth}px)`; + this._menuElm.style.width = `calc(100% - ${gridMenuWidth}px)`; // if header row is enabled, we also need to resize its width const enableResizeHeaderRow = (this.addonOptions && this.addonOptions.resizeOnShowHeaderRow !== undefined) ? this.addonOptions.resizeOnShowHeaderRow : this._defaults.resizeOnShowHeaderRow; @@ -220,10 +191,10 @@ export class GridMenuControl { } } - const showButton = (this.addonOptions && this.addonOptions.showButton !== undefined) ? this.addonOptions.showButton : this._defaults.showButton; + const showButton = (this.addonOptions?.showButton !== undefined) ? this.addonOptions.showButton : this._defaults.showButton; if (showButton) { this._gridMenuButtonElm = document.createElement('button'); - this._gridMenuButtonElm.className = 'slick-gridmenu-button'; + this._gridMenuButtonElm.className = 'slick-grid-menu-button'; if (this.addonOptions && this.addonOptions.iconCssClass) { this._gridMenuButtonElm.classList.add(...this.addonOptions.iconCssClass.split(' ')); } else { @@ -232,9 +203,10 @@ export class GridMenuControl { iconImageElm.src = iconImage; this._gridMenuButtonElm.appendChild(iconImageElm); } - this._headerElm.parentNode?.prepend(this._gridMenuButtonElm); + this._menuElm.parentNode?.prepend(this._gridMenuButtonElm); // show the Grid Menu when hamburger menu is clicked + this.addonOptions.commandTitle = this.addonOptions.customTitle || this.addonOptions.commandTitle; this._bindEventService.bind(this._gridMenuButtonElm, 'click', this.showGridMenu.bind(this) as EventListener); } @@ -243,14 +215,14 @@ export class GridMenuControl { // localization support for the picker this.translateTitleLabels(); - this._gridMenuElm = document.createElement('div'); - this._gridMenuElm.classList.add('slick-gridmenu', this._gridUid); - this._gridMenuElm.style.display = 'none'; + this._menuElm = document.createElement('div'); + this._menuElm.classList.add('slick-grid-menu', this._gridUid); + this._menuElm.style.display = 'none'; const closePickerButtonElm = document.createElement('button'); closePickerButtonElm.className = 'close'; closePickerButtonElm.type = 'button'; - closePickerButtonElm.dataset.dismiss = 'slick-gridmenu'; + closePickerButtonElm.dataset.dismiss = 'slick-grid-menu'; closePickerButtonElm.setAttribute('aria-label', 'Close'); const closeSpanElm = document.createElement('span'); @@ -258,17 +230,30 @@ export class GridMenuControl { closeSpanElm.innerHTML = '×'; closeSpanElm.setAttribute('aria-hidden', 'true'); - this._customMenuElm = document.createElement('div'); - this._customMenuElm.className = 'slick-gridmenu-custom'; + this._commandMenuElm = document.createElement('div'); + this._commandMenuElm.className = 'slick-grid-menu-command-list'; closePickerButtonElm.appendChild(closeSpanElm); - this._gridMenuElm.appendChild(closePickerButtonElm); - this._gridMenuElm.appendChild(this._customMenuElm); - - this.populateCustomMenus(this.addonOptions, this._customMenuElm); + this._menuElm.appendChild(closePickerButtonElm); + this._menuElm.appendChild(this._commandMenuElm); + + this.populateCommandOrOptionItems( + 'command', + this.addonOptions, + this._commandMenuElm, + this.addonOptions?.customItems || [] as any[], + { + grid: this.grid, + menu: this._menuElm, + columns: this.columns, + allColumns: this.getAllColumns(), + visibleColumns: this.getVisibleColumns() + } as GridMenuEventWithElementCallbackArgs, + this.handleMenuItemCommandClick, + ); this.createColumnPickerContainer(); - document.body.appendChild(this._gridMenuElm); + document.body.appendChild(this._menuElm); // hide the menu on outside click. this._bindEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener); @@ -300,10 +285,10 @@ export class GridMenuControl { * @returns */ hideMenu(event: Event) { - if (this._gridMenuElm?.style?.display === 'block') { + if (this._menuElm?.style?.display === 'block') { const callbackArgs = { grid: this.grid, - menu: this._gridMenuElm, + menu: this._menuElm, allColumns: this.columns, visibleColumns: this.getVisibleColumns() } as GridMenuEventWithElementCallbackArgs; @@ -314,7 +299,7 @@ export class GridMenuControl { return; } - this._gridMenuElm.style.display = 'none'; + this._menuElm.style.display = 'none'; this._isMenuOpen = false; // we also want to resize the columns if the user decided to hide certain column(s) @@ -331,146 +316,51 @@ export class GridMenuControl { } } - /** - * Create and populate the Custom Menu Items and add them to the top of the DOM element (before the column picker) - * @param {GridMenu} options - grid menu options - * @param {HTMLDivElement} customMenuElm - custom menu container DOM element - */ - populateCustomMenus(options: GridMenu, customMenuElm: HTMLDivElement) { - if (Array.isArray(options?.customItems)) { - // user could pass a title on top of the custom section - if (this.addonOptions?.customTitle) { - this._customTitleElm = document.createElement('div'); - this._customTitleElm.className = 'title'; - this._customTitleElm.textContent = this.addonOptions.customTitle; - customMenuElm.appendChild(this._customTitleElm); - } - - for (const item of options.customItems) { - const callbackArgs = { - grid: this.grid, - menu: this._gridMenuElm, - columns: this.columns, - allColumns: this.getAllColumns(), - visibleColumns: this.getVisibleColumns() - } as GridMenuEventWithElementCallbackArgs; - - // run each override functions to know if the item is visible and usable - let isItemVisible = true; - let isItemUsable = true; - if (typeof item === 'object') { - isItemVisible = this.extensionUtility.runOverrideFunctionWhenExists(item.itemVisibilityOverride, callbackArgs); - isItemUsable = this.extensionUtility.runOverrideFunctionWhenExists(item.itemUsabilityOverride, callbackArgs); - } - - // if the result is not visible then there's no need to go further - if (!isItemVisible) { - continue; - } - - // when the override is defined, we need to use its result to update the disabled property - // so that "handleMenuItemCommandClick" has the correct flag and won't trigger a command clicked event - if (typeof item === 'object' && item.itemUsabilityOverride) { - item.disabled = isItemUsable ? false : true; - } - - const liElm = document.createElement('li'); - liElm.className = 'slick-gridmenu-item'; - if (typeof item === 'object' && hasData(item?.command)) { - liElm.dataset.command = item.command; - } - customMenuElm.appendChild(liElm); - - if ((typeof item === 'object' && item.divider) || item === 'divider') { - liElm.classList.add('slick-gridmenu-item-divider'); - continue; - } - - if (item.disabled) { - liElm.classList.add('slick-gridmenu-item-disabled'); - } - - if (item.hidden) { - liElm.classList.add('slick-gridmenu-item-hidden'); - } - - if (item.cssClass) { - liElm.classList.add(...item.cssClass.split(' ')); - } - - if (item.tooltip) { - liElm.title = item.tooltip; - } - - const iconElm = document.createElement('div'); - iconElm.className = 'slick-gridmenu-icon'; - liElm.appendChild(iconElm); - - if (item.iconCssClass) { - iconElm.classList.add(...item.iconCssClass.split(' ')); - } - - if (item.iconImage) { - console.warn('[Slickgrid-Universal] The "iconImage" property of a Grid Menu item is now deprecated and will be removed in future version, consider using "iconCssClass" instead.'); - iconElm.style.backgroundImage = `url(${item.iconImage})`; - } - - const textElm = document.createElement('span'); - textElm.className = 'slick-gridmenu-content'; - textElm.textContent = typeof item === 'object' && item.title || ''; - liElm.appendChild(textElm); - - if (item.textCssClass) { - textElm.classList.add(...item.textCssClass.split(' ')); - } - // execute command on menu item clicked - this._bindEventService.bind(liElm, 'click', (e) => this.handleMenuCustomItemClick(e, item)); - } - } - } - recreateGridMenu() { this.deleteMenu(); this.init(); } repositionMenu(e: MouseEvent, addonOptions: GridMenu) { - let buttonElm = (e.target as HTMLButtonElement).nodeName === 'BUTTON' ? (e.target as HTMLButtonElement) : (e.target as HTMLElement).querySelector('button') as HTMLButtonElement; // get button element - if (!buttonElm) { - buttonElm = (e.target as HTMLElement).parentElement as HTMLButtonElement; // external grid menu might fall in this last case if wrapped in a span/div - } - this._gridMenuElm.style.display = 'block'; - const menuIconOffset = getHtmlElementOffset(buttonElm as HTMLButtonElement); - const buttonComptStyle = getComputedStyle(buttonElm as HTMLButtonElement); - const buttonWidth = parseInt(buttonComptStyle?.width ?? this._defaults?.menuWidth, 10); - - const menuWidth = this._gridMenuElm?.offsetWidth ?? 0; - const contentMinWidth = addonOptions?.contentMinWidth ?? this._defaults.contentMinWidth ?? 0; - const currentMenuWidth = ((contentMinWidth > menuWidth) ? contentMinWidth : (menuWidth)) || 0; - const nextPositionTop = menuIconOffset?.bottom ?? 0; - const nextPositionLeft = menuIconOffset?.right ?? 0; - const menuMarginBottom = ((addonOptions?.marginBottom !== undefined) ? addonOptions.marginBottom : this._defaults.marginBottom) || 0; - const calculatedLeftPosition = addonOptions?.alignDropSide === 'left' ? nextPositionLeft - buttonWidth : nextPositionLeft - currentMenuWidth; - - this._gridMenuElm.style.top = `${nextPositionTop}px`; - this._gridMenuElm.style.left = `${calculatedLeftPosition}px`; - this._gridMenuElm.classList.add(addonOptions?.alignDropSide === 'left' ? 'dropleft' : 'dropright'); - this._gridMenuElm.appendChild(this._listElm); - - if (contentMinWidth! > 0) { - this._gridMenuElm.style.minWidth = `${contentMinWidth}px`; - } + if (this._menuElm) { + let buttonElm = (e.target as HTMLButtonElement).nodeName === 'BUTTON' ? (e.target as HTMLButtonElement) : (e.target as HTMLElement).querySelector('button') as HTMLButtonElement; // get button element + if (!buttonElm) { + buttonElm = (e.target as HTMLElement).parentElement as HTMLButtonElement; // external grid menu might fall in this last case if wrapped in a span/div + } - // set 'height' when defined OR ELSE use the 'max-height' with available window size and optional margin bottom - if (addonOptions?.height !== undefined) { - this._gridMenuElm.style.height = `${addonOptions.height}px`; - } else { - this._gridMenuElm.style.maxHeight = `${window.innerHeight - e.clientY - menuMarginBottom}px`; - } + this._menuElm.style.display = 'block'; + const menuIconOffset = getHtmlElementOffset(buttonElm as HTMLButtonElement); + const buttonComptStyle = getComputedStyle(buttonElm as HTMLButtonElement); + const buttonWidth = parseInt(buttonComptStyle?.width ?? this._defaults?.menuWidth, 10); + + const menuWidth = this._menuElm?.offsetWidth ?? 0; + const contentMinWidth = addonOptions?.contentMinWidth ?? this._defaults.contentMinWidth ?? 0; + const currentMenuWidth = ((contentMinWidth > menuWidth) ? contentMinWidth : (menuWidth)) || 0; + const nextPositionTop = menuIconOffset?.bottom ?? 0; + const nextPositionLeft = menuIconOffset?.right ?? 0; + const menuMarginBottom = ((addonOptions?.marginBottom !== undefined) ? addonOptions.marginBottom : this._defaults.marginBottom) || 0; + const calculatedLeftPosition = addonOptions?.alignDropSide === 'left' ? nextPositionLeft - buttonWidth : nextPositionLeft - currentMenuWidth; + + this._menuElm.style.top = `${nextPositionTop}px`; + this._menuElm.style.left = `${calculatedLeftPosition}px`; + this._menuElm.classList.add(addonOptions?.alignDropSide === 'left' ? 'dropleft' : 'dropright'); + this._menuElm.appendChild(this._listElm); + + if (contentMinWidth! > 0) { + this._menuElm.style.minWidth = `${contentMinWidth}px`; + } - this._gridMenuElm.style.display = 'block'; - this._gridMenuElm.appendChild(this._listElm); - this._isMenuOpen = true; + // set 'height' when defined OR ELSE use the 'max-height' with available window size and optional margin bottom + if (addonOptions?.height !== undefined) { + this._menuElm.style.height = `${addonOptions.height}px`; + } else { + this._menuElm.style.maxHeight = `${window.innerHeight - e.clientY - menuMarginBottom}px`; + } + + this._menuElm.style.display = 'block'; + this._menuElm.appendChild(this._listElm); + this._isMenuOpen = true; + } } showGridMenu(e: MouseEvent, options?: GridMenuOption) { @@ -478,20 +368,31 @@ export class GridMenuControl { // empty both the picker list & the command list emptyElement(this._listElm); - emptyElement(this._customMenuElm); - - const addonOptions: GridMenu = { ...this.addonOptions, ...options }; // merge optional picker option - this.populateCustomMenus(addonOptions, this._customMenuElm); - updateColumnPickerOrder.call(this); - this._columnCheckboxes = []; + emptyElement(this._commandMenuElm); const callbackArgs = { grid: this.grid, - menu: this._gridMenuElm, - allColumns: this.columns, + menu: this._menuElm, + columns: this.columns, + allColumns: this.getAllColumns(), visibleColumns: this.getVisibleColumns() } as GridMenuEventWithElementCallbackArgs; + const addonOptions: GridMenu = { ...this.addonOptions, ...options }; // merge optional picker option + addonOptions.customTitle = addonOptions.commandTitle; + + this.populateCommandOrOptionItems( + 'command', + this.addonOptions, + this._commandMenuElm, + addonOptions?.customItems || [] as any[], + callbackArgs, + this.handleMenuItemCommandClick , + ); + + updateColumnPickerOrder.call(this); + this._columnCheckboxes = []; + // run the override function (when defined), if the result is false then we won't go further if (addonOptions && !this.extensionUtility.runOverrideFunctionWhenExists(addonOptions.menuUsabilityOverride, callbackArgs)) { return; @@ -516,10 +417,10 @@ export class GridMenuControl { } } - /** Update the Titles of each sections (command, customTitle, ...) */ + /** Update the Titles of each sections (command, commandTitle, ...) */ updateAllTitles(options: GridMenuOption) { - if (this._columnTitleElm?.textContent && options.customTitle) { - this._columnTitleElm.textContent = options.customTitle; + if (this._commandTitleElm?.textContent && (options.customTitle || options.commandTitle)) { + this._commandTitleElm.textContent = (options.customTitle || options.commandTitle) as string; } if (this._columnTitleElm?.textContent && options.columnTitle) { this._columnTitleElm.textContent = options.columnTitle; @@ -532,6 +433,7 @@ export class GridMenuControl { // we also need to call the control init so that it takes the new Grid object with latest values if (this.addonOptions) { this.addonOptions.customItems = []; + this.addonOptions.commandTitle = ''; this.addonOptions.customTitle = ''; this.addonOptions.columnTitle = ''; this.addonOptions.forceFitTitle = ''; @@ -548,7 +450,7 @@ export class GridMenuControl { // translate all columns (including non-visible) this.extensionUtility.translateItems(this._columns, 'nameKey', 'name'); - // update the Titles of each sections (command, customTitle, ...) + // update the Titles of each sections (command, commandTitle, ...) this.updateAllTitles(this.addonOptions); } } @@ -723,7 +625,10 @@ export class GridMenuControl { // add the custom "Commands" title if there are any commands if (this.gridOptions && this.addonOptions && (Array.isArray(gridMenuCustomItems) && gridMenuCustomItems.length > 0 || (Array.isArray(this.addonOptions.customItems) && this.addonOptions.customItems.length > 0))) { - this.addonOptions.customTitle = this.addonOptions.customTitle || this.extensionUtility.getPickerTitleOutputString('customTitle', 'gridMenu'); + if (this.addonOptions?.customTitleKey) { + this.addonOptions.customTitle = this.addonOptions.customTitle || this.extensionUtility.getPickerTitleOutputString('customTitle', 'gridMenu'); + } + this.addonOptions.commandTitle = this.addonOptions.customTitle || this.addonOptions.commandTitle || this.extensionUtility.getPickerTitleOutputString('commandTitle', 'gridMenu'); } return gridMenuCustomItems; @@ -827,6 +732,7 @@ export class GridMenuControl { protected getDefaultGridMenuOptions(): GridMenu { return { customTitle: undefined, + commandTitle: undefined, columnTitle: this.extensionUtility.getPickerTitleOutputString('columnTitle', 'gridMenu'), forceFitTitle: this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'gridMenu'), syncResizeTitle: this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'gridMenu'), @@ -841,20 +747,20 @@ export class GridMenuControl { /** Mouse down handler when clicking anywhere in the DOM body */ protected handleBodyMouseDown(event: DOMEvent) { - if ((this._gridMenuElm !== event.target && !this._gridMenuElm.contains(event.target) && this._isMenuOpen) || event.target.className === 'close') { + if ((this._menuElm !== event.target && !this._menuElm?.contains(event.target) && this._isMenuOpen) || event.target.className === 'close') { this.hideMenu(event); } } - protected handleMenuCustomItemClick(event: Event, item: GridMenuItem) { - if (item && item.command && !item.disabled && !item.divider) { + protected handleMenuItemCommandClick(event: Event, _type: MenuType, item: ExtractMenuType) { + if (item !== 'divider' && (item as MenuCommandItem).command && !item.disabled && !item.divider) { const callbackArgs = { grid: this.grid, - command: item.command, + command: (item as MenuCommandItem).command, item, allColumns: this.columns, visibleColumns: this.getVisibleColumns() - } as GridMenuCommandItemCallbackArgs; + } as unknown as GridMenuCommandItemCallbackArgs; // execute Grid Menu callback with command, // we'll also execute optional user defined onCommand callback when provided @@ -866,7 +772,7 @@ export class GridMenuControl { // execute action callback when defined if (typeof item.action === 'function') { - item.action.call(this, event, callbackArgs); + (item as MenuCommandItem).action!.call(this, event, callbackArgs as any); } } diff --git a/packages/common/src/extensions/extensionUtility.ts b/packages/common/src/extensions/extensionUtility.ts index 4ffba3ec8..7b8f13dc5 100644 --- a/packages/common/src/extensions/extensionUtility.ts +++ b/packages/common/src/extensions/extensionUtility.ts @@ -39,6 +39,7 @@ export class ExtensionUtility { output = this.translaterService.translate(titleKey || ' '); } else { switch (propName) { + case 'commandTitle': case 'customTitle': output = title || enableTranslate && this.translaterService?.getCurrentLanguage && this.translaterService?.translate(`${translationPrefix}COMMANDS` || ' ') || locales?.TEXT_COMMANDS; break; diff --git a/packages/common/src/interfaces/gridMenu.interface.ts b/packages/common/src/interfaces/gridMenu.interface.ts index 73e156888..d7c4a1ca3 100644 --- a/packages/common/src/interfaces/gridMenu.interface.ts +++ b/packages/common/src/interfaces/gridMenu.interface.ts @@ -19,6 +19,9 @@ export interface GridMenu extends GridMenuOption { /** Callback fired Before the menu is shown. */ onBeforeMenuShow?: (e: Event, args: GridMenuEventWithElementCallbackArgs) => boolean | void; + /** SlickGrid Event fired when the menu is closing. */ + onBeforeMenuClose?: (e: Event, args: GridMenuEventWithElementCallbackArgs) => boolean | void; + /** Callback fired when any of the columns checkbox selection changes. */ onColumnsChanged?: (e: Event, args: GridMenuOnColumnsChangedCallbackArgs) => void; diff --git a/packages/common/src/interfaces/gridMenuCommandItemCallbackArgs.interface.ts b/packages/common/src/interfaces/gridMenuCommandItemCallbackArgs.interface.ts index 90709292f..29a18fcb0 100644 --- a/packages/common/src/interfaces/gridMenuCommandItemCallbackArgs.interface.ts +++ b/packages/common/src/interfaces/gridMenuCommandItemCallbackArgs.interface.ts @@ -1,6 +1,13 @@ import { Column, SlickGrid } from '.'; import { MenuCommandItem } from './menuCommandItem.interface'; +export interface GridMenuCallbackArgs { + grid: SlickGrid; + menu: any; + columns: Column[]; + visibleColumns: Column[] +} + export interface GridMenuCommandItemCallbackArgs { /** A command identifier returned by the onCommand (or action) event callback handler. */ command: string; diff --git a/packages/common/src/interfaces/gridMenuItem.interface.ts b/packages/common/src/interfaces/gridMenuItem.interface.ts index 112a11a01..6bb5e48f3 100644 --- a/packages/common/src/interfaces/gridMenuItem.interface.ts +++ b/packages/common/src/interfaces/gridMenuItem.interface.ts @@ -1,47 +1,7 @@ -import { Column } from './column.interface'; -import { GridMenuCommandItemCallbackArgs } from './gridMenuCommandItemCallbackArgs.interface'; -import { SlickGrid } from './slickGrid.interface'; - -export interface GridMenuItem { - /** A command identifier to be passed to the onCommand event callback handlers. */ - command: string; - - /** A CSS class to be added to the menu item container. */ - cssClass?: string; - - /** Defaults to false, whether the item/command is disabled. */ - disabled?: boolean; - - /** Defaults to false, whether the item/command is hidden. */ - hidden?: boolean; - - /** Defaults to false, whether the command is actually a divider (separator). */ - divider?: boolean; - - /** CSS class to be added to the menu item icon. */ - iconCssClass?: string; - - /** - * @deprecated @use `iconCssClass` - * URL pointing to the icon image. - */ - iconImage?: string; - - /** position order in the list, a lower number will make it on top of the list. Internal commands starts at 50. */ - positionOrder?: number; - - /** CSS class to be added to the menu item text. */ - textCssClass?: string; - - /** Menu item text to show in the list. */ - title?: string; - - /** Same as "title", except that it's a translation key which can be used on page load and/or when switching locale */ - titleKey?: string; - - /** Item tooltip to show while hovering the command. */ - tooltip?: string; +import { GridMenuCallbackArgs, GridMenuCommandItemCallbackArgs } from './gridMenuCommandItemCallbackArgs.interface'; +import { MenuCommandItem } from './menuCommandItem.interface'; +export interface GridMenuItem extends MenuCommandItem { // -- // action/override callbacks @@ -49,8 +9,8 @@ export interface GridMenuItem { action?: (event: Event, callbackArgs: GridMenuCommandItemCallbackArgs) => void; /** Callback method that user can override the default behavior of showing/hiding an item from the list. */ - itemVisibilityOverride?: (args: { grid: SlickGrid; menu: any; columns: Column[]; visibleColumns: Column[] }) => boolean; + itemVisibilityOverride?: (args: GridMenuCallbackArgs) => boolean; /** Callback method that user can override the default behavior of enabling/disabling an item from the list. */ - itemUsabilityOverride?: (args: { grid: SlickGrid; menu: any; columns: Column[]; visibleColumns: Column[] }) => boolean; + itemUsabilityOverride?: (args: GridMenuCallbackArgs) => boolean; } diff --git a/packages/common/src/interfaces/gridMenuOption.interface.ts b/packages/common/src/interfaces/gridMenuOption.interface.ts index 07b99b125..80ea9dce5 100644 --- a/packages/common/src/interfaces/gridMenuOption.interface.ts +++ b/packages/common/src/interfaces/gridMenuOption.interface.ts @@ -1,4 +1,4 @@ -import { Column, GridMenuItem, GridMenuLabel, GridOption, MenuCallbackArgs, } from './index'; +import { Column, GridMenuCallbackArgs, GridMenuCommandItemCallbackArgs, GridMenuLabel, GridOption, MenuCallbackArgs, MenuCommandItem, } from './index'; export interface GridMenuOption { /** Defaults to "right", which side to align the grid menu dropdown? */ @@ -10,16 +10,22 @@ export interface GridMenuOption { */ commandLabels?: GridMenuLabel; + /** Defaults to "Commands" which is the title that shows up over the custom commands list */ + commandTitle?: string; + + /** Same as "commandTitle", except that it's a translation key which can be used on page load and/or when switching locale */ + commandTitleKey?: string; + /** Defaults to 0 (auto), minimum width of grid menu content (command, column list) */ contentMinWidth?: number; /** Array of Custom Items (title, command, disabled, ...) */ - customItems?: Array; + customItems?: Array | 'divider'>; - /** Defaults to "Commands" which is the title that shows up over the custom commands list */ + /** @deprecated @use `commandTitle` Defaults to "Commands" which is the title that shows up over the custom commands list */ customTitle?: string; - /** Same as "customTitle", except that it's a translation key which can be used on page load and/or when switching locale */ + /** @deprecated @use `commandTitleKey` Same as "customTitle", except that it's a translation key which can be used on page load and/or when switching locale */ customTitleKey?: string; /** Defaults to "Columns" which is the title that shows up over the columns */ @@ -46,6 +52,9 @@ export interface GridMenuOption { /** Defaults to true, which will hide the "Unfreeze Columns/Rows" command in the Grid Menu */ hideClearFrozenColumnsCommand?: boolean; + /** Defaults to false, hide the Close button on top right */ + hideCloseButton?: boolean; + /** Defaults to false, which will hide the "Export to CSV" command in the Grid Menu (Grid Option "enableTextExport: true" has to be enabled) */ hideExportCsvCommand?: boolean; @@ -112,6 +121,9 @@ export interface GridMenuOption { /** Defaults to 15, margin to use at the bottom of the grid menu to deduce from the max-height, only in effect when height is undefined */ marginBottom?: number; + /** Maximum height that the drop menu will have, can be a number (250) or text ("none") */ + maxHeight?: number | string; + /** Defaults to 16 pixels (only the number), which is the width in pixels of the Grid Menu icon container */ menuWidth?: number; @@ -127,6 +139,12 @@ export interface GridMenuOption { /** Same as "syncResizeTitle", except that it's a translation key which can be used on page load and/or when switching locale */ syncResizeTitleKey?: string; + /** + * Width (alias to `menuWidth`) that the drop menu can have. + * NOTE: the menu also has a "min-width" defined in CSS/SASS and setting a "width" below that threshold won't work, you change this min-width via SASS `$context-menu-min-width` + */ + width?: number | string; + // -- // action/override callbacks diff --git a/packages/common/src/interfaces/menuCommandItem.interface.ts b/packages/common/src/interfaces/menuCommandItem.interface.ts index a97a596cd..d5d47af10 100644 --- a/packages/common/src/interfaces/menuCommandItem.interface.ts +++ b/packages/common/src/interfaces/menuCommandItem.interface.ts @@ -1,8 +1,9 @@ import { MenuItem } from './menuItem.interface'; import { MenuCommandItemCallbackArgs } from './menuCommandItemCallbackArgs.interface'; import { SlickEventData } from './slickEventData.interface'; +import { MenuCallbackArgs } from './menuCallbackArgs.interface'; -export interface MenuCommandItem extends MenuItem { +export interface MenuCommandItem extends MenuItem { /** A command identifier to be passed to the onCommand event callback handler (when using "commandItems"). */ command: string; @@ -10,5 +11,5 @@ export interface MenuCommandItem extends MenuItem { // action/override callbacks /** Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event) */ - action?: (event: SlickEventData | Event, callbackArgs: MenuCommandItemCallbackArgs) => void; + action?: (event: SlickEventData | Event, callbackArgs: A) => void; } diff --git a/packages/common/src/interfaces/menuCommandItemCallbackArgs.interface.ts b/packages/common/src/interfaces/menuCommandItemCallbackArgs.interface.ts index b4dcb676b..86df20fab 100644 --- a/packages/common/src/interfaces/menuCommandItemCallbackArgs.interface.ts +++ b/packages/common/src/interfaces/menuCommandItemCallbackArgs.interface.ts @@ -8,6 +8,6 @@ export interface MenuCommandItemCallbackArgs extends MenuCallbackArgs { /** Menu item selected */ item: MenuCommandItem; - /** Value of the cell we triggered the context menu from */ + /** Value of the cell we triggered the menu from */ value?: any; } diff --git a/packages/common/src/interfaces/menuItem.interface.ts b/packages/common/src/interfaces/menuItem.interface.ts index 34ed6c2d2..2b6b1e8cb 100644 --- a/packages/common/src/interfaces/menuItem.interface.ts +++ b/packages/common/src/interfaces/menuItem.interface.ts @@ -1,6 +1,6 @@ import { MenuCallbackArgs } from './menuCallbackArgs.interface'; -export interface MenuItem { +export interface MenuItem { /** A CSS class to be added to the menu item container. */ cssClass?: string; @@ -41,8 +41,8 @@ export interface MenuItem { // action/override callbacks /** Callback method that user can override the default behavior of showing/hiding an item from the list. */ - itemVisibilityOverride?: (args: MenuCallbackArgs) => boolean; + itemVisibilityOverride?: (args: O) => boolean; /** Callback method that user can override the default behavior of enabling/disabling an item from the list. */ - itemUsabilityOverride?: (args: MenuCallbackArgs) => boolean; + itemUsabilityOverride?: (args: O) => boolean; } diff --git a/packages/common/src/plugins/__tests__/cellMenu.plugin.spec.ts b/packages/common/src/plugins/__tests__/cellMenu.plugin.spec.ts index 298424ff5..7276b8846 100644 --- a/packages/common/src/plugins/__tests__/cellMenu.plugin.spec.ts +++ b/packages/common/src/plugins/__tests__/cellMenu.plugin.spec.ts @@ -276,20 +276,20 @@ describe('CellMenu Plugin', () => {
-
+
  • Command 1 -
  • -
    + +
  • Command 2 -
  • -
    -
    + +
  • +
  • Delete Row -
  • -
    + +
  • `)); }); @@ -665,20 +665,20 @@ describe('CellMenu Plugin', () => {
    -
    +
  • Option 1 -
  • -
    + +
  • Option 2 -
  • -
    -
    + +
  • +
  • Delete Row -
  • -
    + +
  • `)); }); diff --git a/packages/common/src/plugins/__tests__/contextMenu.plugin.spec.ts b/packages/common/src/plugins/__tests__/contextMenu.plugin.spec.ts index 4cad5becb..aeb2f6c56 100644 --- a/packages/common/src/plugins/__tests__/contextMenu.plugin.spec.ts +++ b/packages/common/src/plugins/__tests__/contextMenu.plugin.spec.ts @@ -314,20 +314,20 @@ describe('ContextMenu Plugin', () => {
    -
    +
  • Command 1 -
  • -
    + +
  • Command 2 -
  • -
    -
    + +
  • +
  • Delete Row -
  • -
    + +
  • `)); }); @@ -1228,20 +1228,20 @@ describe('ContextMenu Plugin', () => {
    -
    +
  • Option 1 -
  • -
    + +
  • Option 2 -
  • -
    -
    + +
  • +
  • Delete Row -
  • -
    + +
  • `)); }); diff --git a/packages/common/src/plugins/__tests__/headerMenu.plugin.spec.ts b/packages/common/src/plugins/__tests__/headerMenu.plugin.spec.ts index 6a0700d1d..920735736 100644 --- a/packages/common/src/plugins/__tests__/headerMenu.plugin.spec.ts +++ b/packages/common/src/plugins/__tests__/headerMenu.plugin.spec.ts @@ -199,7 +199,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces( - `
    `)); + `
    `)); }); it('should populate a Header Menu button with extra button image when header menu option "buttonImage" and cell is being rendered', () => { @@ -211,7 +211,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces( - `
    `)); + `
    `)); }); it('should populate a Header Menu button with extra tooltip title attribute when header menu option "tooltip" and cell is being rendered', () => { @@ -223,7 +223,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces( - `
    `)); + `
    `)); }); it('should populate a Header Menu when cell is being rendered and a 2nd button item visibility callback returns undefined', () => { @@ -236,7 +236,7 @@ describe('HeaderMenu Plugin', () => { // add Header Menu which is visible expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces( - `
    `)); + `
    `)); gridStub.onBeforeHeaderCellDestroy.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); expect(headerDiv.innerHTML).toBe(''); @@ -252,7 +252,7 @@ describe('HeaderMenu Plugin', () => { // add Header Menu which is visible expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces( - `
    `)); + `
    `)); }); it('should populate a Header Menu when cell is being rendered and a 2nd button item visibility & usability callbacks returns true', () => { @@ -263,19 +263,19 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; // add Header Menu which is visible - expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces(`
    `)); + expect(removeExtraSpaces(headerDiv.innerHTML)).toBe(removeExtraSpaces(`
    `)); headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -287,16 +287,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton:nth-child(1)') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button:nth-child(1)') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem.slick-header-menuitem-disabled'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item.slick-header-menu-item-disabled'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -308,16 +308,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem.slick-header-menuitem-disabled'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item.slick-header-menu-item-disabled'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -328,16 +328,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem.slick-header-menuitem-hidden'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item.slick-header-menu-item-hidden'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -348,16 +348,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); expect(consoleWarnSpy).toHaveBeenCalledWith('[Slickgrid-Universal] The "iconImage" property of a Header Menu item is now deprecated and will be removed in future version, consider using "iconCssClass" instead.'); }); @@ -370,16 +370,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - Some Title -
    ` + `
  • +
    + Some Title +
  • ` )); expect(consoleWarnSpy).toHaveBeenCalledWith('[Slickgrid-Universal] The "iconImage" property of a Header Menu item is now deprecated and will be removed in future version, consider using "iconCssClass" instead.'); }); @@ -391,16 +391,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -413,19 +413,19 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); - gridContainerDiv.querySelector('.slick-header-menuitem.mdi-lightbulb-on').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + gridContainerDiv.querySelector('.slick-header-menu-item.mdi-lightbulb-on').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); expect(actionMock).toHaveBeenCalled(); expect(gridContainerDiv.innerHTML).toBe(''); }); @@ -439,19 +439,19 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); - gridContainerDiv.querySelector('.slick-header-menuitem.mdi-lightbulb-on').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); + gridContainerDiv.querySelector('.slick-header-menu-item.mdi-lightbulb-on').dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); expect(onCommandMock).toHaveBeenCalled(); expect(gridContainerDiv.innerHTML).toBe(''); }); @@ -468,16 +468,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -488,9 +488,9 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const buttonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const buttonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; buttonElm.dispatchEvent(new Event('click')); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); const menuElm = gridContainerDiv.querySelector('.slick-header-menu') as HTMLDivElement; const clickEvent = new MouseEvent('click'); Object.defineProperty(buttonElm, 'clientWidth', { writable: true, configurable: true, value: 350 }); @@ -503,10 +503,10 @@ describe('HeaderMenu Plugin', () => { expect(menuElm.style.left).toBe('75px'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); }); @@ -518,9 +518,9 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton:nth-child(1)') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button:nth-child(1)') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem.slick-header-menuitem-disabled'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item.slick-header-menu-item-disabled'); expect(commandElm).toBeFalsy(); }); @@ -537,7 +537,7 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; expect(headerButtonElm).toBeFalsy(); }); @@ -552,16 +552,16 @@ describe('HeaderMenu Plugin', () => { const eventData = { ...new Slick.EventData(), preventDefault: jest.fn() }; gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); - const commandElm = gridContainerDiv.querySelector('.slick-header-menuitem[data-command="show-negative-numbers"]'); + const commandElm = gridContainerDiv.querySelector('.slick-header-menu-item[data-command="show-negative-numbers"]'); expect(commandElm).toBeTruthy(); expect(removeExtraSpaces(commandElm.outerHTML)).toBe(removeExtraSpaces( - `
    -
    - -
    ` + `
  • +
    + +
  • ` )); const bodyElm = document.body; @@ -658,7 +658,7 @@ describe('HeaderMenu Plugin', () => { plugin.init({ onBeforeMenuShow: () => false }); gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: originalColumnDefinitions, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: originalColumnDefinitions[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="freeze-columns"]') as HTMLDivElement; @@ -680,14 +680,14 @@ describe('HeaderMenu Plugin', () => { const onAfterSpy = jest.spyOn(plugin.addonOptions, 'onAfterMenuShow').mockReturnValue(false); gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const clearFilterSpy = jest.spyOn(filterServiceStub, 'clearFilterByColumnId'); const headerMenuExpected = [{ iconCssClass: 'fa fa-filter', title: 'Remove Filter', titleKey: 'REMOVE_FILTER', command: 'clear-filter', positionOrder: 53 }]; const commandDivElm = gridContainerDiv.querySelector('[data-command="clear-filter"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual(headerMenuExpected); expect(commandIconElm.classList.contains('fa-filter')).toBeTruthy(); expect(commandLabelElm.textContent).toBe('Remove Filter'); @@ -715,7 +715,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish'); @@ -725,8 +725,8 @@ describe('HeaderMenu Plugin', () => { { iconCssClass: 'fa fa-times', title: 'Hide Column', titleKey: 'HIDE_COLUMN', command: 'hide-column', positionOrder: 55 } ]; const commandDivElm = gridContainerDiv.querySelector('[data-command="column-resize-by-content"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual(headerMenuExpected); expect(commandIconElm.classList.contains('fa-arrows-h')).toBeTruthy(); expect(commandLabelElm.textContent).toBe('Resize by Content'); @@ -749,7 +749,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const autosizeSpy = jest.spyOn(gridStub, 'autosizeColumns'); @@ -759,8 +759,8 @@ describe('HeaderMenu Plugin', () => { { iconCssClass: 'fa fa-times', title: 'Hide Column', titleKey: 'HIDE_COLUMN', command: 'hide-column', positionOrder: 55 } ]; const commandDivElm = gridContainerDiv.querySelector('[data-command="hide-column"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual(headerMenuExpected); expect(commandIconElm.classList.contains('fa-times')).toBeTruthy(); expect(commandLabelElm.textContent).toBe('Hide Column'); @@ -779,14 +779,14 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const clearFilterSpy = jest.spyOn(filterServiceStub, 'clearFilterByColumnId'); const headerMenuExpected = [{ iconCssClass: 'fa fa-filter', title: 'Remove Filter', titleKey: 'REMOVE_FILTER', command: 'clear-filter', positionOrder: 53 }]; const commandDivElm = gridContainerDiv.querySelector('[data-command="clear-filter"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual(headerMenuExpected); expect(commandIconElm.classList.contains('fa-filter')).toBeTruthy(); expect(commandLabelElm.textContent).toBe('Remove Filter'); @@ -807,12 +807,12 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const clearSortSpy = jest.spyOn(sortServiceStub, 'clearSortByColumnId'); const commandDivElm = gridContainerDiv.querySelector('[data-command="clear-sort"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual([ { iconCssClass: 'fa fa-sort-asc', title: 'Sort Ascending', titleKey: 'SORT_ASCENDING', command: 'sort-asc', positionOrder: 50 }, { iconCssClass: 'fa fa-sort-desc', title: 'Sort Descending', titleKey: 'SORT_DESCENDING', command: 'sort-desc', positionOrder: 51 }, @@ -848,12 +848,12 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="freeze-columns"]') as HTMLDivElement; - const commandIconElm = commandDivElm.querySelector('.slick-header-menuicon') as HTMLDivElement; - const commandLabelElm = commandDivElm.querySelector('.slick-header-menucontent') as HTMLDivElement; + const commandIconElm = commandDivElm.querySelector('.slick-header-menu-icon') as HTMLDivElement; + const commandLabelElm = commandDivElm.querySelector('.slick-header-menu-content') as HTMLDivElement; expect(columnsMock[1].header.menu.items).toEqual([ { iconCssClass: 'fa fa-thumb-tack', title: 'Freeze Columns', titleKey: 'FREEZE_COLUMNS', command: 'freeze-columns', positionOrder: 47 }, { divider: true, command: '', positionOrder: 49 }, @@ -883,7 +883,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[2], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="freeze-columns"]') as HTMLDivElement; @@ -911,7 +911,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: originalColumnDefinitions, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: originalColumnDefinitions[0], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="freeze-columns"]') as HTMLDivElement; @@ -938,7 +938,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="sort-asc"]') as HTMLDivElement; @@ -970,7 +970,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); const commandDivElm = gridContainerDiv.querySelector('[data-command="sort-desc"]') as HTMLDivElement; @@ -1003,7 +1003,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); gridContainerDiv.querySelector('[data-command="sort-desc"]').dispatchEvent(new Event('click')); expect(previousSortSpy).toHaveBeenCalled(); @@ -1027,7 +1027,7 @@ describe('HeaderMenu Plugin', () => { gridStub.onBeforeSetColumns.notify({ previousColumns: [], newColumns: columnsMock, grid: gridStub }, eventData, gridStub); gridStub.onHeaderCellRendered.notify({ column: columnsMock[1], node: headerDiv, grid: gridStub }, eventData, gridStub); - const headerButtonElm = headerDiv.querySelector('.slick-header-menubutton') as HTMLDivElement; + const headerButtonElm = headerDiv.querySelector('.slick-header-menu-button') as HTMLDivElement; headerButtonElm.dispatchEvent(new Event('click', { bubbles: true, cancelable: true, composed: false })); gridContainerDiv.querySelector('[data-command="sort-desc"]').dispatchEvent(new Event('click')); expect(previousSortSpy).toHaveBeenCalled(); diff --git a/packages/common/src/plugins/cellMenu.plugin.ts b/packages/common/src/plugins/cellMenu.plugin.ts index 52f99a9d3..8bc5f272b 100644 --- a/packages/common/src/plugins/cellMenu.plugin.ts +++ b/packages/common/src/plugins/cellMenu.plugin.ts @@ -12,7 +12,7 @@ import { import { ExtensionUtility } from '../extensions/extensionUtility'; import { PubSubService } from '../services/pubSub.service'; import { SharedService } from '../services/shared.service'; -import { MenuBaseClass } from './menuBaseClass'; +import { MenuFromCellBaseClass } from './menuFromCellBaseClass'; /** * A plugin to add Menu on a Cell click (click on the cell that has the cellMenu object defined) @@ -32,7 +32,7 @@ import { MenuBaseClass } from './menuBaseClass'; * } * }]; */ -export class CellMenuPlugin extends MenuBaseClass { +export class CellMenuPlugin extends MenuFromCellBaseClass { protected _defaults = { autoAdjustDrop: true, // dropup/dropdown autoAlignSide: true, // left/right diff --git a/packages/common/src/plugins/contextMenu.plugin.ts b/packages/common/src/plugins/contextMenu.plugin.ts index 76aee07c7..838025cef 100644 --- a/packages/common/src/plugins/contextMenu.plugin.ts +++ b/packages/common/src/plugins/contextMenu.plugin.ts @@ -18,7 +18,7 @@ import { SharedService } from '../services/shared.service'; import { TreeDataService } from '../services/treeData.service'; import { ExcelExportService, TextExportService } from '../services'; import { DelimiterType, FileType } from '../enums'; -import { MenuBaseClass } from './menuBaseClass'; +import { MenuFromCellBaseClass } from './menuFromCellBaseClass'; /** @@ -37,7 +37,7 @@ import { MenuBaseClass } from './menuBaseClass'; * } * }; */ -export class ContextMenuPlugin extends MenuBaseClass { +export class ContextMenuPlugin extends MenuFromCellBaseClass { protected _defaults = { autoAdjustDrop: true, // dropup/dropdown autoAlignSide: true, // left/right diff --git a/packages/common/src/plugins/headerMenu.plugin.ts b/packages/common/src/plugins/headerMenu.plugin.ts index b8f7e5d3a..3c65681d1 100644 --- a/packages/common/src/plugins/headerMenu.plugin.ts +++ b/packages/common/src/plugins/headerMenu.plugin.ts @@ -10,22 +10,18 @@ import { HeaderMenuOption, MenuCommandItem, MenuCommandItemCallbackArgs, + MenuOptionItem, MultiColumnSort, OnHeaderCellRenderedEventArgs, SlickEventHandler, - SlickGrid, - SlickNamespace, } from '../interfaces/index'; -import { arrayRemoveItemByIndex, emptyElement, getElementOffsetRelativeToParent, hasData, } from '../services/index'; -import { BindingEventService } from '../services/bindingEvent.service'; +import { arrayRemoveItemByIndex, emptyElement, getElementOffsetRelativeToParent, } from '../services/index'; import { ExtensionUtility } from '../extensions/extensionUtility'; import { FilterService } from '../services/filter.service'; import { PubSubService } from '../services/pubSub.service'; import { SharedService } from '../services/shared.service'; import { SortService } from '../services/sort.service'; - -// using external SlickGrid JS libraries -declare const Slick: SlickNamespace; +import { ExtractMenuType, MenuBaseClass, MenuType } from './menuBaseClass'; /** * A plugin to add drop-down menus to column headers. @@ -39,12 +35,8 @@ declare const Slick: SlickNamespace; * } * }]; */ -export class HeaderMenuPlugin { - protected _addonOptions?: HeaderMenu; +export class HeaderMenuPlugin extends MenuBaseClass { protected _activeHeaderColumnElm?: HTMLDivElement; - protected _bindEventService: BindingEventService; - protected _eventHandler!: SlickEventHandler; - protected _menuElm?: HTMLDivElement | null; protected _defaults = { autoAlign: true, autoAlignOffset: 0, @@ -65,39 +57,13 @@ export class HeaderMenuPlugin { protected readonly sharedService: SharedService, protected readonly sortService: SortService, ) { - this._bindEventService = new BindingEventService(); - this._eventHandler = new Slick.EventHandler(); + super(extensionUtility, pubSubService, sharedService); + this._menuCssPrefix = 'slick-header-menu'; + this._camelPluginName = 'headerMenu'; this.sharedService.gridOptions.headerMenu = this.addHeaderMenuCustomCommands(this.sharedService.columnDefinitions); this.init(sharedService.gridOptions.headerMenu); } - get addonOptions(): HeaderMenu { - return this._addonOptions as HeaderMenu; - } - set addonOptions(newOptions: HeaderMenu) { - this._addonOptions = newOptions; - } - - get eventHandler(): SlickEventHandler { - return this._eventHandler; - } - - get grid(): SlickGrid { - return this.sharedService.slickGrid; - } - - /** Getter for the grid uid */ - get gridUid(): string { - return this.grid?.getUID() ?? ''; - } - get gridUidSelector(): string { - return this.gridUid ? `.${this.gridUid}` : ''; - } - - get menuElement(): HTMLDivElement | undefined | null { - return this._menuElm; - } - /** Initialize plugin. */ init(headerMenuOptions?: HeaderMenu) { this._addonOptions = { ...this._defaults, ...headerMenuOptions }; @@ -217,7 +183,7 @@ export class HeaderMenuPlugin { } const headerButtonDivElm = document.createElement('div'); - headerButtonDivElm.className = 'slick-header-menubutton'; + headerButtonDivElm.className = 'slick-header-menu-button'; if (this.addonOptions.buttonCssClass) { headerButtonDivElm.classList.add(...this.addonOptions.buttonCssClass.split(' ')); @@ -249,7 +215,7 @@ export class HeaderMenuPlugin { // Removing buttons will also clean up any event handlers and data. // NOTE: If you attach event handlers directly or using a different framework, // you must also clean them up here to avoid memory leaks. - args.node.querySelectorAll('.slick-header-menubutton').forEach(elm => elm.remove()); + args.node.querySelectorAll('.slick-header-menu-button').forEach(elm => elm.remove()); } } @@ -260,12 +226,11 @@ export class HeaderMenuPlugin { } } - protected handleMenuItemClick(event: DOMEvent, item: MenuCommandItem, columnDef: Column) { - if (item?.command && !item.disabled && !item.divider) { - + protected handleMenuItemCommandClick(event: DOMEvent, _type: MenuType, item: ExtractMenuType, columnDef?: Column) { + if (item !== 'divider' && (item as MenuCommandItem).command && !item.disabled && !item.divider) { const callbackArgs = { grid: this.grid, - command: item.command, + command: (item as MenuCommandItem).command, column: columnDef, item, } as MenuCommandItemCallbackArgs; @@ -280,7 +245,7 @@ export class HeaderMenuPlugin { // execute action callback when defined if (typeof item.action === 'function') { - item.action.call(this, event, callbackArgs); + (item as MenuCommandItem).action!.call(this, event, callbackArgs); } } @@ -491,82 +456,14 @@ export class HeaderMenuPlugin { } protected populateHeaderMenuCommandList(e: MouseEvent, columnDef: Column, menu: HeaderMenuItems, args: HeaderMenuCommandItemCallbackArgs) { - const menuItems = menu.items; - - // Construct the menu items. - for (const item of menuItems) { - // run each override functions to know if the item is visible and usable - let isItemVisible = true; - let isItemUsable = true; - if (typeof item === 'object') { - isItemVisible = this.extensionUtility.runOverrideFunctionWhenExists(item.itemVisibilityOverride, args); - isItemUsable = this.extensionUtility.runOverrideFunctionWhenExists(item.itemUsabilityOverride, args); - } - - // if the result is not visible then there's no need to go further - if (!isItemVisible) { - continue; - } - - // when the override is defined, we need to use its result to update the disabled property - // so that "handleMenuItemCommandClick" has the correct flag and won't trigger a command clicked event - if (typeof item === 'object' && item.itemUsabilityOverride) { - item.disabled = isItemUsable ? false : true; - } - - const liElm = document.createElement('div'); - liElm.className = 'slick-header-menuitem'; - if (typeof item === 'object' && hasData(item?.command)) { - liElm.dataset.command = item.command; - } - this._menuElm?.appendChild(liElm); - - if ((typeof item === 'object' && item.divider) || item === 'divider') { - liElm.classList.add('slick-header-menuitem-divider'); - continue; - } - - if (item.disabled) { - liElm.classList.add('slick-header-menuitem-disabled'); - } - - if (item.hidden) { - liElm.classList.add('slick-header-menuitem-hidden'); - } - - if (item.cssClass) { - liElm.classList.add(...item.cssClass.split(' ')); - } - - if (item.tooltip) { - liElm.title = item.tooltip; - } - - const iconElm = document.createElement('div'); - iconElm.className = 'slick-header-menuicon'; - liElm.appendChild(iconElm); - - if (item.iconCssClass) { - iconElm.classList.add(...item.iconCssClass.split(' ')); - } - - if (item.iconImage) { - console.warn('[Slickgrid-Universal] The "iconImage" property of a Header Menu item is now deprecated and will be removed in future version, consider using "iconCssClass" instead.'); - iconElm.style.backgroundImage = `url(${item.iconImage})`; - } - - const textElm = document.createElement('span'); - textElm.className = 'slick-header-menucontent'; - textElm.textContent = typeof item === 'object' && item.title || ''; - liElm.appendChild(textElm); - - if (item.textCssClass) { - textElm.classList.add(...item.textCssClass.split(' ')); - } - - // execute command on menu item clicked - this._bindEventService.bind(liElm, 'click', ((clickEvent: DOMEvent) => this.handleMenuItemClick(clickEvent, item, columnDef)) as EventListener); - } + this.populateCommandOrOptionItems( + 'command', + this.addonOptions, + this._menuElm as HTMLDivElement, + menu.items, + args, + this.handleMenuItemCommandClick , + ); this.repositionMenu(e); @@ -583,7 +480,7 @@ export class HeaderMenuPlugin { protected repositionMenu(e: MouseEvent) { const buttonElm = e.target as HTMLDivElement; // get header button createElement - if (this._menuElm && buttonElm.classList.contains('slick-header-menubutton')) { + if (this._menuElm && buttonElm.classList.contains('slick-header-menu-button')) { const relativePos = getElementOffsetRelativeToParent(this.sharedService.gridContainerElement, buttonElm); let leftPos = relativePos?.left ?? 0; diff --git a/packages/common/src/plugins/menuBaseClass.ts b/packages/common/src/plugins/menuBaseClass.ts index fe20517df..b93df7150 100644 --- a/packages/common/src/plugins/menuBaseClass.ts +++ b/packages/common/src/plugins/menuBaseClass.ts @@ -1,20 +1,20 @@ import { CellMenu, + Column, ContextMenu, DOMMouseEvent, + GridMenu, GridOption, + HeaderMenu, MenuCommandItem, - MenuCommandItemCallbackArgs, - MenuFromCellCallbackArgs, MenuOptionItem, - MenuOptionItemCallbackArgs, SlickEventHandler, SlickGrid, SlickNamespace, } from '../interfaces/index'; import { BindingEventService } from '../services/bindingEvent.service'; import { ExtensionUtility } from '../extensions/extensionUtility'; -import { findWidthOrDefault, getHtmlElementOffset, windowScrollPosition } from '../services/domUtilities'; +import { getHtmlElementOffset, windowScrollPosition } from '../services/domUtilities'; import { PubSubService } from '../services/pubSub.service'; import { SharedService } from '../services/shared.service'; import { hasData, toSentenceCase } from '../services/utilities'; @@ -22,17 +22,24 @@ import { hasData, toSentenceCase } from '../services/utilities'; // using external SlickGrid JS libraries declare const Slick: SlickNamespace; -export class MenuBaseClass { +export type MenuType = 'command' | 'option'; +/* eslint-disable @typescript-eslint/indent */ +export type ExtractMenuType = + T extends 'command' ? A : + T extends 'option' ? A : + A extends 'divider' ? A : never; +/* eslint-enable @typescript-eslint/indent */ + +export class MenuBaseClass { + protected _addonOptions: M = {} as unknown as M; protected _bindEventService: BindingEventService; - protected _addonOptions: T = {} as unknown as T; - protected _currentCell = -1; - protected _currentRow = -1; + protected _camelPluginName = ''; protected _commandTitleElm?: HTMLDivElement; - protected _optionTitleElm?: HTMLDivElement; protected _eventHandler!: SlickEventHandler; + protected _gridUid = ''; protected _menuElm?: HTMLDivElement | null; - protected _camelPluginName = ''; protected _menuCssPrefix = ''; + protected _optionTitleElm?: HTMLDivElement; /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ constructor( @@ -44,10 +51,10 @@ export class MenuBaseClass { this._eventHandler = new Slick.EventHandler(); } - get addonOptions(): T { - return this._addonOptions as T; + get addonOptions(): M { + return this._addonOptions as M; } - set addonOptions(newOptions: T) { + set addonOptions(newOptions: M) { this._addonOptions = newOptions; } @@ -65,7 +72,7 @@ export class MenuBaseClass { /** Getter for the grid uid */ get gridUid(): string { - return this.grid?.getUID() ?? ''; + return this._gridUid || (this.grid?.getUID() ?? ''); } get gridUidSelector(): string { return this.gridUid ? `.${this.gridUid}` : ''; @@ -85,143 +92,7 @@ export class MenuBaseClass { this.menuElement?.remove(); } - createMenu(event: DOMMouseEvent) { - this.menuElement?.remove(); - this._menuElm = undefined; - const cell = this.grid.getCellFromEvent(event); - - if (cell) { - this._currentCell = cell.cell ?? 0; - this._currentRow = cell.row ?? 0; - const columnDef = this.grid.getColumns()[this._currentCell]; - const dataContext = this.grid.getDataItem(this._currentRow); - - - const commandItems = this._addonOptions?.commandItems || []; - const optionItems = this._addonOptions?.optionItems || []; - - // make sure there's at least something to show before creating the Menu - if (this._camelPluginName === 'contextMenu') { - const isColumnOptionAllowed = this.checkIsColumnAllowed((this._addonOptions as ContextMenu)?.optionShownOverColumnIds ?? [], columnDef.id); - const isColumnCommandAllowed = this.checkIsColumnAllowed((this._addonOptions as ContextMenu)?.commandShownOverColumnIds ?? [], columnDef.id); - if (!columnDef || ((!isColumnCommandAllowed || !commandItems.length) && (!isColumnOptionAllowed || !optionItems.length))) { - this.hideMenu(); - return; - } - } else { - if (!columnDef || !columnDef.cellMenu || (!commandItems.length && !optionItems.length)) { - return; - } - } - - // Let the user modify the menu or cancel altogether, - // or provide alternative menu implementation. - const callbackArgs = { - cell: this._currentCell, - row: this._currentRow, - grid: this.grid, - // menu: this._pluginOptions, - } as MenuFromCellCallbackArgs; - - // delete any prior Menu - this.closeMenu(event, callbackArgs); - - // execute optional callback method defined by the user, if it returns false then we won't go further and not open the Menu - if (typeof event.stopPropagation === 'function') { - this.pubSubService.publish(`${this._camelPluginName}:onBeforeMenuShow`, callbackArgs); - if (typeof this.addonOptions?.onBeforeMenuShow === 'function' && this.addonOptions?.onBeforeMenuShow(event, callbackArgs) === false) { - return; - } - } - - const maxHeight = isNaN(this.addonOptions.maxHeight as any) ? this.addonOptions.maxHeight : `${this.addonOptions.maxHeight ?? 0}px`; - - // create a new Menu - this._menuElm = document.createElement('div'); - this._menuElm.classList.add(this._menuCssPrefix); - this._menuElm.classList.add(this.gridUid); - this._menuElm.style.maxHeight = maxHeight as string; - this._menuElm.style.width = findWidthOrDefault(this.addonOptions?.width); - this._menuElm.style.top = `${event.pageY + 5}px`; - this._menuElm.style.left = `${event.pageX}px`; - this._menuElm.style.display = 'none'; - - const closeButtonElm = document.createElement('button'); - closeButtonElm.className = 'close'; - closeButtonElm.type = 'button'; - closeButtonElm.dataset.dismiss = this._menuCssPrefix; - closeButtonElm.setAttribute('aria-label', 'Close'); - - const closeSpanElm = document.createElement('span'); - closeSpanElm.className = 'close'; - closeSpanElm.innerHTML = '×'; - closeSpanElm.setAttribute('aria-hidden', 'true'); - closeButtonElm.appendChild(closeSpanElm); - - // -- Option List section - if (!this.addonOptions.hideOptionSection && optionItems.length > 0) { - const optionMenuElm = document.createElement('div'); - optionMenuElm.className = `${this._menuCssPrefix}-option-list`; - if (!this.addonOptions.hideCloseButton) { - this._bindEventService.bind(closeButtonElm, 'click', ((e: DOMMouseEvent) => this.handleCloseButtonClicked(e)) as EventListener); - this._menuElm.appendChild(closeButtonElm); - } - this._menuElm.appendChild(optionMenuElm); - this.populateCommandOrOptionItems( - 'option', - this.addonOptions, - optionMenuElm, - optionItems, - { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this.grid } - ); - } - - // -- Command List section - if (!this.addonOptions.hideCommandSection && commandItems.length > 0) { - const commandMenuElm = document.createElement('div'); - commandMenuElm.className = `${this._menuCssPrefix}-command-list`; - if (!this.addonOptions.hideCloseButton && (optionItems.length === 0 || this.addonOptions.hideOptionSection)) { - this._bindEventService.bind(closeButtonElm, 'click', ((e: DOMMouseEvent) => this.handleCloseButtonClicked(e)) as EventListener); - this._menuElm.appendChild(closeButtonElm); - } - this._menuElm.appendChild(commandMenuElm); - this.populateCommandOrOptionItems( - 'command', - this.addonOptions, - commandMenuElm, - commandItems, - { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this.grid } - ); - } - - this._menuElm.style.display = 'block'; - document.body.appendChild(this._menuElm); - - // execute optional callback method defined by the user - this.pubSubService.publish(`${this._camelPluginName}:onAfterMenuShow`, callbackArgs); - if (typeof this.addonOptions?.onAfterMenuShow === 'function' && this.addonOptions?.onAfterMenuShow(event, callbackArgs) === false) { - return; - } - } - return this._menuElm; - } - - closeMenu(e: DOMMouseEvent, args: MenuFromCellCallbackArgs) { - if (this.menuElement) { - if (typeof this.addonOptions?.onBeforeMenuClose === 'function' && this.addonOptions?.onBeforeMenuClose(e, args) === false) { - return; - } - this.hideMenu(); - } - } - - /** Hide the Menu */ - hideMenu() { - this.menuElement?.remove(); - this._menuElm = null; - } - - setOptions(newOptions: T) { + setOptions(newOptions: M) { this._addonOptions = { ...this._addonOptions, ...newOptions }; } @@ -252,77 +123,22 @@ export class MenuBaseClass { return availableSpace; } - protected checkIsColumnAllowed(columnIds: Array, columnId: number | string): boolean { - if (columnIds?.length > 0) { - return columnIds.findIndex(colId => colId === columnId) >= 0; - } - return true; - } - - /** Mouse down handler when clicking anywhere in the DOM body */ - protected handleBodyMouseDown(e: DOMMouseEvent) { - if ((this.menuElement !== e.target && !this.menuElement?.contains(e.target)) || e.target.className === 'close') { - this.closeMenu(e, { cell: this._currentCell, row: this._currentRow, grid: this.grid }); - } - } - - protected handleCloseButtonClicked(e: DOMMouseEvent) { - if (!e.defaultPrevented) { - this.closeMenu(e, { cell: 0, row: 0, grid: this.grid, }); - } - } - - protected handleMenuItemCommandOrOptionClick(event: DOMMouseEvent, type: 'command' | 'option', item: M, row: number, cell: number) { - if ((item as never)?.[type] !== undefined && !item.disabled && !item.divider) { - if (type === 'option' && !this.grid.getEditorLock().commitCurrentEdit()) { - return; - } - - const columnDef = this.grid.getColumns()[cell]; - const dataContext = this.grid.getDataItem(row); - - // user could execute a callback through 2 ways - // via the onOptionSelected event and/or an action callback - const callbackArgs = { - cell, - row, - grid: this.grid, - [type]: (item as never)[type], - item, - column: columnDef, - dataContext, - } as C; - - // execute Menu callback with command, - // we'll also execute optional user defined onOptionSelected callback when provided - const eventType = type === 'command' ? 'onCommand' : 'onOptionSelected'; - const eventName = `${this._camelPluginName}:${eventType}`; - this.pubSubService.publish(eventName, callbackArgs); - if (typeof (this._addonOptions as never)?.[eventType] === 'function') { - (this._addonOptions as any)[eventType](event, callbackArgs); - } - - // execute action callback when defined - if (typeof item.action === 'function') { - (item as any).action.call(this, event, callbackArgs); - } - - // does the user want to leave open the Cell Menu after executing a command? - if (!event.defaultPrevented) { - this.closeMenu(event, { cell, row, grid: this.grid }); - } - } - } - /** Construct the Command/Options Items section. */ - protected populateCommandOrOptionItems(itemType: 'command' | 'option', menu: T, commandOrOptionMenuElm: HTMLElement, commandOrOptionItems: Array, args: Partial) { - if (args && commandOrOptionItems && menu) { + protected populateCommandOrOptionItems( + itemType: MenuType, + menuOptions: M, + commandOrOptionMenuElm: HTMLElement, + commandOrOptionItems: Array>, + args: any, + itemClickCallback: (event: DOMMouseEvent, type: MenuType, item: ExtractMenuType, columnDef?: Column) => void + ) { + if (args && commandOrOptionItems && menuOptions) { // user could pass a title on top of the Commands/Options section const titleProp = itemType === 'command' ? 'commandTitle' : 'optionTitle'; - if (menu?.[titleProp]) { + if ((menuOptions as CellMenu | ContextMenu)?.[titleProp]) { this[`_${itemType}TitleElm`] = document.createElement('div'); this[`_${itemType}TitleElm`]!.className = 'title'; - this[`_${itemType}TitleElm`]!.textContent = (menu as never)[titleProp]; + this[`_${itemType}TitleElm`]!.textContent = (menuOptions as never)[titleProp]; commandOrOptionMenuElm.appendChild(this[`_${itemType}TitleElm`]!); } @@ -346,37 +162,37 @@ export class MenuBaseClass { item.disabled = isItemUsable ? false : true; } - const divOptionElm = document.createElement('div'); - divOptionElm.className = `${this._menuCssPrefix}-item`; + const commandLiElm = document.createElement('li'); + commandLiElm.className = `${this._menuCssPrefix}-item`; if (typeof item === 'object' && hasData((item as never)[itemType])) { - divOptionElm.dataset[itemType] = (item as never)?.[itemType]; + commandLiElm.dataset[itemType] = (item as never)?.[itemType]; } - commandOrOptionMenuElm.appendChild(divOptionElm); + commandOrOptionMenuElm.appendChild(commandLiElm); if ((typeof item === 'object' && item.divider) || item === 'divider') { - divOptionElm.classList.add(`${this._menuCssPrefix}-item-divider`); + commandLiElm.classList.add(`${this._menuCssPrefix}-item-divider`); continue; } if (item.disabled) { - divOptionElm.classList.add(`${this._menuCssPrefix}-item-disabled`); + commandLiElm.classList.add(`${this._menuCssPrefix}-item-disabled`); } if (item.hidden) { - divOptionElm.classList.add(`${this._menuCssPrefix}-item-hidden`); + commandLiElm.classList.add(`${this._menuCssPrefix}-item-hidden`); } if (item.cssClass) { - divOptionElm.classList.add(...item.cssClass.split(' ')); + commandLiElm.classList.add(...item.cssClass.split(' ')); } if (item.tooltip) { - divOptionElm.title = item.tooltip; + commandLiElm.title = item.tooltip; } const iconElm = document.createElement('div'); iconElm.className = `${this._menuCssPrefix}-icon`; - divOptionElm.appendChild(iconElm); + commandLiElm.appendChild(iconElm); if (item.iconCssClass) { iconElm.classList.add(...item.iconCssClass.split(' ')); @@ -390,81 +206,15 @@ export class MenuBaseClass { const textElm = document.createElement('span'); textElm.className = `${this._menuCssPrefix}-content`; textElm.textContent = typeof item === 'object' && item.title || ''; - divOptionElm.appendChild(textElm); + commandLiElm.appendChild(textElm); if (item.textCssClass) { textElm.classList.add(...item.textCssClass.split(' ')); } // execute command on menu item clicked - this._bindEventService.bind(divOptionElm, 'click', ((e: DOMMouseEvent) => this.handleMenuItemCommandOrOptionClick(e, itemType, item, this._currentRow, this._currentCell)) as EventListener); - } - } - } - - protected repositionMenu(event: DOMMouseEvent) { - if (this._menuElm && event.target) { - // move to 0,0 before calulating height/width since it could be cropped values - // when element is outside browser viewport - this._menuElm.style.top = `0px`; - this._menuElm.style.left = `0px`; - - const parentElm = event.target.closest('.slick-cell') as HTMLDivElement; - let menuOffsetLeft = (parentElm && this._camelPluginName === 'cellMenu') ? getHtmlElementOffset(parentElm)?.left ?? 0 : event.pageX; - let menuOffsetTop = (parentElm && this._camelPluginName === 'cellMenu') ? getHtmlElementOffset(parentElm)?.top ?? 0 : event.pageY; - const parentCellWidth = parentElm.offsetWidth || 0; - const menuHeight = this._menuElm?.offsetHeight || 0; - const menuWidth = this._menuElm?.offsetWidth || this._addonOptions.width || 0; - const rowHeight = this.gridOptions.rowHeight || 0; - const dropOffset = +(this._addonOptions.autoAdjustDropOffset || 0); - const sideOffset = +(this._addonOptions.autoAlignSideOffset || 0); - - // if autoAdjustDrop is enable, we first need to see what position the drop will be located (defaults to bottom) - // without necessary toggling it's position just yet, we just want to know the future position for calculation - if (this._addonOptions.autoAdjustDrop || this._addonOptions.alignDropDirection) { - // since we reposition menu below slick cell, we need to take it in consideration and do our calculation from that element - const spaceBottom = this.calculateAvailableSpaceBottom(parentElm); - const spaceTop = this.calculateAvailableSpaceTop(parentElm); - const spaceBottomRemaining = spaceBottom + dropOffset - rowHeight; - const spaceTopRemaining = spaceTop - dropOffset + rowHeight; - const dropPosition = ((spaceBottomRemaining < menuHeight) && (spaceTopRemaining > spaceBottomRemaining)) ? 'top' : 'bottom'; - if (dropPosition === 'top' || this._addonOptions.alignDropDirection === 'top') { - this._menuElm.classList.remove('dropdown'); - this._menuElm.classList.add('dropup'); - menuOffsetTop = menuOffsetTop - menuHeight - dropOffset; - } else { - this._menuElm.classList.remove('dropup'); - this._menuElm.classList.add('dropdown'); - menuOffsetTop = menuOffsetTop + dropOffset; - if (this._camelPluginName === 'cellMenu') { - menuOffsetTop += rowHeight; - } - } + this._bindEventService.bind(commandLiElm, 'click', ((e: DOMMouseEvent) => + itemClickCallback.call(this, e, itemType, item, args?.column)) as EventListener); } - - // when auto-align is set, it will calculate whether it has enough space in the viewport to show the drop menu on the right (default) - // if there isn't enough space on the right, it will automatically align the drop menu to the left (defaults to the right) - // to simulate an align left, we actually need to know the width of the drop menu - if (this._addonOptions.autoAlignSide || this._addonOptions.alignDropSide === 'left') { - const gridPos = this.grid.getGridPosition(); - const dropSide = ((menuOffsetLeft + (+menuWidth)) >= gridPos.width) ? 'left' : 'right'; - if (dropSide === 'left' || this._addonOptions.alignDropSide === 'left') { - this._menuElm.classList.remove('dropright'); - this._menuElm.classList.add('dropleft'); - if (this._camelPluginName === 'cellMenu') { - menuOffsetLeft = (menuOffsetLeft - ((+menuWidth) - parentCellWidth) - sideOffset); - } else { - menuOffsetLeft = menuOffsetLeft - (+menuWidth) - sideOffset; - } - } else { - this._menuElm.classList.remove('dropleft'); - this._menuElm.classList.add('dropright'); - menuOffsetLeft = menuOffsetLeft + sideOffset; - } - } - - // ready to reposition the menu - this._menuElm.style.top = `${menuOffsetTop}px`; - this._menuElm.style.left = `${menuOffsetLeft}px`; } } } \ No newline at end of file diff --git a/packages/common/src/plugins/menuFromCellBaseClass.ts b/packages/common/src/plugins/menuFromCellBaseClass.ts new file mode 100644 index 000000000..3212b21e0 --- /dev/null +++ b/packages/common/src/plugins/menuFromCellBaseClass.ts @@ -0,0 +1,305 @@ +import { + CellMenu, + ContextMenu, + DOMMouseEvent, + MenuCallbackArgs, + MenuCommandItem, + MenuCommandItemCallbackArgs, + MenuFromCellCallbackArgs, + MenuOptionItem, + MenuOptionItemCallbackArgs, +} from '../interfaces/index'; +import { ExtensionUtility } from '../extensions/extensionUtility'; +import { findWidthOrDefault, getHtmlElementOffset, } from '../services/domUtilities'; +import { PubSubService } from '../services/pubSub.service'; +import { SharedService } from '../services/shared.service'; +import { ExtractMenuType, MenuBaseClass, MenuType } from './menuBaseClass'; + +export class MenuFromCellBaseClass extends MenuBaseClass { + protected _currentCell = -1; + protected _currentRow = -1; + + /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ + constructor( + protected readonly extensionUtility: ExtensionUtility, + protected readonly pubSubService: PubSubService, + protected readonly sharedService: SharedService, + ) { + super(extensionUtility, pubSubService, sharedService); + } + + createMenu(event: DOMMouseEvent) { + this.menuElement?.remove(); + this._menuElm = undefined; + const cell = this.grid.getCellFromEvent(event); + + if (cell) { + this._currentCell = cell.cell ?? 0; + this._currentRow = cell.row ?? 0; + const columnDef = this.grid.getColumns()[this._currentCell]; + const dataContext = this.grid.getDataItem(this._currentRow); + + + const commandItems = this._addonOptions?.commandItems || []; + const optionItems = this._addonOptions?.optionItems || []; + + // make sure there's at least something to show before creating the Menu + if (this._camelPluginName === 'contextMenu') { + const isColumnOptionAllowed = this.checkIsColumnAllowed((this._addonOptions as ContextMenu)?.optionShownOverColumnIds ?? [], columnDef.id); + const isColumnCommandAllowed = this.checkIsColumnAllowed((this._addonOptions as ContextMenu)?.commandShownOverColumnIds ?? [], columnDef.id); + if (!columnDef || ((!isColumnCommandAllowed || !commandItems.length) && (!isColumnOptionAllowed || !optionItems.length))) { + this.hideMenu(); + return; + } + } else { + if (!columnDef || !columnDef.cellMenu || (!commandItems.length && !optionItems.length)) { + return; + } + } + + // Let the user modify the menu or cancel altogether, + // or provide alternative menu implementation. + const callbackArgs = { + cell: this._currentCell, + row: this._currentRow, + grid: this.grid, + // menu: this._pluginOptions, + } as MenuFromCellCallbackArgs; + + // delete any prior Menu + this.closeMenu(event, callbackArgs); + + // execute optional callback method defined by the user, if it returns false then we won't go further and not open the Menu + if (typeof event.stopPropagation === 'function') { + this.pubSubService.publish(`${this._camelPluginName}:onBeforeMenuShow`, callbackArgs); + if (typeof this.addonOptions?.onBeforeMenuShow === 'function' && (this.addonOptions as CellMenu | ContextMenu).onBeforeMenuShow!(event, callbackArgs) === false) { + return; + } + } + + const maxHeight = isNaN(this.addonOptions.maxHeight as any) ? this.addonOptions.maxHeight : `${this.addonOptions.maxHeight ?? 0}px`; + + // create a new Menu + this._menuElm = document.createElement('div'); + this._menuElm.classList.add(this._menuCssPrefix); + this._menuElm.classList.add(this.gridUid); + if (maxHeight) { + this._menuElm.style.maxHeight = maxHeight as string; + } + this._menuElm.style.width = findWidthOrDefault(this.addonOptions?.width); + this._menuElm.style.top = `${event.pageY + 5}px`; + this._menuElm.style.left = `${event.pageX}px`; + this._menuElm.style.display = 'none'; + + const closeButtonElm = document.createElement('button'); + closeButtonElm.className = 'close'; + closeButtonElm.type = 'button'; + closeButtonElm.dataset.dismiss = this._menuCssPrefix; + closeButtonElm.setAttribute('aria-label', 'Close'); + + const closeSpanElm = document.createElement('span'); + closeSpanElm.className = 'close'; + closeSpanElm.innerHTML = '×'; + closeSpanElm.setAttribute('aria-hidden', 'true'); + closeButtonElm.appendChild(closeSpanElm); + + // -- Option List section + if (!(this.addonOptions as CellMenu | ContextMenu).hideOptionSection && optionItems.length > 0) { + const optionMenuElm = document.createElement('div'); + optionMenuElm.className = `${this._menuCssPrefix}-option-list`; + if (!this.addonOptions.hideCloseButton) { + this._bindEventService.bind(closeButtonElm, 'click', ((e: DOMMouseEvent) => this.handleCloseButtonClicked(e)) as EventListener); + this._menuElm.appendChild(closeButtonElm); + } + this._menuElm.appendChild(optionMenuElm); + this.populateCommandOrOptionItems( + 'option', + this.addonOptions, + optionMenuElm, + optionItems, + { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this.grid } as MenuCallbackArgs, + this.handleMenuItemCommandClick, + ); + } + + // -- Command List section + if (!(this.addonOptions as CellMenu | ContextMenu).hideCommandSection && commandItems.length > 0) { + const commandMenuElm = document.createElement('div'); + commandMenuElm.className = `${this._menuCssPrefix}-command-list`; + if (!this.addonOptions.hideCloseButton && (optionItems.length === 0 || (this.addonOptions as CellMenu | ContextMenu).hideOptionSection)) { + this._bindEventService.bind(closeButtonElm, 'click', ((e: DOMMouseEvent) => this.handleCloseButtonClicked(e)) as EventListener); + this._menuElm.appendChild(closeButtonElm); + } + this._menuElm.appendChild(commandMenuElm); + this.populateCommandOrOptionItems( + 'command', + this.addonOptions, + commandMenuElm, + commandItems, + { cell: this._currentCell, row: this._currentRow, column: columnDef, dataContext, grid: this.grid } as MenuCallbackArgs, + this.handleMenuItemCommandClick, + ); + } + + this._menuElm.style.display = 'block'; + document.body.appendChild(this._menuElm); + + // execute optional callback method defined by the user + this.pubSubService.publish(`${this._camelPluginName}:onAfterMenuShow`, callbackArgs); + if (typeof this.addonOptions?.onAfterMenuShow === 'function' && (this.addonOptions as CellMenu | ContextMenu).onAfterMenuShow!(event, callbackArgs) === false) { + return; + } + } + return this._menuElm; + } + + closeMenu(e: DOMMouseEvent, args: MenuFromCellCallbackArgs) { + if (this.menuElement) { + if (typeof this.addonOptions?.onBeforeMenuClose === 'function' && (this.addonOptions as CellMenu | ContextMenu).onBeforeMenuClose!(e, args) === false) { + return; + } + this.hideMenu(); + } + } + + /** Hide the Menu */ + hideMenu() { + this.menuElement?.remove(); + this._menuElm = null; + } + + // -- + // protected functions + // ------------------ + + protected checkIsColumnAllowed(columnIds: Array, columnId: number | string): boolean { + if (columnIds?.length > 0) { + return columnIds.findIndex(colId => colId === columnId) >= 0; + } + return true; + } + + /** Mouse down handler when clicking anywhere in the DOM body */ + protected handleBodyMouseDown(e: DOMMouseEvent) { + if ((this.menuElement !== e.target && !this.menuElement?.contains(e.target)) || e.target.className === 'close') { + this.closeMenu(e, { cell: this._currentCell, row: this._currentRow, grid: this.grid }); + } + } + + protected handleCloseButtonClicked(e: DOMMouseEvent) { + if (!e.defaultPrevented) { + this.closeMenu(e, { cell: 0, row: 0, grid: this.grid, }); + } + } + + protected handleMenuItemCommandClick(event: DOMMouseEvent, type: MenuType, item: ExtractMenuType) { + if ((item as never)?.[type] !== undefined && item !== 'divider' && !item.disabled && !item.divider && this._currentCell !== undefined && this._currentRow !== undefined) { + if (type === 'option' && !this.grid.getEditorLock().commitCurrentEdit()) { + return; + } + + const cell = this._currentCell; + const row = this._currentRow; + const columnDef = this.grid.getColumns()[this._currentCell]; + const dataContext = this.grid.getDataItem(this._currentRow); + + // user could execute a callback through 2 ways + // via the onOptionSelected event and/or an action callback + const callbackArgs = { + cell: this._currentCell, + row: this._currentRow, + grid: this.grid, + [type]: (item as never)[type], + item, + column: columnDef, + dataContext, + } as ExtractMenuType; + + // execute Menu callback with command, + // we'll also execute optional user defined onOptionSelected callback when provided + const eventType = type === 'command' ? 'onCommand' : 'onOptionSelected'; + const eventName = `${this._camelPluginName}:${eventType}`; + this.pubSubService.publish(eventName, callbackArgs); + if (typeof (this._addonOptions as never)?.[eventType] === 'function') { + (this._addonOptions as any)[eventType](event, callbackArgs); + } + + // execute action callback when defined + if (typeof item.action === 'function') { + (item as any).action.call(this, event, callbackArgs); + } + + // does the user want to leave open the Cell Menu after executing a command? + if (!event.defaultPrevented) { + this.closeMenu(event, { cell, row, grid: this.grid }); + } + } + } + + protected repositionMenu(event: DOMMouseEvent) { + if (this._menuElm && event.target) { + // move to 0,0 before calulating height/width since it could be cropped values + // when element is outside browser viewport + this._menuElm.style.top = `0px`; + this._menuElm.style.left = `0px`; + + const parentElm = event.target.closest('.slick-cell') as HTMLDivElement; + let menuOffsetLeft = (parentElm && this._camelPluginName === 'cellMenu') ? getHtmlElementOffset(parentElm)?.left ?? 0 : event.pageX; + let menuOffsetTop = (parentElm && this._camelPluginName === 'cellMenu') ? getHtmlElementOffset(parentElm)?.top ?? 0 : event.pageY; + const parentCellWidth = parentElm.offsetWidth || 0; + const menuHeight = this._menuElm?.offsetHeight || 0; + const menuWidth = this._menuElm?.offsetWidth || this._addonOptions.width || 0; + const rowHeight = this.gridOptions.rowHeight || 0; + const dropOffset = +((this._addonOptions as CellMenu | ContextMenu).autoAdjustDropOffset || 0); + const sideOffset = +((this._addonOptions as CellMenu | ContextMenu).autoAlignSideOffset || 0); + + // if autoAdjustDrop is enable, we first need to see what position the drop will be located (defaults to bottom) + // without necessary toggling it's position just yet, we just want to know the future position for calculation + if ((this._addonOptions as CellMenu | ContextMenu).autoAdjustDrop || (this._addonOptions as CellMenu | ContextMenu).alignDropDirection) { + // since we reposition menu below slick cell, we need to take it in consideration and do our calculation from that element + const spaceBottom = this.calculateAvailableSpaceBottom(parentElm); + const spaceTop = this.calculateAvailableSpaceTop(parentElm); + const spaceBottomRemaining = spaceBottom + dropOffset - rowHeight; + const spaceTopRemaining = spaceTop - dropOffset + rowHeight; + const dropPosition = ((spaceBottomRemaining < menuHeight) && (spaceTopRemaining > spaceBottomRemaining)) ? 'top' : 'bottom'; + if (dropPosition === 'top' || (this._addonOptions as CellMenu | ContextMenu).alignDropDirection === 'top') { + this._menuElm.classList.remove('dropdown'); + this._menuElm.classList.add('dropup'); + menuOffsetTop = menuOffsetTop - menuHeight - dropOffset; + } else { + this._menuElm.classList.remove('dropup'); + this._menuElm.classList.add('dropdown'); + menuOffsetTop = menuOffsetTop + dropOffset; + if (this._camelPluginName === 'cellMenu') { + menuOffsetTop += rowHeight; + } + } + } + + // when auto-align is set, it will calculate whether it has enough space in the viewport to show the drop menu on the right (default) + // if there isn't enough space on the right, it will automatically align the drop menu to the left (defaults to the right) + // to simulate an align left, we actually need to know the width of the drop menu + if ((this._addonOptions as CellMenu | ContextMenu).autoAlignSide || this._addonOptions.alignDropSide === 'left') { + const gridPos = this.grid.getGridPosition(); + const dropSide = ((menuOffsetLeft + (+menuWidth)) >= gridPos.width) ? 'left' : 'right'; + if (dropSide === 'left' || this._addonOptions.alignDropSide === 'left') { + this._menuElm.classList.remove('dropright'); + this._menuElm.classList.add('dropleft'); + if (this._camelPluginName === 'cellMenu') { + menuOffsetLeft = (menuOffsetLeft - ((+menuWidth) - parentCellWidth) - sideOffset); + } else { + menuOffsetLeft = menuOffsetLeft - (+menuWidth) - sideOffset; + } + } else { + this._menuElm.classList.remove('dropleft'); + this._menuElm.classList.add('dropright'); + menuOffsetLeft = menuOffsetLeft + sideOffset; + } + } + + // ready to reposition the menu + this._menuElm.style.top = `${menuOffsetTop}px`; + this._menuElm.style.left = `${menuOffsetLeft}px`; + } + } +} \ No newline at end of file diff --git a/packages/common/src/styles/slick-controls.scss b/packages/common/src/styles/slick-controls.scss index b6dfab594..96fb589b5 100644 --- a/packages/common/src/styles/slick-controls.scss +++ b/packages/common/src/styles/slick-controls.scss @@ -50,8 +50,6 @@ li { list-style: none; - margin: 0; - padding: 0; background: none; a { @@ -147,7 +145,7 @@ // Grid Menu aka Hamburger Menu // ---------------------------------------------- -.slick-gridmenu { +.slick-grid-menu { font-family: var(--slick-font-family, $font-family); background-color: var(--slick-grid-menu-background-color, $grid-menu-background-color); border: var(--slick-grid-menu-border, $grid-menu-border); @@ -195,8 +193,6 @@ li { list-style: none; - margin: 0; - padding: 0; background: none; a { display: block; @@ -218,7 +214,7 @@ } } -.slick-gridmenu-button { +.slick-grid-menu-button { position: absolute; cursor: pointer; right: 0; @@ -231,12 +227,12 @@ z-index: 2; } -.slick-gridmenu-custom { +.slick-grid-menu-command-list { margin-bottom: 10px; } /* Menu items */ -.slick-gridmenu-item { +.slick-grid-menu-item { cursor: pointer; display: block; border: var(--slick-grid-menu-item-border, $grid-menu-item-border); @@ -249,7 +245,7 @@ background-color: var(--slick-grid-menu-item-hover-color, $grid-menu-item-hover-color); } - &.slick-gridmenu-item-divider { + &.slick-grid-menu-item-divider { cursor: default; border: none; overflow: hidden; @@ -267,24 +263,24 @@ } } } -.slick-gridmenu-item-divider.slick-gridmenu-item:hover { +.slick-grid-menu-item-divider.slick-grid-menu-item:hover { background-color: var(--slick-grid-menu-divider-color, $grid-menu-divider-color); } -.slick-gridmenu-item-disabled { +.slick-grid-menu-item-disabled { cursor: inherit; border-color: transparent !important; background: inherit !important; color: var(--slick-grid-menu-item-disabled-color, $grid-menu-item-disabled-color); - .slick-gridmenu-icon, .slick-gridmenu-content { + .slick-grid-menu-icon, .slick-grid-menu-content { color: var(--slick-grid-menu-item-disabled-color, $grid-menu-item-disabled-color); } } -.slick-gridmenu-item-hidden { +.slick-grid-menu-item-hidden { display: none; } -.slick-gridmenu-icon { +.slick-grid-menu-icon { display: inline-block; font-size: var(--slick-grid-menu-icon-font-size, $grid-menu-icon-font-size); line-height: var(--slick-grid-menu-icon-line-height, $grid-menu-icon-line-height); @@ -295,12 +291,12 @@ background-position: center center; } -.slick-gridmenu-content { +.slick-grid-menu-content { display: inline-block; vertical-align: middle; } -.slick-gridmenu-list { +.slick-grid-menu-list { /** make sure the hidden class exist, it was removed in BS4 */ li.hidden { display: none; diff --git a/packages/common/src/styles/slick-plugins.scss b/packages/common/src/styles/slick-plugins.scss index 609439e09..b0690a17a 100644 --- a/packages/common/src/styles/slick-plugins.scss +++ b/packages/common/src/styles/slick-plugins.scss @@ -308,7 +308,7 @@ // Header Menu Plugin - Excel-like header // ---------------------------------------------- -.slick-header-menubutton { +.slick-header-menu-button { background-position: center center; background-repeat: no-repeat; cursor: pointer; @@ -326,7 +326,7 @@ top: 0; width: var(--slick-header-menu-button-width, $header-menu-button-width); } -.slick-header-menubutton:before { +.slick-header-menu-button:before { display: inline-block; content: var(--slick-header-menu-button-icon, $header-menu-button-icon); font-family: var(--slick-icon-font-family, $icon-font-family); @@ -337,11 +337,11 @@ .slick-header-column { // if user when to show header menu only while hovering, then the display var will be "none" else it could be "inline-block" - .slick-header-menubutton { + .slick-header-menu-button { display: var(--slick-header-menu-display, $header-menu-display); } &:hover { - .slick-header-menubutton { + .slick-header-menu-button { display: inline-block; } } @@ -383,7 +383,7 @@ } } -.slick-header-menuitem { +.slick-header-menu-item { cursor: pointer; display: block; border: var(--slick-header-menu-item-border, $header-menu-item-border); @@ -396,7 +396,7 @@ background-color: var(--slick-header-menu-item-hover-color, $header-menu-item-hover-color); } - &.slick-header-menuitem-divider { + &.slick-header-menu-item-divider { cursor: default; border: none; overflow: hidden; @@ -414,12 +414,12 @@ } } } -.slick-header-menuitem-divider.slick-header-menuitem:hover { +.slick-header-menu-item-divider.slick-header-menu-item:hover { background-color: var(--slick-header-menu-divider-color, $header-menu-divider-color); } -.slick-header-menuicon { +.slick-header-menu-icon { background-position: center center; background-repeat: no-repeat; display: inline-block; @@ -441,22 +441,22 @@ } } -.slick-header-menucontent { +.slick-header-menu-content { display: inline-block; vertical-align: middle; } /* Disabled */ -.slick-header-menuitem-disabled { +.slick-header-menu-item-disabled { border-color: transparent !important; background: inherit !important; color: var(--slick-header-menu-item-disabled-color, $header-menu-item-disabled-color); cursor: inherit; - .slick-header-menuicon, .slick-header-menucontent { + .slick-header-menu-icon, .slick-header-menu-content { color: var(--slick-header-menu-item-disabled-color, $header-menu-item-disabled-color); } } -.slick-header-menuitem-hidden { +.slick-header-menu-item-hidden { display: none; } diff --git a/test/cypress/integration/example01.spec.js b/test/cypress/integration/example01.spec.js index 6b85db73e..f216328ed 100644 --- a/test/cypress/integration/example01.spec.js +++ b/test/cypress/integration/example01.spec.js @@ -46,14 +46,14 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { .find('.slick-header-column') .first() .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(4)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(4)') + .children('.slick-header-menu-content') .should('contain', 'Sort Descending') .click(); @@ -81,14 +81,14 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { .find('.slick-header-column') .first() .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(3)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(3)') + .children('.slick-header-menu-content') .should('contain', 'Sort Ascending') .click(); @@ -103,14 +103,14 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { cy.get('.grid2') .find('.slick-header-column:nth-child(2)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.grid2') .find('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(4)') + .children('.slick-header-menu-item:nth-child(4)') .click(); cy.get('.grid2') @@ -128,7 +128,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should clear sorting of grid2 using the Grid Menu "Clear all Sorting" command', () => { cy.get('.grid2') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click(); let gridUid = ''; @@ -140,8 +140,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}.dropright`) - .find('.slick-gridmenu-item:nth(1)') + cy.get(`.slick-grid-menu.${gridUid}.dropright`) + .find('.slick-grid-menu-item:nth(1)') .find('span') .contains('Clear all Sorting') .click(); @@ -209,8 +209,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}.dropleft`) - .find('.slick-gridmenu-item:nth(0)') + cy.get(`.slick-grid-menu.${gridUid}.dropleft`) + .find('.slick-grid-menu-item:nth(0)') .find('span') .contains('Clear all Filters') .click(); @@ -247,7 +247,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should open the Grid Menu on 1st Grid and expect all Columns to be checked', () => { let gridUid = ''; cy.get('.grid1') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid1 .slickgrid-container') @@ -257,8 +257,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -274,15 +274,15 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should then hide "Title" column from same 1st Grid and expect the column to be removed from 1st Grid', () => { const newColumnList = ['Duration (days)', '% Complete', 'Start', 'Finish', 'Effort Driven']; cy.get('.grid1') - .get('.slick-gridmenu:visible') - .find('.slick-gridmenu-list') + .get('.slick-grid-menu:visible') + .find('.slick-grid-menu-list') .children('li:visible:nth(0)') .children('label') .should('contain', 'Title') .click({ force: true }); cy.get('.grid1') - .get('.slick-gridmenu:visible') + .get('.slick-grid-menu:visible') .find('span.close') .click({ force: true }); @@ -295,7 +295,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should open the Grid Menu off 2nd Grid and expect all Columns to still be all checked', () => { let gridUid = ''; cy.get('.grid2') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid2 .slickgrid-container') @@ -305,8 +305,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -322,15 +322,15 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should then hide "% Complete" column from this same 2nd Grid and expect the column to be removed from 2nd Grid', () => { const newColumnList = ['Title', 'Duration (days)', 'Start', 'Finish', 'Effort Driven']; cy.get('.grid2') - .get('.slick-gridmenu:visible') - .find('.slick-gridmenu-list') + .get('.slick-grid-menu:visible') + .find('.slick-grid-menu-list') .children('li:visible:nth(2)') .children('label') .should('contain', '% Complete') .click({ force: true }); cy.get('.grid2') - .get('.slick-gridmenu:visible') + .get('.slick-grid-menu:visible') .find('span.close') .click({ force: true }); @@ -342,10 +342,10 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should go back to 1st Grid and open its Grid Menu and we expect this grid to stil have the "Title" column be hidden (unchecked)', () => { cy.get('.grid1') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); - cy.get('.slick-gridmenu-list') + cy.get('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -364,15 +364,15 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should hide "Start" column from 1st Grid and expect to have 2 hidden columns (Title, Start)', () => { const newColumnList = ['Duration (days)', '% Complete', 'Finish', 'Effort Driven']; cy.get('.grid1') - .get('.slick-gridmenu:visible') - .find('.slick-gridmenu-list') + .get('.slick-grid-menu:visible') + .find('.slick-grid-menu-list') .children('li:visible:nth(3)') .children('label') .should('contain', 'Start') .click({ force: true }); cy.get('.grid1') - .get('.slick-gridmenu:visible') + .get('.slick-grid-menu:visible') .find('span.close') .click({ force: true }); @@ -429,7 +429,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should open the Grid Menu on 2nd Grid and expect all Columns to be checked', () => { let gridUid = ''; cy.get('.grid2') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid2 .slickgrid-container') @@ -439,8 +439,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -465,7 +465,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { it('should open the Grid Menu on 1st Grid and also expect to only have 4 columns checked (visible)', () => { let gridUid = ''; cy.get('.grid1') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid1 .slickgrid-container') @@ -475,8 +475,8 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -493,7 +493,7 @@ describe('Example 01 - Basic Grids', { retries: 1 }, () => { }); cy.get('.grid1') - .get('.slick-gridmenu:visible') + .get('.slick-grid-menu:visible') .find('span.close') .click({ force: true }); }); diff --git a/test/cypress/integration/example02.spec.js b/test/cypress/integration/example02.spec.js index 774ad7294..06155e29f 100644 --- a/test/cypress/integration/example02.spec.js +++ b/test/cypress/integration/example02.spec.js @@ -89,7 +89,7 @@ describe('Example 02 - Grouping & Aggregators', { retries: 1 }, () => { it('should clear filters of grid2 using the Grid Menu "Clear all Filters" command', () => { cy.get('.grid2') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click({ force: true }); }); diff --git a/test/cypress/integration/example04.spec.js b/test/cypress/integration/example04.spec.js index 567d634b4..a27965ceb 100644 --- a/test/cypress/integration/example04.spec.js +++ b/test/cypress/integration/example04.spec.js @@ -41,12 +41,12 @@ describe('Example 04 - Frozen Grid', { retries: 1 }, () => { const newColumnList = ['', '% Complete', 'Start', 'Finish', 'Completed', 'Cost | Duration', 'City of Origin', 'Action']; cy.get('.grid4') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid4') - .get('.slick-gridmenu:visible') - .find('.slick-gridmenu-list') + .get('.slick-grid-menu:visible') + .find('.slick-grid-menu-list') .children('li:visible:nth(0)') .children('label') .should('contain', 'Title') @@ -68,15 +68,15 @@ describe('Example 04 - Frozen Grid', { retries: 1 }, () => { it('should show again "Title" column from Grid Menu and expect last frozen column to still be "% Complete"', () => { cy.get('.grid4') - .get('.slick-gridmenu:visible') - .find('.slick-gridmenu-list') + .get('.slick-grid-menu:visible') + .find('.slick-grid-menu-list') .children('li:visible:nth(0)') .children('label') .should('contain', 'Title') .click({ force: true }); cy.get('.grid4') - .get('.slick-gridmenu:visible') + .get('.slick-grid-menu:visible') .find('span.close') .click({ force: true }); @@ -101,13 +101,13 @@ describe('Example 04 - Frozen Grid', { retries: 1 }, () => { cy.get('.grid4') .find('.slick-header-column:nth(1)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(9)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(9)') + .children('.slick-header-menu-content') .should('contain', 'Hide Column') .click(); @@ -204,7 +204,7 @@ describe('Example 04 - Frozen Grid', { retries: 1 }, () => { it('should click on the Grid Menu command "Unfreeze Columns/Rows" to switch to a regular grid without frozen columns/rows', () => { cy.get('.grid4') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.contains('Unfreeze Columns/Rows') diff --git a/test/cypress/integration/example05.spec.js b/test/cypress/integration/example05.spec.js index 6c2d1e509..8c3b648cf 100644 --- a/test/cypress/integration/example05.spec.js +++ b/test/cypress/integration/example05.spec.js @@ -130,7 +130,7 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' it('should open the Grid Menu "Clear all Filters" command', () => { cy.get('.grid5') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click({ force: true }); @@ -143,8 +143,8 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-item:nth(0)') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-item:nth(0)') .find('span') .contains('Clear all Filters') .click(); @@ -191,7 +191,7 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' it('should open the Grid Menu "Clear all Filters" command', () => { cy.get('.grid5') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); @@ -204,8 +204,8 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-item:nth(0)') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-item:nth(0)') .find('span') .contains('Clear all Filters') .click(); @@ -237,7 +237,7 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' it('should open the Grid Menu "Clear all Filters" command', () => { cy.get('.grid5') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); @@ -250,8 +250,8 @@ describe('Example 05 - Tree Data (from a flat dataset with parentId references)' expect(gridUid).to.not.be.null; }) .then(() => { - cy.get(`.slick-gridmenu.${gridUid}`) - .find('.slick-gridmenu-item:nth(0)') + cy.get(`.slick-grid-menu.${gridUid}`) + .find('.slick-grid-menu-item:nth(0)') .find('span') .contains('Clear all Filters') .click(); diff --git a/test/cypress/integration/example06.spec.js b/test/cypress/integration/example06.spec.js index 3c650938c..cc7d3a569 100644 --- a/test/cypress/integration/example06.spec.js +++ b/test/cypress/integration/example06.spec.js @@ -134,12 +134,12 @@ describe('Example 06 - Tree Data (from a Hierarchical Dataset)', { retries: 1 }, it('should Clear all Filters and default list', () => { cy.get('.grid6') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click({ force: true }); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item') .first() .find('span') .contains('Clear all Filters') diff --git a/test/cypress/integration/example07.spec.js b/test/cypress/integration/example07.spec.js index 3a737559c..7e06d52ea 100644 --- a/test/cypress/integration/example07.spec.js +++ b/test/cypress/integration/example07.spec.js @@ -201,7 +201,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries .children() .each(($child, index) => expect($child.text()).to.eq(updatedTitles[index])); - cy.get('.slick-header-menubutton') + cy.get('.slick-header-menu-button') .should('have.length', 9); cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'Task 0'); @@ -213,13 +213,13 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const updatedTitles = ['', '', 'Title', 'Action', 'Duration', '% Complete', 'Start', 'Finish', 'Completed', 'Prerequisites', 'Title', 'Title']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid7 .slickgrid-container') .then(() => { - cy.get(`.slick-gridmenu`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -245,14 +245,14 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.slick-header-column:nth(10)') .first() .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') // .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click(); @@ -366,12 +366,12 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const expectedFullHeaderMenuCommands = ['Clear all Filters', 'Clear all Sorting', 'Toggle Filter Row', 'Export to Excel']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click({ force: true }); - cy.get('.slick-gridmenu-custom') - .find('.slick-gridmenu-item') + cy.get('.slick-grid-menu-command-list') + .find('.slick-grid-menu-item') .each(($child, index) => { const commandTitle = $child.text(); expect(commandTitle).to.eq(expectedFullHeaderMenuCommands[index]); @@ -417,7 +417,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.grid7') .find('.slick-header-column:nth(8)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') @@ -437,12 +437,12 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const expectedFullHeaderMenuCommands = ['Clear all Filters', 'Clear all Sorting', 'Toggle Filter Row', 'Export to Excel']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get('.slick-gridmenu-custom') - .find('.slick-gridmenu-item') + cy.get('.slick-grid-menu-command-list') + .find('.slick-grid-menu-item') .each(($child, index) => { const commandTitle = $child.text(); expect(commandTitle).to.eq(expectedFullHeaderMenuCommands[index]); @@ -466,7 +466,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.grid7') .find('.slick-header-column:nth(8)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') @@ -482,12 +482,12 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const expectedFullHeaderMenuCommands = ['Clear all Filters', 'Clear all Sorting', 'Toggle Filter Row', 'Export to Excel']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get('.slick-gridmenu-custom') - .find('.slick-gridmenu-item') + cy.get('.slick-grid-menu-command-list') + .find('.slick-grid-menu-item') .each(($child, index) => { const commandTitle = $child.text(); expect(commandTitle).to.eq(expectedFullHeaderMenuCommands[index]); @@ -511,7 +511,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.grid7') .find('.slick-header-column:nth(5)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') @@ -535,7 +535,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.grid7') .find('.slick-header-column:nth(5)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') @@ -555,12 +555,12 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const expectedFullHeaderMenuCommands = ['Clear all Filters', 'Clear all Sorting', 'Toggle Filter Row', 'Export to Excel']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get('.slick-gridmenu-custom') - .find('.slick-gridmenu-item') + cy.get('.slick-grid-menu-command-list') + .find('.slick-grid-menu-item') .each(($child, index) => { const commandTitle = $child.text(); expect(commandTitle).to.eq(expectedFullHeaderMenuCommands[index]); @@ -801,7 +801,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries cy.get('.grid7') .find('.slick-header-column:nth(9)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') @@ -821,13 +821,13 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries const updatedTitles = ['', '', 'Titre', 'Action', 'Durée', '% Achevée', 'Fin', 'Terminé', 'Début', 'Prerequisites', 'Titre']; cy.get('.grid7') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.get('.grid7 .slickgrid-container') .then(() => { - cy.get(`.slick-gridmenu`) - .find('.slick-gridmenu-list') + cy.get(`.slick-grid-menu`) + .find('.slick-grid-menu-list') .children('li') .each(($child, index) => { if (index <= 5) { @@ -843,7 +843,7 @@ describe('Example 07 - Row Move & Checkbox Selector Selector Plugins', { retries }); }); - cy.get('.slick-gridmenu') + cy.get('.slick-grid-menu') .find('span.close') .click(); }); diff --git a/test/cypress/integration/example08.spec.js b/test/cypress/integration/example08.spec.js index 08835f894..d3d262799 100644 --- a/test/cypress/integration/example08.spec.js +++ b/test/cypress/integration/example08.spec.js @@ -108,7 +108,7 @@ describe('Example 08 - Column Span & Header Grouping', { retries: 1 }, () => { it('should click on the Grid Menu command "Unfreeze Columns/Rows" to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('.grid2') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.contains('Unfreeze Columns/Rows') diff --git a/test/cypress/integration/example09.spec.js b/test/cypress/integration/example09.spec.js index 1cf38f370..267ab4214 100644 --- a/test/cypress/integration/example09.spec.js +++ b/test/cypress/integration/example09.spec.js @@ -199,11 +199,11 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => { it('should Clear all Filters and expect to go back to first page', () => { cy.get('.grid9') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item') .first() .find('span') .contains('Clear all Filters') @@ -242,12 +242,12 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => { it('should Clear all Sorting', () => { cy.get('.grid9') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item:nth(1)') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item:nth(1)') .find('span') .contains('Clear all Sorting') .click(); @@ -334,12 +334,12 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => { describe('when "enableCount" is unchecked (not set)', () => { it('should Clear all Filters, set 20 items per page & uncheck "enableCount"', () => { cy.get('.grid9') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item') .first() .find('span') .contains('Clear all Filters') @@ -416,12 +416,12 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => { it('should Clear all Sorting', () => { cy.get('.grid9') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item:nth(1)') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item:nth(1)') .find('span') .contains('Clear all Sorting') .click(); @@ -692,13 +692,13 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => { cy.get('.grid9') .find('.slick-header-left .slick-header-column:nth(1)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click(); diff --git a/test/cypress/integration/example10.spec.js b/test/cypress/integration/example10.spec.js index d15289cdd..5f62cdbcd 100644 --- a/test/cypress/integration/example10.spec.js +++ b/test/cypress/integration/example10.spec.js @@ -170,13 +170,13 @@ describe('Example 10 - GraphQL Grid', { retries: 1 }, () => { cy.get('.grid10') .find('.slick-header-left .slick-header-column:nth(0)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click(); @@ -199,14 +199,14 @@ describe('Example 10 - GraphQL Grid', { retries: 1 }, () => { cy.get('.grid10') .find('.slick-header-left .slick-header-column:nth(0)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click(); @@ -229,13 +229,13 @@ describe('Example 10 - GraphQL Grid', { retries: 1 }, () => { cy.get('.grid10') .find('.slick-header-left .slick-header-column:nth(5)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click(); @@ -403,60 +403,60 @@ describe('Example 10 - GraphQL Grid', { retries: 1 }, () => { .find('.slick-header-columns.slick-header-columns-left .slick-header-column') .first() .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(3)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(3)') + .children('.slick-header-menu-content') .should('contain', 'Sort Ascending'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(4)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(4)') + .children('.slick-header-menu-content') .should('contain', 'Sort Descending'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(7)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(7)') + .children('.slick-header-menu-content') .should('contain', 'Remove Sort'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(8)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(8)') + .children('.slick-header-menu-content') .should('contain', 'Hide Column'); }); it('should open the Grid Menu and expect all commands be displayed in English', () => { cy.get('.grid10') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click'); - cy.get('.slick-gridmenu .title:nth(0)') + cy.get('.slick-grid-menu .title:nth(0)') .contains('Commands'); - cy.get('.slick-gridmenu-item:nth(0) > span') + cy.get('.slick-grid-menu-item:nth(0) > span') .contains('Clear all Filters'); - cy.get('.slick-gridmenu-item:nth(1) > span') + cy.get('.slick-grid-menu-item:nth(1) > span') .contains('Clear all Sorting'); - cy.get('.slick-gridmenu .title:nth(1)') + cy.get('.slick-grid-menu .title:nth(1)') .contains('Columns'); - cy.get('.slick-gridmenu-list li:nth(0)') + cy.get('.slick-grid-menu-list li:nth(0)') .contains('Customer Information - Name'); - cy.get('.slick-gridmenu-list li:nth(1)') + cy.get('.slick-grid-menu-list li:nth(1)') .contains('Customer Information - Gender'); - cy.get('.slick-gridmenu [data-dismiss=slick-gridmenu] > span.close') + cy.get('.slick-grid-menu [data-dismiss=slick-grid-menu] > span.close') .click({ force: true }); }); @@ -510,60 +510,60 @@ describe('Example 10 - GraphQL Grid', { retries: 1 }, () => { .find('.slick-header-columns.slick-header-columns-left .slick-header-column') .first() .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(3)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(3)') + .children('.slick-header-menu-content') .should('contain', 'Trier par ordre croissant'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(4)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(4)') + .children('.slick-header-menu-content') .should('contain', 'Trier par ordre décroissant'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Supprimer le filtre'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(7)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(7)') + .children('.slick-header-menu-content') .should('contain', 'Supprimer le tri'); cy.get('.slick-header-menu') - .children('.slick-header-menuitem:nth-child(8)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(8)') + .children('.slick-header-menu-content') .should('contain', 'Cacher la colonne'); }); it('should open the Grid Menu and expect all commands be displayed in French', () => { cy.get('.grid10') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click'); - cy.get('.slick-gridmenu .title:nth(0)') + cy.get('.slick-grid-menu .title:nth(0)') .contains('Commandes'); - cy.get('.slick-gridmenu-item:nth(0) > span') + cy.get('.slick-grid-menu-item:nth(0) > span') .contains('Supprimer tous les filtres'); - cy.get('.slick-gridmenu-item:nth(1) > span') + cy.get('.slick-grid-menu-item:nth(1) > span') .contains('Supprimer tous les tris'); - cy.get('.slick-gridmenu .title:nth(1)') + cy.get('.slick-grid-menu .title:nth(1)') .contains('Colonnes'); - cy.get('.slick-gridmenu-list li:nth(0)') + cy.get('.slick-grid-menu-list li:nth(0)') .contains('Information Client - Nom'); - cy.get('.slick-gridmenu-list li:nth(1)') + cy.get('.slick-grid-menu-list li:nth(1)') .contains('Information Client - Sexe'); - cy.get('.slick-gridmenu [data-dismiss=slick-gridmenu] > span.close') + cy.get('.slick-grid-menu [data-dismiss=slick-grid-menu] > span.close') .click({ force: true }); }); diff --git a/test/cypress/integration/example11.spec.js b/test/cypress/integration/example11.spec.js index ea74e2c0e..37f6f4196 100644 --- a/test/cypress/integration/example11.spec.js +++ b/test/cypress/integration/example11.spec.js @@ -593,14 +593,14 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { .find('.slick-header-columns') .find('.slick-header-column:nth(2)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(1)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(1)') + .children('.slick-header-menu-content') .should('contain', 'Freeze Column') .click(); }); @@ -617,11 +617,11 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').contains(/\$[0-9\.]*/); cy.get('.slick-pane-left') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('not.exist'); cy.get('.slick-pane-right') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('exist'); }); @@ -676,11 +676,11 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 9); cy.get('.slick-pane-left') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('exist'); cy.get('.slick-pane-right') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('not.exist'); }); @@ -707,11 +707,11 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').contains(/\$?[0-9\.]*/); cy.get('.slick-pane-left') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('not.exist'); cy.get('.slick-pane-right') - .find('.slick-gridmenu-button') + .find('.slick-grid-menu-button') .should('exist'); }); @@ -747,7 +747,7 @@ describe('Example 11 - Batch Editing', { retries: 1 }, () => { it('should clear pinning from Grid Menu & expect to no longer have any columns freezed', () => { cy.get('.grid11') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .click({ force: true }); cy.contains('Unfreeze Columns/Rows') diff --git a/test/cypress/integration/example14.spec.js b/test/cypress/integration/example14.spec.js index 60de6a787..857646f23 100644 --- a/test/cypress/integration/example14.spec.js +++ b/test/cypress/integration/example14.spec.js @@ -76,14 +76,14 @@ describe('Example 14 - Columns Resize by Content', { retries: 1 }, () => { cy.get('.grid14') .find('.slick-header-column:nth-child(10)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(2)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(2)') + .children('.slick-header-menu-content') .should('contain', 'Resize by Content') .click(); diff --git a/test/cypress/integration/example15.spec.js b/test/cypress/integration/example15.spec.js index d529af295..3b7d219d4 100644 --- a/test/cypress/integration/example15.spec.js +++ b/test/cypress/integration/example15.spec.js @@ -197,12 +197,12 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => { it('should Clear all Filters and expect to go back to first page', () => { cy.get('.grid15') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click({ force: true }); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item') .first() .find('span') .contains('Clear all Filters') @@ -241,12 +241,12 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => { it('should Clear all Sorting', () => { cy.get('.grid15') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item:nth(1)') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item:nth(1)') .find('span') .contains('Clear all Sorting') .click(); @@ -333,12 +333,12 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => { describe('when "enableCount" is unchecked (not set)', () => { it('should Clear all Filters, set 20 items per page & uncheck "enableCount"', () => { cy.get('.grid15') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item') .first() .find('span') .contains('Clear all Filters') @@ -415,12 +415,12 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => { it('should Clear all Sorting', () => { cy.get('.grid15') - .find('button.slick-gridmenu-button') + .find('button.slick-grid-menu-button') .trigger('click') .click(); - cy.get(`.slick-gridmenu:visible`) - .find('.slick-gridmenu-item:nth(1)') + cy.get(`.slick-grid-menu:visible`) + .find('.slick-grid-menu-item:nth(1)') .find('span') .contains('Clear all Sorting') .click(); @@ -794,14 +794,14 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => { cy.get('.grid15') .find('.slick-header-left .slick-header-column:nth(1)') .trigger('mouseover') - .children('.slick-header-menubutton') + .children('.slick-header-menu-button') .invoke('show') .click(); cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') - .children('.slick-header-menucontent') + .children('.slick-header-menu-item:nth-child(6)') + .children('.slick-header-menu-content') .should('contain', 'Remove Filter') .click();