Skip to content
Open
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 @@ -120,11 +120,37 @@ export class FocusController extends core.ViewController {
const isFocusedRowVisible = focusedRowIndex >= 0;
const isFocusedRowInChange = !!e.rowIndices?.includes(focusedRowIndex);

if (isFocusedRowVisible && isFocusedRowInChange) {
if (!isFocusedRowVisible || !isFocusedRowInChange) {
return;
}

if (this.hasStaleFocusedRow(focusedRowIndex)) {
this.updateFocusedRow({ focusedRowKey, preventScroll: true });
} else {
this.restoreFocusedRowTabIndex(focusedRowIndex);
}
}

private restoreFocusedRowTabIndex(focusedRowIndex: number): void {
const $focusedRows = $(this.getRowsView().getRowElement(focusedRowIndex));
const hasFocusableCell = $focusedRows.children('[tabindex]').length > 0;

if (!hasFocusableCell) {
$focusedRows.attr('tabindex', this.option('tabIndex') ?? 0);
}
}

private hasStaleFocusedRow(focusedRowIndex: number): boolean {
const rowsView = this.getRowsView();

return rowsView.getTableElements().toArray().some((tableElement) => {
const $tableElement = $(tableElement);
const $focusedRow = rowsView._getRowElements($tableElement).eq(focusedRowIndex);

return this.findFocusedRows($tableElement).not($focusedRow).length > 0;
});
}

private updateFocusedRowIfNeeded(e: DataChange, forceUpdate = false): void {
const operationTypes = e.operationTypes ?? {};
const {
Expand Down Expand Up @@ -536,15 +562,19 @@ export class FocusController extends core.ViewController {
}
}

private _clearPreviousFocusedRow($tableElement: dxElementWrapper, focusedRowIndex: number): void {
private findFocusedRows($tableElement: dxElementWrapper): dxElementWrapper {
const isNotMasterDetailFocusedRow = (_: number, focusedRow: Element): boolean => {
const $focusedRowTable = $(focusedRow).closest(`.${this.addWidgetPrefix(VIEW_CLASSES.table)}`);
return $tableElement.is($focusedRowTable);
};

const $prevRowFocusedElement = $tableElement
return $tableElement
.find(FOCUSED_ROW_SELECTOR)
.filter(isNotMasterDetailFocusedRow);
}

private _clearPreviousFocusedRow($tableElement: dxElementWrapper, focusedRowIndex: number): void {
const $prevRowFocusedElement = this.findFocusedRows($tableElement);

$prevRowFocusedElement
.removeClass(CLASSES.rowFocused)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
afterEach, beforeEach, describe, expect, it,
} from '@jest/globals';
import $ from '@js/core/renderer';

import { flushAsync } from '../../grid_core/__tests__/__mock__/helpers/utils';
import {
Expand Down Expand Up @@ -89,3 +90,62 @@ describe('TreeList focus — navigating to the focused row', () => {
expect(instance.option('focusedRowIndex')).toBe(1);
});
});

describe('TreeList focus — partial update of the focused row', () => {
beforeEach(beforeTest);
afterEach(afterTest);

it('keeps DOM focus on the focused cell when the focused row is expanded with repaintChangesOnly (T1338023)', async () => {
const { instance } = await createTreeList({
dataSource: DATA,
columns: ['id', 'name'],
focusedRowEnabled: true,
focusedRowKey: 1,
repaintChangesOnly: true,
autoExpandAll: false,
});
await flushAsync();

const cell = instance.getCellElement(0, 'name') as HTMLElement;
instance.focus(cell);
await flushAsync();
cell.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift', bubbles: true }));

expect(document.activeElement).toBe(cell);

const expanding = instance.expandRow(1);
await flushAsync();
await expanding;

expect(visibleKeys(instance)).toEqual([1, 11, 2]);
expect(cell.hasAttribute('tabindex')).toBe(true);
expect(document.activeElement).toBe(cell);
});

it('keeps the focused row tab stop when the focused row is expanded with repaintChangesOnly (T1338023)', async () => {
const { instance } = await createTreeList({
dataSource: DATA,
columns: ['id', 'name'],
focusedRowEnabled: true,
focusedRowKey: 1,
repaintChangesOnly: true,
autoExpandAll: false,
tabIndex: 3,
});
await flushAsync();

const row = $(instance.getRowElement(0)).get(0) as HTMLElement;
instance.focus(row);
await flushAsync();
row.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift', bubbles: true }));

expect(document.activeElement).toBe(row);

const expanding = instance.expandRow(1);
await flushAsync();
await expanding;

expect(visibleKeys(instance)).toEqual([1, 11, 2]);
expect(row.getAttribute('tabindex')).toBe('3');
});
});
Loading