diff --git a/package.json b/package.json
index 9a0d8cab1..8c0ad9e13 100644
--- a/package.json
+++ b/package.json
@@ -88,6 +88,7 @@
"@slickgrid-universal/empty-warning-component": "^0.19.0",
"@slickgrid-universal/event-pub-sub": "^0.19.0",
"@slickgrid-universal/pagination-component": "^0.19.0",
+ "@slickgrid-universal/row-detail-view-plugin": "^0.19.0",
"@slickgrid-universal/rxjs-observable": "^0.19.0",
"@types/dompurify": "^2.3.1",
"@types/jquery": "^3.5.8",
@@ -174,4 +175,4 @@
"node": ">=14.15.0",
"npm": ">=6.14.8"
}
-}
+}
\ No newline at end of file
diff --git a/src/app/examples/grid-contextmenu.component.ts b/src/app/examples/grid-contextmenu.component.ts
index 38c47d5e6..fe2303f52 100644
--- a/src/app/examples/grid-contextmenu.component.ts
+++ b/src/app/examples/grid-contextmenu.component.ts
@@ -13,6 +13,8 @@ import {
Formatter,
Formatters,
GridOption,
+ SlickCellMenu,
+ SlickContextMenu,
unsubscribeAllObservables,
} from './../modules/angular-slickgrid';
@@ -108,12 +110,12 @@ export class GridContextMenuComponent implements OnInit, OnDestroy {
this.angularGrid = angularGrid;
}
- get cellMenuInstance(): any {
- return this.angularGrid?.extensionService?.getSlickgridAddonInstance?.(ExtensionName.cellMenu) ?? {};
+ get cellMenuInstance(): SlickCellMenu {
+ return (this.angularGrid?.extensionService?.getExtensionInstanceByName?.(ExtensionName.cellMenu) ?? {}) as SlickCellMenu;
}
- get contextMenuInstance(): any {
- return this.angularGrid?.extensionService?.getSlickgridAddonInstance?.(ExtensionName.contextMenu) ?? {};
+ get contextMenuInstance(): SlickContextMenu {
+ return (this.angularGrid?.extensionService?.getExtensionInstanceByName?.(ExtensionName.contextMenu) ?? {}) as SlickContextMenu;
}
ngOnInit() {
diff --git a/src/app/examples/grid-draggrouping.component.ts b/src/app/examples/grid-draggrouping.component.ts
index 4a95e28ce..01766ddeb 100644
--- a/src/app/examples/grid-draggrouping.component.ts
+++ b/src/app/examples/grid-draggrouping.component.ts
@@ -188,7 +188,7 @@ export class GridDraggableGroupingComponent implements OnInit {
}
},
{
- id: 'effortDriven', name: 'Effort Driven', field: 'effortDriven',
+ id: 'effortDriven', name: 'Effort-Driven', field: 'effortDriven',
width: 80, minWidth: 20, maxWidth: 100,
cssClass: 'cell-effort-driven',
sortable: true,
@@ -218,6 +218,7 @@ export class GridDraggableGroupingComponent implements OnInit {
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 40,
+ showCustomFooter: true,
enableFiltering: true,
// you could debounce/throttle the input text filter if you have lots of data
// filterTypingDebounce: 250,
diff --git a/src/app/examples/grid-frozen.component.ts b/src/app/examples/grid-frozen.component.ts
index e85835368..73a8925a6 100644
--- a/src/app/examples/grid-frozen.component.ts
+++ b/src/app/examples/grid-frozen.component.ts
@@ -56,7 +56,7 @@ export class GridFrozenComponent implements OnInit, OnDestroy {
highlightRow(event: Event, isMouseEnter: boolean) {
const cell = this.gridObj.getCellFromEvent(event);
- const rows = isMouseEnter ? [cell.row] : [];
+ const rows = isMouseEnter ? [cell?.row ?? 0] : [];
this.gridObj.setSelectedRows(rows); // highlight current row
event.preventDefault();
}
diff --git a/src/app/examples/grid-graphql.component.ts b/src/app/examples/grid-graphql.component.ts
index 3398c3c07..66df53d36 100644
--- a/src/app/examples/grid-graphql.component.ts
+++ b/src/app/examples/grid-graphql.component.ts
@@ -135,7 +135,7 @@ export class GridGraphqlComponent implements OnInit, OnDestroy {
i18n: this.translate,
gridMenu: {
resizeOnShowHeaderRow: true,
- customItems: [
+ commandItems: [
{
iconCssClass: 'fa fa-times text-danger',
title: 'Reset Grid',
diff --git a/src/app/examples/grid-headerbutton.component.html b/src/app/examples/grid-headerbutton.component.html
index c3ab7bf64..aae058577 100644
--- a/src/app/examples/grid-headerbutton.component.html
+++ b/src/app/examples/grid-headerbutton.component.html
@@ -11,13 +11,21 @@
-
+ Grid 1
+
+
+
+
+ Grid 2 - with both Header Buttons & Menus
+
+
+
\ No newline at end of file
diff --git a/src/app/examples/grid-headerbutton.component.scss b/src/app/examples/grid-headerbutton.component.scss
new file mode 100644
index 000000000..192dcca7f
--- /dev/null
+++ b/src/app/examples/grid-headerbutton.component.scss
@@ -0,0 +1,10 @@
+/* 1st grid */
+#grid7-1 {
+ --slick-header-button-float: right;
+}
+
+/* 2nd grid */
+#grid7-2 {
+ --slick-header-button-margin: 4px 0 50px 0;
+ --slick-header-button-float: left;
+}
\ No newline at end of file
diff --git a/src/app/examples/grid-headerbutton.component.ts b/src/app/examples/grid-headerbutton.component.ts
index d12f8fa84..74404427c 100644
--- a/src/app/examples/grid-headerbutton.component.ts
+++ b/src/app/examples/grid-headerbutton.component.ts
@@ -1,18 +1,14 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewEncapsulation } from '@angular/core';
-import { Column, Formatter, GridOption, SlickDataView, SlickGrid } from './../modules/angular-slickgrid';
+import { AngularGridInstance, Column, GridOption, } from './../modules/angular-slickgrid';
// create a custom Formatter to highlight negative values in red
-let columnsWithHighlightingById = {};
-const highlightingFormatter: Formatter = (_row, _cell, value, columnDef) => {
- if (columnsWithHighlightingById && (columnsWithHighlightingById as any)[columnDef.id] && value < 0) {
- return `${value}
`;
- } else {
- return value;
- }
-};
+let columns1WithHighlightingById: any = {};
+let columns2WithHighlightingById: any = {};
@Component({
+ styleUrls: ['./grid-headerbutton.component.scss'],
+ encapsulation: ViewEncapsulation.None,
templateUrl: './grid-headerbutton.component.html'
})
export class GridHeaderButtonComponent implements OnInit {
@@ -25,7 +21,6 @@ export class GridHeaderButtonComponent implements OnInit {
Resize the 1st column to see all icon/command
Mouse hover the 2nd column to see it's icon/command
For all the other columns, click on top-right red circle icon to enable highlight of negative numbers.
- Note: The "Header Button" & "Header Menu" Plugins cannot be used at the same time
Use override callback functions to change the properties of show/hide, enable/disable the menu or certain item(s) from the list
- These callbacks are: "itemVisibilityOverride", "itemUsabilityOverride"
@@ -35,16 +30,38 @@ export class GridHeaderButtonComponent implements OnInit {
`;
- columnDefinitions!: Column[];
- gridOptions!: GridOption;
- dataset!: any[];
- gridObj: any;
- dataviewObj: any;
- visibleColumns!: Column[];
+ columnDefinitions1: Column[] = [];
+ columnDefinitions2: Column[] = [];
+ gridOptions1!: GridOption;
+ gridOptions2!: GridOption;
+ dataset1: any[] = [];
+ dataset2: any[] = [];
+ angularGrid1!: AngularGridInstance;
+ angularGrid2!: AngularGridInstance;
+
+ constructor() {
+ columns1WithHighlightingById = {};
+ columns2WithHighlightingById = {};
+ }
ngOnInit(): void {
- this.columnDefinitions = [];
- this.gridOptions = {
+ this.defineGrid();
+
+ // populate the dataset once the grid is ready
+ this.dataset1 = this.loadData(200, 1);
+ this.dataset2 = this.loadData(200, 2);
+ }
+
+ angularGrid1Ready(angularGrid: AngularGridInstance) {
+ this.angularGrid1 = angularGrid;
+ }
+
+ angularGrid2Ready(angularGrid: AngularGridInstance) {
+ this.angularGrid2 = angularGrid;
+ }
+
+ defineGrid() {
+ this.gridOptions1 = {
enableAutoResize: true,
enableHeaderButton: true,
enableHeaderMenu: false,
@@ -53,62 +70,94 @@ export class GridHeaderButtonComponent implements OnInit {
rightPadding: 10
},
enableFiltering: false,
+ enableExcelCopyBuffer: true,
+ excelCopyBufferOptions: {
+ onCopyCells: (e, args) => console.log('onCopyCells', e, args),
+ onPasteCells: (e, args) => console.log('onPasteCells', e, args),
+ onCopyCancelled: (e, args) => console.log('onCopyCancelled', e, args),
+ },
enableCellNavigation: true,
+ gridHeight: 275,
headerButton: {
// you can use the "onCommand" (in Grid Options) and/or the "action" callback (in Column Definition)
- onCommand: (e, args) => {
- const column = args.column;
- const button = args.button;
- const command = args.command;
- if (!columnsWithHighlightingById) {
- columnsWithHighlightingById = {};
- }
-
- if (command === 'toggle-highlight') {
- if (button.cssClass === 'fa fa-circle red') {
- delete (columnsWithHighlightingById as any)[column.id];
- button.cssClass = 'fa fa-circle-o red faded';
- button.tooltip = 'Highlight negative numbers.';
- } else {
- (columnsWithHighlightingById as any)[column.id] = true;
- button.cssClass = 'fa fa-circle red';
- button.tooltip = 'Remove highlight.';
- }
+ onCommand: (_e, args) => this.handleOnCommand(_e, args, 1)
+ }
+ };
- this.gridObj.invalidate();
- }
- }
+ // grid 2 options, same as grid 1 + extras
+ this.gridOptions2 = {
+ ...this.gridOptions1,
+ enableHeaderMenu: true,
+ enableFiltering: true,
+ // frozenColumn: 2,
+ // frozenRow: 2,
+ headerButton: {
+ // when floating to left, you might want to inverse the icon orders
+ onCommand: (_e, args) => this.handleOnCommand(_e, args, 2)
}
};
+ }
- this.getData();
+ handleOnCommand(_e: Event, args: any, gridNo: 1 | 2) {
+ const column = args.column;
+ const button = args.button;
+ const command = args.command;
+
+ if (command === 'toggle-highlight') {
+ if (button.cssClass === 'fa fa-circle red') {
+ if (gridNo === 1) {
+ delete columns1WithHighlightingById[column.id];
+ } else {
+ delete columns2WithHighlightingById[column.id];
+ }
+ button.cssClass = 'fa fa-circle-o red faded';
+ button.tooltip = 'Highlight negative numbers.';
+ } else {
+ if (gridNo === 1) {
+ columns1WithHighlightingById[column.id] = true;
+ } else {
+ columns2WithHighlightingById[column.id] = true;
+ }
+ button.cssClass = 'fa fa-circle red';
+ button.tooltip = 'Remove highlight.';
+ }
+ ((this as any)[`angularGrid${gridNo}`] as AngularGridInstance).slickGrid.invalidate();
+ }
}
- getData() {
+ loadData(count: number, gridNo: 1 | 2) {
// Set up some test columns.
for (let i = 0; i < 10; i++) {
- this.columnDefinitions.push({
+ (this as any)[`columnDefinitions${gridNo}`].push({
id: i,
name: 'Column ' + String.fromCharCode('A'.charCodeAt(0) + i),
field: i + '',
width: i === 0 ? 70 : 100, // have the 2 first columns wider
+ filterable: true,
sortable: true,
- formatter: highlightingFormatter,
+ formatter: (_row: number, _cell: number, value: any, columnDef: Column) => {
+ if (gridNo === 1 && columns1WithHighlightingById[columnDef.id] && value < 0) {
+ return `${value}
`;
+ } else if (gridNo === 2 && columns2WithHighlightingById[columnDef.id] && value < 0) {
+ return `${value}
`;
+ }
+ return value;
+ },
header: {
buttons: [
{
cssClass: 'fa fa-circle-o red faded',
command: 'toggle-highlight',
tooltip: 'Highlight negative numbers.',
- itemVisibilityOverride: (args) => {
+ itemVisibilityOverride: (args: any) => {
// for example don't show the header button on column "E"
return args.column.name !== 'Column E';
},
- itemUsabilityOverride: (args) => {
+ itemUsabilityOverride: (args: any) => {
// for example the button usable everywhere except on last column ='J"
return args.column.name !== 'Column J';
},
- action: (e, args) => {
+ action: (_e: Event, args: any) => {
// you can use the "action" callback and/or subscribe to the "onCallback" event, they both have the same arguments
// do something
console.log(`execute a callback action to "${args.command}" on ${args.column.name}`);
@@ -120,45 +169,50 @@ export class GridHeaderButtonComponent implements OnInit {
}
// Set multiple buttons on the first column to demonstrate overflow.
- this.columnDefinitions[0].name = 'Resize me!';
- this.columnDefinitions[0].header = {
+ (this as any)[`columnDefinitions${gridNo}`][0].name = 'Resize me!';
+ (this as any)[`columnDefinitions${gridNo}`][0].header = {
buttons: [
{
cssClass: 'fa fa-tag',
- handler: (e) => {
+ handler: () => {
alert('Tag');
}
},
{
cssClass: 'fa fa-comment',
- handler: (e) => {
+ handler: () => {
alert('Comment');
}
},
{
cssClass: 'fa fa-info-circle',
- handler: (e) => {
+ handler: () => {
alert('Info');
}
},
{
cssClass: 'fa fa-question-circle',
- handler: (e) => {
+ handler: () => {
alert('Help');
}
}
]
};
+ // when floating to left, you might want to inverse the icon orders
+ if (gridNo === 2) {
+ this.columnDefinitions2[0].header?.buttons?.reverse();
+ }
+
// Set a button on the second column to demonstrate hover.
- this.columnDefinitions[1].name = 'Hover me!';
- this.columnDefinitions[1].header = {
+ (this as any)[`columnDefinitions${gridNo}`][1].name = 'Hover me!';
+ (this as any)[`columnDefinitions${gridNo}`][1].header = {
buttons: [
{
cssClass: 'fa fa-question-circle',
showOnHover: true,
tooltip: 'This button only appears on hover.',
- handler: (e) => {
+ handler: () => {
alert('Help');
}
}
@@ -167,20 +221,13 @@ export class GridHeaderButtonComponent implements OnInit {
// mock a dataset
const mockDataset = [];
- for (let i = 0; i < 100; i++) {
+ for (let i = 0; i < count; i++) {
const d: any = (mockDataset[i] = {});
d['id'] = i;
- for (let j = 0; j < this.columnDefinitions.length; j++) {
+ for (let j = 0; j < (this as any)[`columnDefinitions${gridNo}`].length; j++) {
d[j] = Math.round(Math.random() * 10) - 5;
}
}
- this.dataset = mockDataset;
- }
-
- gridReady(grid: SlickGrid) {
- this.gridObj = grid;
- }
- dataviewReady(dataview: SlickDataView) {
- this.dataviewObj = dataview;
+ return mockDataset;
}
-}
+}
\ No newline at end of file
diff --git a/src/app/examples/grid-menu.component.ts b/src/app/examples/grid-menu.component.ts
index 5643c6cf5..51f297b1e 100644
--- a/src/app/examples/grid-menu.component.ts
+++ b/src/app/examples/grid-menu.component.ts
@@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
-import { AngularGridInstance, Column, ExtensionName, FieldType, Filters, Formatters, GridOption, unsubscribeAllObservables } from './../modules/angular-slickgrid';
+import { AngularGridInstance, Column, ExtensionName, FieldType, Filters, Formatters, GridOption, SlickGridMenu, unsubscribeAllObservables } from './../modules/angular-slickgrid';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
@@ -88,12 +88,6 @@ export class GridMenuComponent implements OnInit, OnDestroy {
// we could disable the menu entirely by returning false depending on some code logic
menuUsabilityOverride: (args) => true,
- // use the click event position to reposition the grid menu (defaults to false)
- // basically which offset do we want to use for reposition the grid menu,
- // option1 is where we clicked (true) or option2 is where the icon button is located (false and is the defaults)
- // you probably want to set this to True if you use an external grid menu button BUT set to False when using default grid menu
- useClickToRepositionMenu: true,
-
// all titles optionally support translation keys, if you wish to use that feature then use the title properties with the 'Key' suffix (e.g: titleKey)
// example "customTitle" for a plain string OR "customTitleKey" to use a translation key
customTitleKey: 'CUSTOM_COMMANDS',
@@ -103,7 +97,7 @@ export class GridMenuComponent implements OnInit, OnDestroy {
hideToggleFilterCommand: false, // show/hide internal custom commands
menuWidth: 17,
resizeOnShowHeaderRow: true,
- customItems: [
+ commandItems: [
// add Custom Items Commands which will be appended to the existing internal custom items
// you cannot override an internal items but you can hide them and create your own
// also note that the internal custom commands are in the positionOrder range of 50-60,
@@ -215,10 +209,12 @@ export class GridMenuComponent implements OnInit, OnDestroy {
);
}
- toggleGridMenu(e: Event) {
+ toggleGridMenu(e: any) {
if (this.angularGrid && this.angularGrid.extensionService) {
- const gridMenuInstance = this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.gridMenu);
- gridMenuInstance.showGridMenu(e);
+ const gridMenuInstance = this.angularGrid.extensionService.getExtensionInstanceByName(ExtensionName.gridMenu) as SlickGridMenu;
+ // open the external button Grid Menu, you can also optionally pass Grid Menu options as 2nd argument
+ // for example we want to align our external button on the right without affecting the menu within the grid which will stay aligned on the left
+ gridMenuInstance.showGridMenu(e, { dropSide: 'right' });
}
}
diff --git a/src/app/examples/grid-rowdetail.component.ts b/src/app/examples/grid-rowdetail.component.ts
index 8a9e09fa9..e12845160 100644
--- a/src/app/examples/grid-rowdetail.component.ts
+++ b/src/app/examples/grid-rowdetail.component.ts
@@ -6,7 +6,8 @@ import {
FieldType,
Filters,
Formatters,
- GridOption
+ GridOption,
+ SlickRowDetailView
} from './../modules/angular-slickgrid';
import { RowDetailViewComponent } from './rowdetail-view.component';
import { RowDetailPreloadComponent } from './rowdetail-preload.component';
@@ -42,14 +43,14 @@ export class GridRowDetailComponent implements OnInit {
this.angularGrid = angularGrid;
}
- get rowDetailInstance(): any {
+ get rowDetailInstance(): SlickRowDetailView {
// you can get the SlickGrid RowDetail plugin (addon) instance via 2 ways
// option 1
- return this.angularGrid.extensions.rowDetailView.instance || {};
+ return (this.angularGrid.extensions.rowDetailView.instance || {}) as SlickRowDetailView;
// OR options 2
- // return this.angularGrid && this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView) || {};
+ // return (this.angularGrid && this.angularGrid.extensionService.getExtensionInstanceByName(ExtensionName.rowDetailView) || {}) as SlickRowDetailView;
}
ngOnInit(): void {
@@ -119,7 +120,7 @@ export class GridRowDetailComponent implements OnInit {
// you can override the logic for showing (or not) the expand icon
// for example, display the expand icon only on every 2nd row
- // expandableOverride: (row: number, dataContext: any, grid: SlickGrid) => (dataContext.id % 2 === 1),
+ // expandableOverride: (row: number, dataContext: any, grid: SlickGrid) => (dataContext.rowId % 2 === 1),
// Preload View Component
preloadComponent: RowDetailPreloadComponent,
diff --git a/src/app/examples/grid-rowmove.component.ts b/src/app/examples/grid-rowmove.component.ts
index 41d1f84fc..61df71f99 100644
--- a/src/app/examples/grid-rowmove.component.ts
+++ b/src/app/examples/grid-rowmove.component.ts
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
-import { AngularGridInstance, Column, ExtensionName, Filters, Formatters, GridOption } from './../modules/angular-slickgrid';
+import { AngularGridInstance, Column, ExtensionName, Filters, Formatters, GridOption, SlickRowMoveManager } from './../modules/angular-slickgrid';
@Component({
templateUrl: './grid-rowmove.component.html'
@@ -32,8 +32,8 @@ export class GridRowMoveComponent implements OnInit {
this.angularGrid = angularGrid;
}
- get rowMoveInstance(): any {
- return this.angularGrid?.extensionService?.getSlickgridAddonInstance?.(ExtensionName.rowMoveManager) ?? {};
+ get rowMoveInstance(): SlickRowMoveManager {
+ return (this.angularGrid?.extensionService?.getExtensionInstanceByName?.(ExtensionName.rowMoveManager) ?? {}) as SlickRowMoveManager;
}
ngOnInit(): void {
diff --git a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid.component.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid.component.spec.ts
index 94dde35c6..c9792581e 100644
--- a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid.component.spec.ts
+++ b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid.component.spec.ts
@@ -35,17 +35,18 @@ import {
SlickGrid,
SlickDraggableGrouping,
SortService,
- TreeDataService
+ TreeDataService,
+ SlickGroupItemMetadataProvider
} from '@slickgrid-universal/common';
import * as formatterUtilities from '@slickgrid-universal/common/dist/commonjs/formatters/formatterUtilities';
import { SlickFooterComponent } from '@slickgrid-universal/custom-footer-component';
import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
import { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';
import { GraphqlPaginatedResult, GraphqlService, GraphqlServiceApi, GraphqlServiceOption } from '@slickgrid-universal/graphql';
-import { TextExportService } from '@slickgrid-universal/text-export';
import { of, throwError } from 'rxjs';
import { AngularSlickgridComponent } from '../angular-slickgrid.component';
+import { SlickRowDetailView } from '../../extensions/slickRowDetailView';
import { TranslaterServiceStub } from '../../../../../../test/translaterServiceStub';
import { AngularUtilService, ContainerService, TranslaterService } from '../../services';
import { GridOption } from '../../models';
@@ -58,7 +59,15 @@ const mockAutoAddCustomEditorFormatter = jest.fn();
declare const Slick: any;
const slickEventHandler = new MockSlickEventHandler();
jest.mock('flatpickr', () => { });
-const sharedService = new SharedService();
+
+const mockSlickRowDetailView = {
+ create: jest.fn(),
+ init: jest.fn(),
+} as unknown as SlickRowDetailView;
+
+jest.mock('../../extensions/slickRowDetailView', () => ({
+ SlickRowDetailView: jest.fn().mockImplementation(() => mockSlickRowDetailView),
+}));
const angularUtilServiceStub = {
createAngularComponent: jest.fn(),
@@ -84,16 +93,13 @@ const mockAppRef = {
} as unknown as ApplicationRef;
const extensionServiceStub = {
+ addExtensionToList: jest.fn(),
bindDifferentExtensions: jest.fn(),
createExtensionsBeforeGridCreation: jest.fn(),
dispose: jest.fn(),
renderColumnHeaders: jest.fn(),
- translateCellMenu: jest.fn(),
+ translateAllExtensions: jest.fn(),
translateColumnHeaders: jest.fn(),
- translateColumnPicker: jest.fn(),
- translateContextMenu: jest.fn(),
- translateGridMenu: jest.fn(),
- translateHeaderMenu: jest.fn(),
} as unknown as ExtensionService;
Object.defineProperty(extensionServiceStub, 'extensionList', { get: jest.fn(() => { }), set: jest.fn(), configurable: true });
@@ -718,7 +724,6 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
describe('use grouping', () => {
it('should load groupItemMetaProvider to the DataView when using "draggableGrouping" feature', () => {
const dataviewSpy = jest.spyOn(mockDataViewImplementation.prototype, 'constructor');
- const groupMetaSpy = jest.spyOn(mockGroupItemMetaProviderImplementation.prototype, 'constructor');
const sharedMetaSpy = jest.spyOn(SharedService.prototype, 'groupItemMetadataProvider', 'set');
jest.spyOn(extensionServiceStub, 'extensionList', 'get').mockReturnValue({ draggableGrouping: { pluginName: 'DraggableGrouping' } } as unknown as ExtensionList);
@@ -726,8 +731,8 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
component.initialization(slickEventHandler);
expect(dataviewSpy).toHaveBeenCalledWith({ inlineFilters: false, groupItemMetadataProvider: expect.anything() });
- expect(groupMetaSpy).toHaveBeenCalledWith();
- expect(sharedMetaSpy).toHaveBeenCalledWith(mockGroupItemMetaProvider);
+ expect(sharedService.groupItemMetadataProvider instanceof SlickGroupItemMetadataProvider).toBeTruthy();
+ expect(sharedMetaSpy).toHaveBeenCalledWith(expect.toBeObject());
component.destroy();
});
@@ -741,8 +746,8 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
component.initialization(slickEventHandler);
expect(dataviewSpy).toHaveBeenCalledWith({ inlineFilters: false, groupItemMetadataProvider: expect.anything() });
- expect(groupMetaSpy).toHaveBeenCalledWith();
- expect(sharedMetaSpy).toHaveBeenCalledWith(mockGroupItemMetaProvider);
+ expect(sharedMetaSpy).toHaveBeenCalledWith(expect.toBeObject());
+ expect(sharedService.groupItemMetadataProvider instanceof SlickGroupItemMetadataProvider).toBeTruthy();
component.destroy();
});
@@ -897,24 +902,25 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
expect(spy).not.toHaveBeenCalled();
});
- it('should call "translateColumnHeaders" from ExtensionService when "enableTranslate" is set', () => {
- const spy = jest.spyOn(extensionServiceStub, 'translateColumnHeaders');
+ it('should create the Row Detail View plugin when "enableRowDetailView" is enabled', () => {
+ const initSpy = jest.spyOn(mockSlickRowDetailView, 'init');
+ const createSpy = jest.spyOn(mockSlickRowDetailView, 'create');
- component.gridOptions = { enableTranslate: true } as unknown as GridOption;
+ component.gridOptions = { enableRowDetailView: true } as unknown as GridOption;
component.initialization(slickEventHandler);
- expect(spy).toHaveBeenCalled();
+ expect(component.registeredResources.length).toBe(4);
+ expect(createSpy).toHaveBeenCalled();
+ expect(initSpy).toHaveBeenCalled();
});
- it('should initialize ExportService when "enableTextExport" is set when using Salesforce', () => {
- const fileExportMock = new TextExportService();
- const fileExportSpy = jest.spyOn(fileExportMock, 'init');
- component.gridOptions = { enableTextExport: true, registerExternalResources: [fileExportMock] } as GridOption;
+ it('should call "translateColumnHeaders" from ExtensionService when "enableTranslate" is set', () => {
+ const spy = jest.spyOn(extensionServiceStub, 'translateColumnHeaders');
+
+ component.gridOptions = { enableTranslate: true } as unknown as GridOption;
component.initialization(slickEventHandler);
- expect(fileExportSpy).toHaveBeenCalled();
- expect(component.registeredResources.length).toBe(4); // TextExportService, GridService, GridStateService, SlickEmptyCompositeEditorComponent
- expect(component.registeredResources[0] instanceof TextExportService).toBeTrue();
+ expect(spy).toHaveBeenCalled();
});
it('should add RxJS resource to all necessary Services when RxJS external resource is registered', () => {
@@ -1302,12 +1308,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
});
it('should call multiple translate methods when locale changes', (done) => {
- const transCellMenuSpy = jest.spyOn(extensionServiceStub, 'translateCellMenu');
- const transColHeaderSpy = jest.spyOn(extensionServiceStub, 'translateColumnHeaders');
- const transColPickerSpy = jest.spyOn(extensionServiceStub, 'translateColumnPicker');
- const transContextMenuSpy = jest.spyOn(extensionServiceStub, 'translateContextMenu');
- const transGridMenuSpy = jest.spyOn(extensionServiceStub, 'translateGridMenu');
- const transHeaderMenuSpy = jest.spyOn(extensionServiceStub, 'translateHeaderMenu');
+ const transAllExtSpy = jest.spyOn(extensionServiceStub, 'translateAllExtensions');
const transGroupingColSpanSpy = jest.spyOn(groupingAndColspanServiceStub, 'translateGroupingAndColSpan');
const setHeaderRowSpy = jest.spyOn(mockGrid, 'setHeaderRowVisibility');
@@ -1319,24 +1320,14 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
setTimeout(() => {
expect(setHeaderRowSpy).not.toHaveBeenCalled();
expect(transGroupingColSpanSpy).not.toHaveBeenCalled();
- expect(transCellMenuSpy).toHaveBeenCalled();
- expect(transColHeaderSpy).toHaveBeenCalled();
- expect(transColPickerSpy).toHaveBeenCalled();
- expect(transContextMenuSpy).toHaveBeenCalled();
- expect(transGridMenuSpy).toHaveBeenCalled();
- expect(transHeaderMenuSpy).toHaveBeenCalled();
+ expect(transAllExtSpy).toHaveBeenCalled();
done();
});
});
it('should call "setHeaderRowVisibility", "translateGroupingAndColSpan" and other methods when locale changes', (done) => {
component.columnDefinitions = [{ id: 'firstName', field: 'firstName', filterable: true }];
- const transCellMenuSpy = jest.spyOn(extensionServiceStub, 'translateCellMenu');
- const transColHeaderSpy = jest.spyOn(extensionServiceStub, 'translateColumnHeaders');
- const transColPickerSpy = jest.spyOn(extensionServiceStub, 'translateColumnPicker');
- const transContextMenuSpy = jest.spyOn(extensionServiceStub, 'translateContextMenu');
- const transGridMenuSpy = jest.spyOn(extensionServiceStub, 'translateGridMenu');
- const transHeaderMenuSpy = jest.spyOn(extensionServiceStub, 'translateHeaderMenu');
+ const transAllExtSpy = jest.spyOn(extensionServiceStub, 'translateAllExtensions');
const transGroupingColSpanSpy = jest.spyOn(groupingAndColspanServiceStub, 'translateGroupingAndColSpan');
component.gridOptions = { enableTranslate: true, createPreHeaderPanel: true, enableDraggableGrouping: false } as unknown as GridOption;
@@ -1346,12 +1337,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
setTimeout(() => {
expect(transGroupingColSpanSpy).toHaveBeenCalled();
- expect(transCellMenuSpy).toHaveBeenCalled();
- expect(transColHeaderSpy).toHaveBeenCalled();
- expect(transColPickerSpy).toHaveBeenCalled();
- expect(transContextMenuSpy).toHaveBeenCalled();
- expect(transGridMenuSpy).toHaveBeenCalled();
- expect(transHeaderMenuSpy).toHaveBeenCalled();
+ expect(transAllExtSpy).toHaveBeenCalled();
done();
});
});
diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts
index cc526c004..8e9fbd074 100644
--- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts
+++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts
@@ -8,7 +8,6 @@ import 'slickgrid/lib/jquery.mousewheel';
import 'slickgrid/slick.core';
import 'slickgrid/slick.grid';
import 'slickgrid/slick.dataview';
-import 'slickgrid/slick.groupitemmetadataprovider';
// ...then everything else...
import { AfterViewInit, ApplicationRef, ChangeDetectorRef, Component, ElementRef, Inject, Input, OnDestroy, Optional, } from '@angular/core';
@@ -23,6 +22,7 @@ import {
Column,
ColumnEditor,
DataViewOption,
+ ExtensionName,
ExternalResource,
Locale,
Metrics,
@@ -39,6 +39,7 @@ import {
CollectionService,
EventNamingStyle,
ExtensionService,
+ ExtensionUtility,
FilterFactory,
FilterService,
GridEventService,
@@ -53,27 +54,11 @@ import {
SortService,
TreeDataService,
- // extensions
- AutoTooltipExtension,
- CheckboxSelectorExtension,
- CellExternalCopyManagerExtension,
- CellMenuExtension,
- ColumnPickerExtension,
- ContextMenuExtension,
- DraggableGroupingExtension,
- ExtensionUtility,
- GridMenuExtension,
- GroupItemMetaProviderExtension,
- HeaderMenuExtension,
- HeaderButtonExtension,
- RowSelectionExtension,
- RowMoveManagerExtension,
-
// utilities
autoAddEditorFormatterToColumnsWithEditor,
emptyElement,
- GetSlickEventType,
GridStateType,
+ SlickGroupItemMetadataProvider,
} from '@slickgrid-universal/common';
import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
import { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';
@@ -90,7 +75,7 @@ import { unsubscribeAllObservables } from './../services/utilities';
// Services
import { AngularUtilService } from '../services/angularUtil.service';
-import { RowDetailViewExtension } from '../extensions/rowDetailViewExtension';
+import { SlickRowDetailView } from '../extensions/slickRowDetailView';
import { ContainerService } from '../services/container.service';
// using external non-typed js libraries
@@ -103,7 +88,7 @@ declare const Slick: SlickNamespace;
// make everything transient (non-singleton)
AngularUtilService,
ApplicationRef,
- RowDetailViewExtension,
+ SlickRowDetailView,
TranslaterService,
]
})
@@ -122,10 +107,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
private _isLocalGrid = true;
private _paginationOptions: Pagination | undefined;
private _registeredResources: ExternalResource[] = [];
+ private slickRowDetailView?: SlickRowDetailView;
dataView!: SlickDataView;
slickGrid!: SlickGrid;
groupingDefinition: any = {};
- groupItemMetadataProvider: any;
+ groupItemMetadataProvider?: SlickGroupItemMetadataProvider;
backendServiceApi?: BackendServiceApi;
locales!: Locale;
metrics?: Metrics;
@@ -143,13 +129,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
slickFooter?: SlickFooterComponent;
slickPagination?: SlickPaginationComponent;
- // extensions
- extensionUtility: ExtensionUtility;
-
// services
backendUtilityService!: BackendUtilityService;
collectionService: CollectionService;
extensionService: ExtensionService;
+ extensionUtility: ExtensionUtility;
filterFactory!: FilterFactory;
filterService: FilterService;
gridEventService: GridEventService;
@@ -299,7 +283,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this.gridEventService = externalServices?.gridEventService ?? new GridEventService();
this.sharedService = externalServices?.sharedService ?? new SharedService();
this.collectionService = externalServices?.collectionService ?? new CollectionService(this.translaterService);
- this.extensionUtility = externalServices?.extensionUtility ?? new ExtensionUtility(this.sharedService, this.translaterService);
+ this.extensionUtility = externalServices?.extensionUtility ?? new ExtensionUtility(this.sharedService, this.backendUtilityService, this.translaterService);
this.filterFactory = new FilterFactory(slickgridConfig, this.translaterService, this.collectionService);
this.filterService = externalServices?.filterService ?? new FilterService(this.filterFactory as any, this._eventPubSubService, this.sharedService, this.backendUtilityService);
this.resizerService = externalServices?.resizerService ?? new ResizerService(this._eventPubSubService);
@@ -307,44 +291,19 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this.treeDataService = externalServices?.treeDataService ?? new TreeDataService(this._eventPubSubService, this.sharedService, this.sortService);
this.paginationService = externalServices?.paginationService ?? new PaginationService(this._eventPubSubService, this.sharedService, this.backendUtilityService);
- // extensions
- const autoTooltipExtension = new AutoTooltipExtension(this.sharedService);
- const cellExternalCopyManagerExtension = new CellExternalCopyManagerExtension(this.extensionUtility, this.sharedService);
- const cellMenuExtension = new CellMenuExtension(this.extensionUtility, this.sharedService, this.translaterService);
- const contextMenuExtension = new ContextMenuExtension(this.extensionUtility, this._eventPubSubService, this.sharedService, this.treeDataService, this.translaterService);
- const columnPickerExtension = new ColumnPickerExtension(this.extensionUtility, this.sharedService);
- const checkboxExtension = new CheckboxSelectorExtension(this.sharedService);
- const draggableGroupingExtension = new DraggableGroupingExtension(this.extensionUtility, this._eventPubSubService, this.sharedService);
- const gridMenuExtension = new GridMenuExtension(this.extensionUtility, this.filterService, this.sharedService, this.sortService, this.backendUtilityService, this.translaterService);
- const groupItemMetaProviderExtension = new GroupItemMetaProviderExtension(this.sharedService);
- const headerButtonExtension = new HeaderButtonExtension(this.extensionUtility, this.sharedService);
- const headerMenuExtension = new HeaderMenuExtension(this.extensionUtility, this.filterService, this._eventPubSubService, this.sharedService, this.sortService, this.translaterService);
- const rowDetailViewExtension = new RowDetailViewExtension(this.angularUtilService, this.appRef, this._eventPubSubService, this.sharedService, this.rxjs);
- const rowMoveManagerExtension = new RowMoveManagerExtension(this.sharedService);
- const rowSelectionExtension = new RowSelectionExtension(this.sharedService);
-
this.extensionService = externalServices?.extensionService ?? new ExtensionService(
- autoTooltipExtension,
- cellExternalCopyManagerExtension,
- cellMenuExtension,
- checkboxExtension,
- columnPickerExtension,
- contextMenuExtension,
- draggableGroupingExtension,
- gridMenuExtension,
- groupItemMetaProviderExtension,
- headerButtonExtension,
- headerMenuExtension,
- rowDetailViewExtension,
- rowMoveManagerExtension,
- rowSelectionExtension,
+ this.extensionUtility,
+ this.filterService,
+ this._eventPubSubService,
this.sharedService,
+ this.sortService,
+ this.treeDataService,
this.translaterService,
);
this.gridStateService = externalServices?.gridStateService ?? new GridStateService(this.extensionService, this.filterService, this._eventPubSubService, this.sharedService, this.sortService, this.treeDataService);
this.gridService = externalServices?.gridService ?? new GridService(this.gridStateService, this.filterService, this._eventPubSubService, this.paginationService, this.sharedService, this.sortService, this.treeDataService);
- this.groupingService = externalServices?.groupingAndColspanService ?? new GroupingAndColspanService(this.extensionUtility, this.extensionService, this._eventPubSubService);
+ this.groupingService = externalServices?.groupingAndColspanService ?? new GroupingAndColspanService(this.extensionUtility, this._eventPubSubService);
this.serviceList = [
this.extensionService,
@@ -519,7 +478,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
let dataViewOptions: DataViewOption = { inlineFilters: dataviewInlineFilters };
if (this.gridOptions.draggableGrouping || this.gridOptions.enableGrouping) {
- this.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
+ this.groupItemMetadataProvider = new SlickGroupItemMetadataProvider();
this.sharedService.groupItemMetadataProvider = this.groupItemMetadataProvider;
dataViewOptions = { ...dataViewOptions, groupItemMetadataProvider: this.groupItemMetadataProvider };
}
@@ -556,6 +515,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this.slickGrid = new Slick.Grid(`#${this.gridId}`, this.customDataView || this.dataView, this._columnDefinitions, this.gridOptions);
this.sharedService.dataView = this.dataView;
this.sharedService.slickGrid = this.slickGrid;
+ this.sharedService.gridContainerElement = this.elm.nativeElement as HTMLDivElement;
this.extensionService.bindDifferentExtensions();
this.bindDifferentHooks(this.slickGrid, this.gridOptions, this.dataView);
@@ -593,7 +553,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
// if you don't want the items that are not visible (due to being filtered out or being on a different page)
// to stay selected, pass 'false' to the second arg
- const selectionModel = this.slickGrid && this.slickGrid.getSelectionModel();
+ const selectionModel = this.slickGrid?.getSelectionModel();
if (selectionModel && this.gridOptions && this.gridOptions.dataView && this.gridOptions.dataView.hasOwnProperty('syncGridSelection')) {
// if we are using a Backend Service, we will do an extra flag check, the reason is because it might have some unintended behaviors
// with the BackendServiceApi because technically the data in the page changes the DataView on every page change.
@@ -833,6 +793,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
if (this.translate?.onLangChange) {
// translate some of them on first load, then on each language change
if (gridOptions.enableTranslate) {
+ this.extensionService.translateAllExtensions();
this.translateColumnHeaderTitleKeys();
this.translateColumnGroupKeys();
}
@@ -843,12 +804,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this._eventPubSubService.publish('onLanguageChange');
if (gridOptions.enableTranslate) {
- this.extensionService.translateCellMenu();
- this.extensionService.translateColumnHeaders();
- this.extensionService.translateColumnPicker();
- this.extensionService.translateContextMenu();
- this.extensionService.translateGridMenu();
- this.extensionService.translateHeaderMenu();
+ this.extensionService.translateAllExtensions();
this.translateColumnHeaderTitleKeys();
this.translateColumnGroupKeys();
if (gridOptions.createPreHeaderPanel && !gridOptions.enableDraggableGrouping) {
@@ -874,9 +830,8 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
// expose all Slick Grid Events through dispatch
for (const prop in grid) {
if (grid.hasOwnProperty(prop) && prop.startsWith('on')) {
- const gridEventHandler = (grid as any)[prop];
const gridEventName = this._eventPubSubService.getEventNameByNamingConvention(prop, slickgridEventPrefix);
- (this._eventHandler as SlickEventHandler>).subscribe(gridEventHandler, (event, args) => {
+ this._eventHandler.subscribe((grid as any)[prop], (event, args) => {
return this._eventPubSubService.dispatchCustomEvent(gridEventName, { eventData: event, args });
});
}
@@ -885,8 +840,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
// expose all Slick DataView Events through dispatch
for (const prop in dataView) {
if (dataView.hasOwnProperty(prop) && prop.startsWith('on')) {
- const dataViewEventHandler = (dataView as any)[prop];
- (this._eventHandler as SlickEventHandler>).subscribe(dataViewEventHandler, (event, args) => {
+ this._eventHandler.subscribe((dataView as any)[prop], (event, args) => {
const dataViewEventName = this._eventPubSubService.getEventNameByNamingConvention(prop, slickgridEventPrefix);
return this._eventPubSubService.dispatchCustomEvent(dataViewEventName, { eventData: event, args });
});
@@ -925,13 +879,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this.loadFilterPresetsWhenDatasetInitialized();
// When data changes in the DataView, we need to refresh the metrics and/or display a warning if the dataset is empty
- const onRowCountChangedHandler = dataView.onRowCountChanged;
- (this._eventHandler as SlickEventHandler>).subscribe(onRowCountChangedHandler, (_e, args) => {
+ this._eventHandler.subscribe(dataView.onRowCountChanged, () => {
grid.invalidate();
this.handleOnItemCountChanged(this.dataView.getFilteredItemCount() || 0, dataView.getItemCount());
});
- const onSetItemsCalledHandler = dataView.onSetItemsCalled;
- (this._eventHandler as SlickEventHandler>).subscribe(onSetItemsCalledHandler, (_e, args) => {
+ this._eventHandler.subscribe(dataView.onSetItemsCalled, (_e, args) => {
grid.invalidate();
this.handleOnItemCountChanged(this.dataView.getFilteredItemCount(), args.itemCount);
@@ -941,8 +893,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
}
});
- const onRowsChangedHandler = dataView.onRowsChanged;
- (this._eventHandler as SlickEventHandler>).subscribe(onRowsChangedHandler, (_e, args) => {
+ this._eventHandler.subscribe(dataView.onRowsChanged, (_e, args) => {
// filtering data with local dataset will not always show correctly unless we call this updateRow/render
// also don't use "invalidateRows" since it destroys the entire row and as bad user experience when updating a row
// see commit: https://github.com/ghiscoding/aurelia-slickgrid/commit/8c503a4d45fba11cbd8d8cc467fae8d177cc4f60
@@ -1196,7 +1147,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
private loadRowSelectionPresetWhenExists() {
// if user entered some Row Selections "presets"
const presets = this.gridOptions?.presets;
- const selectionModel = this.slickGrid?.getSelectionModel?.();
+ const selectionModel = this.slickGrid?.getSelectionModel();
const enableRowSelection = this.gridOptions && (this.gridOptions.enableCheckboxSelector || this.gridOptions.enableRowSelection);
if (enableRowSelection && selectionModel && presets && presets.rowSelection && (Array.isArray(presets.rowSelection.gridRowIndexes) || Array.isArray(presets.rowSelection.dataContextIds))) {
let dataContextIds = presets.rowSelection.dataContextIds;
@@ -1292,6 +1243,13 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy {
this.extensionService.translateColumnHeaders();
}
+ if (this.gridOptions.enableRowDetailView) {
+ this.slickRowDetailView = new SlickRowDetailView(this.angularUtilService, this.appRef, this._eventPubSubService, this.rxjs);
+ this.slickRowDetailView.create(this.columnDefinitions, this.gridOptions);
+ this._registeredResources.push(this.slickRowDetailView);
+ this.extensionService.addExtensionToList(ExtensionName.rowDetailView, { name: ExtensionName.rowDetailView, class: this.slickRowDetailView, instance: this.slickRowDetailView });
+ }
+
// also initialize (render) the empty warning component
this.slickEmptyWarning = new SlickEmptyWarningComponent();
this._registeredResources.push(this.slickEmptyWarning);
diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/index.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/index.spec.ts
new file mode 100644
index 000000000..3c92837f2
--- /dev/null
+++ b/src/app/modules/angular-slickgrid/extensions/__tests__/index.spec.ts
@@ -0,0 +1,11 @@
+import * as entry from '../index';
+
+describe('Testing library entry point', () => {
+ it('should have an index entry point defined', () => {
+ expect(entry).toBeTruthy();
+ });
+
+ it('should have all exported object defined', () => {
+ expect(typeof entry.SlickRowDetailView).toBe('function');
+ });
+});
\ No newline at end of file
diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/rowDetailViewExtension.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/rowDetailViewExtension.spec.ts
deleted file mode 100644
index 0e0149d59..000000000
--- a/src/app/modules/angular-slickgrid/extensions/__tests__/rowDetailViewExtension.spec.ts
+++ /dev/null
@@ -1,674 +0,0 @@
-import { ApplicationRef, Component } from '@angular/core';
-import { TestBed, } from '@angular/core/testing';
-import { TranslateModule } from '@ngx-translate/core';
-import { Column, CurrentFilter, FilterService, SharedService, SlickEventHandler, SlickDataView, SlickGrid, SlickNamespace, SortService, SlickRowDetailView, RxJsFacade, } from '@slickgrid-universal/common';
-import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
-import { of, Subject } from 'rxjs';
-
-import { GridOption } from '../../models/gridOption.interface';
-import { RowDetailViewExtension } from '../rowDetailViewExtension';
-import { AngularUtilService } from '../../services';
-import { RowDetailView } from '../../models/rowDetailView.interface';
-import { RxJsResourceStub } from '../../../../../../test/rxjsResourceStub';
-
-declare const Slick: SlickNamespace;
-const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
-const PRELOAD_CONTAINER_PREFIX = 'container_loading';
-
-const applicationRefStub = {
- detachView: jest.fn(),
-} as unknown as ApplicationRef;
-
-const angularUtilServiceStub = {
- createAngularComponent: jest.fn(),
- createAngularComponentAppendToDom: jest.fn(),
-} as unknown as AngularUtilService;
-
-const filterServiceStub = {
- clearFilters: jest.fn(),
- onFilterChanged: new Subject(),
-} as unknown as FilterService;
-
-const sortServiceStub = {
- clearSorting: jest.fn(),
-} as unknown as SortService;
-
-const dataViewStub = {
- refresh: jest.fn(),
-} as unknown as SlickDataView;
-
-const gridStub = {
- getOptions: jest.fn(),
- getSelectionModel: jest.fn(),
- registerPlugin: jest.fn(),
- setSelectionModel: jest.fn(),
- onColumnsReordered: new Slick.Event(),
- onSelectedRowsChanged: new Slick.Event(),
- onSort: new Slick.Event(),
-} as unknown as SlickGrid;
-
-const mockAddon = jest.fn().mockImplementation(() => ({
- init: jest.fn(),
- destroy: jest.fn(),
- getColumnDefinition: jest.fn(),
- onAsyncResponse: new Slick.Event(),
- onAsyncEndUpdate: new Slick.Event(),
- onAfterRowDetailToggle: new Slick.Event(),
- onBeforeRowDetailToggle: new Slick.Event(),
- onRowOutOfViewportRange: new Slick.Event(),
- onRowBackToViewportRange: new Slick.Event()
-}));
-
-const mockSelectionModel = jest.fn().mockImplementation(() => ({
- init: jest.fn(),
- destroy: jest.fn()
-}));
-
-@Component({
- template: `Loading...
`
-})
-export class TestPreloadComponent { }
-
-describe('rowDetailViewExtension', () => {
- jest.mock('slickgrid/plugins/slick.rowdetailview', () => mockAddon);
- Slick.Plugins = {
- RowDetailView: mockAddon
- } as any;
-
- jest.mock('slickgrid/plugins/slick.rowselectionmodel', () => mockSelectionModel);
- Slick.RowSelectionModel = mockSelectionModel;
-
- let eventHandler: SlickEventHandler;
- let extension: RowDetailViewExtension;
- let eventPubSubService: EventPubSubService;
- let rxjsResourceStub: RxJsResourceStub;
- const div = document.createElement('div');
- div.innerHTML = ``;
- document.body.appendChild(div);
-
- const gridOptionsMock = {
- enableRowDetailView: true,
- rowDetailView: {
- cssClass: 'detail-view-toggle',
- panelRows: 1,
- keyPrefix: '__',
- useRowClick: true,
- useSimpleViewportCalc: true,
- saveDetailViewOnScroll: false,
- process: () => new Promise((resolve) => resolve('process resolved')),
- viewComponent: null,
- onExtensionRegistered: jest.fn(),
- onAsyncResponse: (e: Event, args: { item: any; detailView?: any }) => { },
- onAsyncEndUpdate: (e: Event, args: { item: any; grid: SlickGrid; }) => { },
- onAfterRowDetailToggle: (e: Event, args: { item: any; expandedRows: any[]; grid: SlickGrid; }) => { },
- onBeforeRowDetailToggle: (e: Event, args: { item: any; grid: SlickGrid; }) => { },
- onRowOutOfViewportRange: (e: Event, args: { item: any; rowId: number; rowIndex: number; expandedRows: any[]; rowIdsOutOfViewport: number[]; grid: SlickGrid; }) => { },
- onRowBackToViewportRange: (e: Event, args: { item: any; rowId: number; rowIndex: number; expandedRows: any[]; rowIdsOutOfViewport: number[]; grid: SlickGrid; }) => { },
- }
- } as unknown as GridOption;
-
- beforeEach(async () => {
- eventHandler = new Slick.EventHandler();
- eventPubSubService = new EventPubSubService(div);
- rxjsResourceStub = new RxJsResourceStub();
-
- await TestBed.configureTestingModule({
- declarations: [TestPreloadComponent],
- providers: [
- RowDetailViewExtension,
- SharedService,
- { provide: ApplicationRef, useValue: applicationRefStub },
- { provide: AngularUtilService, useValue: angularUtilServiceStub },
- { provide: EventPubSubService, useValue: eventPubSubService },
- { provide: FilterService, useValue: filterServiceStub },
- { provide: RxJsFacade, useValue: rxjsResourceStub },
- { provide: SortService, useValue: sortServiceStub },
- ],
- imports: [TranslateModule.forRoot()]
- });
- extension = TestBed.inject(RowDetailViewExtension);
- });
-
- afterEach(() => {
- eventHandler.unsubscribeAll();
- jest.clearAllMocks();
- });
-
- it('should return null after calling "create" method when either the column definitions or the grid options is missing', () => {
- const output = extension.create([] as Column[], null as any);
- expect(output).toBeNull();
- });
-
- it('should return null after calling "register" method when either the grid object or the grid options is missing', () => {
- const output = extension.register();
- expect(output).toBeNull();
- });
-
- describe('create method', () => {
- let columnsMock: Column[];
-
- beforeEach(() => {
- gridOptionsMock.datasetIdPropertyName = 'id';
- columnsMock = [
- { id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
- { id: 'field2', field: 'field2', width: 50 }
- ];
- jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
- jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(dataViewStub);
- jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
- jest.clearAllMocks();
- });
-
- it('should create the addon', () => {
- extension.create(columnsMock, gridOptionsMock);
-
- expect(mockAddon).toHaveBeenCalledWith({
- cssClass: 'detail-view-toggle',
- keyPrefix: '__',
- panelRows: 1,
- postTemplate: expect.anything(),
- preTemplate: expect.anything(),
- process: expect.anything(),
- saveDetailViewOnScroll: false,
- useRowClick: true,
- useSimpleViewportCalc: true,
- viewComponent: null,
- onExtensionRegistered: expect.anything(),
- onAsyncResponse: expect.anything(),
- onAsyncEndUpdate: expect.anything(),
- onAfterRowDetailToggle: expect.anything(),
- onBeforeRowDetailToggle: expect.anything(),
- onRowOutOfViewportRange: expect.anything(),
- onRowBackToViewportRange: expect.anything(),
- });
- });
-
- it('should run the "process" method when defined', async () => {
- (gridOptionsMock.rowDetailView as RowDetailView).process = () => new Promise((resolve) => resolve('process resolved'));
- const output = await (gridOptionsMock.rowDetailView as RowDetailView).process({ id: 'field1', field: 'field1' });
- expect(output).toBe('process resolved');
- });
-
- it('should use "addRxJsResource" method and run the "process" method when defined', async () => {
- extension.addRxJsResource(rxjsResourceStub);
- (gridOptionsMock.rowDetailView as RowDetailView).process = () => new Promise((resolve) => resolve('process resolved'));
- const output = await (gridOptionsMock.rowDetailView as RowDetailView).process({ id: 'field1', field: 'field1' });
- expect(output).toBe('process resolved');
- });
-
- it('should provide a sanitized "preTemplate" when only a "preloadComponent" is provided (meaning no "preTemplate" is originally provided)', async () => {
- (gridOptionsMock.rowDetailView as RowDetailView).preloadComponent = TestPreloadComponent;
- const output = await (gridOptionsMock.rowDetailView as RowDetailView).preTemplate!();
- expect(output).toEqual(``);
- });
-
- it('should provide a sanitized "postTemplate" when only a "viewComponent" is provided (meaning no "postTemplate" is originally provided)', async () => {
- (gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestPreloadComponent;
- const output = await (gridOptionsMock.rowDetailView as RowDetailView).postTemplate!({ id: 'field1', field: 'field1' });
- expect(output).toEqual(``);
- });
-
- it('should define "datasetIdPropertyName" with different "id" and provide a sanitized "postTemplate" when only a "viewComponent" is provided (meaning no "postTemplate" is originally provided)', async () => {
- (gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestPreloadComponent;
- gridOptionsMock.datasetIdPropertyName = 'rowId';
- const output = await (gridOptionsMock.rowDetailView as RowDetailView).postTemplate!({ rowId: 'field1', field: 'field1' });
- expect(output).toEqual(``);
- });
-
- it('should add a reserved column for icons in 1st column index', () => {
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- const spy = jest.spyOn(instance, 'getColumnDefinition').mockReturnValue({ id: '_detail_selector', field: 'sel' });
- extension.create(columnsMock, gridOptionsMock);
-
- expect(spy).toHaveBeenCalled();
- expect(columnsMock).toEqual([
- {
- excludeFromColumnPicker: true,
- excludeFromExport: true,
- excludeFromGridMenu: true,
- excludeFromHeaderMenu: true,
- excludeFromQuery: true,
- field: 'sel',
- id: '_detail_selector'
- },
- { id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
- { id: 'field2', field: 'field2', width: 50 },
- ]);
- });
-
- it('should expect the column to be at a different column index position when "columnIndexPosition" is defined', () => {
- (gridOptionsMock.rowDetailView as RowDetailView).columnIndexPosition = 2;
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- const spy = jest.spyOn(instance, 'getColumnDefinition').mockReturnValue({ id: '_detail_selector', field: 'sel' });
- extension.create(columnsMock, gridOptionsMock);
-
- expect(spy).toHaveBeenCalled();
- expect(columnsMock).toEqual([
- { id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
- { id: 'field2', field: 'field2', width: 50 },
- {
- excludeFromColumnPicker: true,
- excludeFromExport: true,
- excludeFromGridMenu: true,
- excludeFromHeaderMenu: true,
- excludeFromQuery: true,
- field: 'sel',
- id: '_detail_selector'
- },
- ]);
- });
- });
-
- describe('registered addon', () => {
- let columnsMock: Column[];
-
- beforeEach(() => {
- (gridOptionsMock.rowDetailView as RowDetailView).preloadComponent = TestPreloadComponent;
- (gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestPreloadComponent;
- columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }];
- jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
- jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
- jest.clearAllMocks();
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should register the addon', () => {
- const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onExtensionRegistered');
- const pluginSpy = jest.spyOn(SharedService.prototype.slickGrid, 'registerPlugin');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- const addonInstance = extension.getAddonInstance();
- extension.register();
-
- expect(instance).toBeTruthy();
- expect(instance).toEqual(addonInstance);
- expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
- expect(mockSelectionModel).toHaveBeenCalledWith({ selectActiveRow: true });
- expect(pluginSpy).toHaveBeenCalledWith(instance);
- });
-
- it('should call internal event handler subscribe and expect the "onAsyncResponse" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onAsyncResponse!.notify({ item: columnsMock[0], detailView: {} }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], detailView: {} });
- expect(onAsyncEndSpy).not.toHaveBeenCalled();
- expect(onAfterRowSpy).not.toHaveBeenCalled();
- expect(onBeforeRowSpy).not.toHaveBeenCalled();
- expect(onRowOutViewSpy).not.toHaveBeenCalled();
- expect(onRowBackViewSpy).not.toHaveBeenCalled();
- });
-
- it('should call internal event handler subscribe and expect the "onAsyncEndUpdate" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const renderSpy = jest.spyOn(extension, 'renderViewModel');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onAsyncEndUpdate!.notify({ item: columnsMock[0], grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).not.toHaveBeenCalled();
- expect(onAsyncEndSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], grid: gridStub });
- expect(renderSpy).toHaveBeenCalledWith({ cssClass: 'red', field: 'field1', id: 'field1', width: 100, });
- expect(onAfterRowSpy).not.toHaveBeenCalled();
- expect(onBeforeRowSpy).not.toHaveBeenCalled();
- expect(onRowOutViewSpy).not.toHaveBeenCalled();
- expect(onRowBackViewSpy).not.toHaveBeenCalled();
- });
-
- it('should call internal event handler subscribe and expect the "onAfterRowDetailToggle" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onAfterRowDetailToggle!.notify({ item: columnsMock[0], expandedRows: [0], grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).not.toHaveBeenCalled();
- expect(onAsyncEndSpy).not.toHaveBeenCalled();
- expect(onAfterRowSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], expandedRows: [0], grid: gridStub });
- expect(onBeforeRowSpy).not.toHaveBeenCalled();
- expect(onRowOutViewSpy).not.toHaveBeenCalled();
- expect(onRowBackViewSpy).not.toHaveBeenCalled();
- });
-
- it('should call internal event handler subscribe and expect the "onBeforeRowDetailToggle" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onBeforeRowDetailToggle!.notify({ item: columnsMock[0], grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).not.toHaveBeenCalled();
- expect(onAsyncEndSpy).not.toHaveBeenCalled();
- expect(onAfterRowSpy).not.toHaveBeenCalled();
- expect(onBeforeRowSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], grid: gridStub });
- expect(onRowOutViewSpy).not.toHaveBeenCalled();
- expect(onRowBackViewSpy).not.toHaveBeenCalled();
- });
-
- it('should call internal event handler subscribe and expect the "onRowOutOfViewportRange" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onRowOutOfViewportRange!.notify(
- { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub },
- new Slick.EventData(),
- gridStub
- );
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).not.toHaveBeenCalled();
- expect(onAsyncEndSpy).not.toHaveBeenCalled();
- expect(onAfterRowSpy).not.toHaveBeenCalled();
- expect(onBeforeRowSpy).not.toHaveBeenCalled();
- expect(onRowOutViewSpy).toHaveBeenCalledWith(
- expect.anything(), {
- item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub
- });
- expect(onRowBackViewSpy).not.toHaveBeenCalled();
- });
-
- it('should call internal event handler subscribe and expect the "onRowBackToViewportRange" option to be called when addon notify is called', () => {
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
-
- const onAsyncRespSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncResponse');
- const onAsyncEndSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
- const onAfterRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
- const onBeforeRowSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
- const onRowOutViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
- const onRowBackViewSpy = jest.spyOn(SharedService.prototype.gridOptions.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- instance.onRowBackToViewportRange!.notify(
- { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub },
- new Slick.EventData(),
- gridStub
- );
-
- expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
- expect(handlerSpy).toHaveBeenCalledWith(
- { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
- expect.anything()
- );
- expect(onAsyncRespSpy).not.toHaveBeenCalled();
- expect(onAsyncEndSpy).not.toHaveBeenCalled();
- expect(onAfterRowSpy).not.toHaveBeenCalled();
- expect(onBeforeRowSpy).not.toHaveBeenCalled();
- expect(onRowOutViewSpy).not.toHaveBeenCalled();
- expect(onRowBackViewSpy).toHaveBeenCalledWith(
- expect.anything(), {
- item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub
- });
- });
-
- it('should call Angular Util "createAngularComponentAppendToDom" when grid "onColumnsReordered" is triggered', () => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn() } } as any);
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- eventHandler.subscribe(instance.onBeforeRowDetailToggle!, () => {
- gridStub.onColumnsReordered.notify({ impactedColumns: [mockColumn], grid: gridStub }, new Slick.EventData(), gridStub);
- expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- });
-
- it('should call "redrawAllViewComponents" when using Row Selection and the event "onSelectedRowsChanged" is triggered', () => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- gridOptionsMock.enableCheckboxSelector = true;
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const redrawSpy = jest.spyOn(extension, 'redrawAllViewComponents');
- const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- extension.register();
- eventHandler.subscribe(instance.onBeforeRowDetailToggle!, () => {
- gridStub.onSelectedRowsChanged.notify({ rows: [0], previousSelectedRows: [], grid: gridStub }, new Slick.EventData(), gridStub);
- expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
- instance.onBeforeRowDetailToggle!.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- expect(redrawSpy).toHaveBeenCalledTimes(2);
- });
-
- it('should call "redrawAllViewComponents" when event "filterChanged" is triggered', () => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const redrawSpy = jest.spyOn(extension, 'redrawAllViewComponents');
- const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- extension.register();
- eventHandler.subscribe(instance.onBeforeRowDetailToggle!, () => {
- eventPubSubService.publish('onFilterChanged', { columnId: 'field1', operator: '=', searchTerms: [] });
- expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
- instance.onBeforeRowDetailToggle!.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- expect(redrawSpy).toHaveBeenCalledTimes(2);
- });
-
- it('should call "renderAllViewComponents" when grid event "onAfterRowDetailToggle" is triggered', (done) => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const getElementSpy = jest.spyOn(document, 'getElementsByClassName');
- const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- extension.register();
- instance.onAfterRowDetailToggle!.subscribe(() => {
- expect(getElementSpy).toHaveBeenCalledWith('container_field1');
- expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
- done();
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
- instance.onAfterRowDetailToggle!.notify({ item: mockColumn, expandedRows: [], grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- });
-
- it('should call "redrawViewComponent" when grid event "onRowBackToViewportRange" is triggered', (done) => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const getElementSpy = jest.spyOn(document, 'getElementsByClassName');
- const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
- const renderSpy = jest.spyOn(extension, 'renderViewModel');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- extension.register();
- instance.onRowBackToViewportRange!.subscribe(() => {
- expect(getElementSpy).toHaveBeenCalledWith('container_field1');
- expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
- expect(renderSpy).toReturnWith(
- expect.objectContaining({
- componentRef: {
- destroy: expect.anything(),
- instance: expect.objectContaining({ model: mockColumn, addon: expect.anything(), grid: gridStub, dataView: dataViewStub })
- }
- })
- );
- done();
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
- instance.onRowBackToViewportRange!.notify({ item: mockColumn, rowId: 0, rowIndex: 0, expandedRows: [], rowIdsOutOfViewport: [], grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- });
-
- it('should run the internal "onProcessing" and call "notifyTemplate" with a Promise when "process" method is defined and executed', (done) => {
- const mockItem = { id: 2, firstName: 'John', lastName: 'Doe' };
- (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => new Promise((resolve) => resolve(item));
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- instance.onAsyncResponse!.subscribe((_e: Event, response: any) => {
- expect(response).toEqual(expect.objectContaining({ item: mockItem }));
- done();
- });
-
- (gridOptionsMock.rowDetailView as RowDetailView).process(mockItem);
- });
-
- it('should run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', (done) => {
- const mockItem = { id: 2, firstName: 'John', lastName: 'Doe' };
- (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => of(mockItem);
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- instance.onAsyncResponse!.subscribe((_e: Event, response: any) => {
- expect(response).toEqual(expect.objectContaining({ item: mockItem }));
- done();
- });
-
- (gridOptionsMock.rowDetailView as RowDetailView).process({ id: 'field1', field: 'field1' });
- });
-
- it('should define "datasetIdPropertyName" with different "id" and run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', (done) => {
- const mockItem = { rowId: 2, firstName: 'John', lastName: 'Doe' };
- (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => of(mockItem);
- gridOptionsMock.datasetIdPropertyName = 'rowId';
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
-
- instance.onAsyncResponse!.subscribe((_e: Event, response: any) => {
- expect(response).toEqual(expect.objectContaining({ item: mockItem }));
- done();
- });
-
- (gridOptionsMock.rowDetailView as RowDetailView).process({ rowId: 'field1', field: 'field1' });
- });
-
- it('should run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', async () => {
- const mockItem = { firstName: 'John', lastName: 'Doe' };
- (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => new Promise((resolve) => resolve(item));
- extension.create(columnsMock, gridOptionsMock);
-
- try {
- await (gridOptionsMock.rowDetailView as RowDetailView).process(mockItem);
- } catch (e) {
- expect(e.toString()).toContain(`[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback`);
- }
- });
-
- it('should call Angular Util "disposeAllViewComponents" when grid "onSort" is triggered', () => {
- const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
- jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
- const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
- const disposeSpy = jest.spyOn(extension, 'disposeAllViewComponents');
-
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- eventHandler.subscribe(instance.onBeforeRowDetailToggle!, () => {
- gridStub.onSort.notify({ columnId: 'field1', sortCol: mockColumn, sortAsc: true }, new Slick.EventData(), gridStub);
- expect(disposeSpy).toHaveBeenCalled();
- });
- instance.onBeforeRowDetailToggle!.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
-
- expect(handlerSpy).toHaveBeenCalled();
- });
-
- it('should dispose of the addon', () => {
- const instance = extension.create(columnsMock, gridOptionsMock) as SlickRowDetailView;
- extension.register();
- const destroySpy = jest.spyOn(instance, 'destroy');
-
- extension.dispose();
-
- expect(destroySpy).toHaveBeenCalled();
- });
- });
-
- describe('possible error thrown', () => {
- it('should throw an error when calling "create" method without "rowDetailView" options defined', () => {
- const copyGridOptionsMock = { ...gridOptionsMock };
- copyGridOptionsMock.rowDetailView = undefined;
- jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
-
- expect(() => extension.create([], copyGridOptionsMock)).toThrowError(`The Row Detail View requires options to be passed via the "rowDetailView" property of the Grid Options`);
- });
-
- it('should throw an error when calling "create" method without "rowDetailView" options defined', () => {
- const copyGridOptionsMock = { ...gridOptionsMock };
- (copyGridOptionsMock.rowDetailView as RowDetailView).process = undefined as any;
- jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
-
- expect(() => extension.create([], copyGridOptionsMock)).toThrowError(`You need to provide a "process" function for the Row Detail Extension to work properly`);
- });
- });
-});
diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts
new file mode 100644
index 000000000..566282d68
--- /dev/null
+++ b/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts
@@ -0,0 +1,611 @@
+import { ApplicationRef, Component } from '@angular/core';
+import { TestBed, } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { Column, SharedService, SlickEventHandler, SlickGrid, SlickNamespace, RxJsFacade, } from '@slickgrid-universal/common';
+import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
+import { SlickRowSelectionModel } from '@slickgrid-universal/common/dist/commonjs/extensions/slickRowSelectionModel';
+import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
+import { of } from 'rxjs';
+
+import { GridOption } from '../../models/gridOption.interface';
+import { AngularUtilService } from '../../services';
+import { RowDetailView } from '../../models/rowDetailView.interface';
+import { RxJsResourceStub } from '../../../../../../test/rxjsResourceStub';
+import { SlickRowDetailView } from '../slickRowDetailView';
+jest.mock('@slickgrid-universal/row-detail-view-plugin');
+
+declare const Slick: SlickNamespace;
+const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
+const PRELOAD_CONTAINER_PREFIX = 'container_loading';
+
+const applicationRefStub = {
+ detachView: jest.fn(),
+} as unknown as ApplicationRef;
+
+const angularUtilServiceStub = {
+ createAngularComponent: jest.fn(),
+ createAngularComponentAppendToDom: jest.fn(),
+} as unknown as AngularUtilService;
+
+const gridOptionsMock = {
+ enableRowDetailView: true,
+ rowDetailView: {
+ cssClass: 'detail-view-toggle',
+ panelRows: 1,
+ keyPrefix: '__',
+ useRowClick: true,
+ useSimpleViewportCalc: true,
+ saveDetailViewOnScroll: false,
+ process: () => new Promise((resolve) => resolve('process resolved')),
+ viewComponent: null,
+ onExtensionRegistered: jest.fn(),
+ onAsyncResponse: () => { },
+ onAsyncEndUpdate: () => { },
+ onAfterRowDetailToggle: () => { },
+ onBeforeRowDetailToggle: () => { },
+ onRowOutOfViewportRange: () => { },
+ onRowBackToViewportRange: () => { },
+ }
+} as unknown as GridOption;
+
+const gridStub = {
+ getUID: jest.fn(),
+ getOptions: () => gridOptionsMock,
+ getSelectionModel: jest.fn(),
+ setSelectionModel: jest.fn(),
+ onColumnsReordered: new Slick.Event(),
+ onSelectedRowsChanged: new Slick.Event(),
+ onSort: new Slick.Event(),
+} as unknown as SlickGrid;
+
+const mockRowSelectionModel = {
+ constructor: jest.fn(),
+ init: jest.fn(),
+ destroy: jest.fn(),
+ dispose: jest.fn(),
+ getSelectedRows: jest.fn(),
+ setSelectedRows: jest.fn(),
+ getSelectedRanges: jest.fn(),
+ setSelectedRanges: jest.fn(),
+ onSelectedRangesChanged: new Slick.Event(),
+} as unknown as SlickRowSelectionModel;
+
+jest.mock('@slickgrid-universal/common/dist/commonjs/extensions/slickRowSelectionModel', () => ({
+ SlickRowSelectionModel: jest.fn().mockImplementation(() => mockRowSelectionModel),
+}));
+
+@Component({
+ template: `Loading...
`
+})
+export class TestPreloadComponent { }
+
+describe('SlickRowDetailView', () => {
+ let eventHandler: SlickEventHandler;
+ let plugin: SlickRowDetailView;
+ let eventPubSubService: EventPubSubService;
+ let rxjsResourceStub: RxJsResourceStub;
+ const div = document.createElement('div');
+ div.innerHTML = ``;
+ document.body.appendChild(div);
+
+ beforeEach(async () => {
+ eventHandler = new Slick.EventHandler();
+ eventPubSubService = new EventPubSubService(div);
+ rxjsResourceStub = new RxJsResourceStub();
+
+ await TestBed.configureTestingModule({
+ declarations: [TestPreloadComponent],
+ providers: [
+ SlickRowDetailView,
+ UniversalSlickRowDetailView,
+ { provide: ApplicationRef, useValue: applicationRefStub },
+ { provide: AngularUtilService, useValue: angularUtilServiceStub },
+ { provide: EventPubSubService, useValue: eventPubSubService },
+ { provide: RxJsFacade, useValue: rxjsResourceStub },
+ ],
+ imports: [TranslateModule.forRoot()]
+ });
+ plugin = TestBed.inject(SlickRowDetailView);
+ plugin.eventHandler = new Slick.EventHandler();
+ });
+
+ afterEach(() => {
+ eventHandler.unsubscribeAll();
+ jest.clearAllMocks();
+ });
+
+ it('should create the RowDetailView plugin', () => {
+ expect(plugin).toBeTruthy();
+ });
+
+ it('should expect "getOptions" to be called when calling addonOptions GETTER', () => {
+ const getOptionSpy = jest.spyOn(plugin, 'getOptions').mockReturnValue({ cssClass: 'some-class' } as any);
+
+ const options = plugin.addonOptions;
+
+ expect(options).toEqual({ cssClass: 'some-class' });
+ expect(getOptionSpy).toHaveBeenCalled();
+ });
+
+ describe('registered plugin', () => {
+ beforeEach(() => {
+ gridOptionsMock.datasetIdPropertyName = 'id';
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should run the "process" method when defined', async () => {
+ gridOptionsMock.rowDetailView!.process = () => new Promise((resolve) => resolve('process resolved'));
+ const output = await gridOptionsMock.rowDetailView!.process({ id: 'field1', field: 'field1' });
+ expect(output).toBe('process resolved');
+ });
+
+ it('should use "addRxJsResource" method and run the "process" method when defined', async () => {
+ plugin.addRxJsResource(rxjsResourceStub);
+ (gridOptionsMock.rowDetailView as RowDetailView).process = () => new Promise((resolve) => resolve('process resolved'));
+ const output = await (gridOptionsMock.rowDetailView as RowDetailView).process({ id: 'field1', field: 'field1' });
+ expect(output).toBe('process resolved');
+ });
+
+ it('should provide a sanitized "preTemplate" when only a "preloadComponent" is provided (meaning no "preTemplate" is originally provided)', async () => {
+ (gridOptionsMock.rowDetailView as RowDetailView).preloadComponent = TestPreloadComponent;
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+
+ plugin.init(gridStub);
+ const output = await (gridOptionsMock.rowDetailView as RowDetailView).preTemplate!();
+
+ expect(output).toEqual(``);
+ });
+
+ it('should provide a sanitized "postTemplate" when only a "viewComponent" is provided (meaning no "postTemplate" is originally provided)', async () => {
+ (gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestPreloadComponent;
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+
+ const output = await gridOptionsMock.rowDetailView!.postTemplate!({ id: 'field1', field: 'field1' });
+ expect(output).toEqual(``);
+ });
+
+ it('should define "datasetIdPropertyName" with different "id" and provide a sanitized "postTemplate" when only a "viewComponent" is provided (meaning no "postTemplate" is originally provided)', async () => {
+ (gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestPreloadComponent;
+ gridOptionsMock.datasetIdPropertyName = 'rowId';
+ const output = await gridOptionsMock.rowDetailView!.postTemplate!({ rowId: 'field1', field: 'field1' });
+ expect(output).toEqual(``);
+ });
+
+ describe('registered addon', () => {
+ let columnsMock: Column[];
+
+ beforeEach(() => {
+ gridOptionsMock.datasetIdPropertyName = 'id';
+ gridOptionsMock.rowDetailView!.preloadComponent = TestPreloadComponent;
+ gridOptionsMock.rowDetailView!.viewComponent = TestPreloadComponent;
+ columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }];
+ jest.spyOn(SharedService.prototype, 'slickGrid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+ jest.clearAllMocks();
+ gridStub.onColumnsReordered = new Slick.Event();
+ gridStub.onSort = new Slick.Event();
+ });
+
+ afterEach(() => {
+ plugin?.eventHandler?.unsubscribeAll();
+ plugin?.dispose();
+ jest.clearAllMocks();
+ (plugin.onAsyncResponse as any) = null;
+ (plugin.onAsyncEndUpdate as any) = null;
+ (plugin.onAfterRowDetailToggle as any) = null;
+ (plugin.onBeforeRowDetailToggle as any) = null;
+ (plugin.onRowBackToViewportRange as any) = null;
+ (plugin.onRowOutOfViewportRange as any) = null;
+ });
+
+ it('should register the addon', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock };
+ gridOptionsMock.rowDetailView!.onExtensionRegistered = jest.fn();
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(copyGridOptionsMock);
+ const onRegisteredSpy = jest.spyOn(copyGridOptionsMock.rowDetailView!, 'onExtensionRegistered');
+
+ plugin.init(gridStub);
+ const instance = plugin.register();
+ const addonInstance = plugin.getAddonInstance();
+
+ expect(instance).toBeTruthy();
+ expect(instance).toEqual(addonInstance);
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(SlickRowSelectionModel).toHaveBeenCalledWith({ selectActiveRow: true });
+ });
+
+ it('should call internal event handler subscribe and expect the "onAsyncResponse" option to be called when addon notify is called', () => {
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onAsyncResponse = new Slick.Event();
+ plugin.register();
+
+ plugin.onAsyncResponse.notify({ item: columnsMock[0], detailView: {} }, new Slick.EventData(), gridStub);
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onAsyncRespSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], detailView: {} });
+ expect(onAsyncEndSpy).not.toHaveBeenCalled();
+ expect(onAfterRowSpy).not.toHaveBeenCalled();
+ expect(onBeforeRowSpy).not.toHaveBeenCalled();
+ expect(onRowOutViewSpy).not.toHaveBeenCalled();
+ expect(onRowBackViewSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onAsyncEndUpdate" option to be called when addon notify is called', () => {
+ // const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const renderSpy = jest.spyOn(plugin, 'renderViewModel');
+
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onAsyncEndUpdate = new Slick.Event();
+ plugin.register();
+ plugin.onAsyncEndUpdate.notify({ item: columnsMock[0], grid: gridStub }, new Slick.EventData(), gridStub);
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ // expect(handlerSpy).toHaveBeenCalledWith(
+ // { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ // expect.anything()
+ // );
+ expect(onAsyncRespSpy).not.toHaveBeenCalled();
+ expect(onAsyncEndSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], grid: gridStub });
+ expect(renderSpy).toHaveBeenCalledWith({ cssClass: 'red', field: 'field1', id: 'field1', width: 100, });
+ expect(onAfterRowSpy).not.toHaveBeenCalled();
+ expect(onBeforeRowSpy).not.toHaveBeenCalled();
+ expect(onRowOutViewSpy).not.toHaveBeenCalled();
+ expect(onRowBackViewSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onAfterRowDetailToggle" option to be called when addon notify is called', () => {
+ // const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onAfterRowDetailToggle = new Slick.Event();
+ plugin.register();
+ plugin.onAfterRowDetailToggle.notify({ item: columnsMock[0], expandedRows: [0], grid: gridStub }, new Slick.EventData(), gridStub);
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ // expect(handlerSpy).toHaveBeenCalledWith(
+ // { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ // expect.anything()
+ // );
+ expect(onAsyncRespSpy).not.toHaveBeenCalled();
+ expect(onAsyncEndSpy).not.toHaveBeenCalled();
+ expect(onAfterRowSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], expandedRows: [0], grid: gridStub });
+ expect(onBeforeRowSpy).not.toHaveBeenCalled();
+ expect(onRowOutViewSpy).not.toHaveBeenCalled();
+ expect(onRowBackViewSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onBeforeRowDetailToggle" option to be called when addon notify is called', () => {
+ // const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.register();
+ plugin.onBeforeRowDetailToggle.notify({ item: columnsMock[0], grid: gridStub }, new Slick.EventData(), gridStub);
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ // expect(handlerSpy).toHaveBeenCalledWith(
+ // { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ // expect.anything()
+ // );
+ expect(onAsyncRespSpy).not.toHaveBeenCalled();
+ expect(onAsyncEndSpy).not.toHaveBeenCalled();
+ expect(onAfterRowSpy).not.toHaveBeenCalled();
+ expect(onBeforeRowSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], grid: gridStub });
+ expect(onRowOutViewSpy).not.toHaveBeenCalled();
+ expect(onRowBackViewSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onRowOutOfViewportRange" option to be called when addon notify is called', () => {
+ // const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onRowOutOfViewportRange = new Slick.Event();
+ plugin.register();
+ plugin.onRowOutOfViewportRange.notify(
+ { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub },
+ new Slick.EventData(),
+ gridStub
+ );
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ // expect(handlerSpy).toHaveBeenCalledWith(
+ // { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ // expect.anything()
+ // );
+ expect(onAsyncRespSpy).not.toHaveBeenCalled();
+ expect(onAsyncEndSpy).not.toHaveBeenCalled();
+ expect(onAfterRowSpy).not.toHaveBeenCalled();
+ expect(onBeforeRowSpy).not.toHaveBeenCalled();
+ expect(onRowOutViewSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [0], rowIdsOutOfViewport: [], grid: gridStub });
+ expect(onRowBackViewSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onRowBackToViewportRange" option to be called when addon notify is called', () => {
+ // const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const onAsyncRespSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
+ const onAsyncEndSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
+ const onAfterRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
+ const onBeforeRowSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
+ const onRowOutViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
+ const onRowBackViewSpy = jest.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
+
+ plugin.init(gridStub);
+ plugin.onRowBackToViewportRange = new Slick.Event();
+ plugin.register();
+ plugin.onRowBackToViewportRange.notify(
+ { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [columnsMock[0] as any], rowIdsOutOfViewport: [], grid: gridStub },
+ new Slick.EventData(),
+ gridStub
+ );
+
+ // expect(handlerSpy).toHaveBeenCalledTimes(8); // there are an extra 2x on the grid itself
+ // expect(handlerSpy).toHaveBeenCalledWith(
+ // { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ // expect.anything()
+ // );
+ expect(onAsyncRespSpy).not.toHaveBeenCalled();
+ expect(onAsyncEndSpy).not.toHaveBeenCalled();
+ expect(onAfterRowSpy).not.toHaveBeenCalled();
+ expect(onBeforeRowSpy).not.toHaveBeenCalled();
+ expect(onRowOutViewSpy).not.toHaveBeenCalled();
+ expect(onRowBackViewSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], rowId: 0, rowIndex: 0, expandedRows: [columnsMock[0] as any], rowIdsOutOfViewport: [], grid: gridStub });
+ });
+
+ it('should call Angular Util "createAngularComponentAppendToDom" when grid "onColumnsReordered" is triggered', (done) => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn() } } as any);
+
+ plugin.init(gridStub);
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.register();
+ plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
+ gridStub.onColumnsReordered.notify({ impactedColumns: [mockColumn] } as any, new Slick.EventData(), gridStub);
+ expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
+ done();
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('should call "redrawAllViewComponents" when using Row Selection and the event "onSelectedRowsChanged" is triggered', () => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ gridOptionsMock.enableCheckboxSelector = true;
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
+
+ plugin.init(gridStub);
+ const redrawSpy = jest.spyOn(plugin, 'redrawAllViewComponents');
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.register();
+ plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
+ gridStub.onSelectedRowsChanged.notify({ rows: [0], previousSelectedRows: [], grid: gridStub }, new Slick.EventData(), gridStub);
+ expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
+ plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ expect(redrawSpy).toHaveBeenCalledTimes(2);
+ });
+
+ it('should call "redrawAllViewSlots" when event "filterChanged" is triggered', () => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
+
+ plugin.init(gridStub);
+ const redrawSpy = jest.spyOn(plugin, 'redrawAllViewComponents');
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.register();
+
+ plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
+ eventPubSubService.publish('onFilterChanged', { columnId: 'field1', operator: '=', searchTerms: [] });
+ expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
+ plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ expect(redrawSpy).toHaveBeenCalledTimes(2);
+ });
+
+ it('should call "renderAllViewModels" when grid event "onAfterRowDetailToggle" is triggered', () => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const getElementSpy = jest.spyOn(document, 'getElementsByClassName');
+ const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
+
+ plugin.init(gridStub);
+ plugin.onAfterRowDetailToggle = new Slick.Event();
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.register();
+ plugin.onAfterRowDetailToggle.subscribe(() => {
+ expect(getElementSpy).toHaveBeenCalledWith('container_field1');
+ expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub } as any, new Slick.EventData(), gridStub);
+ plugin.onAfterRowDetailToggle.notify({ item: mockColumn, grid: gridStub } as any, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('should call "redrawViewSlot" when grid event "onRowBackToViewportRange" is triggered', (done) => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const getElementSpy = jest.spyOn(document, 'getElementsByClassName');
+ const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
+
+ plugin.init(gridStub);
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.onRowBackToViewportRange = new Slick.Event();
+ plugin.register();
+ plugin.onRowBackToViewportRange.subscribe(() => {
+ expect(getElementSpy).toHaveBeenCalledWith('container_field1');
+ expect(appendSpy).toHaveBeenCalledWith(undefined, expect.objectContaining({ className: 'container_field1' }), true);
+ done();
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub } as any, new Slick.EventData(), gridStub);
+ plugin.onRowBackToViewportRange.notify({ item: mockColumn, grid: gridStub } as any, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('should run the internal "onProcessing" and call "notifyTemplate" with a Promise when "process" method is defined and executed', (done) => {
+ const mockItem = { id: 2, firstName: 'John', lastName: 'Doe' };
+ gridOptionsMock.rowDetailView!.process = () => new Promise((resolve) => resolve(mockItem));
+ plugin.init(gridStub);
+ plugin.onAsyncResponse = new Slick.Event();
+ plugin.onAsyncResponse.subscribe((_e, response) => {
+ expect(response).toEqual(expect.objectContaining({ item: mockItem }));
+ done();
+ });
+
+ gridOptionsMock.rowDetailView!.process(mockItem);
+ });
+
+ it('should run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', (done) => {
+ const mockItem = { id: 2, firstName: 'John', lastName: 'Doe' };
+ (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => of(mockItem);
+
+ plugin.init(gridStub);
+ plugin.onAsyncResponse = new Slick.Event();
+ plugin.onAsyncResponse.subscribe((_e: Event, response: any) => {
+ expect(response).toEqual(expect.objectContaining({ item: mockItem }));
+ done();
+ });
+
+ (gridOptionsMock.rowDetailView as RowDetailView).process({ id: 'field1', field: 'field1' });
+ });
+
+ it('should define "datasetIdPropertyName" with different "id" and run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', (done) => {
+ const mockItem = { rowId: 2, firstName: 'John', lastName: 'Doe' };
+ (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => of(mockItem);
+ gridOptionsMock.datasetIdPropertyName = 'rowId';
+
+ plugin.init(gridStub);
+ plugin.onAsyncResponse = new Slick.Event();
+ plugin.onAsyncResponse.subscribe((_e: Event, response: any) => {
+ expect(response).toEqual(expect.objectContaining({ item: mockItem }));
+ done();
+ });
+
+ (gridOptionsMock.rowDetailView as RowDetailView).process({ rowId: 'field1', field: 'field1' });
+ });
+
+ it('should run the internal "onProcessing" and call "notifyTemplate" with an Object to simular HttpClient call when "process" method is defined and executed', async () => {
+ const mockItem = { firstName: 'John', lastName: 'Doe' };
+ (gridOptionsMock.rowDetailView as RowDetailView).process = (item) => new Promise((resolve) => resolve(item));
+ plugin.init(gridStub);
+
+ try {
+ await (gridOptionsMock.rowDetailView as RowDetailView).process(mockItem);
+ } catch (e) {
+ expect(e.toString()).toContain(`[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback`);
+ }
+ });
+
+ it('should throw an error when running the "process" that does not return an object with an "id" property', async () => {
+ const mockItem = { firstName: 'John', lastName: 'Doe' };
+ gridOptionsMock.rowDetailView!.process = (item) => new Promise((resolve) => resolve(item));
+ plugin.init(gridStub);
+
+ try {
+ await gridOptionsMock.rowDetailView!.process(mockItem);
+ } catch (e) {
+ expect(e.toString()).toContain(`[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback`);
+ }
+ });
+
+ it('should call Aurelia Util "disposeAllViewSlot" when grid "onSort" is triggered', (done) => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, cssClass: 'red', __collapsed: true };
+ // const appendSpy = jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { instance: jest.fn(), destroy: jest.fn() } } as any);
+ jest.spyOn(angularUtilServiceStub, 'createAngularComponentAppendToDom').mockReturnValue({ componentRef: { plugin: {} } } as any);
+ const handlerSpy = jest.spyOn(plugin.eventHandler, 'subscribe');
+ const disposeSpy = jest.spyOn(plugin, 'disposeAllViewComponents');
+
+ plugin.init(gridStub);
+ plugin.onBeforeRowDetailToggle = new Slick.Event();
+ plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
+ gridStub.onSort.notify({ impactedColumns: [mockColumn] } as any, new Slick.EventData(), gridStub);
+ expect(disposeSpy).toHaveBeenCalled();
+ done();
+ });
+ plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('should dispose of the addon', () => {
+ plugin.init(gridStub);
+ const disposeSpy = jest.spyOn(plugin, 'dispose');
+ const disposeAllSpy = jest.spyOn(plugin, 'disposeAllViewComponents');
+
+ plugin.dispose();
+
+ expect(disposeSpy).toHaveBeenCalled();
+ expect(disposeAllSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('possible error thrown', () => {
+ it('should throw an error when creating with "init" and the row detail is without a "process" method defined', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock };
+ copyGridOptionsMock.rowDetailView!.process = undefined as any;
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+
+ expect(() => plugin.init(gridStub)).toThrowError(`[Angular-Slickgrid] You need to provide a "process" function for the Row Detail Extension to work properly`);
+ });
+
+ it('should throw an error when creating with "register" and the row detail is without a "process" method defined', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock };
+ copyGridOptionsMock.rowDetailView!.process = undefined as any;
+ jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
+
+ expect(() => plugin.register()).toThrowError(`[Angular-Slickgrid] You need to provide a "process" function for the Row Detail Extension to work properly`);
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/app/modules/angular-slickgrid/extensions/index.ts b/src/app/modules/angular-slickgrid/extensions/index.ts
index e35fe9f91..5da5386a6 100644
--- a/src/app/modules/angular-slickgrid/extensions/index.ts
+++ b/src/app/modules/angular-slickgrid/extensions/index.ts
@@ -1 +1 @@
-export * from './rowDetailViewExtension';
+export * from './slickRowDetailView';
diff --git a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts b/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts
deleted file mode 100644
index 47d0d5d6e..000000000
--- a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts
+++ /dev/null
@@ -1,401 +0,0 @@
-import 'slickgrid/plugins/slick.rowdetailview';
-import 'slickgrid/plugins/slick.rowselectionmodel';
-
-import { ApplicationRef, ComponentRef, Injectable, Type, ViewContainerRef } from '@angular/core';
-import {
- addToArrayWhenNotExists,
- castObservableToPromise,
- Column,
- RowDetailViewExtension as UniversalRowDetailViewExtension,
- RxJsFacade,
- SharedService,
- SlickEventHandler,
- SlickGrid,
- SlickNamespace,
- SlickRowDetailView,
-} from '@slickgrid-universal/common';
-import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
-import { Observable, Subject, Subscription } from 'rxjs';
-import * as DOMPurify_ from 'dompurify';
-const DOMPurify = DOMPurify_; // patch to fix rollup to work
-
-import { GridOption, RowDetailView } from '../models/index';
-import { AngularUtilService } from '../services/angularUtil.service';
-import { unsubscribeAllObservables } from '../services/utilities';
-
-// using external non-typed js libraries
-declare const Slick: SlickNamespace;
-
-const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
-const PRELOAD_CONTAINER_PREFIX = 'container_loading';
-
-export interface CreatedView {
- id: string | number;
- dataContext: any;
- componentRef?: ComponentRef;
-}
-
-@Injectable()
-export class RowDetailViewExtension implements UniversalRowDetailViewExtension {
- rowDetailContainer!: ViewContainerRef;
- private _addon: any;
- private _addonOptions!: RowDetailView | null;
- private _eventHandler: SlickEventHandler;
- private _preloadComponent: Type