diff --git a/e2e/datagrid-suite/datagrid-data-change.spec.ts b/e2e/datagrid-suite/datagrid-data-change.spec.ts index f4409e32220..0938b5ebe91 100644 --- a/e2e/datagrid-suite/datagrid-data-change.spec.ts +++ b/e2e/datagrid-suite/datagrid-data-change.spec.ts @@ -29,7 +29,7 @@ e2e.scenario({ cy.get('[data-testid="glide-cell-2-1"]').should('have.attr', 'aria-selected', 'true'); cy.get('body').type('12{enter}', { delay: 500 }); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get('[data-testid="query-editor-row"]').contains('Snapshot'); }, diff --git a/e2e/datagrid-suite/datagrid-editing-features.spec.ts b/e2e/datagrid-suite/datagrid-editing-features.spec.ts index e71d1f31967..c9bd5d6720b 100644 --- a/e2e/datagrid-suite/datagrid-editing-features.spec.ts +++ b/e2e/datagrid-suite/datagrid-editing-features.spec.ts @@ -17,7 +17,7 @@ e2e.scenario({ cy.get('[data-testid="glide-cell-2-1"]').should('have.attr', 'aria-selected', 'true'); cy.get('body').type('123{enter}', { delay: 500 }); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); // Delete a cell cy.get('.dvn-scroller').click(200, 200); @@ -57,7 +57,7 @@ e2e.scenario({ cy.get('.dvn-scroller').click(20, 190, { waitForAnimations: true }); cy.get('.dvn-scroller').click(20, 90, { shiftKey: true, waitForAnimations: true }); // with shift to select all rows between clicks cy.get('body').type('{del}'); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get('[data-testid="glide-cell-1-4"]').should('have.text', ''); cy.get('[data-testid="glide-cell-1-3"]').should('have.text', ''); cy.get('[data-testid="glide-cell-1-2"]').should('have.text', ''); @@ -72,7 +72,7 @@ e2e.scenario({ cy.get('.dvn-scroller').click(20, 90, { commandKey: true, waitForAnimations: true }); // with cmd to select only clicked rows cy.get('body').type('{del}'); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get('[data-testid="glide-cell-1-1"]').should('have.text', ''); cy.get('[data-testid="glide-cell-2-1"]').should('have.text', 0); @@ -89,7 +89,7 @@ e2e.scenario({ // Delete column through header dropdown menu cy.get('.dvn-scroller').click(250, 15); // click header dropdown cy.get('body').click(450, 420); // click delete column - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).should('have.length', 1); // Delete row through context menu @@ -108,7 +108,7 @@ e2e.scenario({ cy.get('.dvn-scroller').click(20, 90, { commandKey: true, waitForAnimations: true }); // with shift to select all rows between clicks cy.get('.dvn-scroller').rightclick(40, 90); cy.get('[aria-label="Context menu"]').click(10, 10); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).should('have.length', 5); // there are 5 data rows + 1 for the add new row btns // Delete column through context menu @@ -121,7 +121,7 @@ e2e.scenario({ // Add a new column cy.get('body').click(350, 200).type('New Column{enter}'); - cy.get('[aria-label="Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); cy.get('body') .click(350, 230) .type('Value 1{enter}') @@ -138,7 +138,7 @@ e2e.scenario({ cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).contains('Renamed column'); // Change column field type - cy.get('.dvn-scroller').click(250, 15); + cy.get('.dvn-scroller').click(310, 15); cy.get('[aria-label="Context menu"]').click(50, 50); cy.get('.dvn-scroller').click(200, 100); cy.get('body').type('Str Value{enter}'); diff --git a/public/app/plugins/panel/datagrid/DataGridPanel.test.tsx b/public/app/plugins/panel/datagrid/DataGridPanel.test.tsx index d2e13269bf7..1b7ff145e54 100644 --- a/public/app/plugins/panel/datagrid/DataGridPanel.test.tsx +++ b/public/app/plugins/panel/datagrid/DataGridPanel.test.tsx @@ -111,14 +111,28 @@ describe('DataGrid', () => { expect(screen.getByText('Remove all data')).toBeInTheDocument(); expect(screen.getByText('Search...')).toBeInTheDocument(); - // click on header cell should show only column options + // right clicking on header cell without clicking/selecting the cell should show only general options + fireEvent.contextMenu(scroller, { + clientX: 50, + clientY: 36, + }); + + expect(screen.getByText('Remove all data')).toBeInTheDocument(); + expect(screen.getByText('Search...')).toBeInTheDocument(); + + // selecting the header first and then right click on header cell should show only column options + const canvas = screen.getByTestId('data-grid-canvas'); + sendClick(canvas, { + clientX: 50, + clientY: 36, + }); + fireEvent.contextMenu(scroller, { clientX: 50, clientY: 36, }); expect(screen.getByText('Delete column')).toBeInTheDocument(); - expect(screen.getByText('Clear column')).toBeInTheDocument(); expect(screen.getByText('Remove all data')).toBeInTheDocument(); expect(screen.getByText('Search...')).toBeInTheDocument(); diff --git a/public/app/plugins/panel/datagrid/DataGridPanel.tsx b/public/app/plugins/panel/datagrid/DataGridPanel.tsx index a79fc9f45c6..679049e51da 100644 --- a/public/app/plugins/panel/datagrid/DataGridPanel.tsx +++ b/public/app/plugins/panel/datagrid/DataGridPanel.tsx @@ -154,8 +154,29 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }: return true; } - if (selection.rows) { - updateSnapshot(deleteRows(frame, selection.rows.toArray()), onUpdateData); + const rows = selection.rows.toArray(); + const cols = selection.columns.toArray(); + + if (rows.length) { + updateSnapshot(deleteRows(frame, rows), onUpdateData); + return true; + } + + if (cols.length) { + const copiedFrame = { + ...frame, + fields: frame.fields.map((field, index) => { + if (cols.includes(index)) { + return { + ...field, + values: new Array(frame.length).fill(null), + }; + } + + return field; + }), + }; + updateSnapshot(copiedFrame, onUpdateData); return true; } diff --git a/public/app/plugins/panel/datagrid/components/DatagridContextMenu.tsx b/public/app/plugins/panel/datagrid/components/DatagridContextMenu.tsx index 648fbd45160..697f5bdfa01 100644 --- a/public/app/plugins/panel/datagrid/components/DatagridContextMenu.tsx +++ b/public/app/plugins/panel/datagrid/components/DatagridContextMenu.tsx @@ -53,10 +53,11 @@ export const DatagridContextMenu = ({ columnDeletionLabel = `Delete ${selectedColumns.length} columns`; } + // Show delete/clear options on cell right click, but not on header right click, unless header column is specifically selected. const showDeleteRow = (row !== undefined && row >= 0) || selectedRows.length; - const showDeleteColumn = (column !== undefined && column >= 0) || selectedColumns.length; + const showDeleteColumn = (column !== undefined && column >= 0 && row !== undefined) || selectedColumns.length; const showClearRow = row !== undefined && row >= 0 && !selectedRows.length; - const showClearColumn = column !== undefined && column >= 0 && !selectedColumns.length; + const showClearColumn = column !== undefined && column >= 0 && row !== undefined && !selectedColumns.length; const renderContextMenuItems = () => ( <> @@ -85,6 +86,7 @@ export const DatagridContextMenu = ({ ...data, fields: data.fields.filter((_, index) => !selectedColumns.includes(index)), }); + dispatch({ type: DatagridActionType.gridSelectionCleared }); return; } @@ -215,20 +217,25 @@ export const DatagridContextMenu = ({ { - if (selectedColumns.length) { - saveData({ - ...data, - fields: data.fields.filter((_, index) => !selectedColumns.includes(index)), - }); - return; - } - saveData({ ...data, fields: data.fields.filter((_, index) => index !== column), }); + + // also clear selection since it will change it if the deleted column is selected or if indexes shift + dispatch({ type: DatagridActionType.gridSelectionCleared }); + }} + /> + { + const field = data.fields[column]; + field.values = field.values.map(() => null); + saveData({ + ...data, + }); }} />