Skip to content

Commit a629871

Browse files
authored
DataGrid - Fix sticky master-detail for Safari and Firefox (T1288112) (#30313)
1 parent 7dd67ea commit a629871

File tree

10 files changed

+183
-35
lines changed

10 files changed

+183
-35
lines changed
1.77 KB
Loading

packages/devextreme-scss/scss/widgets/base/_gridBase.scss

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -971,12 +971,21 @@
971971
&:focus {
972972
outline: 0;
973973
}
974+
}
974975

975-
&.dx-#{$widget-name}-sticky-column-left {
976-
display: inline-block;
977-
overflow: hidden;
978-
left: 0;
979-
right: 0;
976+
&.dx-#{$widget-name}-sticky-columns {
977+
.dx-row > .dx-master-detail-cell {
978+
overflow: visible;
979+
980+
.dx-#{$widget-name}-master-detail-container {
981+
overflow: hidden;
982+
text-overflow: ellipsis;
983+
984+
&.dx-#{$widget-name}-sticky-column-left {
985+
left: 0;
986+
right: 0;
987+
}
988+
}
980989
}
981990
}
982991

packages/devextreme-scss/scss/widgets/fluent/gridBase/_index.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ $fluent-grid-base-group-panel-message-line-height: $fluent-button-text-line-heig
954954
> .dx-master-detail-cell {
955955
padding: $grid-masterdetail-padding;
956956
}
957+
957958
> .dx-#{$widget-name}-group-space,
958959
.dx-master-detail-cell:not(.dx-row-lines) {
959960
border-top: $datagrid-border;
@@ -1178,6 +1179,14 @@ $fluent-grid-base-group-panel-message-line-height: $fluent-button-text-line-heig
11781179
.dx-row-removed.dx-row-lines > td {
11791180
border-top-width: 0;
11801181
}
1182+
1183+
.dx-master-detail-row > .dx-master-detail-cell {
1184+
padding: 0;
1185+
1186+
> .dx-#{$widget-name}-master-detail-container {
1187+
padding: $grid-masterdetail-padding;
1188+
}
1189+
}
11811190
}
11821191
}
11831192

