Repository navigation
DataGrid: Refactor ResizingController._synchronizeColumns #34325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
9771eca
Refactor _synchronizeColumns method
nightskylark 3d602d3
fix(popover): remove unnecessary type assertion for overlay stack check
nightskylark 9957424
fix(m_utils): handle non-numeric selectionStart and selectionEnd values
nightskylark 32ad957
fix(grid_view): correct spacing in _synchronizeColumns method
nightskylark dfde56e
fix(m_utils): remove selectionDirection from SelectionRange interface…
nightskylark ab0d0ef
Merge branch 'main' into T1329677
nightskylark c3b08be
Merge branch 'main' into T1329677
nightskylark 304db48
Merge branch 'main' into T1329677
nightskylark 7cb4328
Merge branch 'main' into T1329677
nightskylark 99c13db
Merge branch 'main' into T1329677
nightskylark f73f438
Merge branch 'main' into T1329677
nightskylark a0ab2ec
Merge branch 'main' into T1329677
nightskylark 781388e
Update after review
nightskylark fb7512e
Merge branch 'main' into T1329677
nightskylark 1312c25
refactor(ResizingController): simplify expand column width calculation
nightskylark e86dc6d
refactor(ResizingController): remove redundant comment before measure…
nightskylark 57b55a3
feat(SelectionRange): add SelectionRange interface and update imports
nightskylark 9f83616
refactor(ResizingController): rename private methods for consistency
nightskylark cdce32f
refactor(ResizingController): rename private method _setMaxWidth to s…
nightskylark File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
...rnal/grids/grid_core/views/__tests__/grid_view.normalize_widths_by_expand_columns.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import { describe, expect, it } from '@jest/globals'; | ||
|
|
||
| import type { Column } from '../../columns_controller/types'; | ||
| import { ResizingController } from '../m_grid_view'; | ||
|
|
||
| type ColumnWidth = number | string | undefined; | ||
|
|
||
| // NOTE: the method is private, so it is picked from the prototype to be tested in isolation. | ||
| const resizingControllerPrototype = ResizingController.prototype as unknown as { | ||
| _normalizeWidthsByExpandColumns: ( | ||
| resultWidths: ColumnWidth[], | ||
| visibleColumns: Column[], | ||
| ) => void; | ||
| }; | ||
|
|
||
| const normalizeWidthsByExpandColumns = ( | ||
| resultWidths: ColumnWidth[], | ||
| visibleColumns: Column[], | ||
| ): ColumnWidth[] => { | ||
| resizingControllerPrototype._normalizeWidthsByExpandColumns(resultWidths, visibleColumns); | ||
|
|
||
| return resultWidths; | ||
| }; | ||
|
|
||
| const expandColumn = (): Column => ({ type: 'groupExpand', command: 'expand' } as Column); | ||
| const dataColumn = (dataField: string): Column => ({ dataField } as Column); | ||
|
|
||
| describe('ResizingController._normalizeWidthsByExpandColumns', () => { | ||
| it('leaves the widths as is when there are no expand columns', () => { | ||
| const columns = [dataColumn('a'), dataColumn('b')]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([100, 200], columns)).toEqual([100, 200]); | ||
| }); | ||
|
|
||
| it('leaves the widths as is when there is a single expand column', () => { | ||
| const columns = [expandColumn(), dataColumn('a')]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([30, 200], columns)).toEqual([30, 200]); | ||
| }); | ||
|
|
||
| // NOTE: all groupExpand columns share a single column id (command:expand), so the width | ||
| // of the last one is the value that _setVisibleWidths actually applies to all of them. | ||
| it('applies the width of the LAST expand column to every expand column', () => { | ||
| const columns = [expandColumn(), expandColumn(), dataColumn('a')]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([21, 30, 200], columns)).toEqual([30, 30, 200]); | ||
| }); | ||
|
|
||
| it('normalizes expand columns that are not adjacent to each other', () => { | ||
| const columns = [ | ||
| dataColumn('a'), | ||
| expandColumn(), | ||
| dataColumn('b'), | ||
| expandColumn(), | ||
| dataColumn('c'), | ||
| ]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([100, 21, 200, 30, 300], columns)) | ||
| .toEqual([100, 30, 200, 30, 300]); | ||
| }); | ||
|
|
||
| it('ignores the detailExpand column', () => { | ||
| const columns = [ | ||
| { type: 'detailExpand', command: 'expand' } as Column, | ||
| expandColumn(), | ||
| expandColumn(), | ||
| ]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([15, 21, 30], columns)).toEqual([15, 30, 30]); | ||
| }); | ||
|
|
||
| // NOTE: a falsy width means the column could not be measured (e.g. the grid is hidden). | ||
| it.each([ | ||
| ['zero', 0], | ||
| ['undefined', undefined], | ||
| ])('keeps the measured widths when the last expand column width is %s', (_, width) => { | ||
| const columns = [expandColumn(), expandColumn(), dataColumn('a')]; | ||
|
|
||
| expect(normalizeWidthsByExpandColumns([21, width, 200], columns)).toEqual([21, width, 200]); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,12 +21,14 @@ import { A11yStatusContainerComponent } from '@ts/grids/grid_core/views/a11y_sta | |
| import type { FooterView } from '../../data_grid/summary/m_summary'; | ||
| import type { AdaptiveColumnsController } from '../adaptivity/m_adaptivity'; | ||
| import type { ColumnHeadersView } from '../column_headers/m_column_headers'; | ||
| import { GROUP_COMMAND_COLUMN_NAME } from '../columns_controller/const'; | ||
| import type { ColumnsController } from '../columns_controller/m_columns_controller'; | ||
| import type { Column } from '../columns_controller/types'; | ||
| import type { DataController } from '../data_controller/data_controller'; | ||
| import type { DataChange } from '../data_controller/types'; | ||
| import type { DataSourceController } from '../data_source/data_source_controller'; | ||
| import modules from '../m_modules'; | ||
| import gridCoreUtils from '../m_utils'; | ||
| import gridCoreUtils, { type SelectionRange } from '../m_utils'; | ||
| import type { RowsView } from './m_rows_view'; | ||
|
|
||
| const BORDERS_CLASS = 'borders'; | ||
|
|
@@ -37,6 +39,8 @@ const GROUP_ROW_SELECTOR = 'tr.dx-group-row'; | |
|
|
||
| const HIDDEN_COLUMNS_WIDTH = 'adaptiveHidden'; | ||
|
|
||
| type ColumnWidth = number | string | undefined; | ||
|
nightskylark marked this conversation as resolved.
Outdated
|
||
|
|
||
| const VIEW_NAMES = [ | ||
| 'columnsSeparatorView', | ||
| 'blockSeparatorView', | ||
|
|
@@ -81,7 +85,7 @@ const calculateFreeWidthWithCurrentMinWidth = function (that, columnIndex, curre | |
| return calculateFreeWidth(that, widths.map((width, index) => (index === columnIndex ? currentMinWidth : width))); | ||
| }; | ||
|
|
||
| const restoreFocus = function (focusedElement, selectionRange) { | ||
| const restoreFocus = (focusedElement: Element, selectionRange: SelectionRange): void => { | ||
| accessibility.hiddenFocus(focusedElement, true); | ||
| gridCoreUtils.setSelectionRange(focusedElement, selectionRange); | ||
| }; | ||
|
|
@@ -105,8 +109,6 @@ export class ResizingController extends modules.ViewController { | |
|
|
||
| private _prevContentMinHeight: any; | ||
|
|
||
| private _maxWidth: any; | ||
|
|
||
| private _hasWidth: any; | ||
|
|
||
| private _hasHeight: any; | ||
|
|
@@ -127,6 +129,8 @@ export class ResizingController extends modules.ViewController { | |
|
|
||
| public resizeCompleted!: Callback; | ||
|
|
||
| private _isMaxWidthSet = false; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: An underscore in the name.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| protected callbackNames() { | ||
| return ['resizeCompleted']; | ||
| } | ||
|
|
@@ -345,85 +349,74 @@ export class ResizingController extends modules.ViewController { | |
| } | ||
| } | ||
|
|
||
| private _synchronizeColumns() { | ||
| const columnsController = this._columnsController; | ||
| const visibleColumns = columnsController.getVisibleColumns(); | ||
| const columnAutoWidth = this.option('columnAutoWidth'); | ||
| const hasUndefinedColumnWidth = visibleColumns.some((column) => !isDefined(column.width)); | ||
| let needBestFit = this._needBestFit(); | ||
| let hasMinWidth = false; | ||
| let resetBestFitMode; | ||
| let isColumnWidthsCorrected = false; | ||
| let resultWidths: any[] = []; | ||
| let focusedElement; | ||
| let selectionRange; | ||
| private _setMaxWidth(value: number): void { | ||
| this._isMaxWidthSet = true; | ||
| this.component.$element().css('maxWidth', value); | ||
| } | ||
|
|
||
| const normalizeWidthsByExpandColumns = function () { | ||
| let expandColumnWidth; | ||
| private _clearMaxWidth(): void { | ||
| if (!this._isMaxWidthSet) { | ||
| return; | ||
| } | ||
|
|
||
| each(visibleColumns, (index, column) => { | ||
| if (column.type === 'groupExpand') { | ||
| expandColumnWidth = resultWidths[index]; | ||
| } | ||
| }); | ||
| this._isMaxWidthSet = false; | ||
|
|
||
| each(visibleColumns, (index, column) => { | ||
| if (column.type === 'groupExpand' && expandColumnWidth) { | ||
| resultWidths[index] = expandColumnWidth; | ||
| } | ||
| }); | ||
| }; | ||
| const element = this.component.$element().get(0) as HTMLElement | undefined; | ||
|
|
||
| !needBestFit && each(visibleColumns, (index, column) => { | ||
| if (column.width === 'auto') { | ||
| needBestFit = true; | ||
| return false; | ||
| } | ||
| return undefined; | ||
| }); | ||
| if (element) { | ||
| element.style.maxWidth = ''; | ||
| } | ||
| } | ||
|
|
||
| each(visibleColumns, (index, column) => { | ||
| if (column.minWidth) { | ||
| hasMinWidth = true; | ||
| return false; | ||
| } | ||
| return undefined; | ||
| }); | ||
| private _enableTemporaryBestFitMode(): () => void { | ||
| const $element = this.component.$element(); | ||
| const focusedElement = domAdapter.getActiveElement($element.get(0) as HTMLElement | null); | ||
| const selectionRange = gridCoreUtils.getSelectionRange(focusedElement); | ||
|
|
||
| this._toggleContentMinHeight(this._hasHeight); // T1047239, T1270354 | ||
| this._toggleBestFitMode(true); | ||
|
|
||
| this._setVisibleWidths(visibleColumns, []); | ||
| return (): void => { | ||
| this._toggleBestFitMode(false); | ||
|
|
||
| const $element = this.component.$element(); | ||
| if (focusedElement && focusedElement !== domAdapter.getActiveElement()) { | ||
| const isFocusOutsideWindow = getBoundingRect(focusedElement).bottom < 0; | ||
|
|
||
| if (needBestFit) { | ||
| // @ts-expect-error | ||
| focusedElement = domAdapter.getActiveElement($element.get(0)); | ||
| selectionRange = gridCoreUtils.getSelectionRange(focusedElement); | ||
| this._toggleBestFitMode(true); | ||
| resetBestFitMode = true; | ||
| } | ||
| if (!isFocusOutsideWindow) { | ||
| restoreFocus(focusedElement, selectionRange); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| if ($element && $element.get(0) && this._maxWidth) { | ||
| delete this._maxWidth; | ||
| $element[0].style.maxWidth = ''; | ||
| } | ||
| private _synchronizeColumns(): void { | ||
| const columnsController = this._columnsController; | ||
| const visibleColumns = columnsController.getVisibleColumns(); | ||
| const columnAutoWidth = this.option('columnAutoWidth') as boolean; | ||
| const hasUndefinedColumnWidth = visibleColumns.some((column) => !isDefined(column.width)); | ||
| const needBestFit = this._needBestFit() || visibleColumns.some((column) => column.width === 'auto'); | ||
| const hasMinWidth = visibleColumns.some((column) => !!column.minWidth); | ||
|
|
||
| // Prepare for measurement | ||
| this._toggleContentMinHeight(this._hasHeight); // T1047239, T1270354 | ||
| this._setVisibleWidths(visibleColumns, []); | ||
| const restoreAfterBestFitMode = needBestFit && this._enableTemporaryBestFitMode(); | ||
| this._clearMaxWidth(); | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| deferUpdate(() => { | ||
| if (needBestFit) { | ||
| let resultWidths: ColumnWidth[] = []; | ||
|
|
||
| if (needBestFit || hasMinWidth) { | ||
| resultWidths = this._getBestFitWidths(); | ||
| } | ||
|
|
||
| each(visibleColumns, (index, column) => { | ||
| each(visibleColumns, (index, column) => { | ||
| if (needBestFit) { | ||
| const columnId = columnsController.getColumnId(column); | ||
| columnsController.columnOption(columnId, 'bestFitWidth', resultWidths[index], true); | ||
| }); | ||
| } else if (hasMinWidth) { | ||
| resultWidths = this._getBestFitWidths(); | ||
| } | ||
| } | ||
|
|
||
| each(visibleColumns, function (index) { | ||
| const { width } = this; | ||
| const { width } = column; | ||
| if (width !== 'auto') { | ||
| if (isDefined(width)) { | ||
| resultWidths[index] = isNumeric(width) || isPixelWidth(width) ? parseFloat(width) : width; | ||
|
|
@@ -433,21 +426,14 @@ export class ResizingController extends modules.ViewController { | |
| } | ||
| }); | ||
|
|
||
| if (resetBestFitMode) { | ||
| this._toggleBestFitMode(false); | ||
| resetBestFitMode = false; | ||
| if (focusedElement && focusedElement !== domAdapter.getActiveElement()) { | ||
| const isFocusOutsideWindow = getBoundingRect(focusedElement).bottom < 0; | ||
| if (!isFocusOutsideWindow) { | ||
| restoreFocus(focusedElement, selectionRange); | ||
| } | ||
| } | ||
| if (restoreAfterBestFitMode) { | ||
| restoreAfterBestFitMode(); | ||
| } | ||
|
|
||
| isColumnWidthsCorrected = this._correctColumnWidths(resultWidths, visibleColumns); | ||
| const isColumnWidthsCorrected = this._correctColumnWidths(resultWidths, visibleColumns); | ||
|
|
||
| if (columnAutoWidth) { | ||
| normalizeWidthsByExpandColumns(); | ||
| this._normalizeWidthsByExpandColumns(resultWidths, visibleColumns); | ||
| if (this._needStretch()) { | ||
| this._processStretch(resultWidths, visibleColumns); | ||
| } | ||
|
|
@@ -485,6 +471,34 @@ export class ResizingController extends modules.ViewController { | |
| return freeWidth / columnCountWithoutWidth; | ||
| } | ||
|
|
||
| private _normalizeWidthsByExpandColumns( | ||
| resultWidths: ColumnWidth[], | ||
| visibleColumns: Column[], | ||
| ): void { | ||
| const isExpandColumn = (column: Column): boolean => column.type === GROUP_COMMAND_COLUMN_NAME; | ||
|
|
||
| const lastExpandColumnIndex = visibleColumns.reduce( | ||
| (result, column, index) => (isExpandColumn(column) ? index : result), | ||
| -1, | ||
| ); | ||
|
|
||
| if (lastExpandColumnIndex < 0) { | ||
| return; | ||
| } | ||
|
|
||
| const expandColumnWidth = resultWidths[lastExpandColumnIndex]; | ||
|
|
||
| if (!expandColumnWidth) { | ||
| return; | ||
| } | ||
|
|
||
| visibleColumns.forEach((column, index) => { | ||
| if (isExpandColumn(column)) { | ||
| resultWidths[index] = expandColumnWidth; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * @extended: adaptivity | ||
| */ | ||
|
|
@@ -494,7 +508,6 @@ export class ResizingController extends modules.ViewController { | |
| let hasPercentWidth = false; | ||
| let hasAutoWidth = false; | ||
| let isColumnWidthsCorrected = false; | ||
| const $element = that.component.$element(); | ||
| const hasWidth = that._hasWidth; | ||
|
|
||
| for (i = 0; i < visibleColumns.length; i++) { | ||
|
|
@@ -547,9 +560,7 @@ export class ResizingController extends modules.ViewController { | |
| if (hasWidth === false && !hasPercentWidth) { | ||
| const borderWidth = gridCoreUtils.getComponentBorderWidth(this, $rowsViewElement); | ||
|
|
||
| that._maxWidth = totalWidth + scrollbarWidth + borderWidth; | ||
|
|
||
| $element.css('maxWidth', that._maxWidth); | ||
| that._setMaxWidth(totalWidth + scrollbarWidth + borderWidth); | ||
| } | ||
| } | ||
| } | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually keep types and interfaces in a separate types.ts file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done