Skip to content

Commit 2f249fc

Browse files
authored
Merge pull request #1540 from ghiscoding/bugfix/row-detail-auto-height
fix: Row Detail should also work with fixed grid height or w/o autoHeight
2 parents 3fbf4ba + 7b28641 commit 2f249fc

File tree

9 files changed

+591
-559
lines changed

9 files changed

+591
-559
lines changed

Diff for: package.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@
5252
},
5353
"dependencies": {
5454
"@ngx-translate/core": "^15.0.0",
55-
"@slickgrid-universal/common": "~5.13.0",
56-
"@slickgrid-universal/custom-footer-component": "~5.13.0",
57-
"@slickgrid-universal/empty-warning-component": "~5.13.0",
55+
"@slickgrid-universal/common": "~5.13.1",
56+
"@slickgrid-universal/custom-footer-component": "~5.13.1",
57+
"@slickgrid-universal/empty-warning-component": "~5.13.1",
5858
"@slickgrid-universal/event-pub-sub": "~5.13.0",
59-
"@slickgrid-universal/pagination-component": "~5.13.0",
60-
"@slickgrid-universal/row-detail-view-plugin": "~5.13.0",
61-
"@slickgrid-universal/rxjs-observable": "~5.13.0",
59+
"@slickgrid-universal/pagination-component": "~5.13.1",
60+
"@slickgrid-universal/row-detail-view-plugin": "~5.13.1",
61+
"@slickgrid-universal/rxjs-observable": "~5.13.1",
6262
"dequal": "^2.0.3",
6363
"rxjs": "^7.8.2"
6464
},
@@ -88,7 +88,7 @@
8888
"@angular/platform-browser": "^18.2.13",
8989
"@angular/platform-browser-dynamic": "^18.2.13",
9090
"@angular/router": "^18.2.13",
91-
"@faker-js/faker": "^9.5.1",
91+
"@faker-js/faker": "^9.6.0",
9292
"@fnando/sparkline": "^0.3.10",
9393
"@formkit/tempo": "^0.1.2",
9494
"@ng-select/ng-select": "^13.9.1",
@@ -98,12 +98,12 @@
9898
"@nx/vite": "^20.6.1",
9999
"@popperjs/core": "^2.11.8",
100100
"@release-it/conventional-changelog": "^10.0.0",
101-
"@slickgrid-universal/composite-editor-component": "~5.13.0",
102-
"@slickgrid-universal/custom-tooltip-plugin": "~5.13.0",
103-
"@slickgrid-universal/excel-export": "~5.13.0",
104-
"@slickgrid-universal/graphql": "~5.13.0",
105-
"@slickgrid-universal/odata": "~5.13.0",
106-
"@slickgrid-universal/text-export": "~5.13.0",
101+
"@slickgrid-universal/composite-editor-component": "~5.13.1",
102+
"@slickgrid-universal/custom-tooltip-plugin": "~5.13.1",
103+
"@slickgrid-universal/excel-export": "~5.13.1",
104+
"@slickgrid-universal/graphql": "~5.13.1",
105+
"@slickgrid-universal/odata": "~5.13.1",
106+
"@slickgrid-universal/text-export": "~5.13.1",
107107
"@types/fnando__sparkline": "^0.3.7",
108108
"@types/node": "^22.13.10",
109109
"@types/sortablejs": "^1.15.8",

Diff for: src/app/examples/grid45-detail.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<h4>{{ model.companyName }} - Order Details (id: {{ model.id }})</h4>
33
<div class="container-fluid">
44
<angular-slickgrid
5+
class="innergrid"
56
[gridId]="innerGridId"
67
[columnDefinitions]="innerColDefs"
78
[gridOptions]="innerGridOptions"

Diff for: src/app/examples/grid45-detail.component.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface OrderData {
2121
}
2222

2323
@Component({
24-
styles: ['.detail-label { display: inline-flex; align-items: center; gap: 4px; padding: 4px; }', 'label { font-weight: 600; }'],
24+
styles: ['.innergrid { --slick-header-menu-display: inline-block; }'],
2525
templateUrl: './grid45-detail.component.html',
2626
encapsulation: ViewEncapsulation.None,
2727
})
@@ -47,7 +47,7 @@ export class Grid45DetailComponent implements OnDestroy, OnInit {
4747
}
4848

4949
ngOnDestroy(): void {
50-
console.log('destroying row detail');
50+
console.log('destroying row detail', this.model.id);
5151
}
5252

