Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 363065a

Browse files
committed
fix: add sanitizer to Row Detail create dynamic component
1 parent f24e4e5 commit 363065a

File tree

4 files changed

+64
-23
lines changed

4 files changed

+64
-23
lines changed

src/app/modules/angular-slickgrid/extensions/__tests__/slickRowDetailView.spec.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,18 @@ describe('SlickRowDetailView', () => {
416416
plugin.register();
417417
plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
418418
gridStub.onColumnsReordered.notify({ impactedColumns: [mockColumn] } as any, new SlickEventData(), gridStub);
419-
expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), {
420-
model: mockColumn,
421-
addon: expect.anything(),
422-
grid: gridStub,
423-
dataView: undefined,
424-
parent: undefined,
425-
});
419+
expect(appendSpy).toHaveBeenCalledWith(
420+
TestComponent,
421+
expect.objectContaining({ className: 'container_field1' }),
422+
{
423+
model: mockColumn,
424+
addon: expect.anything(),
425+
grid: gridStub,
426+
dataView: undefined,
427+
parent: undefined,
428+
},
429+
{ sanitizer: expect.any(Function) }
430+
);
426431
done();
427432
});
428433
plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub);
@@ -448,13 +453,18 @@ describe('SlickRowDetailView', () => {
448453
new SlickEventData(),
449454
gridStub
450455
);
451-
expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), {
452-
model: mockColumn,
453-
addon: expect.anything(),
454-
grid: gridStub,
455-
dataView: undefined,
456-
parent: undefined,
457-
});
456+
expect(appendSpy).toHaveBeenCalledWith(
457+
TestComponent,
458+
expect.objectContaining({ className: 'container_field1' }),
459+
{
460+
model: mockColumn,
461+
addon: expect.anything(),
462+
grid: gridStub,
463+
dataView: undefined,
464+
parent: undefined,
465+
},
466+
{ sanitizer: expect.any(Function) }
467+
);
458468
});
459469
plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub);
460470
plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new SlickEventData(), gridStub);
@@ -478,13 +488,18 @@ describe('SlickRowDetailView', () => {
478488

479489
plugin.eventHandler.subscribe(plugin.onBeforeRowDetailToggle, () => {
480490
eventPubSubService.publish(eventName, { columnId: 'field1', operator: '=', searchTerms: [] });
481-
expect(appendSpy).toHaveBeenCalledWith(TestComponent, expect.objectContaining({ className: 'container_field1' }), {
482-
model: mockColumn,
483-
addon: expect.anything(),
484-
grid: gridStub,
485-
dataView: undefined,
486-
parent: undefined,
487-
});
491+
expect(appendSpy).toHaveBeenCalledWith(
492+
TestComponent,
493+
expect.objectContaining({ className: 'container_field1' }),
494+
{
495+
model: mockColumn,
496+
addon: expect.anything(),
497+
grid: gridStub,
498+
dataView: undefined,
499+
parent: undefined,
500+
},
501+
{ sanitizer: expect.any(Function) }
502+
);
488503
});
489504
plugin.onBeforeRowDetailToggle.notify({ item: mockColumn, grid: gridStub }, new SlickEventData(), gridStub);
490505
plugin.onBeforeRowDetailToggle.notify({ item: { ...mockColumn, __collapsed: false }, grid: gridStub }, new SlickEventData(), gridStub);

src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,9 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
282282
if (this._preloadComponent && containerElements?.length >= 0) {
283283
const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(
284284
this._preloadComponent,
285-
containerElements[containerElements.length - 1]
285+
containerElements[containerElements.length - 1],
286+
{},
287+
{ sanitizer: this._grid.sanitizeHtmlString }
286288
);
287289
this._preloadCompRef = preloadComp.componentRef;
288290
}
@@ -305,6 +307,9 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
305307
grid: this._grid,
306308
dataView: this.dataView,
307309
parent: this.rowDetailViewOptions?.parent,
310+
},
311+
{
312+
sanitizer: this._grid.sanitizeHtmlString,
308313
}
309314
);
310315
if (componentOutput?.componentRef) {

src/app/modules/angular-slickgrid/services/__tests__/angularUtilService.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ describe('AngularUtilService', () => {
7979
expect(output).toEqual({ componentRef: mockComponentFactory, domElement: h1Mock });
8080
});
8181

82+
it('should create an Angular Component with optional target element and extra data and sanitizer', () => {
83+
const titleMock = 'Some Title';
84+
const h1Mock = document.createElement('h1');
85+
h1Mock.textContent = titleMock;
86+
mockComponentFactory.hostView.rootNodes[0] = h1Mock;
87+
const sanitizerMock = jest.fn().mockReturnValue(titleMock);
88+
89+
// @ts-ignore
90+
const createCompSpy = jest.spyOn(viewContainerRefStub, 'createComponent').mockReturnValue(mockComponentFactory);
91+
const output = service.createAngularComponent(TestComponent, domParentElm, { title: titleMock }, { sanitizer: sanitizerMock });
92+
93+
expect(sanitizerMock).toHaveBeenCalled();
94+
expect(createCompSpy).toHaveBeenCalled();
95+
expect(domParentElm.innerHTML).toBe('Some Title');
96+
expect(output).toEqual({ componentRef: mockComponentFactory, domElement: h1Mock });
97+
});
98+
8299
it('should create an Interactive Angular Component with target element and extra data to provide to the component instance', () => {
83100
const titleMock = 'Some Title';
84101
const h1Mock = document.createElement('h1');

src/app/modules/angular-slickgrid/services/angularUtil.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface CreateComponentOption {
99
ngModuleRef?: NgModuleRef<unknown>;
1010
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
1111
projectableNodes?: Node[][];
12+
sanitizer?: (dirtyHtml: string) => string;
1213
}
1314

1415
@Injectable()
@@ -82,7 +83,10 @@ export class AngularUtilService {
8283

8384
// when user provides the DOM element target, we will read the new Component html and use it to replace the target html
8485
if (targetElement && domElem) {
85-
targetElement.innerHTML = domElem.innerHTML;
86+
targetElement.innerHTML =
87+
typeof createCompOptions?.sanitizer === 'function'
88+
? createCompOptions.sanitizer(domElem.innerHTML || '')
89+
: domElem.innerHTML;
8690
}
8791
}
8892

0 commit comments

Comments
 (0)