From 09c78e0e9c6ea0d168451f93dbe364691bfd4f03 Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Tue, 9 Sep 2025 18:47:27 +0200 Subject: [PATCH] Log details: column sizing and JSON values (#110807) * LogLineDetails: switch from minmax to fit-content * LogLineDetailsFields: switch from syntaxHighlighting to prettifyJSON * Coverage --- .../components/panel/LogLineDetails.test.tsx | 30 +++++++++++++++++++ .../components/panel/LogLineDetailsFields.tsx | 19 +++++------- .../components/panel/LogLineDetailsLinks.tsx | 8 ++--- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/public/app/features/logs/components/panel/LogLineDetails.test.tsx b/public/app/features/logs/components/panel/LogLineDetails.test.tsx index 0333a5cede3..a56166a2c9d 100644 --- a/public/app/features/logs/components/panel/LogLineDetails.test.tsx +++ b/public/app/features/logs/components/panel/LogLineDetails.test.tsx @@ -519,6 +519,36 @@ describe('LogLineDetails', () => { expect(onClickHideField).toHaveBeenCalledWith('key1'); }); + test('Renders JSON field values', async () => { + setup( + undefined, + { labels: { label1: 'value of label1', label2: '{"key1":"value1", "key2": "value2"}' } }, + { prettifyJSON: false } + ); + + expect(screen.getByText('label1')).toBeInTheDocument(); + expect(screen.getByText('value of label1')).toBeInTheDocument(); + expect(screen.getByText('label2')).toBeInTheDocument(); + expect(screen.getByText('{"key1":"value1", "key2": "value2"}')).toBeInTheDocument(); + }); + + test('Renders prettify JSON field values', async () => { + setup( + undefined, + { labels: { label1: 'value of label1', label2: '{"key1":"value1", "key2": "value2"}' } }, + { prettifyJSON: true } + ); + + expect(screen.getByText('label1')).toBeInTheDocument(); + expect(screen.getByText('value of label1')).toBeInTheDocument(); + expect(screen.getByText('label2')).toBeInTheDocument(); + expect(screen.queryByText('{"key1":"value1", "key2": "value2"}')).not.toBeInTheDocument(); + expect(screen.getByText(/key1/)).toBeInTheDocument(); + expect(screen.getByText(/value1/)).toBeInTheDocument(); + expect(screen.getByText(/key2/)).toBeInTheDocument(); + expect(screen.getByText(/value2/)).toBeInTheDocument(); + }); + test('Exposes buttons to reorder displayed fields', async () => { const setDisplayedFields = jest.fn(); const onClickHideField = jest.fn(); diff --git a/public/app/features/logs/components/panel/LogLineDetailsFields.tsx b/public/app/features/logs/components/panel/LogLineDetailsFields.tsx index 3a7d01d10ff..35fd1ff1dac 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsFields.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsFields.tsx @@ -103,7 +103,7 @@ const getFieldsStyles = (theme: GrafanaTheme2) => ({ fieldsTable: css({ display: 'grid', gap: theme.spacing(1), - gridTemplateColumns: `${theme.spacing(11.5)} minmax(auto, 40%) 1fr`, + gridTemplateColumns: `${theme.spacing(11.5)} fit-content(30%) 1fr`, }), fieldsTableNoActions: css({ display: 'grid', @@ -148,7 +148,7 @@ export const LogLineDetailsField = ({ onClickHideField, onPinLine, pinLineButtonTooltipTitle, - syntaxHighlighting, + prettifyJSON, } = useLogListContext(); const styles = useStyles2(getFieldStyles); @@ -317,7 +317,7 @@ export const LogLineDetailsField = ({
{singleValue ? ( - + ) : ( )} @@ -383,6 +383,7 @@ const getFieldStyles = (theme: GrafanaTheme2) => ({ whiteSpace: 'nowrap', }), label: css({ + paddingRight: theme.spacing(1), overflowWrap: 'break-word', wordBreak: 'break-word', }), @@ -484,15 +485,9 @@ export const MultipleValue = ({ showCopy, values = [] }: { showCopy?: boolean; v ); }; -export const SingleValue = ({ - value: originalValue, - syntaxHighlighting, -}: { - value: string; - syntaxHighlighting?: boolean; -}) => { +export const SingleValue = ({ value: originalValue, prettifyJSON }: { value: string; prettifyJSON?: boolean }) => { const value = useMemo(() => { - if (!syntaxHighlighting) { + if (!prettifyJSON) { return originalValue; } try { @@ -502,7 +497,7 @@ export const SingleValue = ({ } } catch (error) {} return originalValue; - }, [originalValue, syntaxHighlighting]); + }, [originalValue, prettifyJSON]); return ( <> diff --git a/public/app/features/logs/components/panel/LogLineDetailsLinks.tsx b/public/app/features/logs/components/panel/LogLineDetailsLinks.tsx index f33b21fc76d..c791facd8d4 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsLinks.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsLinks.tsx @@ -42,7 +42,7 @@ const getFieldsStyles = (theme: GrafanaTheme2) => ({ linksTable: css({ display: 'grid', gap: theme.spacing(1), - gridTemplateColumns: `minmax(auto, 40%) 1fr`, + gridTemplateColumns: `fit-content(30%) 1fr`, marginBottom: theme.spacing(1), }), }); @@ -53,7 +53,7 @@ interface LogLineDetailsFieldProps { } export const LogLineDetailsField = ({ field, log }: LogLineDetailsFieldProps) => { - const { closeDetails, onPinLine, pinLineButtonTooltipTitle, syntaxHighlighting } = useLogListContext(); + const { closeDetails, onPinLine, pinLineButtonTooltipTitle, prettifyJSON } = useLogListContext(); const styles = useStyles2(getFieldStyles); @@ -65,14 +65,14 @@ export const LogLineDetailsField = ({ field, log }: LogLineDetailsFieldProps) =>
{singleValue ? ( - + ) : ( )}
), - [field.values, singleValue, styles.value, styles.valueContainer, syntaxHighlighting] + [field.values, singleValue, styles.value, styles.valueContainer, prettifyJSON] ); return (