From 2c2c391a7cc9e5f93b0cdadae8867d087f47d79f Mon Sep 17 00:00:00 2001 From: Arun Kumar Date: Sun, 12 Apr 2026 22:25:10 +0530 Subject: [PATCH 1/3] feat: pretty-print JSON and add copy button in cell popup --- src/components/QueryResultTable/Cell/Cell.tsx | 18 ++++++++++++++++-- .../QueryResultTable/QueryResultTable.scss | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/QueryResultTable/Cell/Cell.tsx b/src/components/QueryResultTable/Cell/Cell.tsx index 1c87e008ed..923b810c9c 100644 --- a/src/components/QueryResultTable/Cell/Cell.tsx +++ b/src/components/QueryResultTable/Cell/Cell.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {Popup} from '@gravity-ui/uikit'; +import {ClipboardButton, Popup} from '@gravity-ui/uikit'; import {b} from '../QueryResultTable'; @@ -9,11 +9,22 @@ interface CellProps { value: string; } +//helper function to try to format a string as JSON, if it fails, return the original string +function tryFormatJson(value: string): {formatted: string; isJson: boolean} { + try { + const parsed = JSON.parse(value); + return {formatted: JSON.stringify(parsed, null, 2), isJson: true}; + } catch { + return {formatted: value, isJson: false}; + } +} + export const Cell = React.memo(function Cell(props: CellProps) { const {className, value} = props; const [open, setOpen] = React.useState(false); const anchorRef = React.useRef(null); + const {formatted, isJson} = React.useMemo(() => tryFormatJson(value), [value]); const handleToggle = React.useCallback(() => { setOpen((prevOpen) => !prevOpen); @@ -32,7 +43,10 @@ export const Cell = React.memo(function Cell(props: CellProps) { anchorRef={anchorRef} onOutsideClick={handleClose} > -
{value}
+
+ {isJson ?
{formatted}
: formatted} + +
{value} diff --git a/src/components/QueryResultTable/QueryResultTable.scss b/src/components/QueryResultTable/QueryResultTable.scss index 99bd990e22..0f25952a9f 100644 --- a/src/components/QueryResultTable/QueryResultTable.scss +++ b/src/components/QueryResultTable/QueryResultTable.scss @@ -13,6 +13,23 @@ word-break: break-word; } + &__cell-popup-json { + overflow-x: auto; + + max-height: 300px; + margin: 0; + + font-family: monospace; + font-size: 12px; + white-space: pre; + } + + &__cell-popup-copy { + display: flex; + + margin-top: 8px; + } + &__message { padding: 15px 10px; } From f0f4be5c81b2fbc3c9837a6c927b52f61ac20c52 Mon Sep 17 00:00:00 2001 From: Arun Kumar Date: Sun, 12 Apr 2026 22:54:14 +0530 Subject: [PATCH 2/3] fix: guard primitive JSON values and copy formatted JSON in cell popup --- src/components/QueryResultTable/Cell/Cell.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/QueryResultTable/Cell/Cell.tsx b/src/components/QueryResultTable/Cell/Cell.tsx index 923b810c9c..2a1ec0f71f 100644 --- a/src/components/QueryResultTable/Cell/Cell.tsx +++ b/src/components/QueryResultTable/Cell/Cell.tsx @@ -13,6 +13,9 @@ interface CellProps { function tryFormatJson(value: string): {formatted: string; isJson: boolean} { try { const parsed = JSON.parse(value); + if (typeof parsed !== 'object' || parsed === null) { + return {formatted: value, isJson: false}; + } return {formatted: JSON.stringify(parsed, null, 2), isJson: true}; } catch { return {formatted: value, isJson: false}; From b4ac3cf326a0f5d7ae83636676e08a52c56e9aae Mon Sep 17 00:00:00 2001 From: Arun Kumar Date: Mon, 13 Apr 2026 19:32:55 +0530 Subject: [PATCH 3/3] fix: reset word-break on cell-popup-json to allow horizontal scroll --- src/components/QueryResultTable/QueryResultTable.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/QueryResultTable/QueryResultTable.scss b/src/components/QueryResultTable/QueryResultTable.scss index 0f25952a9f..1bc3f65bdc 100644 --- a/src/components/QueryResultTable/QueryResultTable.scss +++ b/src/components/QueryResultTable/QueryResultTable.scss @@ -22,6 +22,7 @@ font-family: monospace; font-size: 12px; white-space: pre; + word-break: normal; } &__cell-popup-copy {