Skip to content

Commit

Permalink
fix: add sanitizer to Row Detail create dynamic component
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Mar 1, 2025
1 parent f24e4e5 commit 363065a
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface CreateComponentOption {
ngModuleRef?: NgModuleRef<unknown>;
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
projectableNodes?: Node[][];
sanitizer?: (dirtyHtml: string) => string;
}

@Injectable()
Expand Down Expand Up @@ -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;
}
}

Expand Down

0 comments on commit 363065a

Please sign in to comment.