packages/devextreme-scss/scss/widgets/generic/gridBase/_index.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ $generic-grid-base-cell-input-height: round($generic-base-line-height * $generic
711711
> .dx-master-detail-cell {
712712
padding: $grid-masterdetail-padding;
713713
}
714+
714715
> .dx-#{$widget-name}-group-space,
715716
.dx-master-detail-cell:not(.dx-row-lines) {
716717
border-top: $datagrid-border;
@@ -1064,6 +1065,14 @@ $generic-grid-base-cell-input-height: round($generic-base-line-height * $generic
10641065
.dx-row-removed.dx-row-lines > td {
10651066
border-top-width: 0;
10661067
}
1068+
1069+
.dx-master-detail-row > .dx-master-detail-cell {
1070+
padding: 0;
1071+
1072+
> .dx-#{$widget-name}-master-detail-container {
1073+
padding: $grid-masterdetail-padding;
1074+
}
1075+
}
10671076
}
10681077

10691078
.dx-#{$widget-name}-sticky-columns .dx-#{$widget-name}-draggable-column {

packages/devextreme-scss/scss/widgets/material/gridBase/_index.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ $material-grid-base-group-panel-message-line-height: $material-button-text-line-
929929
> .dx-master-detail-cell {
930930
padding: $grid-masterdetail-padding;
931931
}
932+
932933
> .dx-#{$widget-name}-group-space,
933934
.dx-master-detail-cell:not(.dx-row-lines) {
934935
border-top: $datagrid-border;
@@ -1160,6 +1161,14 @@ $material-grid-base-group-panel-message-line-height: $material-button-text-line-
11601161
.dx-row-removed.dx-row-lines > td {
11611162
border-top-width: 0;
11621163
}
1164+
1165+
.dx-master-detail-row > .dx-master-detail-cell {
1166+
padding: 0;
1167+
1168+
> .dx-#{$widget-name}-master-detail-container {
1169+
padding: $grid-masterdetail-padding;
1170+
}
1171+
}
11631172
}
11641173
}
11651174

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
export const CLASSES = {
22
detailRow: 'dx-master-detail-row',
3+
detailCell: 'dx-master-detail-cell',
4+
detailContainer: 'master-detail-container',
5+
cellFocusDisabledClass: 'dx-cell-focus-disabled',
6+
rowLines: 'dx-row-lines',
37
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { afterEach } from 'node:test';
2+
3+
import { describe, expect, it } from '@jest/globals';
4+
import $ from 'jquery';
5+
6+
import type { Properties as DataGridProperties } from '../../../../ui/data_grid';
7+
import DataGrid from '../../../../ui/data_grid';
8+
9+
const SELECTORS = {
10+
gridContainer: '#gridContainer',
11+
detailCell: 'dx-master-detail-cell',
12+
detailContainer: 'dx-datagrid-master-detail-container',
13+
};
14+
15+
const GRID_CONTAINER_ID = 'gridContainer';
16+
17+
const createDataGrid = async (
18+
options: DataGridProperties = {},
19+
): Promise<{ $container: JQuery; instance: DataGrid }> => new Promise((resolve) => {
20+
const $container = $('<div>')
21+
.attr('id', GRID_CONTAINER_ID)
22+
.appendTo(document.body);
23+
24+
const instance = new DataGrid($container.get(0) as HTMLDivElement, options);
25+
26+
const contentReadyHandler = (): void => {
27+
resolve({ $container, instance });
28+
instance.off('contentReady', contentReadyHandler);
29+
};
30+
31+
instance.on('contentReady', contentReadyHandler);
32+
});
33+
34+
describe('GridCore master_detail', () => {
35+
afterEach(() => {
36+
const $container = $(SELECTORS.gridContainer);
37+
const dataGrid = ($container as any).dxDataGrid('instance') as DataGrid;
38+
39+
dataGrid.dispose();
40+
$container.remove();
41+
});
42+
43+
describe('master detail container', () => {
44+
it('container is td element', async () => {
45+
let containerElement: HTMLElement | undefined;
46+
47+
await createDataGrid({
48+
columns: ['field1', 'field2'],
49+
dataSource: [{ field1: 'value1', field2: 'value2' }],
50+
masterDetail: {
51+
enabled: true,
52+
autoExpandAll: true,
53+
template: (container) => {
54+
containerElement = container;
55+
},
56+
},
57+
});
58+
59+
expect(containerElement?.tagName).toBe('TD');
60+
expect(containerElement?.classList).toContain(SELECTORS.detailCell);
61+
});
62+
63+
it('container is div element when sticky columns enabled', async () => {
64+
let containerElement: HTMLElement | undefined;
65+
66+
await createDataGrid({
67+
columns: [{ dataField: 'field1', fixed: true }, 'field2'],
68+
dataSource: [{ field1: 'value1', field2: 'value2' }],
69+
masterDetail: {
70+
enabled: true,
71+
autoExpandAll: true,
72+
template: (container) => {
73+
containerElement = container;
74+
},
75+
},
76+
});
77+
78+
expect(containerElement?.parentElement?.tagName).toBe('TD');
79+
expect(containerElement?.parentElement?.classList).toContain(SELECTORS.detailCell);
80+
expect(containerElement?.tagName).toBe('DIV');
81+
expect(containerElement?.classList).toContain(SELECTORS.detailContainer);
82+
});
83+
});
84+
});

packages/devextreme/js/__internal/grids/grid_core/master_detail/m_master_detail.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view';
1515
import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view';
1616

1717
import gridCoreUtils from '../m_utils';
18-
19-
const MASTER_DETAIL_CELL_CLASS = 'dx-master-detail-cell';
20-
const MASTER_DETAIL_ROW_CLASS = 'dx-master-detail-row';
21-
const CELL_FOCUS_DISABLED_CLASS = 'dx-cell-focus-disabled';
22-
const ROW_LINES_CLASS = 'dx-row-lines';
18+
import { CLASSES } from './const';
19+
import { isDetailRow } from './utils';
2320

2421
const columns = (Base: ModuleType<ColumnsController>) => class ColumnsMasterDetailExtender extends Base {
2522
protected _getExpandColumnsCore() {
@@ -238,7 +235,7 @@ const resizing = (Base: ModuleType<ResizingController>) => class ResizingMasterD
238235
}
239236

240237
private _updateParentDataGrids($element) {
241-
const $masterDetailRow = $element.closest(`.${MASTER_DETAIL_ROW_CLASS}`);
238+
const $masterDetailRow = $element.closest(`.${CLASSES.detailRow}`);
242239

243240
if ($masterDetailRow.length) {
244241
when(this._updateMasterDataGrid($masterDetailRow, $element)).done(() => {
@@ -312,13 +309,21 @@ const resizing = (Base: ModuleType<ResizingController>) => class ResizingMasterD
312309

313310
protected _toggleBestFitMode(isBestFit) {
314311
super._toggleBestFitMode.apply(this, arguments as any);
315-
if (this.option('masterDetail.template')) {
316-
const $rowsTable = this._rowsView.getTableElement();
317-
if ($rowsTable) {
318-
$rowsTable
319-
.find('.dx-master-detail-cell')
320-
.css('maxWidth', isBestFit ? 0 : '');
321-
}
312+
313+
const hasMasterDetailTemplate = this.option('masterDetail.template');
314+
315+
if (!hasMasterDetailTemplate) {
316+
return;
317+
}
318+
319+
const $rowsTable = this._rowsView.getTableElement();
320+
321+
if ($rowsTable) {
322+
const detailSelector = `.${this.addWidgetPrefix(CLASSES.detailContainer)}, .${CLASSES.detailCell}`;
323+
324+
$rowsTable
325+
.find(detailSelector)
326+
.css('maxWidth', isBestFit ? 0 : '');
322327
}
323328
}
324329
};
@@ -340,16 +345,16 @@ const rowsView = (Base: ModuleType<RowsView>) => class RowsViewMasterDetailExten
340345
return template;
341346
}
342347

343-
protected _isDetailRow(row) {
344-
return row?.rowType && row.rowType.indexOf('detail') === 0;
345-
}
346-
347348
protected _createRow(row) {
348349
const $row = super._createRow.apply(this, arguments as any);
350+
const isDetailRowResult = isDetailRow(row);
351+
352+
if (isDetailRowResult) {
353+
const showRowLines = this.option('showRowLines');
349354

350-
if (row && this._isDetailRow(row)) {
351-
this.option('showRowLines') && $row.addClass(ROW_LINES_CLASS);
352-
$row.addClass(MASTER_DETAIL_ROW_CLASS);
355+
$row
356+
.addClass(CLASSES.detailRow)
357+
.toggleClass(CLASSES.rowLines, showRowLines);
353358

354359
if (isDefined(row.visible)) {
355360
$row.toggle(row.visible);
@@ -360,8 +365,9 @@ const rowsView = (Base: ModuleType<RowsView>) => class RowsViewMasterDetailExten
360365

361366
protected _renderCells($row, options) {
362367
const { row } = options;
368+
const isDetailRowResult = isDetailRow(row);
363369

364-
if (row.rowType && this._isDetailRow(row)) {
370+
if (isDetailRowResult) {
365371
if (this._needRenderCell(0, options.columnIndices)) {
366372
this._renderMasterDetailCell($row, row, options);
367373
}
@@ -383,8 +389,8 @@ const rowsView = (Base: ModuleType<RowsView>) => class RowsViewMasterDetailExten
383389
});
384390

385391
$detailCell
386-
.addClass(CELL_FOCUS_DISABLED_CLASS)
387-
.addClass(MASTER_DETAIL_CELL_CLASS)
392+
.addClass(CLASSES.cellFocusDisabledClass)
393+
.addClass(CLASSES.detailCell)
388394
.attr('colSpan', visibleColumns.length);
389395

390396
const isEditForm = row.isEditing;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2+
export function isDetailRow(row): boolean {
3+
const rowType = row?.rowType;
4+
return rowType === 'detail' || rowType === 'detailAdaptive';
5+
}

packages/devextreme/js/__internal/grids/grid_core/sticky_columns/m_sticky_columns.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import type { ModuleType } from '../m_types';
2323
import gridCoreUtils from '../m_utils';
2424
import { CLASSES as MASTER_DETAIL_CLASSES } from '../master_detail/const';
25+
import { isDetailRow } from '../master_detail/utils';
2526
import type { ColumnsView } from '../views/m_columns_view';
2627
import type { RowsView } from '../views/m_rows_view';
2728
import { isGroupRow } from '../views/m_rows_view';
@@ -341,7 +342,6 @@ const columnHeadersView = (
341342
});
342343
}
343344

344-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
345345
items = items || [];
346346
items.push(
347347
{
@@ -379,19 +379,26 @@ const rowsView = (
379379
const $detailCell: dxElementWrapper = super._renderMasterDetailCell($row, row, options);
380380

381381
if (this.hasStickyColumns()) {
382-
$detailCell.addClass(this.addWidgetPrefix(CLASSES.stickyColumnLeft));
383-
setWidth($detailCell, this._getMasterDetailWidth());
382+
const detailContainerSelector = `.${this.addWidgetPrefix(MASTER_DETAIL_CLASSES.detailContainer)}`;
383+
const $detailContainer = $detailCell.find(detailContainerSelector);
384+
385+
$detailContainer.addClass(this.addWidgetPrefix(CLASSES.stickyColumnLeft));
386+
387+
setWidth($detailContainer, this._getMasterDetailWidth());
384388
}
385389

386390
return $detailCell;
387391
}
388392

389393
private _updateMasterDetailWidths() {
394+
const $detailContainers = this._getRowElements()
395+
.children(`.${MASTER_DETAIL_CLASSES.detailCell}`)
396+
.children(`.${this.addWidgetPrefix(MASTER_DETAIL_CLASSES.detailContainer)}`);
397+
390398
const width = this._getMasterDetailWidth();
391-
const $masterDetailCells = this._getRowElements().children('.dx-master-detail-cell');
392399

393400
setWidth(
394-
$masterDetailCells,
401+
$detailContainers,
395402
`${width}px`,
396403
);
397404
}
@@ -446,12 +453,18 @@ const rowsView = (
446453
}
447454

448455
protected _renderCellContent($cell, options, renderOptions) {
449-
if (!isGroupRow(options) || !this.hasStickyColumns()) {
456+
const hasStickyColumns = this.hasStickyColumns();
457+
const isGroupRowResult = isGroupRow(options);
458+
const isDetailRowResult = isDetailRow(options);
459+
const needWrapContent = isGroupRowResult || isDetailRowResult;
460+
461+
if (!hasStickyColumns || !needWrapContent) {
450462
return super._renderCellContent($cell, options, renderOptions);
451463
}
452464

453465
const $container = $('<div>')
454-
.addClass(this.addWidgetPrefix(CLASSES.groupRowContainer))
466+
.toggleClass(this.addWidgetPrefix(CLASSES.groupRowContainer), isGroupRowResult)
467+
.toggleClass(this.addWidgetPrefix(MASTER_DETAIL_CLASSES.detailContainer), isDetailRowResult)
455468
.appendTo($cell);
456469

457470
return super._renderCellContent($container, options, renderOptions);

0 commit comments

Comments
 (0)