mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Unify trace view panel (#70408)
* PanelChrome scales vertically * Trace View with old Trace View Header works with PanelChrome * Add top margin * Remove console.log * Use contain: 'strict' only when height is passed * Clean up: use title prop in PanelChrome and everything else should be put under * Remove test as the title will be passed to PanelChrome * Remove unused import * Remove titleWithLinks from PanelChrome * Make NewTracePageHeader compatible with PanelChrome * Fix test * Remove margin * Remove unused css * Revert config changes * Clean up: remove console log and commented out css * Clean up unused css * Fix tests * Revert to a brighter color * Show links next to duration * Cleanup * Remove container style and use default displayMode in PanelChrome * Clean up * Revert 'Give feedback' styling * PanelChrome's width auto scales * Fix GraphContainer's props * TraceViewContainer doesn't need a div wrapper * Remove loading * Fix build issue * FIx merge conflict * Revert old trace page header so the header items shows correctly in dashboards * Revert to match old trace view header * Revert NewTracePageHeader so it can show header items in dashboards * Revert * Revert tests * Make 'Give feedback' link aligned vertically * Remove unused css * Reduce spacing between title and container in dashboards; remove top padding for NewTracePageHeader --------- Co-authored-by: Giordano Ricci <me@giordanoricci.com>
This commit is contained in:
parent
5f8ace33fb
commit
fd01f6cf31
@ -300,7 +300,18 @@ export const ExamplesHoverHeader = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Basic: StoryFn<typeof PanelChrome> = (args: PanelChromeProps) => {
|
||||
export const Basic: StoryFn<typeof PanelChrome> = (overrides?: Partial<PanelChromeProps>) => {
|
||||
const args = {
|
||||
width: 400,
|
||||
height: 200,
|
||||
title: 'Very long title that should get ellipsis when there is no more space',
|
||||
description,
|
||||
menu,
|
||||
children: () => undefined,
|
||||
};
|
||||
|
||||
merge(args, overrides);
|
||||
|
||||
const contentStyle = getContentStyle();
|
||||
|
||||
return (
|
||||
@ -345,12 +356,4 @@ Basic.argTypes = {
|
||||
},
|
||||
};
|
||||
|
||||
Basic.args = {
|
||||
width: 400,
|
||||
height: 200,
|
||||
title: 'Very long title that should get ellipsis when there is no more space',
|
||||
description,
|
||||
menu,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { CSSProperties, ReactElement, ReactNode } from 'react';
|
||||
import { useMeasure } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -20,10 +21,8 @@ import { TitleItem } from './TitleItem';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface PanelChromeProps {
|
||||
width: number;
|
||||
height: number;
|
||||
children: (innerWidth: number, innerHeight: number) => ReactNode;
|
||||
export type PanelChromeProps = FixedDimensions | AutoSize;
|
||||
interface BaseProps {
|
||||
padding?: PanelPadding;
|
||||
hoverHeaderOffset?: number;
|
||||
title?: string;
|
||||
@ -59,6 +58,18 @@ export interface PanelChromeProps {
|
||||
onOpenMenu?: () => void;
|
||||
}
|
||||
|
||||
interface FixedDimensions extends BaseProps {
|
||||
width: number;
|
||||
height: number;
|
||||
children: (innerWidth: number, innerHeight: number) => ReactNode;
|
||||
}
|
||||
|
||||
interface AutoSize extends BaseProps {
|
||||
width?: never;
|
||||
height?: never;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -98,7 +109,7 @@ export function PanelChrome({
|
||||
const showOnHoverClass = 'show-on-hover';
|
||||
|
||||
const headerHeight = getHeaderHeight(theme, hasHeader);
|
||||
const { contentStyle, innerWidth, innerHeight } = getContentStyle(padding, theme, width, headerHeight, height);
|
||||
const { contentStyle, innerWidth, innerHeight } = getContentStyle(padding, theme, headerHeight, height, width);
|
||||
|
||||
const headerStyles: CSSProperties = {
|
||||
height: headerHeight,
|
||||
@ -111,6 +122,8 @@ export function PanelChrome({
|
||||
containerStyles.border = 'none';
|
||||
}
|
||||
|
||||
const [ref, { width: loadingBarWidth }] = useMeasure<HTMLDivElement>();
|
||||
|
||||
/** Old property name now maps to actions */
|
||||
if (leftItems) {
|
||||
actions = leftItems;
|
||||
@ -130,7 +143,6 @@ export function PanelChrome({
|
||||
<PanelDescription description={description} className={dragClassCancel} />
|
||||
{titleItems}
|
||||
</div>
|
||||
|
||||
{loadingState === LoadingState.Streaming && (
|
||||
<Tooltip content={onCancelQuery ? 'Stop streaming' : 'Streaming'}>
|
||||
<TitleItem className={dragClassCancel} data-testid="panel-streaming" onClick={onCancelQuery}>
|
||||
@ -160,9 +172,11 @@ export function PanelChrome({
|
||||
return (
|
||||
// tabIndex={0} is needed for keyboard accessibility in the plot area
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
<div className={styles.container} style={containerStyles} data-testid={testid} tabIndex={0}>
|
||||
<div className={styles.container} style={containerStyles} data-testid={testid} tabIndex={0} ref={ref}>
|
||||
<div className={styles.loadingBarContainer}>
|
||||
{loadingState === LoadingState.Loading ? <LoadingBar width={width} ariaLabel="Panel loading bar" /> : null}
|
||||
{loadingState === LoadingState.Loading ? (
|
||||
<LoadingBar width={loadingBarWidth} ariaLabel="Panel loading bar" />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{hoverHeader && (
|
||||
@ -207,8 +221,8 @@ export function PanelChrome({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.content} style={contentStyle}>
|
||||
{children(innerWidth, innerHeight)}
|
||||
<div className={cx(styles.content, height === undefined && styles.containNone)} style={contentStyle}>
|
||||
{typeof children === 'function' ? children(innerWidth, innerHeight) : children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -230,22 +244,29 @@ const getHeaderHeight = (theme: GrafanaTheme2, hasHeader: boolean) => {
|
||||
const getContentStyle = (
|
||||
padding: string,
|
||||
theme: GrafanaTheme2,
|
||||
width: number,
|
||||
headerHeight: number,
|
||||
height: number
|
||||
height?: number,
|
||||
width?: number
|
||||
) => {
|
||||
const chromePadding = (padding === 'md' ? theme.components.panel.padding : 0) * theme.spacing.gridSize;
|
||||
|
||||
const panelPadding = chromePadding * 2;
|
||||
const panelBorder = 1 * 2;
|
||||
|
||||
const innerWidth = width - panelPadding - panelBorder;
|
||||
const innerHeight = height - headerHeight - panelPadding - panelBorder;
|
||||
let innerWidth = 0;
|
||||
if (width) {
|
||||
innerWidth = width - panelPadding - panelBorder;
|
||||
}
|
||||
|
||||
const contentStyle: CSSProperties = {
|
||||
padding: chromePadding,
|
||||
};
|
||||
|
||||
let innerHeight = 0;
|
||||
if (height) {
|
||||
innerHeight = height - headerHeight - panelPadding - panelBorder;
|
||||
}
|
||||
|
||||
return { contentStyle, innerWidth, innerHeight };
|
||||
};
|
||||
|
||||
@ -293,6 +314,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
containNone: css({
|
||||
contain: 'none',
|
||||
}),
|
||||
content: css({
|
||||
label: 'panel-content',
|
||||
flexGrow: 1,
|
||||
|
@ -18,7 +18,9 @@ import { ExploreGraph } from './ExploreGraph';
|
||||
import { ExploreGraphLabel } from './ExploreGraphLabel';
|
||||
import { loadGraphStyle } from './utils';
|
||||
|
||||
interface Props extends Pick<PanelChromeProps, 'width' | 'height' | 'statusMessage'> {
|
||||
interface Props extends Pick<PanelChromeProps, 'statusMessage'> {
|
||||
width: number;
|
||||
height: number;
|
||||
data: DataFrame[];
|
||||
annotations?: DataFrame[];
|
||||
eventBus: EventBus;
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { RefObject, useMemo, useState } from 'react';
|
||||
|
||||
import { DataFrame, SplitOpen, PanelData, GrafanaTheme2 } from '@grafana/data';
|
||||
import { DataFrame, PanelData, SplitOpen } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { PanelChrome } from '@grafana/ui/src/components/PanelChrome/PanelChrome';
|
||||
import { StoreState, useSelector } from 'app/types';
|
||||
|
||||
import { TraceView } from './TraceView';
|
||||
@ -11,6 +10,7 @@ import TracePageSearchBar from './components/TracePageHeader/SearchBar/TracePage
|
||||
import { TopOfViewRefType } from './components/TraceTimelineViewer/VirtualizedTraceView';
|
||||
import { useSearch } from './useSearch';
|
||||
import { transformDataFrames } from './utils/transform';
|
||||
|
||||
interface Props {
|
||||
dataFrames: DataFrame[];
|
||||
splitOpenFn: SplitOpen;
|
||||
@ -20,26 +20,9 @@ interface Props {
|
||||
topOfViewRef: RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
container: css`
|
||||
label: container;
|
||||
margin-bottom: ${theme.spacing(1)};
|
||||
background-color: ${theme.colors.background.primary};
|
||||
border: 1px solid ${theme.colors.border.medium};
|
||||
position: relative;
|
||||
border-radius: ${theme.shape.radius.default};
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
padding: ${config.featureToggles.newTraceViewHeader ? 0 : theme.spacing(theme.components.panel.padding)};
|
||||
`,
|
||||
});
|
||||
|
||||
export function TraceViewContainer(props: Props) {
|
||||
// At this point we only show single trace
|
||||
const frame = props.dataFrames[0];
|
||||
const style = useStyles2(getStyles);
|
||||
const { dataFrames, splitOpenFn, exploreId, scrollElement, topOfViewRef, queryResponse } = props;
|
||||
const traceProp = useMemo(() => transformDataFrames(frame), [frame]);
|
||||
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
|
||||
@ -55,20 +38,25 @@ export function TraceViewContainer(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={style.container}>
|
||||
{!config.featureToggles.newTraceViewHeader && (
|
||||
<TracePageSearchBar
|
||||
navigable={true}
|
||||
searchValue={search}
|
||||
setSearch={setSearch}
|
||||
spanFindMatches={spanFindMatches}
|
||||
searchBarSuffix={searchBarSuffix}
|
||||
setSearchBarSuffix={setSearchBarSuffix}
|
||||
focusedSpanIdForSearch={focusedSpanIdForSearch}
|
||||
setFocusedSpanIdForSearch={setFocusedSpanIdForSearch}
|
||||
datasourceType={datasourceType}
|
||||
/>
|
||||
)}
|
||||
<PanelChrome
|
||||
padding="none"
|
||||
title="Trace"
|
||||
actions={
|
||||
!config.featureToggles.newTraceViewHeader && (
|
||||
<TracePageSearchBar
|
||||
navigable={true}
|
||||
searchValue={search}
|
||||
setSearch={setSearch}
|
||||
spanFindMatches={spanFindMatches}
|
||||
searchBarSuffix={searchBarSuffix}
|
||||
setSearchBarSuffix={setSearchBarSuffix}
|
||||
focusedSpanIdForSearch={focusedSpanIdForSearch}
|
||||
setFocusedSpanIdForSearch={setFocusedSpanIdForSearch}
|
||||
datasourceType={datasourceType}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<TraceView
|
||||
exploreId={exploreId}
|
||||
dataFrames={dataFrames}
|
||||
@ -83,6 +71,6 @@ export function TraceViewContainer(props: Props) {
|
||||
topOfViewRef={topOfViewRef}
|
||||
topOfViewRefType={TopOfViewRefType.Explore}
|
||||
/>
|
||||
</div>
|
||||
</PanelChrome>
|
||||
);
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
label: ActionButton;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 110px;
|
||||
justify-content: center;
|
||||
&:after {
|
||||
content: '';
|
||||
background: ${theme.colors.primary.main};
|
||||
@ -46,6 +44,7 @@ export default function ActionButton(props: ActionButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
className={styles.ActionButton}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
fill={'outline'}
|
||||
type="button"
|
||||
|
@ -15,8 +15,9 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
TracePageActions: css`
|
||||
label: TracePageActions;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
margin-top: 2px;
|
||||
`,
|
||||
feedback: css`
|
||||
margin: 6px;
|
||||
|
@ -156,7 +156,6 @@ const getNewStyles = (theme: GrafanaTheme2) => {
|
||||
header: css`
|
||||
label: TracePageHeader;
|
||||
background-color: ${theme.colors.background.primary};
|
||||
padding: 0.5em 0 0 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
|
@ -33,13 +33,8 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: ${theme.zIndex.navbarFixed};
|
||||
background: ${theme.colors.background.primary};
|
||||
margin-bottom: -48px;
|
||||
padding: 8px;
|
||||
margin-right: 2px;
|
||||
border-radius: ${theme.shape.radius.default};
|
||||
box-shadow: ${theme.shadows.z2};
|
||||
`,
|
||||
TracePageSearchBarBar: css`
|
||||
label: TracePageSearchBarBar;
|
||||
@ -55,17 +50,8 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
`,
|
||||
TracePageSearchBarBtn: css`
|
||||
label: TracePageSearchBarBtn;
|
||||
transition: 0.2s;
|
||||
margin-left: 8px;
|
||||
`,
|
||||
TracePageSearchBarBtnDisabled: css`
|
||||
label: TracePageSearchBarBtnDisabled;
|
||||
opacity: 0.5;
|
||||
`,
|
||||
TracePageSearchBarLocateBtn: css`
|
||||
label: TracePageSearchBarLocateBtn;
|
||||
padding: 1px 8px 4px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
@ -101,7 +87,6 @@ export default memo(function TracePageSearchBar(props: TracePageSearchBarProps)
|
||||
</span>
|
||||
) : null;
|
||||
|
||||
const btnClass = cx(styles.TracePageSearchBarBtn, { [styles.TracePageSearchBarBtnDisabled]: !searchValue });
|
||||
const SearchBarInputProps = {
|
||||
className: cx(styles.TracePageSearchBarBar, ubFlexAuto),
|
||||
name: 'search',
|
||||
@ -182,7 +167,7 @@ export default memo(function TracePageSearchBar(props: TracePageSearchBarProps)
|
||||
{navigable && (
|
||||
<>
|
||||
<Button
|
||||
className={btnClass}
|
||||
className={styles.TracePageSearchBarBtn}
|
||||
variant="secondary"
|
||||
disabled={!searchValue}
|
||||
type="button"
|
||||
@ -191,7 +176,7 @@ export default memo(function TracePageSearchBar(props: TracePageSearchBarProps)
|
||||
onClick={nextResult}
|
||||
/>
|
||||
<Button
|
||||
className={btnClass}
|
||||
className={styles.TracePageSearchBarBtn}
|
||||
variant="secondary"
|
||||
disabled={!searchValue}
|
||||
type="button"
|
||||
|
@ -91,11 +91,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
color: unset;
|
||||
}
|
||||
`,
|
||||
TracePageHeaderArchiveIcon: css`
|
||||
label: TracePageHeaderArchiveIcon;
|
||||
font-size: 1.78em;
|
||||
margin-right: 0.15em;
|
||||
`,
|
||||
TracePageHeaderTraceId: css`
|
||||
label: TracePageHeaderTraceId;
|
||||
white-space: nowrap;
|
||||
|
Loading…
Reference in New Issue
Block a user