Table: Get rid of ContextMenu on right click (#109825)
* Table: Get rid of ContextMenu on right click * fix a scroll thing for expanded table, git rid of some conditional accessors * remove context menu test * remove context menu test * i18n * fix lint issue
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import {
|
||||
@@ -1242,57 +1242,6 @@ describe('TableNG', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO we need to test this with an e2e rather than a unit test, because the element dimensions calcs
|
||||
// don't work in unit tests (no clientWidth/Height)
|
||||
describe.skip('Resizing', () => {
|
||||
beforeEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
||||
window.HTMLElement.prototype.setPointerCapture = jest.fn();
|
||||
window.HTMLElement.prototype.hasPointerCapture = jest.fn();
|
||||
window.HTMLElement.prototype.releasePointerCapture = jest.fn();
|
||||
window.HTMLElement.prototype.getBoundingClientRect = jest.fn(() => ({
|
||||
width: 100,
|
||||
height: 20,
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
toJSON: jest.fn(() => ''),
|
||||
}));
|
||||
});
|
||||
|
||||
it('calls onColumnResize when column is resized', async () => {
|
||||
const onColumnResize = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<TableNG
|
||||
enableVirtualization={false}
|
||||
data={createBasicDataFrame()}
|
||||
width={800}
|
||||
height={600}
|
||||
onColumnResize={onColumnResize}
|
||||
/>
|
||||
);
|
||||
|
||||
// Find resize handle
|
||||
const resizeHandles = container.querySelectorAll('.rdg-header-row > [role="columnheader"] > div:last-child');
|
||||
const handle = resizeHandles[0];
|
||||
|
||||
if (!handle) {
|
||||
throw new Error('Resize handle not found');
|
||||
}
|
||||
|
||||
// simulate a click, then drag, then release.
|
||||
await userEvent.pointer({ keys: '[MouseLeft>]', coords: { x: 0, y: 0 }, target: handle });
|
||||
await userEvent.pointer({ coords: { x: 250, y: 0 }, target: handle });
|
||||
await userEvent.pointer({ keys: '[/MouseLeft]', coords: { x: 250, y: 0 }, target: handle });
|
||||
|
||||
await waitFor(() => expect(onColumnResize).toHaveBeenCalled());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Text wrapping', () => {
|
||||
it('defaults to not wrapping text', () => {
|
||||
const { container } = render(
|
||||
@@ -1344,34 +1293,6 @@ describe('TableNG', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Context menu', () => {
|
||||
it('should show context menu on right-click', async () => {
|
||||
const { container } = render(
|
||||
<TableNG enableVirtualization={false} data={createBasicDataFrame()} width={400} height={400} />
|
||||
);
|
||||
|
||||
const cell = container.querySelector('[role="gridcell"]');
|
||||
expect(cell).toBeInTheDocument();
|
||||
|
||||
// Trigger context menu directly on the cell element
|
||||
if (cell) {
|
||||
fireEvent.contextMenu(cell);
|
||||
}
|
||||
|
||||
// Check that context menu is shown
|
||||
const menu = await screen.findByRole('menu');
|
||||
expect(menu).toBeInTheDocument();
|
||||
|
||||
// Check for the Inspect value menu item
|
||||
const menuItem = await screen.findByText('Inspect value');
|
||||
expect(menuItem).toBeInTheDocument();
|
||||
|
||||
// close the menu
|
||||
await userEvent.click(container);
|
||||
expect(menuItem).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cell inspection', () => {
|
||||
it('shows inspect icon when hovering over a cell with inspection enabled', async () => {
|
||||
const inspectDataFrame = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'react-data-grid/lib/styles.css';
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import { CSSProperties, Key, ReactNode, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { CSSProperties, Key, ReactNode, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Cell,
|
||||
CellRendererProps,
|
||||
@@ -23,12 +23,10 @@ import {
|
||||
getDisplayProcessor,
|
||||
ReducerID,
|
||||
} from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { FieldColorModeId, TableCellTooltipPlacement } from '@grafana/schema';
|
||||
|
||||
import { useStyles2, useTheme2 } from '../../../themes/ThemeContext';
|
||||
import { ContextMenu } from '../../ContextMenu/ContextMenu';
|
||||
import { MenuItem } from '../../Menu/MenuItem';
|
||||
import { Pagination } from '../../Pagination/Pagination';
|
||||
import { PanelContext, usePanelContext } from '../../PanelChrome';
|
||||
import { DataLinksActionsTooltip } from '../DataLinksActionsTooltip';
|
||||
@@ -60,7 +58,7 @@ import {
|
||||
getLinkStyles,
|
||||
getTooltipStyles,
|
||||
} from './styles';
|
||||
import { TableNGProps, TableRow, TableSummaryRow, TableColumn, ContextMenuProps, TableCellStyleOptions } from './types';
|
||||
import { TableNGProps, TableRow, TableSummaryRow, TableColumn, InspectCellProps, TableCellStyleOptions } from './types';
|
||||
import {
|
||||
applySort,
|
||||
canFieldBeColorized,
|
||||
@@ -133,27 +131,8 @@ export function TableNG(props: TableNGProps) {
|
||||
footerOptions.reducer[0] === ReducerID.count
|
||||
);
|
||||
|
||||
const [contextMenuProps, setContextMenuProps] = useState<ContextMenuProps | null>(null);
|
||||
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
|
||||
|
||||
const resizeHandler = useColumnResize(onColumnResize);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isContextMenuOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
function onClick(_event: MouseEvent) {
|
||||
setIsContextMenuOpen(false);
|
||||
}
|
||||
|
||||
window.addEventListener('click', onClick);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('click', onClick);
|
||||
};
|
||||
}, [isContextMenuOpen]);
|
||||
|
||||
const rows = useMemo(() => frameToRecords(data), [data]);
|
||||
const hasNestedFrames = useMemo(() => getIsNestedTable(data.fields), [data]);
|
||||
|
||||
@@ -171,7 +150,7 @@ export function TableNG(props: TableNGProps) {
|
||||
setSortColumns,
|
||||
} = useSortedRows(filteredRows, data.fields, { hasNestedFrames, initialSortBy });
|
||||
|
||||
const [isInspecting, setIsInspecting] = useState(false);
|
||||
const [inspectCell, setInspectCell] = useState<InspectCellProps | null>(null);
|
||||
const [expandedRows, setExpandedRows] = useState(() => new Set<number>());
|
||||
|
||||
// vt scrollbar accounting for column auto-sizing
|
||||
@@ -277,27 +256,6 @@ export function TableNG(props: TableNGProps) {
|
||||
sortable: true,
|
||||
// draggable: true,
|
||||
},
|
||||
onCellContextMenu: ({ row, column }, event) => {
|
||||
// in nested tables, it's possible for this event to trigger in a column header
|
||||
// when holding Ctrl for multi-row sort.
|
||||
if (column.key === 'expanded') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventGridDefault();
|
||||
// Do not show the default context menu
|
||||
event.preventDefault();
|
||||
|
||||
const cellValue = row[column.key];
|
||||
setContextMenuProps({
|
||||
// rowIdx: rows.indexOf(row),
|
||||
value: String(cellValue ?? ''),
|
||||
top: event.clientY,
|
||||
left: event.clientX,
|
||||
});
|
||||
|
||||
setIsContextMenuOpen(true);
|
||||
},
|
||||
onColumnResize: resizeHandler,
|
||||
onSortColumnsChange: (newSortColumns: SortColumn[]) => {
|
||||
setSortColumns(newSortColumns);
|
||||
@@ -475,8 +433,7 @@ export function TableNG(props: TableNGProps) {
|
||||
cellInspect={cellInspect}
|
||||
showFilters={showFilters}
|
||||
className={cellActionClassName}
|
||||
setIsInspecting={setIsInspecting}
|
||||
setContextMenuProps={setContextMenuProps}
|
||||
setInspectCell={setInspectCell}
|
||||
onCellFilterAdded={onCellFilterAdded}
|
||||
/>
|
||||
)}
|
||||
@@ -817,29 +774,11 @@ export function TableNG(props: TableNGProps) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{isContextMenuOpen && (
|
||||
<ContextMenu
|
||||
x={contextMenuProps?.left || 0}
|
||||
y={contextMenuProps?.top || 0}
|
||||
renderMenuItems={() => (
|
||||
<MenuItem
|
||||
label={t('grafana-ui.table.inspect-menu-label', 'Inspect value')}
|
||||
onClick={() => setIsInspecting(true)}
|
||||
className={styles.menuItem}
|
||||
/>
|
||||
)}
|
||||
focusOnOpen={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isInspecting && (
|
||||
{inspectCell && (
|
||||
<TableCellInspector
|
||||
mode={contextMenuProps?.mode ?? TableCellInspectorMode.text}
|
||||
value={contextMenuProps?.value}
|
||||
onDismiss={() => {
|
||||
setIsInspecting(false);
|
||||
setContextMenuProps(null);
|
||||
}}
|
||||
mode={inspectCell.mode ?? TableCellInspectorMode.text}
|
||||
value={inspectCell.value}
|
||||
onDismiss={() => setInspectCell(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -15,8 +15,7 @@ export function TableCellActions(props: TableCellActionsProps) {
|
||||
value,
|
||||
cellOptions,
|
||||
displayName,
|
||||
setIsInspecting,
|
||||
setContextMenuProps,
|
||||
setInspectCell,
|
||||
onCellFilterAdded,
|
||||
className,
|
||||
cellInspect,
|
||||
@@ -47,11 +46,10 @@ export function TableCellActions(props: TableCellActionsProps) {
|
||||
mode = TableCellInspectorMode.code;
|
||||
}
|
||||
|
||||
setContextMenuProps({
|
||||
setInspectCell({
|
||||
value: String(inspectValue ?? ''),
|
||||
mode,
|
||||
});
|
||||
setIsInspecting(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -77,7 +77,8 @@ export const getGridStyles = (
|
||||
gridNested: css({
|
||||
height: '100%',
|
||||
width: `calc(100% - ${COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING * 2 - 1}px)`,
|
||||
overflow: 'visible',
|
||||
overflowX: 'scroll',
|
||||
overflowY: 'hidden',
|
||||
marginLeft: COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING - 1,
|
||||
marginBlock: TABLE.CELL_PADDING,
|
||||
}),
|
||||
|
||||
@@ -169,12 +169,10 @@ export interface TableCellRendererProps {
|
||||
disableSanitizeHtml?: boolean;
|
||||
}
|
||||
|
||||
export type ContextMenuProps = {
|
||||
export type InspectCellProps = {
|
||||
rowIdx?: number;
|
||||
value: string;
|
||||
mode?: TableCellInspectorMode.code | TableCellInspectorMode.text;
|
||||
top?: number;
|
||||
left?: number;
|
||||
};
|
||||
|
||||
export interface TableCellActionsProps {
|
||||
@@ -184,8 +182,7 @@ export interface TableCellActionsProps {
|
||||
displayName: string;
|
||||
cellInspect: boolean;
|
||||
showFilters: boolean;
|
||||
setIsInspecting: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setContextMenuProps: React.Dispatch<React.SetStateAction<ContextMenuProps | null>>;
|
||||
setInspectCell: React.Dispatch<React.SetStateAction<InspectCellProps | null>>;
|
||||
className?: string;
|
||||
onCellFilterAdded?: TableFilterActionCallback;
|
||||
}
|
||||
|
||||
@@ -8744,7 +8744,6 @@
|
||||
"filter-popup-input-placeholder": "Filter values",
|
||||
"filter-popup-match-case": "Match case",
|
||||
"inspect-drawer-title": "Inspect value",
|
||||
"inspect-menu-label": "Inspect value",
|
||||
"nested-table": {
|
||||
"no-data": "No data"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user