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

fix: Row Detail open/close multiple times should always re-render #1566

Merged
merged 1 commit into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -262,35 +262,39 @@ describe('SlickRowDetailView', () => {
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 = vi.spyOn(plugin.eventHandler, 'subscribe');
const renderSpy = vi.spyOn(plugin, 'renderViewModel');
it('should call internal event handler subscribe and expect the "onAsyncEndUpdate" option to be called when addon notify is called', () =>
new Promise((done: any) => {
// const handlerSpy = vi.spyOn(plugin.eventHandler, 'subscribe');
const renderSpy = vi.spyOn(plugin, 'renderViewModel');

const onAsyncRespSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
const onAsyncEndSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
const onAfterRowSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
const onBeforeRowSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
const onRowOutViewSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
const onRowBackViewSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');
const onAsyncRespSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncResponse');
const onAsyncEndSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAsyncEndUpdate');
const onAfterRowSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onAfterRowDetailToggle');
const onBeforeRowSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onBeforeRowDetailToggle');
const onRowOutViewSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowOutOfViewportRange');
const onRowBackViewSpy = vi.spyOn(gridOptionsMock.rowDetailView as RowDetailView, 'onRowBackToViewportRange');

plugin.init(gridStub);
plugin.onAsyncEndUpdate = new SlickEvent();
plugin.register();
plugin.onAsyncEndUpdate.notify({ item: columnsMock[0], itemDetail: columnsMock[0], grid: gridStub }, new SlickEventData(), gridStub);
plugin.init(gridStub);
plugin.onAsyncEndUpdate = new SlickEvent();
plugin.register();
plugin.onAsyncEndUpdate.notify({ item: columnsMock[0], itemDetail: columnsMock[0], grid: gridStub }, new SlickEventData(), 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], itemDetail: 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();
});
// 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()
// );
setTimeout(() => {
expect(onAsyncRespSpy).not.toHaveBeenCalled();
expect(onAsyncEndSpy).toHaveBeenCalledWith(expect.anything(), { item: columnsMock[0], itemDetail: 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();
done();
});
}));

it('should call internal event handler subscribe and expect the "onAfterRowDetailToggle" option to be called when addon notify is called', () => {
// const handlerSpy = vi.spyOn(plugin.eventHandler, 'subscribe');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,14 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
this._preloadCompRef?.destroy();

// triggers after backend called "onAsyncResponse.notify()"
this.renderViewModel(args?.item);
// because of the preload destroy above, we need a small delay to make sure the DOM element is ready to render the Row Detail
queueMicrotask(() => {
this.renderViewModel(args?.item);

if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {
this.rowDetailViewOptions.onAsyncEndUpdate(e, args);
}
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {
this.rowDetailViewOptions.onAsyncEndUpdate(e, args);
}
});
});

this.eventHandler.subscribe(
Expand Down
17 changes: 17 additions & 0 deletions test/cypress/e2e/example21.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,21 @@ describe('Example 21 - Row Detail View', () => {

cy.get('#grid21').find('.slick-cell + .dynamic-cell-detail .innerDetailView_101').should('not.exist');
});

it('should expect the Row Detail to be re-rendered after expanding/collapsing multiple times', () => {
cy.get('#grid21').find('.slick-row:nth(1) .slick-cell:nth(0)').as('toggle1');
cy.get('@toggle1').click();
cy.get('@toggle1').click();
cy.get('@toggle1').click();

cy.get('#grid21').find('.slick-cell + .dynamic-cell-detail .innerDetailView_1').as('detailContainer');
cy.get('@detailContainer').find('h3').contains('Task 1');

cy.get('@toggle1').click();
cy.get('@detailContainer').should('not.exist');

cy.get('@toggle1').click();
cy.get('#grid21').find('.slick-cell + .dynamic-cell-detail .innerDetailView_1').as('detailContainer');
cy.get('@detailContainer').find('h3').contains('Task 1');
});
});