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:
Haris Rozajac
2023-07-18 08:15:07 -06:00
committed by GitHub
parent 5f8ace33fb
commit fd01f6cf31
9 changed files with 81 additions and 85 deletions

View File

@@ -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(); const contentStyle = getContentStyle();
return ( 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; export default meta;

View File

@@ -1,5 +1,6 @@
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import React, { CSSProperties, ReactElement, ReactNode } from 'react'; import React, { CSSProperties, ReactElement, ReactNode } from 'react';
import { useMeasure } from 'react-use';
import { GrafanaTheme2, LoadingState } from '@grafana/data'; import { GrafanaTheme2, LoadingState } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
@@ -20,10 +21,8 @@ import { TitleItem } from './TitleItem';
/** /**
* @internal * @internal
*/ */
export interface PanelChromeProps { export type PanelChromeProps = FixedDimensions | AutoSize;
width: number; interface BaseProps {
height: number;
children: (innerWidth: number, innerHeight: number) => ReactNode;
padding?: PanelPadding; padding?: PanelPadding;
hoverHeaderOffset?: number; hoverHeaderOffset?: number;
title?: string; title?: string;
@@ -59,6 +58,18 @@ export interface PanelChromeProps {
onOpenMenu?: () => void; 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 * @internal
*/ */
@@ -98,7 +109,7 @@ export function PanelChrome({
const showOnHoverClass = 'show-on-hover'; const showOnHoverClass = 'show-on-hover';
const headerHeight = getHeaderHeight(theme, hasHeader); 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 = { const headerStyles: CSSProperties = {
height: headerHeight, height: headerHeight,
@@ -111,6 +122,8 @@ export function PanelChrome({
containerStyles.border = 'none'; containerStyles.border = 'none';
} }
const [ref, { width: loadingBarWidth }] = useMeasure<HTMLDivElement>();
/** Old property name now maps to actions */ /** Old property name now maps to actions */
if (leftItems) { if (leftItems) {
actions = leftItems; actions = leftItems;
@@ -130,7 +143,6 @@ export function PanelChrome({
<PanelDescription description={description} className={dragClassCancel} /> <PanelDescription description={description} className={dragClassCancel} />
{titleItems} {titleItems}
</div> </div>
{loadingState === LoadingState.Streaming && ( {loadingState === LoadingState.Streaming && (
<Tooltip content={onCancelQuery ? 'Stop streaming' : 'Streaming'}> <Tooltip content={onCancelQuery ? 'Stop streaming' : 'Streaming'}>
<TitleItem className={dragClassCancel} data-testid="panel-streaming" onClick={onCancelQuery}> <TitleItem className={dragClassCancel} data-testid="panel-streaming" onClick={onCancelQuery}>
@@ -160,9 +172,11 @@ export function PanelChrome({
return ( return (
// tabIndex={0} is needed for keyboard accessibility in the plot area // tabIndex={0} is needed for keyboard accessibility in the plot area
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex // 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}> <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> </div>
{hoverHeader && ( {hoverHeader && (
@@ -207,8 +221,8 @@ export function PanelChrome({
</div> </div>
)} )}
<div className={styles.content} style={contentStyle}> <div className={cx(styles.content, height === undefined && styles.containNone)} style={contentStyle}>
{children(innerWidth, innerHeight)} {typeof children === 'function' ? children(innerWidth, innerHeight) : children}
</div> </div>
</div> </div>
); );
@@ -230,22 +244,29 @@ const getHeaderHeight = (theme: GrafanaTheme2, hasHeader: boolean) => {
const getContentStyle = ( const getContentStyle = (
padding: string, padding: string,
theme: GrafanaTheme2, theme: GrafanaTheme2,
width: number,
headerHeight: number, headerHeight: number,
height: number height?: number,
width?: number
) => { ) => {
const chromePadding = (padding === 'md' ? theme.components.panel.padding : 0) * theme.spacing.gridSize; const chromePadding = (padding === 'md' ? theme.components.panel.padding : 0) * theme.spacing.gridSize;
const panelPadding = chromePadding * 2; const panelPadding = chromePadding * 2;
const panelBorder = 1 * 2; const panelBorder = 1 * 2;
const innerWidth = width - panelPadding - panelBorder; let innerWidth = 0;
const innerHeight = height - headerHeight - panelPadding - panelBorder; if (width) {
innerWidth = width - panelPadding - panelBorder;
}
const contentStyle: CSSProperties = { const contentStyle: CSSProperties = {
padding: chromePadding, padding: chromePadding,
}; };
let innerHeight = 0;
if (height) {
innerHeight = height - headerHeight - panelPadding - panelBorder;
}
return { contentStyle, innerWidth, innerHeight }; return { contentStyle, innerWidth, innerHeight };
}; };
@@ -293,6 +314,9 @@ const getStyles = (theme: GrafanaTheme2) => {
width: '100%', width: '100%',
overflow: 'hidden', overflow: 'hidden',
}), }),
containNone: css({
contain: 'none',
}),
content: css({ content: css({
label: 'panel-content', label: 'panel-content',
flexGrow: 1, flexGrow: 1,

View File

@@ -18,7 +18,9 @@ import { ExploreGraph } from './ExploreGraph';
import { ExploreGraphLabel } from './ExploreGraphLabel'; import { ExploreGraphLabel } from './ExploreGraphLabel';
import { loadGraphStyle } from './utils'; import { loadGraphStyle } from './utils';
interface Props extends Pick<PanelChromeProps, 'width' | 'height' | 'statusMessage'> { interface Props extends Pick<PanelChromeProps, 'statusMessage'> {
width: number;
height: number;
data: DataFrame[]; data: DataFrame[];
annotations?: DataFrame[]; annotations?: DataFrame[];
eventBus: EventBus; eventBus: EventBus;

View File

@@ -1,9 +1,8 @@
import { css } from '@emotion/css';
import React, { RefObject, useMemo, useState } from 'react'; 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 { 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 { StoreState, useSelector } from 'app/types';
import { TraceView } from './TraceView'; import { TraceView } from './TraceView';
@@ -11,6 +10,7 @@ import TracePageSearchBar from './components/TracePageHeader/SearchBar/TracePage
import { TopOfViewRefType } from './components/TraceTimelineViewer/VirtualizedTraceView'; import { TopOfViewRefType } from './components/TraceTimelineViewer/VirtualizedTraceView';
import { useSearch } from './useSearch'; import { useSearch } from './useSearch';
import { transformDataFrames } from './utils/transform'; import { transformDataFrames } from './utils/transform';
interface Props { interface Props {
dataFrames: DataFrame[]; dataFrames: DataFrame[];
splitOpenFn: SplitOpen; splitOpenFn: SplitOpen;
@@ -20,26 +20,9 @@ interface Props {
topOfViewRef: RefObject<HTMLDivElement>; 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) { export function TraceViewContainer(props: Props) {
// At this point we only show single trace // At this point we only show single trace
const frame = props.dataFrames[0]; const frame = props.dataFrames[0];
const style = useStyles2(getStyles);
const { dataFrames, splitOpenFn, exploreId, scrollElement, topOfViewRef, queryResponse } = props; const { dataFrames, splitOpenFn, exploreId, scrollElement, topOfViewRef, queryResponse } = props;
const traceProp = useMemo(() => transformDataFrames(frame), [frame]); const traceProp = useMemo(() => transformDataFrames(frame), [frame]);
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans); const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
@@ -55,8 +38,11 @@ export function TraceViewContainer(props: Props) {
} }
return ( return (
<div className={style.container}> <PanelChrome
{!config.featureToggles.newTraceViewHeader && ( padding="none"
title="Trace"
actions={
!config.featureToggles.newTraceViewHeader && (
<TracePageSearchBar <TracePageSearchBar
navigable={true} navigable={true}
searchValue={search} searchValue={search}
@@ -68,7 +54,9 @@ export function TraceViewContainer(props: Props) {
setFocusedSpanIdForSearch={setFocusedSpanIdForSearch} setFocusedSpanIdForSearch={setFocusedSpanIdForSearch}
datasourceType={datasourceType} datasourceType={datasourceType}
/> />
)} )
}
>
<TraceView <TraceView
exploreId={exploreId} exploreId={exploreId}
dataFrames={dataFrames} dataFrames={dataFrames}
@@ -83,6 +71,6 @@ export function TraceViewContainer(props: Props) {
topOfViewRef={topOfViewRef} topOfViewRef={topOfViewRef}
topOfViewRefType={TopOfViewRefType.Explore} topOfViewRefType={TopOfViewRefType.Explore}
/> />
</div> </PanelChrome>
); );
} }

View File

@@ -10,8 +10,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
label: ActionButton; label: ActionButton;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
width: 110px;
justify-content: center;
&:after { &:after {
content: ''; content: '';
background: ${theme.colors.primary.main}; background: ${theme.colors.primary.main};
@@ -46,6 +44,7 @@ export default function ActionButton(props: ActionButtonProps) {
return ( return (
<Button <Button
className={styles.ActionButton} className={styles.ActionButton}
size="sm"
variant="secondary" variant="secondary"
fill={'outline'} fill={'outline'}
type="button" type="button"

View File

@@ -15,8 +15,9 @@ export const getStyles = (theme: GrafanaTheme2) => {
TracePageActions: css` TracePageActions: css`
label: TracePageActions; label: TracePageActions;
display: flex; display: flex;
align-items: center;
justify-content: center;
gap: 4px; gap: 4px;
margin-top: 2px;
`, `,
feedback: css` feedback: css`
margin: 6px; margin: 6px;

View File

@@ -156,7 +156,6 @@ const getNewStyles = (theme: GrafanaTheme2) => {
header: css` header: css`
label: TracePageHeader; label: TracePageHeader;
background-color: ${theme.colors.background.primary}; background-color: ${theme.colors.background.primary};
padding: 0.5em 0 0 0;
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 5; z-index: 5;

View File

@@ -33,13 +33,8 @@ export const getStyles = (theme: GrafanaTheme2) => {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
z-index: ${theme.zIndex.navbarFixed};
background: ${theme.colors.background.primary};
margin-bottom: -48px;
padding: 8px; padding: 8px;
margin-right: 2px; margin-right: 2px;
border-radius: ${theme.shape.radius.default};
box-shadow: ${theme.shadows.z2};
`, `,
TracePageSearchBarBar: css` TracePageSearchBarBar: css`
label: TracePageSearchBarBar; label: TracePageSearchBarBar;
@@ -55,17 +50,8 @@ export const getStyles = (theme: GrafanaTheme2) => {
`, `,
TracePageSearchBarBtn: css` TracePageSearchBarBtn: css`
label: TracePageSearchBarBtn; label: TracePageSearchBarBtn;
transition: 0.2s;
margin-left: 8px; 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> </span>
) : null; ) : null;
const btnClass = cx(styles.TracePageSearchBarBtn, { [styles.TracePageSearchBarBtnDisabled]: !searchValue });
const SearchBarInputProps = { const SearchBarInputProps = {
className: cx(styles.TracePageSearchBarBar, ubFlexAuto), className: cx(styles.TracePageSearchBarBar, ubFlexAuto),
name: 'search', name: 'search',
@@ -182,7 +167,7 @@ export default memo(function TracePageSearchBar(props: TracePageSearchBarProps)
{navigable && ( {navigable && (
<> <>
<Button <Button
className={btnClass} className={styles.TracePageSearchBarBtn}
variant="secondary" variant="secondary"
disabled={!searchValue} disabled={!searchValue}
type="button" type="button"
@@ -191,7 +176,7 @@ export default memo(function TracePageSearchBar(props: TracePageSearchBarProps)
onClick={nextResult} onClick={nextResult}
/> />
<Button <Button
className={btnClass} className={styles.TracePageSearchBarBtn}
variant="secondary" variant="secondary"
disabled={!searchValue} disabled={!searchValue}
type="button" type="button"

View File

@@ -91,11 +91,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
color: unset; color: unset;
} }
`, `,
TracePageHeaderArchiveIcon: css`
label: TracePageHeaderArchiveIcon;
font-size: 1.78em;
margin-right: 0.15em;
`,
TracePageHeaderTraceId: css` TracePageHeaderTraceId: css`
label: TracePageHeaderTraceId; label: TracePageHeaderTraceId;
white-space: nowrap; white-space: nowrap;