Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/Span/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ export const Accordion = ({
onToggle: () => void;
children: React.ReactNode;
}) => (
<div className="border-t border-gray-300 dark:border-gray-600 pt-4 mt-2">
<div className="border-t border-gray-300 dark:border-gray-600 py-5 px-2">
{
<button
onClick={onToggle}
className="w-full flex items-center justify-between text-sm font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors duration-200 mb-2"
className="w-full flex items-center text-sm font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors duration-200 mb-2 pl-2 gap-2"
>
<span>{title}</span>
{showToggle && (
<svg
className={clsx('w-4 h-4 transition-transform duration-200', isExpanded ? 'rotate-180' : 'rotate-0')}
Expand All @@ -31,13 +30,14 @@ export const Accordion = ({
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
)}
<span className="uppercase font-light">{title}</span>
</button>
}

<div
className={clsx(
'overflow-hidden transition-all duration-300 ease-in-out',
isExpanded ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
isExpanded ? 'max-h-96 opacity-100 pt-5 pb-2' : 'max-h-0 opacity-0'
)}
>
{children}
Expand Down
143 changes: 89 additions & 54 deletions src/components/Span/SpanDetailsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import clsx from 'clsx';
import { IconButton } from '@grafana/ui';
import { IconButton, Input } from '@grafana/ui';

import type { SpanInfo } from '../../types';
import { mkUnixEpochFromNanoSeconds, formatUnixNanoToDateTime, formatDuration } from 'utils/utils.timeline';
Expand Down Expand Up @@ -113,7 +113,7 @@ function ValueWrapper({
}) {
const [tooltip, setTooltip] = useState('Copy value');
return (
<span className={`p-2 flex gap-1 items-center justify-between ${italic ? 'italic' : ''}`}>
<span className={`flex gap-1 items-center justify-between ${italic ? 'italic' : ''}`}>
<span className={`px-2 py-2 ${color}`}>{displayValue || value}</span>
<IconButton
name="copy"
Expand Down Expand Up @@ -234,7 +234,10 @@ export const SpanDetailPanel = ({
);

const rowClassName = (index: number) => {
return clsx('leading-7', index % 2 === 0 ? 'bg-gray-100 dark:bg-gray-800' : 'bg-gray-50 dark:bg-gray-700');
return clsx(
'text-[0.9rem] hover:bg-neutral-200 dark:hover:bg-neutral-800',
index % 2 === 0 ? 'bg-neutral-100 dark:bg-neutral-900' : 'bg-white dark:bg-black'
);
};

const { spanAttributes, events, resourceAttributes } = React.useMemo(() => {
Expand All @@ -258,94 +261,126 @@ export const SpanDetailPanel = ({
}, [result, debouncedSearch]);

return (
<div className="z-10">
<div className="overflow-hidden text-sm">
<h2 className="uppercase">Span Details</h2>
<input type="text" placeholder="Search" value={search} onChange={(e) => setSearch(e.target.value)} />
<table className="w-full">
<div className="z-10 overflow-hidden text-sm">
<div className="px-2 py-2">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do px-2 py-2, do p-2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do a search and replace on this, we have it elsewhere as well.

<div className="flex flex-col gap-4 items-start justify-between py-4 px-2 mx-4">
<div className="flex items-center justify-between gap-2 w-full">
<span className="uppercase text-lg font-light">Span Details</span>
<IconButton
size="xxl"
name="times"
variant="secondary"
aria-label="Close"
onClick={onClose}
className="w-4 h-4"
/>
</div>
<Input
className="w-full"
type="text"
placeholder="Search details"
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
suffix={
search ? (
<IconButton name="times" variant="secondary" aria-label="Clear" onClick={() => setSearch('')} />
) : (
<IconButton name="search" variant="secondary" aria-label="Search" />
)
}
/>
</div>
</div>
<div className="px-4">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add an extra div here? If it only has a single child, why not add the padding there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is so we can extend the line from end to end of a panel. With padding, that line is broken by the padding width

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure this align better with the accordeon sections:

Image

The scrollbar is over the copy button.

<table className="w-full text-[0.8rem]">
<tbody>
{basicSpanData.map((item, index) => (
<tr key={item.key} className={rowClassName(index)}>
<td className="font-semibold text-gray-700 dark:text-gray-300 border-r border-gray-300 dark:border-gray-600 w-1/3 mx-4">
<span className="px-2 py-2">{item.key}</span>{' '}
{/* TODO: padding & margins are overriden to 0 by the global CSS and it is not possible to set it on the td tag */}
<td className="font-regular text-gray-700 dark:text-gray-300 w-1/3 mx-4">
<span className="px-2 whitespace-nowrap">{item.key}</span>{' '}
</td>
<td>{item.value && <Value value={item.value} />}</td>
<td className="font-light">{item.value && <Value value={item.value} />}</td>
</tr>
))}
</tbody>
</table>
{spanAttributes.length > 0 && (
<Accordion
title="Additional Span Data"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.additionalData}
onToggle={() => toggleSection('additionalData')}
>
</div>
{spanAttributes.length > 0 && (
<Accordion
title="Additional Span Data"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.additionalData}
onToggle={() => toggleSection('additionalData')}
>
<div className="px-2">
<table className="w-full">
<tbody>
{spanAttributes.map(({ key, value }, index) => (
<tr key={key} className={rowClassName(basicSpanData.length + spanAttributes.length + index)}>
<td className="font-semibold text-gray-700 dark:text-gray-300 border-r border-gray-300 dark:border-gray-600 w-1/3">
<span className="px-2 py-2">{key}</span>
<tr key={key} className={rowClassName(index)}>
<td className="font-regular text-gray-700 dark:text-gray-300 w-1/3">
<span className="px-2 whitespace-nowrap">{key}</span>
</td>
<td>{value && <Value value={value} />}</td>
<td className="font-light">{value && <Value value={value} />}</td>
</tr>
))}
</tbody>
</table>
</Accordion>
)}

{/* Resource Section */}
{resourceAttributes.length > 0 && (
<Accordion
title="Resource"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.process}
onToggle={() => toggleSection('process')}
>
</div>
</Accordion>
)}

{/* Resource Section */}
{resourceAttributes.length > 0 && (
<Accordion
title="Resource"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.process}
onToggle={() => toggleSection('process')}
>
<div className="px-2">
<table className="w-full">
<tbody>
{resourceAttributes.map(({ key, value }, index) => (
<tr key={key} className={rowClassName(index)}>
<td className="font-semibold text-gray-700 dark:text-gray-300 border-r border-gray-300 dark:border-gray-600 w-1/3">
<span className="px-2 py-2">{key}</span>
<td className="font-regular text-gray-700 dark:text-gray-300 w-1/3">
<span className="px-2 whitespace-nowrap">{key}</span>
</td>
<td>{value && <Value value={value} />}</td>
<td className="font-light">{value && <Value value={value} />}</td>
</tr>
))}
</tbody>
</table>
</Accordion>
)}

{/* Events Section */}
{events.length > 0 && (
<Accordion
title="Events"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.events}
onToggle={() => toggleSection('events')}
>
</div>
</Accordion>
)}

{/* Events Section */}
{events.length > 0 && (
<Accordion
title="Events"
showToggle={debouncedSearch === ''}
isExpanded={debouncedSearch !== '' || expandedSections.events}
onToggle={() => toggleSection('events')}
>
<div className="px-2">
<table className="w-full">
<tbody>
{events.map((item, index) => (
<tr key={item.time} className={rowClassName(index)}>
<td className="font-semibold text-gray-700 dark:text-gray-300 border-r border-gray-300 dark:border-gray-600 w-1/3">
<span className="px-2 py-2">
<td className="font-regular text-gray-700 dark:text-gray-300 w-1/3">
<span className="px-2 whitespace-nowrap">
{/* print the time in seconds since the start of the span with 3 decimal places */}
{((item.time - span.startTimeUnixNano / 1000000) / 1000).toFixed(3)}s
</span>
</td>
<td>{item.value && <Value value={{ stringValue: item.value }} />}</td>
<td className="font-light">{item.value && <Value value={{ stringValue: item.value }} />}</td>
</tr>
))}
</tbody>
</table>
</Accordion>
)}
</div>
</div>
</Accordion>
)}
</div>
);
};
30 changes: 11 additions & 19 deletions src/components/Span/SpanOverlayDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { Icon } from '@grafana/ui';

interface SpanOverlayDrawerProps {
isOpen: boolean;
Expand All @@ -17,6 +16,7 @@ interface SpanOverlayDrawerProps {
* The maximum width is set to 75% of panel width
*/
const DRAWER_MIN_PERCENT = 10; // minimum comfortable width
const DRAWER_MIN_WIDTH_PX = 384; // minimum width in pixels
const DRAWER_MAX_PERCENT = 75; // maximum as percentage of panel width
const DRAWER_DEFAULT_PERCENT = 25; // default as percentage of panel width

Expand All @@ -28,7 +28,7 @@ export const SpanOverlayDrawer: React.FC<SpanOverlayDrawerProps> = ({
panelWidth,
}) => {
// Default width: up to 25% of panel width
const defaultWidthPx = panelWidth * (DRAWER_DEFAULT_PERCENT / 100);
const defaultWidthPx = Math.max(panelWidth * (DRAWER_DEFAULT_PERCENT / 100), DRAWER_MIN_WIDTH_PX);
const [widthPercent, setWidthPercent] = React.useState<number>((defaultWidthPx / panelWidth) * 100);
const isResizingRef = React.useRef<boolean>(false);
const drawerRef = React.useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -88,12 +88,12 @@ export const SpanOverlayDrawer: React.FC<SpanOverlayDrawerProps> = ({
return (
<>
{/* Backdrop */}
<div className="absolute inset-0 bg-gray-900 dark:bg-black opacity-20 z-[998]" onClick={onClose} />
<div className="absolute inset-0 bg-black/10 dark:bg-black/20 z-[998]" onClick={onClose} />

{/* Drawer */}
<div
ref={drawerRef}
className="absolute top-0 right-0 h-full bg-white dark:bg-gray-800 border-l border-gray-300 dark:border-gray-700 shadow-lg overflow-hidden z-[1000]"
className="absolute top-0 right-0 h-full bg-white dark:bg-black border-l border-t dark:border-t-0 border-gray-300 dark:border-gray-700 shadow-lg overflow-hidden z-[1000]"
style={{
width: drawerWidth,
transform: 'translateX(0)',
Expand All @@ -106,25 +106,17 @@ export const SpanOverlayDrawer: React.FC<SpanOverlayDrawerProps> = ({
title="Drag to resize"
className="absolute top-0 left-0 h-full w-[6px] cursor-col-resize hover:bg-gray-400/50 dark:hover:bg-gray-600/50 active:bg-gray-500/60 dark:active:bg-gray-500/60 z-[1001]"
>
<div className="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 flex flex-col items-center gap-[2px] pointer-events-none">
<span className="block w-1 h-1 rounded-full bg-gray-500 dark:bg-gray-400"></span>
<span className="block w-1 h-1 rounded-full bg-gray-500 dark:bg-gray-400"></span>
<span className="block w-1 h-1 rounded-full bg-gray-500 dark:bg-gray-400"></span>
<div className="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 flex flex-col items-center pointer-events-none">
<div className="flex flex-col items-center gap-[2px] bg-gray-400/10 dark:bg-gray-600/10 w-full px-[1px] py-2">
<span className="block w-[2px] h-[2px] rounded-full bg-gray-500 dark:bg-gray-400"></span>
<span className="block w-[2px] h-[2px] rounded-full bg-gray-500 dark:bg-gray-400"></span>
<span className="block w-[2px] h-[2px] rounded-full bg-gray-500 dark:bg-gray-400"></span>
</div>
</div>
</div>
{/* Header */}
<div className="absolute top-0 right-0">
<button
onClick={onClose}
className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
aria-label="Close drawer"
>
<Icon name="times" className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-white" />
</button>
</div>

{/* Content */}
<div className="h-full overflow-y-auto pl-2 pr-1 py-1">{children}</div>
<div className="h-full overflow-y-auto">{children}</div>
</div>
</>
);
Expand Down