Skip to content

Commit

Permalink
Maintain focus after column collapsing (#10865)
Browse files Browse the repository at this point in the history
  • Loading branch information
budnix committed Apr 3, 2024
1 parent 3fb4db4 commit 5d3f934
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .changelogs/10865.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"issuesOrigin": "private",
"title": "Maintain focus after column collapsing.",
"type": "fixed",
"issueOrPR": 10865,
"breaking": false,
"framework": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
describe('CollapsibleColumns (RTL mode)', () => {
const id = 'testContainer';

beforeEach(function() {
$('html').attr('dir', 'rtl');
this.$container = $(`<div id="${id}"></div>`).appendTo('body');
});

afterEach(function() {
$('html').attr('dir', 'ltr');

if (this.$container) {
destroy();
this.$container.remove();
}
if (this.$wrapper) {
this.$wrapper.remove();
}
});

describe('selection', () => {
it('should move focus to the nearest left column after collapsing (single cell selection)', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 4 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 3 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
});

selectCell(1, 3);
$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Collapse header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : : - : : : |
|===:===:===:===:===:===:===:===|
| : : : : : : : |
| : : : : # : : : |
| : : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,5 from: 1,5 to: 1,5']);

$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Expand header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : : - : : : |
|===:===:===:===:===:===:===:===:===:===|
| : : : : : : : : : |
| : : : : # : : : : : |
| : : : : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,5 from: 1,5 to: 1,5']);
});

it('should move focus to the nearest right column when there is no column visible on the left after collapsing', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 5 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 4 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
});

const columnMapper = columnIndexMapper().createAndRegisterIndexMap('my-hiding-map', 'hiding');

columnMapper.setValueAtIndex(6, true);
columnMapper.setValueAtIndex(7, true);
columnMapper.setValueAtIndex(8, true);
columnMapper.setValueAtIndex(9, true);

render();

selectCell(1, 3, 2, 5);
$(getCell(-3, 1).querySelector('.collapsibleIndicator')) // Collapse header "B1"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : |
| : |
| - : |
|===:===|
| : |
| # : |
| : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,1 from: 1,1 to: 1,1']);

$(getCell(-3, 1).querySelector('.collapsibleIndicator')) // Expand header "B1"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : |
| : |
| : - : |
|===:===:===:===:===:===|
| : : : : : |
| : : : : # : |
| : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,1 from: 1,1 to: 1,1']);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -385,5 +385,199 @@ describe('CollapsibleColumns', () => {
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 2,2 from: -1,2 to: 2,4']);
});

it('should move focus to the nearest right column after collapsing (single cell selection)', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 4 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 3 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
});

selectCell(1, 3);
$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Collapse header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : - : : : : |
|===:===:===:===:===:===:===:===|
| : : : : : : : |
| : : : # : : : : |
| : : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,5 from: 1,5 to: 1,5']);

$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Expand header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : - : : : : |
|===:===:===:===:===:===:===:===:===:===|
| : : : : : : : : : |
| : : : : : # : : : : |
| : : : : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,5 from: 1,5 to: 1,5']);
});

it('should move focus to the nearest right column after collapsing (multiple selection)', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 4 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 3 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
});

selectCell(2, 4, 1, 3);
$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Collapse header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : - : : : : |
|===:===:===:===:===:===:===:===|
| : : : : : : : |
| : : : : : : : |
| : : : # : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 2,5 from: 2,5 to: 2,5']);

$(getCell(-1, 2).querySelector('.collapsibleIndicator')) // Expand header "C3"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : - : : : : |
|===:===:===:===:===:===:===:===:===:===|
| : : : : : : : : : |
| : : : : : : : : : |
| : : : : : # : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 2,5 from: 2,5 to: 2,5']);
});

it('should move focus to the nearest right column after collapsing (column header selection)', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 4 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 3 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
navigableHeaders: true,
});

selectColumns(2, 2, -1);
$(getCell(-2, 1).querySelector('.collapsibleIndicator')) // Collapse header "B2"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : # : : : : |
|===:===:===:===:===:===:===|
| : : : : : : |
| : : : : : : |
| : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: -1,5 from: -1,5 to: -1,5']);

$(getCell(-2, 1).querySelector('.collapsibleIndicator')) // Expand header "B2"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : : : : : |
| : : : : : : |
| : : : # : : : : |
|===:===:===:===:===:===:===:===:===:===|
| : : : : : : : : : |
| : : : : : : : : : |
| : : : : : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: -1,5 from: -1,5 to: -1,5']);
});

it('should move focus to the nearest left column when there is no column visible on the right after collapsing', () => {
handsontable({
data: createSpreadsheetData(3, 10),
colHeaders: true,
nestedHeaders: [
['A1', { label: 'B1', colspan: 5 }, 'G1', 'H1', 'I1', 'J1'],
['A2', { label: 'B2', colspan: 5 }, 'F2', 'G2', 'H2', 'I2', 'J2'],
['A3', 'B3', { label: 'C3', colspan: 4 }, 'F3', 'G3', 'H3', 'I3', 'J3'],
],
collapsibleColumns: true,
});

const columnMapper = columnIndexMapper().createAndRegisterIndexMap('my-hiding-map', 'hiding');

columnMapper.setValueAtIndex(6, true);
columnMapper.setValueAtIndex(7, true);
columnMapper.setValueAtIndex(8, true);
columnMapper.setValueAtIndex(9, true);

render();

selectCell(1, 3, 2, 5);
$(getCell(-3, 1).querySelector('.collapsibleIndicator')) // Collapse header "B1"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : |
| : |
| : - |
|===:===|
| : |
| : # |
| : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,1 from: 1,1 to: 1,1']);

$(getCell(-3, 1).querySelector('.collapsibleIndicator')) // Expand header "B1"
.simulate('mousedown')
.simulate('mouseup')
.simulate('click');

expect(`
| : |
| : |
| : - : |
|===:===:===:===:===:===|
| : : : : : |
| : # : : : : |
| : : : : : |
`).toBeMatchToSelectionPattern();
expect(getSelectedRange()).toEqualCellRange(['highlight: 1,1 from: 1,1 to: 1,1']);
});
});
});
15 changes: 15 additions & 0 deletions handsontable/src/plugins/collapsibleColumns/collapsibleColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,21 @@ export class CollapsibleColumns extends BasePlugin {
}, true);

const isActionPerformed = this.getCollapsedColumns().length !== currentCollapsedColumns.length;
const selectionRange = this.hot.getSelectedRangeLast();

if (action === 'collapse' && isActionPerformed && selectionRange) {
const { row, col } = selectionRange.highlight;
const isHidden = this.hot.rowIndexMapper.isHidden(row) || this.hot.columnIndexMapper.isHidden(col);

if (isHidden && affectedColumnsIndexes.includes(col)) {
const nextRow = row >= 0 ? this.hot.rowIndexMapper.getNearestNotHiddenIndex(row, 1, true) : row;
const nextColumn = col >= 0 ? this.hot.columnIndexMapper.getNearestNotHiddenIndex(col, 1, true) : col;

if (nextRow !== null && nextColumn !== null) {
this.hot.selectCell(nextRow, nextColumn);
}
}
}

this.hot.runHooks(
actionTranslator.afterHook,
Expand Down

0 comments on commit 5d3f934

Please sign in to comment.