From 0a6a9029314cfe8382ff17208f89193d9a23e417 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 31 Oct 2023 23:26:55 -0400 Subject: [PATCH] docs: add ShadowDOM grid demo --- .../src/app-routing.ts | 2 + .../vite-demo-vanilla-bundle/src/app.html | 3 + .../src/examples/example18.ts | 12 +- .../src/examples/example19.ts | 12 +- .../src/examples/example20.html | 14 ++ .../src/examples/example20.ts | 135 ++++++++++++++++++ .../__tests__/slickDraggableGrouping.spec.ts | 2 + .../__tests__/slickGridMenu.spec.ts | 2 + .../src/extensions/slickDraggableGrouping.ts | 10 +- .../common/src/extensions/slickGridMenu.ts | 5 +- .../common/src/extensions/slickHeaderMenu.ts | 2 +- .../src/interfaces/gridOption.interface.ts | 2 +- .../__tests__/extension.service.spec.ts | 31 ++-- test/cypress/e2e/example20.cy.ts | 57 ++++++++ 14 files changed, 247 insertions(+), 42 deletions(-) create mode 100644 examples/vite-demo-vanilla-bundle/src/examples/example20.html create mode 100644 examples/vite-demo-vanilla-bundle/src/examples/example20.ts create mode 100644 test/cypress/e2e/example20.cy.ts diff --git a/examples/vite-demo-vanilla-bundle/src/app-routing.ts b/examples/vite-demo-vanilla-bundle/src/app-routing.ts index 8cb41d816..6e70aac73 100644 --- a/examples/vite-demo-vanilla-bundle/src/app-routing.ts +++ b/examples/vite-demo-vanilla-bundle/src/app-routing.ts @@ -20,6 +20,7 @@ import Example16 from './examples/example16'; import Example17 from './examples/example17'; import Example18 from './examples/example18'; import Example19 from './examples/example19'; +import Example20 from './examples/example20'; export class AppRouting { constructor(private config: RouterConfig) { @@ -45,6 +46,7 @@ export class AppRouting { { route: 'example17', name: 'example17', view: './examples/example17.html', viewModel: Example17, title: 'Example17', }, { route: 'example18', name: 'example18', view: './examples/example18.html', viewModel: Example18, title: 'Example18', }, { route: 'example19', name: 'example19', view: './examples/example19.html', viewModel: Example19, title: 'Example19', }, + { route: 'example20', name: 'example20', view: './examples/example20.html', viewModel: Example20, title: 'Example20', }, { route: '', redirect: 'example01' }, { route: '**', redirect: 'example01' } ]; diff --git a/examples/vite-demo-vanilla-bundle/src/app.html b/examples/vite-demo-vanilla-bundle/src/app.html index ec4ba32ad..8621010f4 100644 --- a/examples/vite-demo-vanilla-bundle/src/app.html +++ b/examples/vite-demo-vanilla-bundle/src/app.html @@ -92,6 +92,9 @@

Slickgrid-Universal

Example19 - ExcelCopyBuffer with Cell Selection + + Example20 - Basic grid inside a Shadow DOM + diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example18.ts b/examples/vite-demo-vanilla-bundle/src/examples/example18.ts index d8b7106c4..ccbcc6756 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example18.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example18.ts @@ -39,17 +39,7 @@ const historicSparklineFormatter: Formatter = (_row: number, _cell: number, _val return svgElem.outerHTML; }; -export default class Example34 { - title = 'Example 34: Real-Time Stock Trading'; - subTitle = `Simulate a stock trading platform with lot of price changes - `; - +export default class Example18 { columnDefinitions: Column[] = []; dataset: any[] = []; gridOptions!: GridOption; diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example19.ts b/examples/vite-demo-vanilla-bundle/src/examples/example19.ts index 6c4a4b18f..bdb4d7afa 100644 --- a/examples/vite-demo-vanilla-bundle/src/examples/example19.ts +++ b/examples/vite-demo-vanilla-bundle/src/examples/example19.ts @@ -6,16 +6,8 @@ import './example19.scss'; const NB_ITEMS = 100; declare const Slick: SlickNamespace; -export default class Example34 { +export default class Example19 { protected _eventHandler: SlickEventHandler; - title = 'Example 19: ExcelCopyBuffer with Cell Selection'; - subTitle = `Cell Selection using "Shift+{key}" where "key" can be any of: - `; columnDefinitions: Column[] = []; dataset: any[] = []; @@ -71,7 +63,7 @@ export default class Example34 { id: i, name: i < 26 ? String.fromCharCode('A'.charCodeAt(0) + (i % 26)) - : String.fromCharCode('A'.charCodeAt(0) + ((i / 26) | 0) -1) + String.fromCharCode('A'.charCodeAt(0) + (i % 26)), + : String.fromCharCode('A'.charCodeAt(0) + (Math.floor(i / 26)) - 1) + String.fromCharCode('A'.charCodeAt(0) + (i % 26)), field: i as any, minWidth: 60, width: 60, diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example20.html b/examples/vite-demo-vanilla-bundle/src/examples/example20.html new file mode 100644 index 000000000..112075be0 --- /dev/null +++ b/examples/vite-demo-vanilla-bundle/src/examples/example20.html @@ -0,0 +1,14 @@ +

+ Example 20 - Basic grid inside a Shadow DOM + (with Salesforce Theme) +
+ see + + code + +
+

+ +
+
\ No newline at end of file diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example20.ts b/examples/vite-demo-vanilla-bundle/src/examples/example20.ts new file mode 100644 index 000000000..9d946b333 --- /dev/null +++ b/examples/vite-demo-vanilla-bundle/src/examples/example20.ts @@ -0,0 +1,135 @@ +import { Column, FieldType, Filters, Formatters, GridOption, SlickEventHandler, SlickNamespace, } from '@slickgrid-universal/common'; +import { Slicker, SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle'; +import { ExampleGridOptions } from './example-grid-options'; + +const NB_ITEMS = 100; +declare const Slick: SlickNamespace; + +interface ShadowContainer { + shadow: ShadowRoot; + gridContainer: HTMLDivElement; +} + +export default class Example20 { + protected _eventHandler: SlickEventHandler; + + columnDefinitions: Column[] = []; + dataset: any[] = []; + gridOptions!: GridOption; + gridContainerElm: HTMLDivElement; + isWithPagination = true; + sgb: SlickVanillaGridBundle; + + attached() { + this._eventHandler = new Slick.EventHandler(); + const shadowObj = this.createShadowElement(); + + // define the grid options & columns and then create the grid itself + this.defineGrid(shadowObj); + + // mock some data (different in each dataset) + this.dataset = this.getData(NB_ITEMS); + this.gridContainerElm = document.querySelector(`#host`) as HTMLDivElement; + document.body.classList.add('salesforce-theme'); + + setTimeout(() => { + this.sgb = new Slicker.GridBundle(shadowObj.gridContainer as HTMLDivElement, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset); + }, 25); + } + + /** + * Build the Shadow DOM. In this example, it will + * have just a div for the grid, and a + * for the Alpine style. + * + * Notice that the tag must be placed inside + * the Shadow DOM tree, it cannot be placed on the + * tag because the Shadow DOM is unaffected by external + * styles + */ + createShadowElement(): ShadowContainer { + const host = document.querySelector('#host') as HTMLDivElement; + const shadow = host.attachShadow({ mode: 'open' }); + const gridContainer = document.createElement('div'); + // gridContainer.style.width = '600px'; + // gridContainer.style.height = '500px'; + gridContainer.classList.add('grid20'); + shadow.appendChild(gridContainer); + + const linkElement = document.createElement('link'); + linkElement.type = 'text/css'; + linkElement.rel = 'stylesheet'; + linkElement.href = './src/styles.scss'; + shadow.appendChild(linkElement); + return { shadow, gridContainer }; + } + + dispose() { + this._eventHandler.unsubscribeAll(); + this.sgb?.dispose(); + this.gridContainerElm.remove(); + document.body.classList.remove('salesforce-theme'); + } + + /* Define grid Options and Columns */ + defineGrid(shadowObj) { + this.columnDefinitions = [ + { id: 'title', name: 'Title', field: 'title', sortable: true, minWidth: 100, filterable: true }, + { id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true, minWidth: 100, filterable: true, type: FieldType.number }, + { id: '%', name: '% Complete', field: 'percentComplete', sortable: true, minWidth: 100, filterable: true, type: FieldType.number }, + { id: 'start', name: 'Start', field: 'start', formatter: Formatters.dateIso, filter: { model: Filters.compoundDate }, type: FieldType.date, exportWithFormatter: true, filterable: true }, + { id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso, filter: { model: Filters.compoundDate }, type: FieldType.date, exportWithFormatter: true, filterable: true }, + { id: 'effort-driven', name: 'Effort Driven', field: 'effortDriven', sortable: true, minWidth: 100, filterable: true } + ]; + + this.gridOptions = { + gridHeight: 450, + gridWidth: 800, + enableCellNavigation: true, + enableFiltering: true, + headerRowHeight: 35, + rowHeight: 30, + shadowRoot: shadowObj.shadow + }; + } + + getData(itemCount: number) { + // mock a dataset + const datasetTmp: any[] = []; + for (let i = 0; i < itemCount; i++) { + const randomYear = 2000 + Math.floor(Math.random() * 10); + const randomMonth = Math.floor(Math.random() * 11); + const randomDay = Math.floor((Math.random() * 29)); + const randomPercent = Math.round(Math.random() * 100); + + datasetTmp.push({ + id: i, + title: 'Task ' + i, + duration: Math.round(Math.random() * 100) + '', + percentComplete: randomPercent, + start: new Date(randomYear, randomMonth + 1, randomDay), + finish: new Date(randomYear + 1, randomMonth + 1, randomDay), + effortDriven: (i % 5 === 0) + }); + } + + return datasetTmp; + } + + generatePhoneNumber(): string { + let phone = ''; + for (let i = 0; i < 10; i++) { + phone += Math.round(Math.random() * 9) + ''; + } + return phone; + } + + // Toggle the Grid Pagination + // IMPORTANT, the Pagination MUST BE CREATED on initial page load before you can start toggling it + // Basically you cannot toggle a Pagination that doesn't exist (must created at the time as the grid) + togglePagination() { + this.isWithPagination = !this.isWithPagination; + this.sgb.paginationService!.togglePaginationVisibility(this.isWithPagination); + this.sgb.slickGrid!.setSelectedRows([]); + } +} diff --git a/packages/common/src/extensions/__tests__/slickDraggableGrouping.spec.ts b/packages/common/src/extensions/__tests__/slickDraggableGrouping.spec.ts index f1887adaf..c10be847b 100644 --- a/packages/common/src/extensions/__tests__/slickDraggableGrouping.spec.ts +++ b/packages/common/src/extensions/__tests__/slickDraggableGrouping.spec.ts @@ -75,6 +75,7 @@ const gridStub = { getCellNode: jest.fn(), getCellFromEvent: jest.fn(), getColumns: jest.fn(), + getContainerNode: jest.fn(), getHeaderColumn: jest.fn(), getOptions: jest.fn(), getPreHeaderPanel: jest.fn(), @@ -141,6 +142,7 @@ describe('Draggable Grouping Plugin', () => { sharedService = new SharedService(); translateService = new TranslateServiceStub(); extensionUtility = new ExtensionUtility(sharedService, backendUtilityService, translateService); + jest.spyOn(gridStub, 'getContainerNode').mockReturnValue(document.body as HTMLDivElement); jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub); jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns); diff --git a/packages/common/src/extensions/__tests__/slickGridMenu.spec.ts b/packages/common/src/extensions/__tests__/slickGridMenu.spec.ts index 57fd5f163..3961dae4c 100644 --- a/packages/common/src/extensions/__tests__/slickGridMenu.spec.ts +++ b/packages/common/src/extensions/__tests__/slickGridMenu.spec.ts @@ -49,6 +49,7 @@ const gridStub = { autosizeColumns: jest.fn(), getColumnIndex: jest.fn(), getColumns: jest.fn(), + getContainerNode: jest.fn(), getGridPosition: () => ({ width: 10, left: 0 }), getOptions: jest.fn(), getSelectedRows: jest.fn(), @@ -156,6 +157,7 @@ describe('GridMenuControl', () => { translateService = new TranslateServiceStub(); extensionUtility = new ExtensionUtility(sharedService, backendUtilityService, translateService); + jest.spyOn(gridStub, 'getContainerNode').mockReturnValue(document.body as HTMLDivElement); jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock); jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(dataViewStub); diff --git a/packages/common/src/extensions/slickDraggableGrouping.ts b/packages/common/src/extensions/slickDraggableGrouping.ts index e11682d9f..d15b8ec82 100644 --- a/packages/common/src/extensions/slickDraggableGrouping.ts +++ b/packages/common/src/extensions/slickDraggableGrouping.ts @@ -129,6 +129,10 @@ export class SlickDraggableGrouping { return this._gridUid || (this.grid?.getUID() ?? ''); } + get gridContainer() { + return this.grid.getContainerNode(); + } + /** Initialize plugin. */ init(grid: SlickGrid, groupingOptions?: DraggableGrouping) { this._addonOptions = { ...this._defaults, ...groupingOptions }; @@ -215,7 +219,7 @@ export class SlickDraggableGrouping { this._eventHandler.unsubscribeAll(); this.pubSubService.unsubscribeAll(this._subscriptions); this._bindingEventService.unbindAll(); - emptyElement(document.querySelector(`.${this.gridUid} .slick-preheader-panel`)); + emptyElement(this.gridContainer.querySelector(`.${this.gridUid} .slick-preheader-panel`)); } clearDroppedGroups() { @@ -354,8 +358,8 @@ export class SlickDraggableGrouping { } } as SortableOptions; - this._sortableLeftInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-left`) as HTMLDivElement, sortableOptions) as SortableInstance; - this._sortableRightInstance = Sortable.create(document.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-right`) as HTMLDivElement, sortableOptions) as SortableInstance; + this._sortableLeftInstance = Sortable.create(this.gridContainer.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-left`) as HTMLDivElement, sortableOptions) as SortableInstance; + this._sortableRightInstance = Sortable.create(this.gridContainer.querySelector(`.${grid.getUID()} .slick-header-columns.slick-header-columns-right`) as HTMLDivElement, sortableOptions) as SortableInstance; return { sortableLeftInstance: this._sortableLeftInstance, diff --git a/packages/common/src/extensions/slickGridMenu.ts b/packages/common/src/extensions/slickGridMenu.ts index 38876c373..8f4af31e0 100644 --- a/packages/common/src/extensions/slickGridMenu.ts +++ b/packages/common/src/extensions/slickGridMenu.ts @@ -187,7 +187,8 @@ export class SlickGridMenu extends MenuBaseClass { 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}`); + const gridContainer = this.grid.getContainerNode(); + this._headerElm = gridContainer.querySelector(`.slick-header-${headerSide}`); if (this._headerElm && this._addonOptions) { // resize the header row to include the hamburger menu icon @@ -196,7 +197,7 @@ export class SlickGridMenu extends MenuBaseClass { // if header row is enabled, we also need to resize its width const enableResizeHeaderRow = this._addonOptions.resizeOnShowHeaderRow ?? this._defaults.resizeOnShowHeaderRow; if (enableResizeHeaderRow && this.gridOptions.showHeaderRow) { - const headerRowElm = document.querySelector(`${gridUidSelector} .slick-headerrow`); + const headerRowElm = gridContainer.querySelector(`${gridUidSelector} .slick-headerrow`); if (headerRowElm) { headerRowElm.style.width = `calc(100% - ${gridMenuWidth}px)`; } diff --git a/packages/common/src/extensions/slickHeaderMenu.ts b/packages/common/src/extensions/slickHeaderMenu.ts index 86e4cdae6..47e16e5fc 100644 --- a/packages/common/src/extensions/slickHeaderMenu.ts +++ b/packages/common/src/extensions/slickHeaderMenu.ts @@ -84,7 +84,7 @@ export class SlickHeaderMenu extends MenuBaseClass { this.grid.setColumns(this.grid.getColumns()); // hide the menu when clicking outside the grid - this._bindEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener, { capture: true }); + this._bindEventService.bind(document.body, 'mousedown', this.handleBodyMouseDown.bind(this) as EventListener); } /** Dispose (destroy) of the plugin */ diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts index b4d522bcd..22d813772 100644 --- a/packages/common/src/interfaces/gridOption.interface.ts +++ b/packages/common/src/interfaces/gridOption.interface.ts @@ -587,7 +587,7 @@ export interface GridOption { /** CSS class name used when cell is selected */ selectedCellCssClass?: string; - /** Defaults to undefined. If we are inside a shadow DOM tree, this must be the shadow root of the tree */ + /** Defaults to undefined. If we are inside a Shadow DOM tree, this must be the shadow root of the tree */ shadowRoot?: ShadowRoot; /** Do we want to show cell selection? */ diff --git a/packages/common/src/services/__tests__/extension.service.spec.ts b/packages/common/src/services/__tests__/extension.service.spec.ts index 9c9551814..3091887fd 100644 --- a/packages/common/src/services/__tests__/extension.service.spec.ts +++ b/packages/common/src/services/__tests__/extension.service.spec.ts @@ -87,6 +87,7 @@ jest.mock('../../extensions/slickRowMoveManager', () => ({ const gridStub = { autosizeColumns: jest.fn(), getColumnIndex: jest.fn(), + getContainerNode: jest.fn(), getOptions: jest.fn(), getPluginByName: jest.fn(), getPreHeaderPanel: jest.fn(), @@ -131,6 +132,7 @@ const filterServiceStub = { const pubSubServiceStub = { publish: jest.fn(), subscribe: jest.fn(), + subscribeEvent: jest.fn(), unsubscribe: jest.fn(), unsubscribeAll: jest.fn(), } as BasePubSubService; @@ -190,6 +192,7 @@ describe('ExtensionService', () => { treeDataServiceStub, translateService, ); + jest.spyOn(gridStub, 'getContainerNode').mockReturnValue(document.body as HTMLDivElement); }); afterEach(() => { @@ -304,7 +307,7 @@ describe('ExtensionService', () => { expect(extSpy).toHaveBeenCalled(); expect(output).toEqual({ name: ExtensionName.autoTooltip, instance: pluginInstance } as ExtensionModel); - expect(output.instance instanceof SlickAutoTooltip).toBeTrue(); + expect(output!.instance instanceof SlickAutoTooltip).toBeTrue(); }); it('should register the ColumnPicker addon when "enableColumnPicker" is set in the grid options', () => { @@ -318,7 +321,7 @@ describe('ExtensionService', () => { expect(gridSpy).toHaveBeenCalled(); expect(output).toEqual({ name: ExtensionName.columnPicker, instance: pluginInstance } as ExtensionModel); - expect(output.instance instanceof SlickColumnPicker).toBeTrue(); + expect(output!.instance instanceof SlickColumnPicker).toBeTrue(); }); it('should call "onExtensionRegistered" when defined in grid option and the ColumnPicker control gets created', () => { @@ -335,7 +338,7 @@ describe('ExtensionService', () => { const output = service.getExtensionByName(ExtensionName.columnPicker); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickColumnPicker).toBeTrue(); + expect(output!.instance instanceof SlickColumnPicker).toBeTrue(); }); it('should register the DraggableGrouping addon when "enableDraggableGrouping" is set in the grid options', () => { @@ -355,7 +358,7 @@ describe('ExtensionService', () => { const output2 = service.getExtensionByName(ExtensionName.groupItemMetaProvider); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickDraggableGrouping).toBeTrue(); + expect(output!.instance instanceof SlickDraggableGrouping).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -377,7 +380,7 @@ describe('ExtensionService', () => { const output = service.getExtensionByName(ExtensionName.gridMenu); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickGridMenu).toBeTrue(); + expect(output!.instance instanceof SlickGridMenu).toBeTrue(); }); it('should register the GroupItemMetaProvider addon when "enableGrouping" is set in the grid options', () => { @@ -389,7 +392,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.groupItemMetaProvider); expect(gridSpy).toHaveBeenCalled(); - expect(output.instance instanceof SlickGroupItemMetadataProvider).toBeTrue(); + expect(output!.instance instanceof SlickGroupItemMetadataProvider).toBeTrue(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); expect(output).toEqual({ name: ExtensionName.groupItemMetaProvider, instance: pluginInstance } as ExtensionModel); @@ -450,7 +453,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.cellMenu); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickCellMenu).toBeTrue(); + expect(output!.instance instanceof SlickCellMenu).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -467,7 +470,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.contextMenu); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickContextMenu).toBeTrue(); + expect(output!.instance instanceof SlickContextMenu).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -484,7 +487,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.headerButton); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickHeaderButtons).toBeTrue(); + expect(output!.instance instanceof SlickHeaderButtons).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -501,7 +504,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.headerMenu); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickHeaderMenu).toBeTrue(); + expect(output!.instance instanceof SlickHeaderMenu).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -519,7 +522,7 @@ describe('ExtensionService', () => { const pluginInstance = service.getExtensionInstanceByName(ExtensionName.cellExternalCopyManager); expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); - expect(output.instance instanceof SlickCellExcelCopyManager).toBeTrue(); + expect(output!.instance instanceof SlickCellExcelCopyManager).toBeTrue(); expect(gridSpy).toHaveBeenCalled(); expect(pluginInstance).toBeTruthy(); expect(output!.instance).toEqual(pluginInstance); @@ -643,7 +646,7 @@ describe('ExtensionService', () => { service.bindDifferentExtensions(); - const columnPickerInstance = service.getExtensionByName(ExtensionName.columnPicker).instance; + const columnPickerInstance = service.getExtensionByName(ExtensionName.columnPicker)!.instance; const extSpy = jest.spyOn(columnPickerInstance, 'translateColumnPicker'); service.translateColumnPicker(); expect(extSpy).toHaveBeenCalled(); @@ -815,7 +818,7 @@ describe('ExtensionService', () => { service.renderColumnHeaders(columnsMock); expect(setColumnsSpy).toHaveBeenCalledWith(columnsMock); - expect(service.getExtensionByName(ExtensionName.columnPicker).instance.columns).toEqual(columnsMock); + expect(service.getExtensionByName(ExtensionName.columnPicker)!.instance.columns).toEqual(columnsMock); }); it('should replace the Grid Menu columns when plugin is enabled and method is called with new column definition collection provided as argument', () => { @@ -834,7 +837,7 @@ describe('ExtensionService', () => { service.renderColumnHeaders(columnsMock); expect(setColumnsSpy).toHaveBeenCalledWith(columnsMock); - expect(service.getExtensionByName(ExtensionName.gridMenu).instance.columns).toEqual(columnsMock); + expect(service.getExtensionByName(ExtensionName.gridMenu)!.instance.columns).toEqual(columnsMock); }); }); }); diff --git a/test/cypress/e2e/example20.cy.ts b/test/cypress/e2e/example20.cy.ts new file mode 100644 index 000000000..8fea00d6e --- /dev/null +++ b/test/cypress/e2e/example20.cy.ts @@ -0,0 +1,57 @@ +describe('Example 20 - Basic grid inside a Shadow DOM', { retries: 0 }, () => { + const fullTitles = ['Title', 'Duration (days)', '% Complete', 'Start', 'Finish', 'Effort Driven']; + + it('should display Example title', () => { + cy.visit(`${Cypress.config('baseUrl')}/example20`); + cy.get('h3').should('contain', 'Example 20 - Basic grid inside a Shadow DOM'); + cy.get('h3 span.subtitle').should('contain', '(with Salesforce Theme)'); + }); + + it('should have a grid with size 800 * 450px', () => { + cy.get('#host').shadow() + .find('.grid20') + .should('have.css', 'width', '800px'); + + cy.get('#host').shadow() + .find('.grid20 > .slickgrid-container') + .should($el => expect(parseInt(`${$el.height()}`, 10)).to.eq(450)); + }); + + it('should have exact column titles in grid', () => { + cy.get('#host').shadow() + .find('.grid20') + .find('.slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); + }); + + it('should click on the "Title" column to "Sort Ascending"', () => { + cy.get('#host').shadow() + .find('.grid20') + .find('.slick-header-column') + .first() + .click(); + + cy.get('#host').shadow() + .find('.slick-row') + .first() + .children('.slick-cell') + .first() + .should('contain', 'Task 0'); + }); + + it('should click again on the "Title" column to "Sort Descending"', () => { + cy.get('#host').shadow() + .find('.grid20') + .find('.slick-header-column') + .first() + .click(); + + cy.get('#host').shadow() + .find('.slick-row') + .first() + .children('.slick-cell') + .first() + .should('contain', 'Task 99'); + }); +});