5353
angularGridReady(angularGrid: AngularGridInstance) {

Diff for: src/app/examples/grid45.component.html

+11
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ <h2>
8484
Use Inner Grid State/Presets
8585
</span>
8686
</label>
87+
88+
<label class="checkbox-inline control-label ms-2" for="useResizeAutoHeight">
89+
<input
90+
type="checkbox"
91+
id="useResizeAutoHeight"
92+
data-test="use-auto-height"
93+
[checked]="isUsingAutoHeight"
94+
(click)="changeUsingResizerAutoHeight()"
95+
/>
96+
Use <code>autoResize.autoHeight</code>
97+
</label>
8798
</span>
8899
</div>
89100
</div>

Diff for: src/app/examples/grid45.component.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ export class Grid45Component implements OnDestroy, OnInit {
1919
gridOptions!: GridOption;
2020
angularGrid!: AngularGridInstance;
2121
dataset: Distributor[] = [];
22-
detailViewRowCount = 8;
22+
detailViewRowCount = 9;
2323
hideSubTitle = false;
2424
isUsingInnerGridStatePresets = false;
25+
isUsingAutoHeight = false;
2526
serverWaitDelay = FAKE_SERVER_DELAY;
2627

2728
get rowDetailInstance(): SlickRowDetailView {
@@ -100,6 +101,7 @@ export class Grid45Component implements OnDestroy, OnInit {
100101
this.gridOptions = {
101102
autoResize: {
102103
container: '#demo-container',
104+
autoHeight: this.isUsingAutoHeight, // works with/without autoHeight
103105
bottomPadding: 20,
104106
},
105107
autoHeight: false,
@@ -159,6 +161,15 @@ export class Grid45Component implements OnDestroy, OnInit {
159161
return true;
160162
}
161163

164+
changeUsingResizerAutoHeight() {
165+
this.isUsingAutoHeight = !this.isUsingAutoHeight;
166+
this.angularGrid.slickGrid?.setOptions({
167+
autoResize: { ...this.gridOptions.autoResize, autoHeight: this.isUsingAutoHeight },
168+
});
169+
this.angularGrid.resizerService.resizeGrid();
170+
return true;
171+
}
172+
162173
closeAllRowDetail() {
163174
this.rowDetailInstance?.collapseAll();
164175
}

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -176,24 +176,24 @@ describe('SlickRowDetailView', () => {
176176
vi.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
177177

178178
plugin.init(gridStub);
179-
const output = await (gridOptionsMock.rowDetailView as RowDetailView).preTemplate!();
179+
const output = (await (gridOptionsMock.rowDetailView as RowDetailView).preTemplate!()) as HTMLElement;
180180

181-
expect(output).toEqual(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`);
181+
expect(output.outerHTML).toEqual(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`);
182182
});
183183

184184
it('should provide a sanitized "postTemplate" when only a "viewComponent" is provided (meaning no "postTemplate" is originally provided)', async () => {
185185
(gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestComponent;
186186
vi.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
187187

188-
const output = await gridOptionsMock.rowDetailView!.postTemplate!({ id: 'field1', field: 'field1' });
189-
expect(output).toEqual(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}field1"></div>`);
188+
const output = (await gridOptionsMock.rowDetailView!.postTemplate!({ id: 'field1', field: 'field1' })) as HTMLElement;
189+
expect(output.outerHTML).toEqual(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}field1"></div>`);
190190
});
191191

192192
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 () => {
193193
(gridOptionsMock.rowDetailView as RowDetailView).viewComponent = TestComponent;
194194
gridOptionsMock.datasetIdPropertyName = 'rowId';
195-
const output = await gridOptionsMock.rowDetailView!.postTemplate!({ rowId: 'field1', field: 'field1' });
196-
expect(output).toEqual(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}field1"></div>`);
195+
const output = (await gridOptionsMock.rowDetailView!.postTemplate!({ rowId: 'field1', field: 'field1' })) as HTMLElement;
196+
expect(output.outerHTML).toEqual(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}field1"></div>`);
197197
});
198198

199199
describe('registered addon', () => {

Diff for: src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts

+23-27
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
import {
1010
addToArrayWhenNotExists,
1111
castObservableToPromise,
12+
createDomElement,
1213
SlickEventData,
1314
SlickRowSelectionModel,
1415
unsubscribeAll,
@@ -119,15 +120,12 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
119120
// when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)
120121
if (!this.gridOptions.rowDetailView.preTemplate) {
121122
this._preloadComponent = this.gridOptions?.rowDetailView?.preloadComponent;
122-
this.addonOptions.preTemplate = () =>
123-
this._grid.sanitizeHtmlString(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`) as string;
123+
this.addonOptions.preTemplate = () => createDomElement('div', { className: `${PRELOAD_CONTAINER_PREFIX}` });
124124
}
125125
if (!this.gridOptions.rowDetailView.postTemplate) {
126126
this._viewComponent = this.gridOptions?.rowDetailView?.viewComponent;
127127
this.addonOptions.postTemplate = (itemDetail: any) =>
128-
this._grid.sanitizeHtmlString(
129-
`<div class="${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}"></div>`
130-
) as string;
128+
createDomElement('div', { className: `${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}` });
131129
}
132130

133131
// this also requires the Row Selection Model to be registered as well
@@ -247,21 +245,19 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
247245

248246
/** Redraw the necessary View Component */
249247
redrawViewComponent(createdView: CreatedView) {
250-
const containerElements = this.gridContainerElement.getElementsByClassName(`${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);
251-
if (containerElements?.length >= 0) {
248+
const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);
249+
if (containerElement) {
252250
this.renderViewModel(createdView.dataContext);
253251
}
254252
}
255253

256254
/** Render (or re-render) the View Component (Row Detail) */
257255
renderPreloadView() {
258-
const containerElements = this.gridContainerElement.getElementsByClassName(
259-
`${PRELOAD_CONTAINER_PREFIX}`
260-
) as HTMLCollectionOf<HTMLElement>;
261-
if (this._preloadComponent && containerElements?.length >= 0) {
256+
const containerElement = this.gridContainerElement.querySelector(`.${PRELOAD_CONTAINER_PREFIX}`);
257+
if (this._preloadComponent && containerElement) {
262258
const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(
263259
this._preloadComponent,
264-
containerElements[containerElements.length - 1],
260+
containerElement,
265261
{},
266262
{ sanitizer: this._grid.sanitizeHtmlString }
267263
);
@@ -271,15 +267,14 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
271267

272268
/** Render (or re-render) the View Component (Row Detail) */
273269
renderViewModel(item: any): CreatedView | undefined {
274-
const containerElements = this.gridContainerElement.getElementsByClassName(
275-
`${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`
276-
) as HTMLCollectionOf<HTMLElement>;
277-
278-
if (this._viewComponent && containerElements?.length > 0) {
270+
const containerElement = this.gridContainerElement.querySelector(
271+
`.${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`
272+
);
273+
if (this._viewComponent && containerElement) {
279274
// render row detail
280275
const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(
281276
this._viewComponent,
282-
containerElements[containerElements.length - 1],
277+
containerElement,
283278
{
284279
model: item,
285280
addon: this,
@@ -291,6 +286,7 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
291286
sanitizer: this._grid.sanitizeHtmlString,
292287
}
293288
);
289+
294290
if (componentOutput?.componentRef) {
295291
const viewObj = this._views.find((obj) => obj.id === item[this.datasetIdPropName]);
296292
if (viewObj) {
@@ -309,22 +305,21 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
309305

310306
protected disposeViewByItem(item: any, removeFromArray = false): void {
311307
const foundViewIndex = this._views.findIndex((view: CreatedView) => view.id === item[this.datasetIdPropName]);
312-
if (foundViewIndex >= 0 && foundViewIndex in this._views) {
313-
const expandedView = this._views[foundViewIndex];
314-
this.disposeView(expandedView);
308+
if (foundViewIndex >= 0) {
309+
this.disposeView(this._views[foundViewIndex]);
315310
if (removeFromArray) {
316311
this._views.splice(foundViewIndex, 1);
317312
}
318313
}
319314
}
320315

321316
protected disposeView(expandedView: CreatedView): CreatedView | void {
317+
expandedView.rendered = false;
322318
const compRef = expandedView?.componentRef;
323319
if (compRef) {
324320
this.appRef.detachView(compRef.hostView);
325321
if (typeof compRef?.destroy === 'function') {
326322
compRef.destroy();
327-
expandedView.rendered = false;
328323
}
329324
return expandedView;
330325
}
@@ -358,8 +353,10 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
358353
}
359354

360355
if (!awaitedItemDetail || !(this.datasetIdPropName in awaitedItemDetail)) {
361-
throw new Error(`[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback
362-
(a Promise or an HttpClient call returning an Observable) returns an item object that has an "${this.datasetIdPropName}" property`);
356+
throw new Error(
357+
'[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback ' +
358+
`returns an item object that has an "${this.datasetIdPropName}" property`
359+
);
363360
}
364361

365362
// notify the plugin with the new item details
@@ -382,8 +379,7 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
382379
dataContext: args.item,
383380
rendered: false,
384381
};
385-
const idPropName = this.gridOptions.datasetIdPropertyName || 'id';
386-
addToArrayWhenNotExists(this._views, viewInfo, idPropName);
382+
addToArrayWhenNotExists(this._views, viewInfo, this.datasetIdPropName);
387383
} else {
388384
// collapsing, so dispose of the View/Component
389385
this.disposeViewByItem(args.item, true);
@@ -392,7 +388,7 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
392388

393389
/** When Row comes back to Viewport Range, we need to redraw the View */
394390
protected handleOnRowBackToViewportRange(_e: SlickEventData<OnRowBackToViewportRangeArgs>, args: OnRowBackToViewportRangeArgs) {
395-
const viewModel = Array.from(this._views).find((x) => x.id === args.rowId);
391+
const viewModel = this._views.find((x) => x.id === args.rowId);
396392
if (viewModel && !viewModel.rendered) {
397393
this.redrawViewComponent(viewModel);
398394
}

0 commit comments

Comments
 (0)