diff --git a/eslint.config.mjs b/eslint.config.mjs index b38e25a31..d1758382b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,6 @@ import globals from 'globals'; import jest from 'eslint-plugin-jest'; import n from 'eslint-plugin-n'; import tseslint from 'typescript-eslint'; -import tsParser from '@typescript-eslint/parser'; export default tseslint.config( eslint.configs.recommended, @@ -44,7 +43,7 @@ export default tseslint.config( ...globals.es2021, ...globals.node, }, - parser: tsParser, + parser: tseslint.parser, parserOptions: { project: ['./tsconfig.base.json'] } diff --git a/packages/binding/src/bindingEvent.service.ts b/packages/binding/src/bindingEvent.service.ts index a5fa458ca..8f9f1595a 100644 --- a/packages/binding/src/bindingEvent.service.ts +++ b/packages/binding/src/bindingEvent.service.ts @@ -19,7 +19,7 @@ export class BindingEventService { /** Bind an event listener to any element */ bind( - elementOrElements: Document | Element | NodeListOf | Window, + elementOrElements: Document | Element | NodeListOf | Array | Window, eventNameOrNames: string | string[], listener: EventListenerOrEventListenerObject, listenerOptions?: boolean | AddEventListenerOptions, groupName = '' @@ -35,7 +35,7 @@ export class BindingEventService { this._boundedEvents.push({ element, eventName, listener, groupName }); } }); - } else { + } else if (elementOrElements) { // single elements to bind to for (const eventName of eventNames) { (elementOrElements as Element).addEventListener(eventName, listener, listenerOptions); diff --git a/packages/common/src/core/__tests__/slickGrid.spec.ts b/packages/common/src/core/__tests__/slickGrid.spec.ts index a10225b07..548b1c08b 100644 --- a/packages/common/src/core/__tests__/slickGrid.spec.ts +++ b/packages/common/src/core/__tests__/slickGrid.spec.ts @@ -5141,9 +5141,36 @@ describe('SlickGrid core file', () => { }); }); - describe('Header Click', () => { - // TODO: need to add another test when "columnResizeDragging" + describe('Pre-Header Click', () => { + it('should trigger onPreHeaderClick notify when not column resizing', () => { + const columns = [{ id: 'name', field: 'name', name: 'Name' }, { id: 'age', field: 'age', name: 'Age', editorClass: InputEditor }] as Column[]; + grid = new SlickGrid(container, items, columns, { ...defaultOptions, enableCellNavigation: true, editable: true, createPreHeaderPanel: true, showPreHeaderPanel: true }); + jest.spyOn(grid, 'getCellFromEvent').mockReturnValue(null); + const onPreHeaderClickSpy = jest.spyOn(grid.onPreHeaderClick, 'notify'); + const preHeaderElms = container.querySelectorAll('.slick-preheader-panel'); + const event = new CustomEvent('click'); + Object.defineProperty(event, 'target', { writable: true, value: preHeaderElms[0] }); + preHeaderElms[0].dispatchEvent(event); + expect(onPreHeaderClickSpy).toHaveBeenCalledWith({ node: preHeaderElms[0], grid }, expect.anything(), grid); + }); + }); + + describe('Pre-Header Context Menu', () => { + it('should trigger onHeaderClick notify grid context menu event is triggered', () => { + const columns = [{ id: 'name', field: 'name', name: 'Name' }, { id: 'age', field: 'age', name: 'Age', editorClass: InputEditor }] as Column[]; + grid = new SlickGrid(container, items, columns, { ...defaultOptions, enableCellNavigation: true, editable: true, createPreHeaderPanel: true, showPreHeaderPanel: true }); + const onContextSpy = jest.spyOn(grid.onPreHeaderContextMenu, 'notify'); + const preHeaderElms = container.querySelectorAll('.slick-preheader-panel'); + const event = new CustomEvent('contextmenu'); + Object.defineProperty(event, 'target', { writable: true, value: preHeaderElms[0] }); + preHeaderElms[0].dispatchEvent(event); + + expect(onContextSpy).toHaveBeenCalledWith({ node: preHeaderElms[0], grid }, expect.anything(), grid); + }); + }); + + describe('Header Click', () => { it('should trigger onHeaderClick notify when not column resizing', () => { const columns = [{ id: 'name', field: 'name', name: 'Name' }, { id: 'age', field: 'age', name: 'Age', editorClass: InputEditor }] as Column[]; grid = new SlickGrid(container, items, columns, { ...defaultOptions, enableCellNavigation: true, editable: true }); diff --git a/packages/common/src/core/slickGrid.ts b/packages/common/src/core/slickGrid.ts index 12532f3cb..9deb3589e 100644 --- a/packages/common/src/core/slickGrid.ts +++ b/packages/common/src/core/slickGrid.ts @@ -80,6 +80,8 @@ import type { OnHeaderMouseEventArgs, OnHeaderRowCellRenderedEventArgs, OnKeyDownEventArgs, + OnPreHeaderClickEventArgs, + OnPreHeaderContextMenuEventArgs, OnRenderedEventArgs, OnScrollEventArgs, OnSelectedRowsChangedEventArgs, @@ -175,6 +177,8 @@ export class SlickGrid = Column, O e onKeyDown: SlickEvent; onMouseEnter: SlickEvent; onMouseLeave: SlickEvent; + onPreHeaderClick: SlickEvent; + onPreHeaderContextMenu: SlickEvent; onRendered: SlickEvent; onScroll: SlickEvent; onSelectedRowsChanged: SlickEvent; @@ -548,6 +552,8 @@ export class SlickGrid = Column, O e this.onKeyDown = new SlickEvent('onKeyDown', externalPubSub); this.onMouseEnter = new SlickEvent('onMouseEnter', externalPubSub); this.onMouseLeave = new SlickEvent('onMouseLeave', externalPubSub); + this.onPreHeaderClick = new SlickEvent('onPreHeaderClick', externalPubSub); + this.onPreHeaderContextMenu = new SlickEvent('onPreHeaderContextMenu', externalPubSub); this.onRendered = new SlickEvent('onRendered', externalPubSub); this.onScroll = new SlickEvent('onScroll', externalPubSub); this.onSelectedRowsChanged = new SlickEvent('onSelectedRowsChanged', externalPubSub); @@ -913,6 +919,8 @@ export class SlickGrid = Column, O e if (this._options.createPreHeaderPanel) { this._bindingEventService.bind(this._preHeaderPanelScroller, 'scroll', this.handlePreHeaderPanelScroll.bind(this) as EventListener); + this._bindingEventService.bind([this._preHeaderPanelScroller, this._preHeaderPanelScrollerR], 'contextmenu', this.handlePreHeaderContextMenu.bind(this) as EventListener); + this._bindingEventService.bind([this._preHeaderPanelScroller, this._preHeaderPanelScrollerR], 'click', this.handlePreHeaderClick.bind(this) as EventListener); } this._bindingEventService.bind(this._focusSink, 'keydown', this.handleKeyDown.bind(this) as EventListener); @@ -5027,14 +5035,22 @@ export class SlickGrid = Column, O e } protected handleHeaderClick(e: MouseEvent & { target: HTMLElement; }): void { - if (this.columnResizeDragging) { - return; + if (!this.columnResizeDragging) { + const header = e.target.closest('.slick-header-column'); + const column = header && Utils.storage.get(header, 'column'); + if (column) { + this.triggerEvent(this.onHeaderClick, { column }, e); + } } + } - const header = e.target.closest('.slick-header-column'); - const column = header && Utils.storage.get(header, 'column'); - if (column) { - this.triggerEvent(this.onHeaderClick, { column }, e); + protected handlePreHeaderContextMenu(e: MouseEvent & { target: HTMLElement; }): void { + this.triggerEvent(this.onPreHeaderContextMenu, { node: e.target }, e); + } + + protected handlePreHeaderClick(e: MouseEvent & { target: HTMLElement; }): void { + if (!this.columnResizeDragging) { + this.triggerEvent(this.onPreHeaderClick, { node: e.target }, e); } } diff --git a/packages/common/src/extensions/__tests__/slickColumnPicker.spec.ts b/packages/common/src/extensions/__tests__/slickColumnPicker.spec.ts index 651270920..17c8a6272 100644 --- a/packages/common/src/extensions/__tests__/slickColumnPicker.spec.ts +++ b/packages/common/src/extensions/__tests__/slickColumnPicker.spec.ts @@ -7,6 +7,7 @@ import { ExtensionUtility } from '../extensionUtility'; import { SharedService } from '../../services/shared.service'; import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; import { BackendUtilityService } from '../../services/backendUtility.service'; +import { createDomElement } from '@slickgrid-universal/utils'; const gridUid = 'slickgrid_124343'; @@ -24,6 +25,8 @@ const gridStub = { onClick: new SlickEvent(), onColumnsReordered: new SlickEvent(), onHeaderContextMenu: new SlickEvent(), + onPreHeaderClick: new SlickEvent(), + onPreHeaderContextMenu: new SlickEvent(), } as unknown as SlickGrid; const pubSubServiceStub = { @@ -148,7 +151,7 @@ describe('ColumnPickerControl', () => { gridStub.onHeaderContextMenu.notify({ column: columnsMock[1], grid: gridStub }, eventData as any, gridStub); control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true })); - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(readjustSpy).toHaveBeenCalledWith(0, columnsMock, columnsMock); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); @@ -172,13 +175,37 @@ describe('ColumnPickerControl', () => { control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true })); const liElmList = control.menuElement!.querySelectorAll('li'); - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(readjustSpy).toHaveBeenCalledWith(0, columnsMock, columnsMock); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); expect(liElmList[2].textContent).toBe('Billing - Field 3'); }); + it('should open the column picker via "onPreHeaderContextMenu" and expect "Forcefit" to be checked when "hideForceFitButton" is false', () => { + const handlerSpy = jest.spyOn(control.eventHandler, 'subscribe'); + jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(undefined as any).mockReturnValue(1); + + gridOptionsMock.columnPicker!.hideForceFitButton = false; + gridOptionsMock.forceFitColumns = true; + control.columns = columnsMock; + control.init(); + + const groupElm = createDomElement('div', { className: 'slick-column-name' }); + gridStub.onPreHeaderContextMenu.notify({ node: groupElm, grid: gridStub }, { ...new SlickEventData(), preventDefault: jest.fn(), target: groupElm } as any, gridStub); + control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true })); + const inputForcefitElm = control.menuElement!.querySelector('#slickgrid_124343-colpicker-forcefit') as HTMLInputElement; + const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-forcefit]') as HTMLDivElement; + + expect(handlerSpy).toHaveBeenCalledTimes(4); + expect(control.menuElement?.style.display).not.toBe('none'); + expect(control.getAllColumns()).toEqual(columnsMock); + expect(control.getVisibleColumns()).toEqual(columnsMock); + expect(inputForcefitElm.checked).toBeTruthy(); + expect(inputForcefitElm.dataset.option).toBe('autoresize'); + expect(labelSyncElm.textContent).toBe('Force fit columns'); + }); + it('should open the column picker via "onHeaderContextMenu" and expect "Forcefit" to be checked when "hideForceFitButton" is false', () => { const handlerSpy = jest.spyOn(control.eventHandler, 'subscribe'); jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(undefined as any).mockReturnValue(1); @@ -193,7 +220,7 @@ describe('ColumnPickerControl', () => { const inputForcefitElm = control.menuElement!.querySelector('#slickgrid_124343-colpicker-forcefit') as HTMLInputElement; const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-forcefit]') as HTMLDivElement; - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); expect(inputForcefitElm.checked).toBeTruthy(); @@ -215,7 +242,7 @@ describe('ColumnPickerControl', () => { const inputSyncElm = control.menuElement!.querySelector('#slickgrid_124343-colpicker-syncresize') as HTMLInputElement; const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-syncresize]') as HTMLDivElement; - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); expect(inputSyncElm.checked).toBeTruthy(); @@ -244,7 +271,7 @@ describe('ColumnPickerControl', () => { visibleColumns: columnsMock, grid: gridStub, }; - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); expect(onColChangedMock).toBeCalledWith(expect.anything(), expectedCallbackArgs); @@ -268,7 +295,7 @@ describe('ColumnPickerControl', () => { const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-forcefit]') as HTMLDivElement; inputForcefitElm.dispatchEvent(new Event('click', { bubbles: true })); - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(inputForcefitElm.checked).toBeTruthy(); expect(inputForcefitElm.dataset.option).toBe('autoresize'); @@ -294,7 +321,7 @@ describe('ColumnPickerControl', () => { const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-syncresize]') as HTMLDivElement; inputSyncElm.dispatchEvent(new Event('click', { bubbles: true })); - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(inputSyncElm.checked).toBeTruthy(); expect(inputSyncElm.dataset.option).toBe('syncresize'); @@ -340,7 +367,7 @@ describe('ColumnPickerControl', () => { control.menuElement!.querySelector('input[type="checkbox"]')!.dispatchEvent(new Event('click', { bubbles: true })); const col4 = control.menuElement!.querySelector('li.hidden input[data-columnid=field4]'); - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(control.getAllColumns()).toEqual(columnsMock); expect(control.getVisibleColumns()).toEqual(columnsMock); expect(control.columns).toEqual(columnsMock); @@ -368,7 +395,7 @@ describe('ColumnPickerControl', () => { const labelForcefitElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-forcefit]') as HTMLDivElement; const labelSyncElm = control.menuElement!.querySelector('label[for=slickgrid_124343-colpicker-syncresize]') as HTMLDivElement; - expect(handlerSpy).toHaveBeenCalledTimes(3); + expect(handlerSpy).toHaveBeenCalledTimes(4); expect(labelForcefitElm.textContent).toBe('Ajustement forcé des colonnes'); expect(labelSyncElm.textContent).toBe('Redimension synchrone'); expect(utilitySpy).toHaveBeenCalled(); diff --git a/packages/common/src/extensions/slickColumnPicker.ts b/packages/common/src/extensions/slickColumnPicker.ts index 81f0b095b..23280e60c 100644 --- a/packages/common/src/extensions/slickColumnPicker.ts +++ b/packages/common/src/extensions/slickColumnPicker.ts @@ -100,6 +100,11 @@ export class SlickColumnPicker { this.addonOptions.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'columnPicker'); this.addonOptions.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'columnPicker'); + this._eventHandler.subscribe(this.grid.onPreHeaderContextMenu, (e) => { + if (['slick-column-name', 'slick-header-column'].some(className => e.target?.classList.contains(className))) { + this.handleHeaderContextMenu(e); // open picker only when preheader has column groups + } + }); this._eventHandler.subscribe(this.grid.onHeaderContextMenu, this.handleHeaderContextMenu.bind(this)); this._eventHandler.subscribe(this.grid.onColumnsReordered, updateColumnPickerOrder.bind(this)); this._eventHandler.subscribe(this.grid.onClick, this.disposeMenu.bind(this)); diff --git a/packages/common/src/interfaces/gridEvents.interface.ts b/packages/common/src/interfaces/gridEvents.interface.ts index 0dde0524d..6974a4425 100644 --- a/packages/common/src/interfaces/gridEvents.interface.ts +++ b/packages/common/src/interfaces/gridEvents.interface.ts @@ -33,6 +33,8 @@ export interface OnHeaderContextMenuEventArgs extends SlickGridArg { column: Col export interface OnHeaderMouseEventArgs extends SlickGridArg { column: Column; } export interface OnHeaderRowCellRenderedEventArgs extends SlickGridArg { node: HTMLDivElement; column: Column; } export interface OnKeyDownEventArgs extends SlickGridArg { row: number; cell: number; } +export interface OnPreHeaderClickEventArgs extends SlickGridArg { node: HTMLElement; } +export interface OnPreHeaderContextMenuEventArgs extends SlickGridArg { node: HTMLElement; } export interface OnValidationErrorEventArgs extends SlickGridArg { row: number; cell: number; validationResults: EditorValidationResult; column: Column; editor: Editor; cellNode: HTMLDivElement; } export interface OnRenderedEventArgs extends SlickGridArg { startRow: number; endRow: number; } export interface OnSelectedRowsChangedEventArgs extends SlickGridArg { rows: number[]; previousSelectedRows: number[]; changedSelectedRows: number[]; changedUnselectedRows: number[]; caller: string; } diff --git a/packages/common/src/services/__tests__/extension.service.spec.ts b/packages/common/src/services/__tests__/extension.service.spec.ts index 46fe90563..55ca87bb7 100644 --- a/packages/common/src/services/__tests__/extension.service.spec.ts +++ b/packages/common/src/services/__tests__/extension.service.spec.ts @@ -106,6 +106,8 @@ const gridStub = { onHeaderCellRendered: new SlickEvent(), onHeaderClick: new SlickEvent(), onHeaderContextMenu: new SlickEvent(), + onPreHeaderClick: new SlickEvent(), + onPreHeaderContextMenu: new SlickEvent(), onKeyDown: new SlickEvent(), onSelectedRowsChanged: new SlickEvent(), onScroll: new SlickEvent(), diff --git a/packages/common/src/services/groupingAndColspan.service.ts b/packages/common/src/services/groupingAndColspan.service.ts index 9b1c0e6bf..33e4f88ff 100644 --- a/packages/common/src/services/groupingAndColspan.service.ts +++ b/packages/common/src/services/groupingAndColspan.service.ts @@ -121,10 +121,7 @@ export class GroupingAndColspanService { preHeaderPanel.className = 'slick-header-columns'; preHeaderPanel.style.left = '-1000px'; preHeaderPanel.style.width = `${this._grid.getHeadersWidth()}px`; - - if (preHeaderPanel.parentElement) { - preHeaderPanel.parentElement.classList.add('slick-header'); - } + preHeaderPanel.parentElement?.classList.add('slick-header'); const headerColumnWidthDiff = this._grid.getHeaderColumnWidthDiff(); @@ -147,6 +144,7 @@ export class GroupingAndColspanService { widthTotal = colDef.width || 0; headerElm = createDomElement('div', { className: `slick-state-default slick-header-column ${isFrozenGrid ? 'frozen' : ''}`, + dataset: { group: colDef.columnGroup }, style: { width: `${widthTotal - headerColumnWidthDiff}px` } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5228b5c5e..2af63c9ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -940,7 +940,7 @@ packages: engines: {node: '>=v18'} dependencies: '@commitlint/types': 19.0.3 - semver: 7.6.0 + semver: 7.6.2 dev: true /@commitlint/lint@19.2.2: @@ -2666,7 +2666,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 - semver: 7.6.0 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.5.1-rc) typescript: 5.5.1-rc transitivePeerDependencies: diff --git a/test/cypress/e2e/example03.cy.ts b/test/cypress/e2e/example03.cy.ts index 8f3c7f87f..a8c380733 100644 --- a/test/cypress/e2e/example03.cy.ts +++ b/test/cypress/e2e/example03.cy.ts @@ -44,7 +44,7 @@ describe('Example 03 - Draggable Grouping', () => { cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(7)`).find('.checkmark-icon').should('have.length', 1); }); - describe('Grouping Tests', () => { + describe('Grouping tests', () => { it('should "Group by Duration & sort groups by value" then Collapse All and expect only group titles', () => { cy.get('[data-test="add-50k-rows-btn"]').click(); cy.get('[data-test="group-duration-sort-value-btn"]').click(); @@ -286,4 +286,157 @@ describe('Example 03 - Draggable Grouping', () => { .should('be.checked'); }); }); + + describe('Column Picker tests', () => { + it('should open Column Picker from 2nd header column and hide Title & Duration which will hide Common Factor Group as well', () => { + const fullTitlesWithGroupNames = ['', 'Common Factor - Title', 'Common Factor - Duration', 'Period - Start', 'Period - Finish', 'Analysis - Cost', 'Analysis - % Complete', 'Analysis - Effort-Driven', 'Action']; + + cy.get('.grid3') + .find('.slick-header-column:nth(1)') + .trigger('mouseover') + .trigger('contextmenu') + .invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children() + .each(($child, index) => { + if (index <= 5) { + expect($child.text()).to.eq(fullTitlesWithGroupNames[index]); + } + }); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(2)') + .children('label') + .should('contain', 'Title') + .click(); + + cy.get('.slick-column-picker .close') + .click(); + }); + + it('should open Column Picker from 2nd header column name and hide Duration which will hide Common Factor Group as well', () => { + const fullTitlesWithGroupNames = ['', 'Common Factor - Title', 'Common Factor - Duration', 'Period - Start', 'Period - Finish', 'Analysis - Cost', 'Analysis - % Complete', 'Analysis - Effort-Driven', 'Action']; + + cy.get('.grid3') + .find('.slick-header-column:nth(1) .slick-column-name') + .trigger('mouseover') + .trigger('contextmenu') + .invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children() + .each(($child, index) => { + if (index <= 5) { + expect($child.text()).to.eq(fullTitlesWithGroupNames[index]); + } + }); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(3)') + .children('label') + .should('contain', 'Duration') + .click(); + + cy.get('.slick-column-picker .close') + .click(); + }); + + it('should expect headers to be without Title/Duration and pre-headers without Common Factor Group header titles', () => { + const preHeadersWithoutFactor = ['', 'Period', 'Analysis', '']; + const titlesWithoutTitleDuration = ['', 'Start', 'Finish', 'Cost', '% Complete', 'Effort-Driven', 'Action']; + + // Column Pre-Headers without Common Factor group + cy.get('.grid3') + .find('.slick-header:not(.slick-preheader-panel) .slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(titlesWithoutTitleDuration[index])); + + // Column Headers without Title & Duration + cy.get('.grid3') + .find('.slick-preheader-panel .slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(preHeadersWithoutFactor[index])); + }); + + it('should open Column Picker from Pre-Header column and show again Title column', () => { + const fullTitlesWithGroupNames = ['', 'Common Factor - Title', 'Common Factor - Duration', 'Period - Start', 'Period - Finish', 'Analysis - Cost', 'Analysis - % Complete', 'Analysis - Effort-Driven', 'Action']; + + cy.get('.grid3') + .find('.slick-preheader-panel .slick-header-column:nth(1)') + .trigger('mouseover') + .trigger('contextmenu') + .invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children() + .each(($child, index) => { + if (index <= 5) { + expect($child.text()).to.eq(fullTitlesWithGroupNames[index]); + } + }); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(2)') + .children('label') + .should('contain', 'Title') + .click(); + + // close picker & reopen from a pre-header column name instead + cy.get('.slick-column-picker .close') + .click(); + }); + + it('should open Column Picker from Pre-Header column name and show again Duration column', () => { + const fullTitlesWithGroupNames = ['', 'Common Factor - Title', 'Common Factor - Duration', 'Period - Start', 'Period - Finish', 'Analysis - Cost', 'Analysis - % Complete', 'Analysis - Effort-Driven', 'Action']; + + cy.get('.grid3') + .find('.slick-preheader-panel .slick-header-column:nth(1)') + .trigger('mouseover') + .trigger('contextmenu') + .invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children() + .each(($child, index) => { + if (index <= 5) { + expect($child.text()).to.eq(fullTitlesWithGroupNames[index]); + } + }); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(3)') + .children('label') + .should('contain', 'Duration') + .click(); + + cy.get('.slick-column-picker .close') + .click(); + }); + + it('should expect header titles to show again Title/Duration and pre-headers with Common Factor Group header titles', () => { + const preHeadersWithFactor = ['', 'Common Factor', 'Period', 'Analysis', '']; + const titlesWithTitleDuration = ['', 'Title', 'Duration', 'Start', 'Finish', 'Cost', '% Complete', 'Effort-Driven', 'Action']; + + // Column Pre-Headers without Common Factor group + cy.get('.grid3') + .find('.slick-header:not(.slick-preheader-panel) .slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(titlesWithTitleDuration[index])); + + // Column Headers without Title & Duration + cy.get('.grid3') + .find('.slick-preheader-panel .slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(preHeadersWithFactor[index])); + }); + }); });