Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit 293ee33

Browse files
authored
Merge pull request #578 from plotly/576-hidden-edits
576 - column edits with hidden columns
2 parents a4ea856 + ccaa2dd commit 293ee33

File tree

5 files changed

+108
-20
lines changed

5 files changed

+108
-20
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
66
### Fixed
7+
[#578](https://github.com/plotly/dash-table/pull/578)
8+
- Fix [#576](https://github.com/plotly/dash-table/issues/576), editing column names or deleting columns while other columns are hidden causing the hidden columns to be lost.
9+
- Fix an unreported bug that clicking "Cancel" at the column name edit prompt would clear the name, rather than leaving it unchanged as it should.
10+
711
[#569](https://github.com/plotly/dash-table/issues/569), [#544](https://github.com/plotly/dash-table/issues/544)
812
- Allow empty strings in all `filter_query` (e.g filter_query: '{colA} eq ""')
913

src/dash-table/derived/header/content.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@ const doAction = (
2626
action: (
2727
column: IColumn,
2828
columns: Columns,
29+
visibleColumns: Columns,
2930
columnRowIndex: any,
3031
mergeDuplicateHeaders: boolean,
3132
data: Data
3233
) => any,
3334
selected_columns: string[],
3435
column: IColumn,
3536
columns: Columns,
37+
visibleColumns: Columns,
3638
columnRowIndex: any,
3739
mergeDuplicateHeaders: boolean,
3840
setFilter: SetFilter,
3941
setProps: SetProps,
4042
map: Map<string, SingleColumnSyntaxTree>,
4143
data: Data
4244
) => () => {
43-
const props = action(column, columns, columnRowIndex, mergeDuplicateHeaders, data);
45+
const props = action(column, columns, visibleColumns, columnRowIndex, mergeDuplicateHeaders, data);
4446

4547
const affectedColumIds = actions.getAffectedColumns(column, columns, columnRowIndex, mergeDuplicateHeaders);
4648

@@ -99,7 +101,10 @@ function doSort(columnId: ColumnId, sortBy: SortBy, mode: SortMode, setProps: Se
99101

100102
function editColumnName(column: IColumn, columns: Columns, columnRowIndex: any, setProps: SetProps, mergeDuplicateHeaders: boolean) {
101103
return () => {
102-
setProps(actions.editColumnName(column, columns, columnRowIndex, mergeDuplicateHeaders));
104+
const update = actions.editColumnName(column, columns, columnRowIndex, mergeDuplicateHeaders);
105+
if (update) {
106+
setProps(update);
107+
}
103108
};
104109
}
105110

@@ -250,7 +255,7 @@ function getter(
250255
null :
251256
(<span
252257
className='column-header--edit'
253-
onClick={editColumnName(column, visibleColumns, headerRowIndex, setProps, mergeDuplicateHeaders)}
258+
onClick={editColumnName(column, columns, headerRowIndex, setProps, mergeDuplicateHeaders)}
254259
>
255260
<FontAwesomeIcon icon='pencil-alt' />
256261
</span>)
@@ -260,7 +265,7 @@ function getter(
260265
null :
261266
(<span
262267
className='column-header--clear'
263-
onClick={doAction(actions.clearColumn, selected_columns, column, visibleColumns, headerRowIndex, mergeDuplicateHeaders, setFilter, setProps, map, data)}
268+
onClick={doAction(actions.clearColumn, selected_columns, column, columns, visibleColumns, headerRowIndex, mergeDuplicateHeaders, setFilter, setProps, map, data)}
264269
>
265270
<FontAwesomeIcon icon='eraser' />
266271
</span>)
@@ -272,7 +277,7 @@ function getter(
272277
className={'column-header--delete' + (spansAllColumns ? ' disabled' : '')}
273278
onClick={spansAllColumns ?
274279
undefined :
275-
doAction(actions.deleteColumn, selected_columns, column, visibleColumns, headerRowIndex, mergeDuplicateHeaders, setFilter, setProps, map, data)
280+
doAction(actions.deleteColumn, selected_columns, column, columns, visibleColumns, headerRowIndex, mergeDuplicateHeaders, setFilter, setProps, map, data)
276281
}
277282
>
278283
<FontAwesomeIcon icon={['far', 'trash-alt']} />

src/dash-table/utils/actions.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,25 @@ export function getAffectedColumns(column, columns, headerRowIndex, mergeDuplica
4747
);
4848
}
4949

50-
export function clearColumn(column, columns, headerRowIndex, mergeDuplicateHeaders, data) {
51-
const rejectedColumnIds = getAffectedColumns(column, columns, headerRowIndex, mergeDuplicateHeaders);
52-
53-
return {
54-
data: R.map(R.omit(rejectedColumnIds), data)
55-
};
50+
export function clearColumn(column, columns, visibleColumns, headerRowIndex, mergeDuplicateHeaders, _data) {
51+
const {data} = deleteColumn(column, columns, visibleColumns, headerRowIndex, mergeDuplicateHeaders, _data);
52+
return {data};
5653
}
5754

58-
export function deleteColumn(column, columns, headerRowIndex, mergeDuplicateHeaders, data) {
59-
const {groupIndexFirst, groupIndexLast} = getGroupedColumnIndices(
60-
column, columns, headerRowIndex, mergeDuplicateHeaders, columns.indexOf(column)
55+
export function deleteColumn(column, columns, visibleColumns, headerRowIndex, mergeDuplicateHeaders, data) {
56+
const rejectedColumnIds = getAffectedColumns(
57+
column,
58+
visibleColumns,
59+
headerRowIndex,
60+
mergeDuplicateHeaders
6161
);
6262

6363
return {
64-
columns: R.remove(
65-
groupIndexFirst,
66-
1 + groupIndexLast - groupIndexFirst,
64+
columns: R.filter(
65+
col => (rejectedColumnIds.indexOf(col.id) === -1),
6766
columns
6867
),
69-
...clearColumn(column, columns, headerRowIndex, mergeDuplicateHeaders, data),
68+
data: R.map(R.omit(rejectedColumnIds), data),
7069
// NOTE - We're just clearing these so that there aren't any
7170
// inconsistencies. In an ideal world, we would probably only
7271
// update them if they contained one of the columns that we're
@@ -106,7 +105,7 @@ export function changeColumnHeader(column, columns, headerRowIndex, mergeDuplica
106105
}
107106

108107
const { groupIndexFirst, groupIndexLast } = getGroupedColumnIndices(
109-
column, newColumns, headerRowIndex, mergeDuplicateHeaders, columnIndex
108+
column, newColumns, headerRowIndex, mergeDuplicateHeaders, columnIndex, true
110109
);
111110

112111
R.range(groupIndexFirst, groupIndexLast + 1).map(i => {
@@ -122,5 +121,8 @@ export function changeColumnHeader(column, columns, headerRowIndex, mergeDuplica
122121

123122
export function editColumnName(column, columns, headerRowIndex, mergeDuplicateHeaders) {
124123
const newColumnName = window.prompt('Enter a new column name');
124+
if (newColumnName === null) {
125+
return null;
126+
}
125127
return changeColumnHeader(column, columns, headerRowIndex, mergeDuplicateHeaders, newColumnName);
126128
}

tests/cypress/tests/standalone/column_test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ describe(`column, mode=${AppMode.Actionable}, flavor=${AppFlavor.Merged}, ${AppF
2727
DashTable.getHeader(1, 1).within(() => cy.get('span.column-header-name').should('have.html', 'Paris'));
2828
});
2929

30+
it('keeps hidden pieces when deleting a merged column', () => {
31+
cy.get('.column-4 .column-header--hide').click(); // Boston
32+
cy.get('.column-2 .column-header--hide').click(); // Montréal
33+
DashTable.deleteColumnById(0, 'ccc'); // City
34+
cy.get('.show-hide').click();
35+
cy.get('.show-hide-menu-item input').eq(1).click(); // Montréal
36+
cy.get('.show-hide-menu-item input').eq(2).click(); // Boston
37+
cy.get('.column-1 .column-header-name').eq(2).should('have.html', 'Montréal');
38+
cy.get('.column-2 .column-header-name').eq(1).should('have.html', 'Boston');
39+
});
40+
3041
it('can clear column', () => {
3142
DashTable.getFilter(0).click();
3243
DOM.focused.type(`is num`);
@@ -62,6 +73,29 @@ describe(`column, mode=${AppMode.Actionable}, flavor=${AppFlavor.Merged}, ${AppF
6273
DashTable.getFilter(4).within(() => cy.get('input').should('have.value', ''));
6374
});
6475

76+
it('keeps hidden pieces when clearing a merged column', () => {
77+
// initial state of city columns
78+
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', '1'));
79+
DashTable.getCell(0, 2).within(() => cy.get('.dash-cell-value').should('have.html', '100'));
80+
DashTable.getCell(0, 3).within(() => cy.get('.dash-cell-value').should('have.html', '1'));
81+
DashTable.getCell(0, 4).within(() => cy.get('.dash-cell-value').should('have.html', '2'));
82+
DashTable.getCell(0, 5).within(() => cy.get('.dash-cell-value').should('have.html', '10'));
83+
84+
cy.get('.column-4 .column-header--hide').click(); // Boston
85+
cy.get('.column-2 .column-header--hide').click(); // Montréal
86+
DashTable.clearColumnById(0, 'ccc'); // City
87+
cy.get('.show-hide').click();
88+
cy.get('.show-hide-menu-item input').eq(2).click(); // Montréal
89+
cy.get('.show-hide-menu-item input').eq(4).click(); // Boston
90+
91+
// after clear and re-show -> only Montréal and Boston have data
92+
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', ''));
93+
DashTable.getCell(0, 2).within(() => cy.get('.dash-cell-value').should('have.html', '100'));
94+
DashTable.getCell(0, 3).within(() => cy.get('.dash-cell-value').should('have.html', ''));
95+
DashTable.getCell(0, 4).within(() => cy.get('.dash-cell-value').should('have.html', '2'));
96+
DashTable.getCell(0, 5).within(() => cy.get('.dash-cell-value').should('have.html', ''));
97+
});
98+
6599
it('can hide column', () => {
66100
DashTable.getHeader(0, 0).within(() => cy.get('span.column-header-name').should('have.html', 'rows'));
67101
DashTable.hideColumnById(0, 'rows');
@@ -313,4 +347,4 @@ describe(`column, mode=${AppMode.Actionable}`, () => {
313347
DashTable.getHeader(1, 2).within(() => cy.get('span.column-header-name').should('have.html', 'France'));
314348
DashTable.getHeader(2, 2).within(() => cy.get('span.column-header-name').should('have.html', 'Paris'));
315349
});
316-
});
350+
});

tests/cypress/tests/standalone/edit_headers.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,49 @@ describe(`edit, mode=${AppMode.Typed}`, () => {
5151
});
5252
});
5353

54+
describe(`edit headers while some columns are hidden`, () => {
55+
beforeEach(() => {
56+
cy.visit(`http://localhost:8080?mode=${AppMode.Actionable}&flavor=${AppFlavor.Merged}`);
57+
DashTable.toggleScroll(false);
58+
})
59+
it('preserves hidden columns unchanged when editing visible names', () => {
60+
cy.window().then((win: any) => {
61+
cy.stub(win, 'prompt').returns('Chill');
62+
});
63+
// hide 3 columns - one at the start of a merged set, one in the middle, one not in the set
64+
cy.get('.column-8 .column-header--hide').click({force: true});
65+
cy.get('.column-6 .column-header--hide').click({force: true});
66+
cy.get('.column-1 .column-header--hide').click({force: true});
67+
// edit the merged name
68+
cy.get('.dash-header.column-5 .column-header--edit').eq(1).click({force: true});
69+
// re-show the hidden columns
70+
cy.get('.show-hide').click();
71+
cy.get('.show-hide-menu-item input').eq(1).click();
72+
cy.get('.show-hide-menu-item input').eq(6).click();
73+
cy.get('.show-hide-menu-item input').eq(8).click();
74+
// all columns still exist
75+
cy.get('.dash-header.column-9 .column-header-name').should('have.html', 'Temperature-RO');
76+
// the columns that were hidden when the merged name was changed
77+
// still changed name - so they're still all merged
78+
cy.get('.dash-header.column-6[colspan="4"] .column-header-name').eq(1).should('have.html', 'Chill');
79+
});
80+
});
81+
82+
describe('edit headers, canceled edit', () => {
83+
beforeEach(() => {
84+
cy.visit(`http://localhost:8080?mode=${AppMode.SingleHeaders}`);
85+
DashTable.toggleScroll(false);
86+
});
87+
it('preserves column name if edit is canceled', () => {
88+
cy.window().then((win: any) => {
89+
// user presses cancel -> prompt returns null
90+
cy.stub(win, 'prompt').returns(null);
91+
});
92+
cy.get('.dash-header.column-0 .column-header--edit').eq(0).click();
93+
cy.get('.dash-header.column-0 > div > span:last-child').should('have.html', 'rows');
94+
});
95+
});
96+
5497
describe(`edit headers, mode=${AppMode.SingleHeaders}`, () => {
5598
beforeEach(() => {
5699
cy.visit(`http://localhost:8080?mode=${AppMode.SingleHeaders}`);

0 commit comments

Comments
 (0)