diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts index 9abbc1ab0..a60b5bbc4 100644 --- a/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts +++ b/src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts @@ -416,13 +416,18 @@ describe('SlickRowDetailView', () => { plugin.register(); plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => { gridStub.onColumnsReordered.notify({ impactedColumns: [mockColumn] } as any, new SlickEventData(), gridStub); - expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), { - model: mockColumn, - addon: expect.anything(), - grid: gridStub, - dataView: undefined, - parent: undefined, - }); + expect(appendSpy).toHaveBeenCalledWith( + TestComponent, + expect.objectContaining({ className: 'container_field1' }), + { + model: mockColumn, + addon: expect.anything(), + grid: gridStub, + dataView: undefined, + parent: undefined, + }, + { sanitizer: expect.any(Function) } + ); done(); }); plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub); @@ -448,13 +453,18 @@ describe('SlickRowDetailView', () => { new SlickEventData(), gridStub ); - expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), { - model: mockColumn, - addon: expect.anything(), - grid: gridStub, - dataView: undefined, - parent: undefined, - }); + expect(appendSpy).toHaveBeenCalledWith( + TestComponent, + expect.objectContaining({ className: 'container_field1' }), + { + model: mockColumn, + addon: expect.anything(), + grid: gridStub, + dataView: undefined, + parent: undefined, + }, + { sanitizer: expect.any(Function) } + ); }); plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub); plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new SlickEventData(), gridStub); @@ -478,13 +488,18 @@ describe('SlickRowDetailView', () => { plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => { eventPubSubService.publish(eventName, { columnId: 'field1', operator: '=', searchTerms: [] }); - expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), { - model: mockColumn, - addon: expect.anything(), - grid: gridStub, - dataView: undefined, - parent: undefined, - }); + expect(appendSpy).toHaveBeenCalledWith( + TestComponent, + expect.objectContaining({ className: 'container_field1' }), + { + model: mockColumn, + addon: expect.anything(), + grid: gridStub, + dataView: undefined, + parent: undefined, + }, + { sanitizer: expect.any(Function) } + ); }); plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub); plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new SlickEventData(), gridStub); diff --git a/src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts b/src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts index 7e65c4ee7..9f3228ec1 100644 --- a/src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts +++ b/src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts @@ -282,7 +282,9 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView { if (this._preloadComponent && containerElements?.length >= 0) { const preloadComp = this.angularUtilService.createAngularComponentAppendToDom( this._preloadComponent, - containerElements[containerElements.length - 1] + containerElements[containerElements.length - 1], + {}, + { sanitizer: this._grid.sanitizeHtmlString } ); this._preloadCompRef = preloadComp.componentRef; } @@ -305,6 +307,9 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView { grid: this._grid, dataView: this.dataView, parent: this.rowDetailViewOptions?.parent, + }, + { + sanitizer: this._grid.sanitizeHtmlString, } ); if (componentOutput?.componentRef) { diff --git a/src/app/modules/angular-slickgrid/services/__tests__/angularUtilService.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/angularUtilService.spec.ts index 38c40586c..5ecfe2f15 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/angularUtilService.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/angularUtilService.spec.ts @@ -79,6 +79,23 @@ describe('AngularUtilService', () => { expect(output).toEqual({ componentRef: mockComponentFactory, domElement: h1Mock }); }); + it('should create an Angular Component with optional target element and extra data and sanitizer', () => { + const titleMock = 'Some Title'; + const h1Mock = document.createElement('h1'); + h1Mock.textContent = titleMock; + mockComponentFactory.hostView.rootNodes[0] = h1Mock; + const sanitizerMock = jest.fn().mockReturnValue(titleMock); + + // @ts-ignore + const createCompSpy = jest.spyOn(viewContainerRefStub, 'createComponent').mockReturnValue(mockComponentFactory); + const output = service.createAngularComponent(TestComponent, domParentElm, { title: titleMock }, { sanitizer: sanitizerMock }); + + expect(sanitizerMock).toHaveBeenCalled(); + expect(createCompSpy).toHaveBeenCalled(); + expect(domParentElm.innerHTML).toBe('Some Title'); + expect(output).toEqual({ componentRef: mockComponentFactory, domElement: h1Mock }); + }); + it('should create an Interactive Angular Component with target element and extra data to provide to the component instance', () => { const titleMock = 'Some Title'; const h1Mock = document.createElement('h1'); diff --git a/src/app/modules/angular-slickgrid/services/angularUtil.service.ts b/src/app/modules/angular-slickgrid/services/angularUtil.service.ts index a2c4086ba..f74727abc 100644 --- a/src/app/modules/angular-slickgrid/services/angularUtil.service.ts +++ b/src/app/modules/angular-slickgrid/services/angularUtil.service.ts @@ -9,6 +9,7 @@ interface CreateComponentOption { ngModuleRef?: NgModuleRef; environmentInjector?: EnvironmentInjector | NgModuleRef; projectableNodes?: Node[][]; + sanitizer?: (dirtyHtml: string) => string; } @Injectable() @@ -82,7 +83,10 @@ export class AngularUtilService { // when user provides the DOM element target, we will read the new Component html and use it to replace the target html if (targetElement && domElem) { - targetElement.innerHTML = domElem.innerHTML; + targetElement.innerHTML = + typeof createCompOptions?.sanitizer === 'function' + ? createCompOptions.sanitizer(domElem.innerHTML || '') + : domElem.innerHTML; } }