mirror of https://github.com/grafana/grafana
TablePanel: Add cell inspect option (#45620)
* TablePanel: Add cell preview option * Review comments * Change modal title * Review * Review 2 * Docspull/45842/head
parent
c014f8b806
commit
eb537e2efd
@ -0,0 +1,70 @@ |
||||
import React, { useCallback, useState } from 'react'; |
||||
import { IconSize } from '../../types/icon'; |
||||
import { IconButton } from '../IconButton/IconButton'; |
||||
import { HorizontalGroup } from '../Layout/Layout'; |
||||
import { TooltipPlacement } from '../Tooltip'; |
||||
import { TableCellInspectModal } from './TableCellInspectModal'; |
||||
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR, TableCellProps, TableFieldOptions } from './types'; |
||||
import { getTextAlign } from './utils'; |
||||
|
||||
interface CellActionProps extends TableCellProps { |
||||
previewMode: 'text' | 'code'; |
||||
} |
||||
|
||||
export function CellActions({ field, cell, previewMode, onCellFilterAdded }: CellActionProps) { |
||||
const [isInspecting, setIsInspecting] = useState(false); |
||||
|
||||
const isRightAligned = getTextAlign(field) === 'flex-end'; |
||||
const showFilters = Boolean(field.config.filterable) && cell.value !== undefined; |
||||
const inspectEnabled = Boolean((field.config.custom as TableFieldOptions)?.inspect); |
||||
const commonButtonProps = { |
||||
size: 'sm' as IconSize, |
||||
tooltipPlacement: 'top' as TooltipPlacement, |
||||
}; |
||||
|
||||
const onFilterFor = useCallback( |
||||
(event: React.MouseEvent<HTMLButtonElement>) => |
||||
onCellFilterAdded({ key: field.name, operator: FILTER_FOR_OPERATOR, value: cell.value }), |
||||
[cell, field, onCellFilterAdded] |
||||
); |
||||
const onFilterOut = useCallback( |
||||
(event: React.MouseEvent<HTMLButtonElement>) => |
||||
onCellFilterAdded({ key: field.name, operator: FILTER_OUT_OPERATOR, value: cell.value }), |
||||
[cell, field, onCellFilterAdded] |
||||
); |
||||
|
||||
return ( |
||||
<> |
||||
<div className={`cellActions ${isRightAligned ? 'cellActionsLeft' : ''}`}> |
||||
<HorizontalGroup spacing="xs"> |
||||
{inspectEnabled && ( |
||||
<IconButton |
||||
name="eye" |
||||
tooltip="Inspect value" |
||||
onClick={() => { |
||||
setIsInspecting(true); |
||||
}} |
||||
{...commonButtonProps} |
||||
/> |
||||
)} |
||||
{showFilters && ( |
||||
<IconButton name={'search-plus'} onClick={onFilterFor} tooltip="Filter for value" {...commonButtonProps} /> |
||||
)} |
||||
{showFilters && ( |
||||
<IconButton name={'search-minus'} onClick={onFilterOut} tooltip="Filter out value" {...commonButtonProps} /> |
||||
)} |
||||
</HorizontalGroup> |
||||
</div> |
||||
|
||||
{isInspecting && ( |
||||
<TableCellInspectModal |
||||
mode={previewMode} |
||||
value={cell.value} |
||||
onDismiss={() => { |
||||
setIsInspecting(false); |
||||
}} |
||||
/> |
||||
)} |
||||
</> |
||||
); |
||||
} |
@ -1,31 +0,0 @@ |
||||
import React, { FC, useCallback } from 'react'; |
||||
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR, TableCellProps } from './types'; |
||||
import { Icon, Tooltip } from '..'; |
||||
|
||||
export const FilterActions: FC<TableCellProps> = ({ cell, field, tableStyles, onCellFilterAdded }) => { |
||||
const onFilterFor = useCallback( |
||||
(event: React.MouseEvent<HTMLDivElement>) => |
||||
onCellFilterAdded({ key: field.name, operator: FILTER_FOR_OPERATOR, value: cell.value }), |
||||
[cell, field, onCellFilterAdded] |
||||
); |
||||
const onFilterOut = useCallback( |
||||
(event: React.MouseEvent<HTMLDivElement>) => |
||||
onCellFilterAdded({ key: field.name, operator: FILTER_OUT_OPERATOR, value: cell.value }), |
||||
[cell, field, onCellFilterAdded] |
||||
); |
||||
|
||||
return ( |
||||
<div className={tableStyles.filterWrapper}> |
||||
<div className={tableStyles.filterItem}> |
||||
<Tooltip content="Filter for value" placement="top"> |
||||
<Icon name={'search-plus'} onClick={onFilterFor} /> |
||||
</Tooltip> |
||||
</div> |
||||
<div className={tableStyles.filterItem}> |
||||
<Tooltip content="Filter out value" placement="top"> |
||||
<Icon name={'search-minus'} onClick={onFilterOut} /> |
||||
</Tooltip> |
||||
</div> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,75 @@ |
||||
import { isString } from 'lodash'; |
||||
import React, { useEffect, useState } from 'react'; |
||||
import { ClipboardButton } from '../ClipboardButton/ClipboardButton'; |
||||
import { Icon } from '../Icon/Icon'; |
||||
import { Modal } from '../Modal/Modal'; |
||||
import { CodeEditor } from '../Monaco/CodeEditor'; |
||||
|
||||
interface TableCellInspectModalProps { |
||||
value: any; |
||||
onDismiss: () => void; |
||||
mode: 'code' | 'text'; |
||||
} |
||||
|
||||
export function TableCellInspectModal({ value, onDismiss, mode }: TableCellInspectModalProps) { |
||||
const [isInClipboard, setIsInClipboard] = useState(false); |
||||
const timeoutRef = React.useRef<number>(); |
||||
|
||||
useEffect(() => { |
||||
if (isInClipboard) { |
||||
timeoutRef.current = window.setTimeout(() => { |
||||
setIsInClipboard(false); |
||||
}, 2000); |
||||
} |
||||
|
||||
return () => { |
||||
if (timeoutRef.current) { |
||||
window.clearTimeout(timeoutRef.current); |
||||
} |
||||
}; |
||||
}, [isInClipboard]); |
||||
|
||||
let displayValue = value; |
||||
if (isString(value)) { |
||||
try { |
||||
value = JSON.parse(value); |
||||
} catch {} // ignore errors
|
||||
} else { |
||||
displayValue = JSON.stringify(value, null, ' '); |
||||
} |
||||
let text = displayValue; |
||||
|
||||
if (mode === 'code') { |
||||
text = JSON.stringify(value, null, ' '); |
||||
} |
||||
|
||||
return ( |
||||
<Modal onDismiss={onDismiss} isOpen={true} title="Inspect value"> |
||||
{mode === 'code' ? ( |
||||
<CodeEditor |
||||
width="100%" |
||||
height={500} |
||||
language="json" |
||||
showLineNumbers={true} |
||||
showMiniMap={(text && text.length) > 100} |
||||
value={text} |
||||
readOnly={true} |
||||
/> |
||||
) : ( |
||||
<pre>{text}</pre> |
||||
)} |
||||
<Modal.ButtonRow> |
||||
<ClipboardButton getText={() => text} onClipboardCopy={() => setIsInClipboard(true)}> |
||||
{!isInClipboard ? ( |
||||
'Copy to Clipboard' |
||||
) : ( |
||||
<> |
||||
<Icon name="check" /> |
||||
Copied to clipboard |
||||
</> |
||||
)} |
||||
</ClipboardButton> |
||||
</Modal.ButtonRow> |
||||
</Modal> |
||||
); |
||||
} |
Loading…
Reference in new issue