mirror of https://github.com/grafana/grafana.git
Tracing: Dark theme styling for TraceView (#23406)
* Add integration with Jeager Add Jaeger datasource and modify derived fields in loki to allow for opening a trace in Jager in separate split. Modifies build so that this branch docker images are pushed to docker hub Add a traceui dir with docker-compose and provision files for demoing.:wq * Enable docker logger plugin to send logs to loki * Add placeholder zipkin datasource * Fixed rebase issues, added enhanceDataFrame to non-legacy code path * Trace selector for jaeger query field * Fix logs default mode for Loki * Fix loading jaeger query field services on split * Updated grafana image in traceui/compose file * Fix prettier error * Hide behind feature flag, clean up unused code. * Fix tests * Fix tests * Cleanup code and review feedback * Remove traceui directory * Remove circle build changes * Fix feature toggles object * Fix merge issues * Add trace ui in Explore * WIP * WIP * WIP * Make jaeger datasource return trace data instead of link * Allow js in jest tests * Return data from Jaeger datasource * Take yarn.lock from master * Fix missing component * Update yarn lock * Fix some ts and lint errors * Fix merge * Fix type errors * Make tests pass again * Add tests * Fix es5 compatibility * Add header with minimap * Fix sizing issue due to column resizer handle * Fix issues with sizing, search functionality, duplicate react, tests * Refactor TraceView component, fix tests * Fix type errors * Add dark theme styling * Add tests for hooks * More color changes * Fix tests to deal with additional theme wrappers. Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
This commit is contained in:
parent
754dfdfa87
commit
cf1ebd5a3d
|
|
@ -36,6 +36,7 @@
|
|||
"moment": "^2.18.1",
|
||||
"react-icons": "2.2.7",
|
||||
"recompose": "^0.25.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tween-functions": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,20 +12,32 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
export type ThemeOptions = Partial<Theme>;
|
||||
|
||||
export enum ThemeType {
|
||||
Dark,
|
||||
Light,
|
||||
}
|
||||
|
||||
export type Theme = {
|
||||
type: ThemeType;
|
||||
borderStyle: string;
|
||||
};
|
||||
|
||||
export const defaultTheme: Theme = {
|
||||
type: ThemeType.Light,
|
||||
borderStyle: '1px solid #bbb',
|
||||
};
|
||||
|
||||
export function isLight(theme: Theme) {
|
||||
return theme.type === ThemeType.Light;
|
||||
}
|
||||
|
||||
const ThemeContext = React.createContext<ThemeOptions | undefined>(undefined);
|
||||
ThemeContext.displayName = 'ThemeContext';
|
||||
|
||||
|
|
@ -60,14 +72,16 @@ export const withTheme = <Props extends { theme: Theme }, Statics extends {} = {
|
|||
let WithTheme: React.ComponentType<Omit<Props, 'theme'>> = props => {
|
||||
return (
|
||||
<ThemeConsumer>
|
||||
{(theme: Theme) => (
|
||||
<Component
|
||||
{...({
|
||||
...props,
|
||||
theme,
|
||||
} as Props & { theme: Theme })}
|
||||
/>
|
||||
)}
|
||||
{(theme: Theme) => {
|
||||
return (
|
||||
<Component
|
||||
{...({
|
||||
...props,
|
||||
theme,
|
||||
} as Props & { theme: Theme })}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</ThemeConsumer>
|
||||
);
|
||||
};
|
||||
|
|
@ -81,6 +95,51 @@ export const withTheme = <Props extends { theme: Theme }, Statics extends {} = {
|
|||
return WithTheme as WrappedWithThemeComponent<Props>;
|
||||
};
|
||||
|
||||
export function useTheme(): Theme {
|
||||
const theme = useContext(ThemeContext);
|
||||
return {
|
||||
...defaultTheme,
|
||||
...theme,
|
||||
};
|
||||
}
|
||||
|
||||
export const createStyle = <Fn extends (this: any, ...newArgs: any[]) => ReturnType<Fn>>(fn: Fn) => {
|
||||
return memoizeOne(fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to get a dark variant color. Either by simply inverting the luminosity and darkening or lightening the color
|
||||
* a bit, or if base is provided, tries 2 variants of lighter and darker colors and checks which is more readable with
|
||||
* the base.
|
||||
* @param theme
|
||||
* @param hex
|
||||
* @param base
|
||||
*/
|
||||
export function autoColor(theme: Theme, hex: string, base?: string) {
|
||||
if (isLight(theme)) {
|
||||
return hex;
|
||||
} else {
|
||||
if (base) {
|
||||
const color = tinycolor(hex);
|
||||
return tinycolor
|
||||
.mostReadable(
|
||||
base,
|
||||
[
|
||||
color.clone().lighten(25),
|
||||
color.clone().lighten(10),
|
||||
color,
|
||||
color.clone().darken(10),
|
||||
color.clone().darken(25),
|
||||
],
|
||||
{
|
||||
includeFallbackColors: false,
|
||||
}
|
||||
)
|
||||
.toHex8String();
|
||||
}
|
||||
const color = tinycolor(hex).toHsl();
|
||||
color.l = 1 - color.l;
|
||||
const newColor = tinycolor(color);
|
||||
return newColor.isLight() ? newColor.darken(5).toHex8String() : newColor.lighten(5).toHex8String();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import CanvasSpanGraph from './CanvasSpanGraph';
|
||||
import { UnthemedCanvasSpanGraph } from './CanvasSpanGraph';
|
||||
import { defaultTheme } from '../../Theme';
|
||||
|
||||
describe('<CanvasSpanGraph>', () => {
|
||||
it('renders without exploding', () => {
|
||||
const items = [{ valueWidth: 1, valueOffset: 1, serviceName: 'service-name-0' }];
|
||||
const wrapper = shallow(<CanvasSpanGraph items={[]} valueWidth={4000} />);
|
||||
const wrapper = shallow(<UnthemedCanvasSpanGraph items={[]} valueWidth={4000} theme={defaultTheme} />);
|
||||
expect(wrapper).toBeDefined();
|
||||
wrapper.instance()._setCanvasRef({
|
||||
getContext: () => ({
|
||||
|
|
|
|||
|
|
@ -16,16 +16,16 @@ import * as React from 'react';
|
|||
import { css } from 'emotion';
|
||||
|
||||
import renderIntoCanvas from './render-into-canvas';
|
||||
import colorGenerator from '../../utils/color-generator';
|
||||
import { getRgbColorByKey } from '../../utils/color-generator';
|
||||
import { TNil } from '../../types';
|
||||
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, withTheme } from '../../Theme';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
CanvasSpanGraph: css`
|
||||
label: CanvasSpanGraph;
|
||||
background: #fafafa;
|
||||
background: ${autoColor(theme, '#fafafa')};
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
@ -36,11 +36,10 @@ const getStyles = createStyle(() => {
|
|||
type CanvasSpanGraphProps = {
|
||||
items: Array<{ valueWidth: number; valueOffset: number; serviceName: string }>;
|
||||
valueWidth: number;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
const getColor = (hex: string) => colorGenerator.getRgbColorByKey(hex);
|
||||
|
||||
export default class CanvasSpanGraph extends React.PureComponent<CanvasSpanGraphProps> {
|
||||
export class UnthemedCanvasSpanGraph extends React.PureComponent<CanvasSpanGraphProps> {
|
||||
_canvasElm: HTMLCanvasElement | TNil;
|
||||
|
||||
constructor(props: CanvasSpanGraphProps) {
|
||||
|
|
@ -48,6 +47,8 @@ export default class CanvasSpanGraph extends React.PureComponent<CanvasSpanGraph
|
|||
this._canvasElm = undefined;
|
||||
}
|
||||
|
||||
getColor = (key: string) => getRgbColorByKey(key, this.props.theme);
|
||||
|
||||
componentDidMount() {
|
||||
this._draw();
|
||||
}
|
||||
|
|
@ -63,11 +64,13 @@ export default class CanvasSpanGraph extends React.PureComponent<CanvasSpanGraph
|
|||
_draw() {
|
||||
if (this._canvasElm) {
|
||||
const { valueWidth: totalValueWidth, items } = this.props;
|
||||
renderIntoCanvas(this._canvasElm, items, totalValueWidth, getColor);
|
||||
renderIntoCanvas(this._canvasElm, items, totalValueWidth, this.getColor, autoColor(this.props.theme, '#fff'));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <canvas className={getStyles().CanvasSpanGraph} ref={this._setCanvasRef} />;
|
||||
return <canvas className={getStyles(this.props.theme).CanvasSpanGraph} ref={this._setCanvasRef} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedCanvasSpanGraph);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import Scrubber from './Scrubber';
|
|||
import ViewingLayer, { dragTypes, getStyles } from './ViewingLayer';
|
||||
import { EUpdateTypes } from '../../utils/DraggableManager';
|
||||
import { polyfill as polyfillAnimationFrame } from '../../utils/test/requestAnimationFrame';
|
||||
import { defaultTheme } from '../../Theme';
|
||||
|
||||
function getViewRange(viewStart, viewEnd) {
|
||||
return {
|
||||
|
|
@ -43,13 +44,19 @@ describe('<SpanGraph>', () => {
|
|||
updateViewRangeTime: jest.fn(),
|
||||
viewRange: getViewRange(0, 1),
|
||||
};
|
||||
wrapper = shallow(<ViewingLayer {...props} />);
|
||||
wrapper = shallow(<ViewingLayer {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
});
|
||||
|
||||
describe('_getDraggingBounds()', () => {
|
||||
beforeEach(() => {
|
||||
props = { ...props, viewRange: getViewRange(0.1, 0.9) };
|
||||
wrapper = shallow(<ViewingLayer {...props} />);
|
||||
wrapper = shallow(<ViewingLayer {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
wrapper.instance()._setRoot({
|
||||
getBoundingClientRect() {
|
||||
return { left: 10, width: 100 };
|
||||
|
|
@ -122,7 +129,10 @@ describe('<SpanGraph>', () => {
|
|||
const anchor = 0.1;
|
||||
const time = { ...props.viewRange.time, reframe: { anchor } };
|
||||
props = { ...props, viewRange: { time } };
|
||||
wrapper = shallow(<ViewingLayer {...props} />);
|
||||
wrapper = shallow(<ViewingLayer {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
wrapper.instance()._handleReframeDragUpdate({ value });
|
||||
const calls = props.updateNextViewRangeTime.mock.calls;
|
||||
expect(calls).toEqual([[{ reframe: { anchor, shift: value } }]]);
|
||||
|
|
@ -149,7 +159,10 @@ describe('<SpanGraph>', () => {
|
|||
const anchor = 0.6;
|
||||
const time = { ...props.viewRange.time, reframe: { anchor } };
|
||||
props = { ...props, viewRange: { time } };
|
||||
wrapper = shallow(<ViewingLayer {...props} />);
|
||||
wrapper = shallow(<ViewingLayer {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
wrapper.instance()._handleReframeDragEnd({ manager, value });
|
||||
|
||||
expect(manager.resetBounds.mock.calls).toEqual([[]]);
|
||||
|
|
@ -162,7 +175,10 @@ describe('<SpanGraph>', () => {
|
|||
const anchor = 0.4;
|
||||
const time = { ...props.viewRange.time, reframe: { anchor } };
|
||||
props = { ...props, viewRange: { time } };
|
||||
wrapper = shallow(<ViewingLayer {...props} />);
|
||||
wrapper = shallow(<ViewingLayer {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
wrapper.instance()._handleReframeDragEnd({ manager, value });
|
||||
|
||||
expect(manager.resetBounds.mock.calls).toEqual([[]]);
|
||||
|
|
@ -258,28 +274,28 @@ describe('<SpanGraph>', () => {
|
|||
|
||||
describe('.ViewingLayer--resetZoom', () => {
|
||||
it('should not render .ViewingLayer--resetZoom if props.viewRange.time.current = [0,1]', () => {
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(0);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(0);
|
||||
wrapper.setProps({ viewRange: { time: { current: [0, 1] } } });
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(0);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(0);
|
||||
});
|
||||
|
||||
it('should render ViewingLayer--resetZoom if props.viewRange.time.current[0] !== 0', () => {
|
||||
// If the test fails on the following expect statement, this may be a false negative
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(0);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(0);
|
||||
wrapper.setProps({ viewRange: { time: { current: [0.1, 1] } } });
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(1);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render ViewingLayer--resetZoom if props.viewRange.time.current[1] !== 1', () => {
|
||||
// If the test fails on the following expect statement, this may be a false negative
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(0);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(0);
|
||||
wrapper.setProps({ viewRange: { time: { current: [0, 0.9] } } });
|
||||
expect(wrapper.find(`.${getStyles().ViewingLayerResetZoom}`).length).toBe(1);
|
||||
expect(wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should call props.updateViewRangeTime when clicked', () => {
|
||||
wrapper.setProps({ viewRange: { time: { current: [0.1, 0.9] } } });
|
||||
const resetZoomButton = wrapper.find(`.${getStyles().ViewingLayerResetZoom}`);
|
||||
const resetZoomButton = wrapper.find(`.${getStyles(defaultTheme).ViewingLayerResetZoom}`);
|
||||
// If the test fails on the following expect statement, this may be a false negative caused
|
||||
// by a regression to rendering.
|
||||
expect(resetZoomButton.length).toBe(1);
|
||||
|
|
@ -296,9 +312,12 @@ describe('<SpanGraph>', () => {
|
|||
|
||||
it('renders a filtering box if leftBound exists', () => {
|
||||
const _props = { ...props, viewRange: getViewRange(0.2, 1) };
|
||||
wrapper = shallow(<ViewingLayer {..._props} />);
|
||||
wrapper = shallow(<ViewingLayer {..._props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
|
||||
const leftBox = wrapper.find(`.${getStyles().ViewingLayerInactive}`);
|
||||
const leftBox = wrapper.find(`.${getStyles(defaultTheme).ViewingLayerInactive}`);
|
||||
expect(leftBox.length).toBe(1);
|
||||
const width = Number(leftBox.prop('width').slice(0, -1));
|
||||
const x = leftBox.prop('x');
|
||||
|
|
@ -308,9 +327,12 @@ describe('<SpanGraph>', () => {
|
|||
|
||||
it('renders a filtering box if rightBound exists', () => {
|
||||
const _props = { ...props, viewRange: getViewRange(0, 0.8) };
|
||||
wrapper = shallow(<ViewingLayer {..._props} />);
|
||||
wrapper = shallow(<ViewingLayer {..._props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
|
||||
const rightBox = wrapper.find(`.${getStyles().ViewingLayerInactive}`);
|
||||
const rightBox = wrapper.find(`.${getStyles(defaultTheme).ViewingLayerInactive}`);
|
||||
expect(rightBox.length).toBe(1);
|
||||
const width = Number(rightBox.prop('width').slice(0, -1));
|
||||
const x = Number(rightBox.prop('x').slice(0, -1));
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ import { css } from 'emotion';
|
|||
import GraphTicks from './GraphTicks';
|
||||
import Scrubber from './Scrubber';
|
||||
import { TUpdateViewRangeTimeFunction, UIButton, ViewRange, ViewRangeTimeUpdate } from '../..';
|
||||
import { withTheme, Theme, autoColor } from '../../Theme';
|
||||
import { TNil } from '../..';
|
||||
import DraggableManager, { DraggableBounds, DraggingUpdate, EUpdateTypes } from '../../utils/DraggableManager';
|
||||
|
||||
import { createStyle } from '../../Theme';
|
||||
|
||||
export const getStyles = createStyle(() => {
|
||||
export const getStyles = createStyle((theme: Theme) => {
|
||||
// Need this cause emotion will merge emotion generated classes into single className if used with cx from emotion
|
||||
// package and the selector won't work
|
||||
const ViewingLayerResetZoomHoverClassName = 'JaegerUiComponents__ViewingLayerResetZoomHoverClassName';
|
||||
|
|
@ -48,7 +49,7 @@ export const getStyles = createStyle(() => {
|
|||
`,
|
||||
ViewingLayerGraph: css`
|
||||
label: ViewingLayerGraph;
|
||||
border: 1px solid #999;
|
||||
border: 1px solid ${autoColor(theme, '#999')};
|
||||
/* need !important here to overcome something from semantic UI */
|
||||
overflow: visible !important;
|
||||
position: relative;
|
||||
|
|
@ -57,11 +58,11 @@ export const getStyles = createStyle(() => {
|
|||
`,
|
||||
ViewingLayerInactive: css`
|
||||
label: ViewingLayerInactive;
|
||||
fill: rgba(214, 214, 214, 0.5);
|
||||
fill: ${autoColor(theme, 'rgba(214, 214, 214, 0.5)')};
|
||||
`,
|
||||
ViewingLayerCursorGuide: css`
|
||||
label: ViewingLayerCursorGuide;
|
||||
stroke: #f44;
|
||||
stroke: ${autoColor(theme, '#f44')};
|
||||
stroke-width: 1;
|
||||
`,
|
||||
ViewingLayerDraggedShift: css`
|
||||
|
|
@ -70,7 +71,7 @@ export const getStyles = createStyle(() => {
|
|||
`,
|
||||
ViewingLayerDrag: css`
|
||||
label: ViewingLayerDrag;
|
||||
fill: #44f;
|
||||
fill: ${autoColor(theme, '#44f')};
|
||||
`,
|
||||
ViewingLayerFullOverlay: css`
|
||||
label: ViewingLayerFullOverlay;
|
||||
|
|
@ -93,6 +94,7 @@ type ViewingLayerProps = {
|
|||
updateViewRangeTime: TUpdateViewRangeTimeFunction;
|
||||
updateNextViewRangeTime: (update: ViewRangeTimeUpdate) => void;
|
||||
viewRange: ViewRange;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
type ViewingLayerState = {
|
||||
|
|
@ -140,7 +142,7 @@ function getNextViewLayout(start: number, position: number) {
|
|||
* `ViewingLayer` is rendered on top of the Canvas rendering of the minimap and
|
||||
* handles showing the current view range and handles mouse UX for modifying it.
|
||||
*/
|
||||
export default class ViewingLayer extends React.PureComponent<ViewingLayerProps, ViewingLayerState> {
|
||||
export class UnthemedViewingLayer extends React.PureComponent<ViewingLayerProps, ViewingLayerState> {
|
||||
state: ViewingLayerState;
|
||||
|
||||
_root: Element | TNil;
|
||||
|
|
@ -298,7 +300,7 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
|
|||
* @returns React.Node[]
|
||||
*/
|
||||
_getMarkers(from: number, to: number) {
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(this.props.theme);
|
||||
const layout = getNextViewLayout(from, to);
|
||||
return [
|
||||
<rect
|
||||
|
|
@ -321,7 +323,7 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
|
|||
}
|
||||
|
||||
render() {
|
||||
const { height, viewRange, numTicks } = this.props;
|
||||
const { height, viewRange, numTicks, theme } = this.props;
|
||||
const { preventCursorLine } = this.state;
|
||||
const { current, cursor, shiftStart, shiftEnd, reframe } = viewRange.time;
|
||||
const haveNextTimeRange = shiftStart != null || shiftEnd != null || reframe != null;
|
||||
|
|
@ -338,7 +340,7 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
|
|||
if (!haveNextTimeRange && cursor != null && !preventCursorLine) {
|
||||
cursorPosition = `${cursor * 100}%`;
|
||||
}
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div aria-hidden className={styles.ViewingLayer} style={{ height }}>
|
||||
|
|
@ -406,3 +408,5 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedViewingLayer);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
import { TNil } from '../..';
|
||||
|
||||
// exported for tests
|
||||
export const BG_COLOR = '#fff';
|
||||
export const ITEM_ALPHA = 0.8;
|
||||
export const MIN_ITEM_HEIGHT = 2;
|
||||
export const MAX_TOTAL_HEIGHT = 200;
|
||||
|
|
@ -27,7 +26,8 @@ export default function renderIntoCanvas(
|
|||
canvas: HTMLCanvasElement,
|
||||
items: Array<{ valueWidth: number; valueOffset: number; serviceName: string }>,
|
||||
totalValueWidth: number,
|
||||
getFillColor: (serviceName: string) => [number, number, number]
|
||||
getFillColor: (serviceName: string) => [number, number, number],
|
||||
bgColor: string
|
||||
) {
|
||||
const fillCache: Map<string, string | TNil> = new Map();
|
||||
const cHeight = items.length < MIN_TOTAL_HEIGHT ? MIN_TOTAL_HEIGHT : Math.min(items.length, MAX_TOTAL_HEIGHT);
|
||||
|
|
@ -40,7 +40,7 @@ export default function renderIntoCanvas(
|
|||
const itemYChange = cHeight / items.length;
|
||||
|
||||
const ctx = canvas.getContext('2d', { alpha: false }) as CanvasRenderingContext2D;
|
||||
ctx.fillStyle = BG_COLOR;
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.fillRect(0, 0, cWidth, cHeight);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { valueWidth, valueOffset, serviceName } = items[i];
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import cx from 'classnames';
|
|||
|
||||
import SpanGraph from './SpanGraph';
|
||||
import TracePageSearchBar from './TracePageSearchBar';
|
||||
import { TUpdateViewRangeTimeFunction, ViewRange, ViewRangeTimeUpdate } from '..';
|
||||
import { autoColor, Theme, TUpdateViewRangeTimeFunction, useTheme, ViewRange, ViewRangeTimeUpdate } from '..';
|
||||
import LabeledList from '../common/LabeledList';
|
||||
import TraceName from '../common/TraceName';
|
||||
import { getTraceName } from '../model/trace-viewer';
|
||||
|
|
@ -35,7 +35,7 @@ import ExternalLinks from '../common/ExternalLinks';
|
|||
import { createStyle } from '../Theme';
|
||||
import { uTxMuted } from '../uberUtilityStyles';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
const TracePageHeaderOverviewItemValueDetail = css`
|
||||
label: TracePageHeaderOverviewItemValueDetail;
|
||||
color: #aaa;
|
||||
|
|
@ -44,21 +44,19 @@ const getStyles = createStyle(() => {
|
|||
TracePageHeader: css`
|
||||
label: TracePageHeader;
|
||||
& > :first-child {
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e8e8e8')};
|
||||
}
|
||||
& > :nth-child(2) {
|
||||
background-color: #eee;
|
||||
border-bottom: 1px solid #e4e4e4;
|
||||
background-color: ${autoColor(theme, '#eee')};
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e4e4e4')};
|
||||
}
|
||||
& > :last-child {
|
||||
background-color: #f8f8f8;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-bottom: 1px solid ${autoColor(theme, '#ccc')};
|
||||
}
|
||||
`,
|
||||
TracePageHeaderTitleRow: css`
|
||||
label: TracePageHeaderTitleRow;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
`,
|
||||
TracePageHeaderBack: css`
|
||||
|
|
@ -81,7 +79,6 @@ const getStyles = createStyle(() => {
|
|||
TracePageHeaderTitleLink: css`
|
||||
label: TracePageHeaderTitleLink;
|
||||
align-items: center;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
|
|
@ -118,7 +115,7 @@ const getStyles = createStyle(() => {
|
|||
TracePageHeaderOverviewItems: css`
|
||||
label: TracePageHeaderOverviewItems;
|
||||
border-bottom: 1px solid #e4e4e4;
|
||||
padding: 0.25rem 0.5rem;
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
`,
|
||||
TracePageHeaderOverviewItemValueDetail,
|
||||
TracePageHeaderOverviewItemValue: css`
|
||||
|
|
@ -163,7 +160,7 @@ export const HEADER_ITEMS = [
|
|||
key: 'timestamp',
|
||||
label: 'Trace Start',
|
||||
renderer: (trace: Trace) => {
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
const dateStr = formatDatetime(trace.startTime);
|
||||
const match = dateStr.match(/^(.+)(:\d\d\.\d+)$/);
|
||||
return match ? (
|
||||
|
|
@ -235,7 +232,7 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) {
|
|||
return { ...rest, value: renderer(trace) };
|
||||
});
|
||||
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
|
||||
const title = (
|
||||
<h1 className={cx(styles.TracePageHeaderTitle, canCollapse && styles.TracePageHeaderTitleCollapsible)}>
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ describe('<SpanBarRow>', () => {
|
|||
beforeEach(() => {
|
||||
props.onDetailToggled.mockReset();
|
||||
props.onChildrenToggled.mockReset();
|
||||
SpanTreeOffset.mockReturnValue(() => {});
|
||||
wrapper = mount(<SpanBarRow {...props} />);
|
||||
});
|
||||
|
||||
|
|
@ -105,7 +106,7 @@ describe('<SpanBarRow>', () => {
|
|||
props.span
|
||||
);
|
||||
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />);
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />).dive().dive().dive();
|
||||
const refButton = spanRow.find(ReferencesButton);
|
||||
expect(refButton.length).toEqual(1);
|
||||
expect(refButton.at(0).props().tooltipText).toEqual('Contains multiple references');
|
||||
|
|
@ -127,7 +128,7 @@ describe('<SpanBarRow>', () => {
|
|||
},
|
||||
props.span
|
||||
);
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />);
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />).dive().dive().dive();
|
||||
const refButton = spanRow.find(ReferencesButton);
|
||||
expect(refButton.length).toEqual(1);
|
||||
expect(refButton.at(0).props().tooltipText).toEqual('This span is referenced by another span');
|
||||
|
|
@ -157,7 +158,7 @@ describe('<SpanBarRow>', () => {
|
|||
},
|
||||
props.span
|
||||
);
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />);
|
||||
const spanRow = shallow(<SpanBarRow {...props} span={span} />).dive().dive().dive();
|
||||
const refButton = spanRow.find(ReferencesButton);
|
||||
expect(refButton.length).toEqual(1);
|
||||
expect(refButton.at(0).props().tooltipText).toEqual('This span is referenced by multiple other spans');
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import Ticks from './Ticks';
|
|||
|
||||
import { TNil } from '../types';
|
||||
import { Span } from '../types/trace';
|
||||
import { createStyle } from '../Theme';
|
||||
import { autoColor, createStyle, Theme, withTheme } from '../Theme';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
const spanBar = css`
|
||||
label: spanBar;
|
||||
`;
|
||||
|
|
@ -40,12 +40,12 @@ const getStyles = createStyle(() => {
|
|||
`;
|
||||
const nameWrapper = css`
|
||||
label: nameWrapper;
|
||||
background: #f8f8f8;
|
||||
background: ${autoColor(theme, '#f8f8f8')};
|
||||
line-height: 27px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
&:hover {
|
||||
border-right: 1px solid #bbb;
|
||||
border-right: 1px solid ${autoColor(theme, '#bbb')};
|
||||
float: left;
|
||||
min-width: calc(100% + 1px);
|
||||
overflow: visible;
|
||||
|
|
@ -54,12 +54,12 @@ const getStyles = createStyle(() => {
|
|||
|
||||
const nameWrapperMatchingFilter = css`
|
||||
label: nameWrapperMatchingFilter;
|
||||
background-color: #fffce4;
|
||||
background-color: ${autoColor(theme, '#fffce4')};
|
||||
`;
|
||||
|
||||
const endpointName = css`
|
||||
label: endpointName;
|
||||
color: #808080;
|
||||
color: ${autoColor(theme, '#808080')};
|
||||
`;
|
||||
|
||||
const view = css`
|
||||
|
|
@ -69,14 +69,14 @@ const getStyles = createStyle(() => {
|
|||
|
||||
const viewExpanded = css`
|
||||
label: viewExpanded;
|
||||
background: #f8f8f8;
|
||||
outline: 1px solid #ddd;
|
||||
background: ${autoColor(theme, '#f8f8f8')};
|
||||
outline: 1px solid ${autoColor(theme, '#ddd')};
|
||||
`;
|
||||
|
||||
const viewExpandedAndMatchingFilter = css`
|
||||
label: viewExpandedAndMatchingFilter;
|
||||
background: #fff3d7;
|
||||
outline: 1px solid #ddd;
|
||||
background: ${autoColor(theme, '#fff3d7')};
|
||||
outline: 1px solid ${autoColor(theme, '#ddd')};
|
||||
`;
|
||||
|
||||
const nameColumn = css`
|
||||
|
|
@ -105,15 +105,20 @@ const getStyles = createStyle(() => {
|
|||
opacity: 1;
|
||||
}
|
||||
&:hover .${spanBarLabel} {
|
||||
color: #000;
|
||||
color: ${autoColor(theme, '#000')};
|
||||
}
|
||||
&:hover .${nameWrapper} {
|
||||
background: #f8f8f8;
|
||||
background: linear-gradient(90deg, #fafafa, #f8f8f8 75%, #eee);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
${autoColor(theme, '#fafafa')},
|
||||
${autoColor(theme, '#f8f8f8')} 75%,
|
||||
${autoColor(theme, '#eee')}
|
||||
);
|
||||
}
|
||||
&:hover .${view} {
|
||||
background-color: #f5f5f5;
|
||||
outline: 1px solid #ddd;
|
||||
background-color: ${autoColor(theme, '#f5f5f5')};
|
||||
outline: 1px solid ${autoColor(theme, '#ddd')};
|
||||
}
|
||||
`,
|
||||
rowClippingLeft: css`
|
||||
|
|
@ -123,7 +128,11 @@ const getStyles = createStyle(() => {
|
|||
height: 100%;
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
background-image: linear-gradient(to right, rgba(25, 25, 25, 0.25), rgba(32, 32, 32, 0));
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
${autoColor(theme, 'rgba(25, 25, 25, 0.25)')},
|
||||
${autoColor(theme, 'rgba(32, 32, 32, 0)')}
|
||||
);
|
||||
left: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
|
@ -135,7 +144,11 @@ const getStyles = createStyle(() => {
|
|||
height: 100%;
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
background-image: linear-gradient(to left, rgba(25, 25, 25, 0.25), rgba(32, 32, 32, 0));
|
||||
background-image: linear-gradient(
|
||||
to left,
|
||||
${autoColor(theme, 'rgba(25, 25, 25, 0.25)')},
|
||||
${autoColor(theme, 'rgba(25, 25, 25, 0.25)')}
|
||||
);
|
||||
right: 0%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
|
@ -146,41 +159,46 @@ const getStyles = createStyle(() => {
|
|||
opacity: 1;
|
||||
}
|
||||
& .${spanBarLabel} {
|
||||
color: #000;
|
||||
color: ${autoColor(theme, '#000')};
|
||||
}
|
||||
& .${nameWrapper}, &:hover .${nameWrapper} {
|
||||
background: #f0f0f0;
|
||||
box-shadow: 0 1px 0 #ddd;
|
||||
background: ${autoColor(theme, '#f0f0f0')};
|
||||
box-shadow: 0 1px 0 ${autoColor(theme, '#ddd')};
|
||||
}
|
||||
& .${nameWrapperMatchingFilter} {
|
||||
background: #fff3d7;
|
||||
background: ${autoColor(theme, '#fff3d7')};
|
||||
}
|
||||
&:hover .${view} {
|
||||
background: #eee;
|
||||
background: ${autoColor(theme, '#eee')};
|
||||
}
|
||||
`,
|
||||
rowMatchingFilter: css`
|
||||
label: rowMatchingFilter;
|
||||
background-color: #fffce4;
|
||||
background-color: ${autoColor(theme, '#fffce4')};
|
||||
&:hover .${nameWrapper} {
|
||||
background: linear-gradient(90deg, #fff5e1, #fff5e1 75%, #ffe6c9);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
${autoColor(theme, '#fff5e1')},
|
||||
${autoColor(theme, '#fff5e1')} 75%,
|
||||
${autoColor(theme, '#ffe6c9')}
|
||||
);
|
||||
}
|
||||
&:hover .${view} {
|
||||
background-color: #fff3d7;
|
||||
outline: 1px solid #ddd;
|
||||
background-color: ${autoColor(theme, '#fff3d7')};
|
||||
outline: 1px solid ${autoColor(theme, '#ddd')};
|
||||
}
|
||||
`,
|
||||
|
||||
rowExpandedAndMatchingFilter: css`
|
||||
label: rowExpandedAndMatchingFilter;
|
||||
&:hover .${view} {
|
||||
background: #ffeccf;
|
||||
background: ${autoColor(theme, '#ffeccf')};
|
||||
}
|
||||
`,
|
||||
|
||||
name: css`
|
||||
label: name;
|
||||
color: #000;
|
||||
color: ${autoColor(theme, '#000')};
|
||||
cursor: pointer;
|
||||
flex: 1 1 auto;
|
||||
outline: none;
|
||||
|
|
@ -213,7 +231,7 @@ const getStyles = createStyle(() => {
|
|||
text-decoration: none;
|
||||
}
|
||||
&:hover > .${endpointName} {
|
||||
color: #000;
|
||||
color: ${autoColor(theme, '#000')};
|
||||
}
|
||||
`,
|
||||
nameDetailExpanded: css`
|
||||
|
|
@ -234,9 +252,9 @@ const getStyles = createStyle(() => {
|
|||
`,
|
||||
errorIcon: css`
|
||||
label: errorIcon;
|
||||
background: #db2828;
|
||||
background: ${autoColor(theme, '#db2828')};
|
||||
border-radius: 6.5px;
|
||||
color: #fff;
|
||||
color: ${autoColor(theme, '#fff')};
|
||||
font-size: 0.85em;
|
||||
margin-right: 0.25rem;
|
||||
padding: 1px;
|
||||
|
|
@ -265,6 +283,7 @@ const getStyles = createStyle(() => {
|
|||
|
||||
type SpanBarRowProps = {
|
||||
className?: string;
|
||||
theme: Theme;
|
||||
color: string;
|
||||
columnDivision: number;
|
||||
isChildrenExpanded: boolean;
|
||||
|
|
@ -302,7 +321,8 @@ type SpanBarRowProps = {
|
|||
* handlers to the onClick props. E.g. for now, the PureComponent is more
|
||||
* performance than the stateless function.
|
||||
*/
|
||||
export default class SpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
||||
export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
||||
static displayName = 'UnthemedSpanBarRow';
|
||||
static defaultProps: Partial<SpanBarRowProps> = {
|
||||
className: '',
|
||||
rpc: null,
|
||||
|
|
@ -336,6 +356,7 @@ export default class SpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||
removeHoverIndentGuideId,
|
||||
clippingLeft,
|
||||
clippingRight,
|
||||
theme,
|
||||
} = this.props;
|
||||
const {
|
||||
duration,
|
||||
|
|
@ -347,7 +368,7 @@ export default class SpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||
const viewBounds = getViewedBounds(span.startTime, span.startTime + span.duration);
|
||||
const viewStart = viewBounds.start;
|
||||
const viewEnd = viewBounds.end;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
const labelDetail = `${serviceName}::${operationName}`;
|
||||
let longLabel;
|
||||
|
|
@ -459,3 +480,5 @@ export default class SpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedSpanBarRow);
|
||||
|
|
|
|||
|
|
@ -22,53 +22,61 @@ import * as markers from './AccordianKeyValues.markers';
|
|||
import KeyValuesTable from './KeyValuesTable';
|
||||
import { TNil } from '../../types';
|
||||
import { KeyValuePair, Link } from '../../types/trace';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { uAlignIcon, uTxEllipsis } from '../../uberUtilityStyles';
|
||||
|
||||
export const getStyles = createStyle(() => {
|
||||
export const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
header: css`
|
||||
label: header;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
padding: 0.25em 0.1em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
background: #e8e8e8;
|
||||
background: ${autoColor(theme, '#e8e8e8')};
|
||||
}
|
||||
`,
|
||||
headerEmpty: css`
|
||||
label: headerEmpty;
|
||||
background: none;
|
||||
cursor: initial;
|
||||
`,
|
||||
headerHighContrast: css`
|
||||
label: headerHighContrast;
|
||||
&:hover {
|
||||
background: #ddd;
|
||||
background: ${autoColor(theme, '#ddd')};
|
||||
}
|
||||
`,
|
||||
emptyIcon: css`
|
||||
color: #aaa;
|
||||
label: emptyIcon;
|
||||
color: ${autoColor(theme, '#aaa')};
|
||||
`,
|
||||
summary: css`
|
||||
label: summary;
|
||||
display: inline;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
`,
|
||||
summaryItem: css`
|
||||
label: summaryItem;
|
||||
display: inline;
|
||||
margin-left: 0.7em;
|
||||
padding-right: 0.5rem;
|
||||
border-right: 1px solid #ddd;
|
||||
border-right: 1px solid ${autoColor(theme, '#ddd')};
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
border-right: none;
|
||||
}
|
||||
`,
|
||||
summaryLabel: css`
|
||||
color: #777;
|
||||
label: summaryLabel;
|
||||
color: ${autoColor(theme, '#777')};
|
||||
`,
|
||||
summaryDelim: css`
|
||||
color: #bbb;
|
||||
label: summaryDelim;
|
||||
color: ${autoColor(theme, '#bbb')};
|
||||
padding: 0 0.2em;
|
||||
`,
|
||||
};
|
||||
|
|
@ -91,7 +99,7 @@ export function KeyValuesSummary(props: { data?: KeyValuePair[] }) {
|
|||
if (!Array.isArray(data) || !data.length) {
|
||||
return null;
|
||||
}
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<ul className={styles.summary}>
|
||||
{data.map((item, i) => (
|
||||
|
|
@ -113,7 +121,7 @@ KeyValuesSummary.defaultProps = {
|
|||
export default function AccordianKeyValues(props: AccordianKeyValuesProps) {
|
||||
const { className, data, highContrast, interactive, isOpen, label, linksGetter, onToggle } = props;
|
||||
const isEmpty = !Array.isArray(data) || !data.length;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
const iconCls = cx(uAlignIcon, { [styles.emptyIcon]: isEmpty });
|
||||
let arrow: React.ReactNode | null = null;
|
||||
let headerProps: {} | null = null;
|
||||
|
|
|
|||
|
|
@ -22,32 +22,36 @@ import AccordianKeyValues from './AccordianKeyValues';
|
|||
import { formatDuration } from '../utils';
|
||||
import { TNil } from '../../types';
|
||||
import { Log, KeyValuePair, Link } from '../../types/trace';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { uAlignIcon, ubMb1 } from '../../uberUtilityStyles';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
AccordianLogs: css`
|
||||
border: 1px solid #d8d8d8;
|
||||
label: AccordianLogs;
|
||||
border: 1px solid ${autoColor(theme, '#d8d8d8')};
|
||||
position: relative;
|
||||
margin-bottom: 0.25rem;
|
||||
`,
|
||||
header: css`
|
||||
background: #e4e4e4;
|
||||
AccordianLogsHeader: css`
|
||||
label: AccordianLogsHeader;
|
||||
background: ${autoColor(theme, '#e4e4e4')};
|
||||
color: inherit;
|
||||
display: block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
&:hover {
|
||||
background: #dadada;
|
||||
background: ${autoColor(theme, '#dadada')};
|
||||
}
|
||||
`,
|
||||
content: css`
|
||||
background: #f0f0f0;
|
||||
border-top: 1px solid #d8d8d8;
|
||||
AccordianLogsContent: css`
|
||||
label: AccordianLogsContent;
|
||||
background: ${autoColor(theme, '#f0f0f0')};
|
||||
border-top: 1px solid ${autoColor(theme, '#d8d8d8')};
|
||||
padding: 0.5rem 0.5rem 0.25rem 0.5rem;
|
||||
`,
|
||||
footer: css`
|
||||
color: #999;
|
||||
AccordianLogsFooter: css`
|
||||
label: AccordianLogsFooter;
|
||||
color: ${autoColor(theme, '#999')};
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
|
@ -78,14 +82,14 @@ export default function AccordianLogs(props: AccordianLogsProps) {
|
|||
};
|
||||
}
|
||||
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<div className={styles.AccordianLogs}>
|
||||
<HeaderComponent className={styles.header} {...headerProps}>
|
||||
<HeaderComponent className={styles.AccordianLogsHeader} {...headerProps}>
|
||||
{arrow} <strong>Logs</strong> ({logs.length})
|
||||
</HeaderComponent>
|
||||
{isOpen && (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.AccordianLogsContent}>
|
||||
{_sortBy(logs, 'timestamp').map((log, i) => (
|
||||
<AccordianKeyValues
|
||||
// `i` is necessary in the key because timestamps can repeat
|
||||
|
|
@ -100,7 +104,9 @@ export default function AccordianLogs(props: AccordianLogsProps) {
|
|||
onToggle={interactive && onItemToggle ? () => onItemToggle(log) : null}
|
||||
/>
|
||||
))}
|
||||
<small className={styles.footer}>Log timestamps are relative to the start time of the full trace.</small>
|
||||
<small className={styles.AccordianLogsFooter}>
|
||||
Log timestamps are relative to the start time of the full trace.
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import IoIosArrowRight from 'react-icons/lib/io/ios-arrow-right';
|
|||
import TextList from './TextList';
|
||||
import { TNil } from '../../types';
|
||||
import { getStyles as getAccordianKeyValuesStyles } from './AccordianKeyValues';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { uAlignIcon } from '../../uberUtilityStyles';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
header: css`
|
||||
cursor: pointer;
|
||||
|
|
@ -32,7 +32,7 @@ const getStyles = createStyle(() => {
|
|||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
background: #e8e8e8;
|
||||
background: ${autoColor(theme, '#e8e8e8')};
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
|
@ -52,7 +52,7 @@ type AccordianTextProps = {
|
|||
export default function AccordianText(props: AccordianTextProps) {
|
||||
const { className, data, headerClassName, interactive, isOpen, label, onToggle } = props;
|
||||
const isEmpty = !Array.isArray(data) || !data.length;
|
||||
const accordianKeyValuesStyles = getAccordianKeyValuesStyles();
|
||||
const accordianKeyValuesStyles = getAccordianKeyValuesStyles(useTheme());
|
||||
const iconCls = cx(uAlignIcon, { [accordianKeyValuesStyles.emptyIcon]: isEmpty });
|
||||
let arrow: React.ReactNode | null = null;
|
||||
let headerProps: {} | null = null;
|
||||
|
|
@ -64,7 +64,7 @@ export default function AccordianText(props: AccordianTextProps) {
|
|||
role: 'switch',
|
||||
};
|
||||
}
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<div className={className || ''}>
|
||||
<div className={cx(styles.header, headerClassName)} {...headerProps} data-test-id="AccordianText--header">
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import CopyIcon from '../../common/CopyIcon';
|
|||
|
||||
import KeyValuesTable, { LinkValue, getStyles } from './KeyValuesTable';
|
||||
import { UIDropdown, UIIcon } from '../../uiElementsContext';
|
||||
import {ubInlineBlock} from "../../uberUtilityStyles";
|
||||
import { ubInlineBlock } from '../../uberUtilityStyles';
|
||||
import { defaultTheme } from '../../Theme';
|
||||
|
||||
describe('LinkValue', () => {
|
||||
const title = 'titleValue';
|
||||
|
|
@ -38,7 +39,7 @@ describe('LinkValue', () => {
|
|||
});
|
||||
|
||||
it('renders correct Icon', () => {
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(defaultTheme);
|
||||
expect(wrapper.find(UIIcon).hasClass(styles.linkIcon)).toBe(true);
|
||||
expect(wrapper.find(UIIcon).prop('type')).toBe('export');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,18 +22,18 @@ import CopyIcon from '../../common/CopyIcon';
|
|||
import { TNil } from '../../types';
|
||||
import { KeyValuePair, Link } from '../../types/trace';
|
||||
import { UIDropdown, UIIcon, UIMenu, UIMenuItem } from '../../uiElementsContext';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { ubInlineBlock, uWidth100 } from '../../uberUtilityStyles';
|
||||
|
||||
export const getStyles = createStyle(() => {
|
||||
export const getStyles = createStyle((theme: Theme) => {
|
||||
const copyIcon = css`
|
||||
label: copyIcon;
|
||||
`;
|
||||
return {
|
||||
KeyValueTable: css`
|
||||
label: KeyValueTable;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
background: ${autoColor(theme, '#fff')};
|
||||
border: 1px solid ${autoColor(theme, '#ddd')};
|
||||
margin-bottom: 0.7em;
|
||||
max-height: 450px;
|
||||
overflow: auto;
|
||||
|
|
@ -50,7 +50,7 @@ export const getStyles = createStyle(() => {
|
|||
vertical-align: top;
|
||||
}
|
||||
&:nth-child(2n) > td {
|
||||
background: #f5f5f5;
|
||||
background: ${autoColor(theme, '#f5f5f5')};
|
||||
}
|
||||
&:not(:hover) .${copyIcon} {
|
||||
display: none;
|
||||
|
|
@ -58,7 +58,7 @@ export const getStyles = createStyle(() => {
|
|||
`,
|
||||
keyColumn: css`
|
||||
label: keyColumn;
|
||||
color: #888;
|
||||
color: ${autoColor(theme, '#888')};
|
||||
white-space: pre;
|
||||
width: 125px;
|
||||
`,
|
||||
|
|
@ -90,7 +90,7 @@ function parseIfComplexJson(value: any) {
|
|||
}
|
||||
|
||||
export const LinkValue = (props: { href: string; title?: string; children: React.ReactNode }) => {
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<a href={props.href} title={props.title} target="_blank" rel="noopener noreferrer">
|
||||
{props.children} <UIIcon className={styles.linkIcon} type="export" />
|
||||
|
|
@ -120,7 +120,7 @@ type KeyValuesTableProps = {
|
|||
|
||||
export default function KeyValuesTable(props: KeyValuesTableProps) {
|
||||
const { data, linksGetter } = props;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<div className={cx(styles.KeyValueTable)} data-test-id="KeyValueTable">
|
||||
<table className={uWidth100}>
|
||||
|
|
|
|||
|
|
@ -27,53 +27,72 @@ import LabeledList from '../../common/LabeledList';
|
|||
import { TNil } from '../../types';
|
||||
import { KeyValuePair, Link, Log, Span } from '../../types/trace';
|
||||
import AccordianReferences from './AccordianReferences';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { UIDivider } from '../../uiElementsContext';
|
||||
import { ubFlex, ubFlexAuto, ubItemsCenter, ubM0, ubMb1, ubMy1, ubTxRightAlign } from '../../uberUtilityStyles';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
divider: css`
|
||||
background: #ddd;
|
||||
label: divider;
|
||||
background: ${autoColor(theme, '#ddd')};
|
||||
`,
|
||||
dividerVertical: css`
|
||||
label: dividerVertical;
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin: 24px 0;
|
||||
clear: both;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -0.06em;
|
||||
`,
|
||||
debugInfo: css`
|
||||
label: debugInfo;
|
||||
display: block;
|
||||
letter-spacing: 0.25px;
|
||||
margin: 0.5em 0 -0.75em;
|
||||
text-align: right;
|
||||
`,
|
||||
debugLabel: css`
|
||||
label: debugLabel;
|
||||
&::before {
|
||||
color: #bbb;
|
||||
color: ${autoColor(theme, '#bbb')};
|
||||
content: attr(data-label);
|
||||
}
|
||||
`,
|
||||
debugValue: css`
|
||||
label: debugValue;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
color: #888;
|
||||
color: ${autoColor(theme, '#888')};
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #333;
|
||||
color: ${autoColor(theme, '#333')};
|
||||
}
|
||||
`,
|
||||
AccordianWarnings: css`
|
||||
background: #fafafa;
|
||||
border: 1px solid #e4e4e4;
|
||||
label: AccordianWarnings;
|
||||
background: ${autoColor(theme, '#fafafa')};
|
||||
border: 1px solid ${autoColor(theme, '#e4e4e4')};
|
||||
margin-bottom: 0.25rem;
|
||||
`,
|
||||
AccordianWarningsHeader: css`
|
||||
background: #fff7e6;
|
||||
label: AccordianWarningsHeader;
|
||||
background: ${autoColor(theme, '#fff7e6')};
|
||||
padding: 0.25rem 0.5rem;
|
||||
&:hover {
|
||||
background: #ffe7ba;
|
||||
background: ${autoColor(theme, '#ffe7ba')};
|
||||
}
|
||||
`,
|
||||
AccordianWarningsHeaderOpen: css`
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
label: AccordianWarningsHeaderOpen;
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e8e8e8')};
|
||||
`,
|
||||
AccordianWarningsLabel: css`
|
||||
color: #d36c08;
|
||||
label: AccordianWarningsLabel;
|
||||
color: ${autoColor(theme, '#d36c08')};
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
|
@ -126,7 +145,7 @@ export default function SpanDetail(props: SpanDetailProps) {
|
|||
},
|
||||
];
|
||||
const deepLinkCopyText = `${window.location.origin}${window.location.pathname}?uiFind=${spanID}`;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -134,7 +153,7 @@ export default function SpanDetail(props: SpanDetailProps) {
|
|||
<h2 className={cx(ubFlexAuto, ubM0)}>{operationName}</h2>
|
||||
<LabeledList className={ubTxRightAlign} dividerClassName={styles.divider} items={overviewItems} />
|
||||
</div>
|
||||
<UIDivider className={cx(styles.divider, ubMy1)} />
|
||||
<UIDivider className={cx(styles.divider, styles.dividerVertical, ubMy1)} />
|
||||
<div>
|
||||
<div>
|
||||
<AccordianKeyValues
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ describe('<SpanDetailRow>', () => {
|
|||
props.logsToggle.mockReset();
|
||||
props.processToggle.mockReset();
|
||||
props.tagsToggle.mockReset();
|
||||
wrapper = shallow(<SpanDetailRow {...props} />);
|
||||
wrapper = shallow(<SpanDetailRow {...props} />).dive().dive().dive();
|
||||
});
|
||||
|
||||
it('renders without exploding', () => {
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ import SpanDetail from './SpanDetail';
|
|||
import DetailState from './SpanDetail/DetailState';
|
||||
import SpanTreeOffset from './SpanTreeOffset';
|
||||
import TimelineRow from './TimelineRow';
|
||||
import { createStyle } from '../Theme';
|
||||
import { autoColor, createStyle, Theme, withTheme } from '../Theme';
|
||||
|
||||
import { Log, Span, KeyValuePair, Link } from '../types/trace';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
expandedAccent: css`
|
||||
cursor: pointer;
|
||||
|
|
@ -57,8 +57,8 @@ const getStyles = createStyle(() => {
|
|||
}
|
||||
`,
|
||||
infoWrapper: css`
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #d3d3d3;
|
||||
background: ${autoColor(theme, '#f5f5f5')};
|
||||
border: 1px solid ${autoColor(theme, '#d3d3d3')};
|
||||
border-top: 3px solid;
|
||||
padding: 0.75rem;
|
||||
`,
|
||||
|
|
@ -83,9 +83,10 @@ type SpanDetailRowProps = {
|
|||
hoverIndentGuideIds: Set<string>;
|
||||
addHoverIndentGuideId: (spanID: string) => void;
|
||||
removeHoverIndentGuideId: (spanID: string) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
|
||||
export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
|
||||
_detailToggle = () => {
|
||||
this.props.onDetailToggled(this.props.span.spanID);
|
||||
};
|
||||
|
|
@ -112,8 +113,9 @@ export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||
hoverIndentGuideIds,
|
||||
addHoverIndentGuideId,
|
||||
removeHoverIndentGuideId,
|
||||
theme,
|
||||
} = this.props;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(theme);
|
||||
return (
|
||||
<TimelineRow>
|
||||
<TimelineRow.Cell width={columnDivision}>
|
||||
|
|
@ -156,3 +158,5 @@ export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedSpanDetailRow);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import IoIosArrowDown from 'react-icons/lib/io/ios-arrow-down';
|
|||
|
||||
import SpanTreeOffset, { getStyles } from './SpanTreeOffset';
|
||||
import spanAncestorIdsSpy from '../utils/span-ancestor-ids';
|
||||
import {defaultTheme} from "../Theme";
|
||||
|
||||
jest.mock('../utils/span-ancestor-ids');
|
||||
|
||||
|
|
@ -42,13 +43,13 @@ describe('SpanTreeOffset', () => {
|
|||
spanID: ownSpanID,
|
||||
},
|
||||
};
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />);
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />).dive().dive().dive();
|
||||
});
|
||||
|
||||
describe('.SpanTreeOffset--indentGuide', () => {
|
||||
it('renders only one .SpanTreeOffset--indentGuide for entire trace if span has no ancestors', () => {
|
||||
spanAncestorIdsSpy.mockReturnValue([]);
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />);
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />).dive().dive().dive();
|
||||
const indentGuides = wrapper.find('[data-test-id="SpanTreeOffset--indentGuide"]');
|
||||
expect(indentGuides.length).toBe(1);
|
||||
expect(indentGuides.prop('data-ancestor-id')).toBe(specialRootID);
|
||||
|
|
@ -64,8 +65,8 @@ describe('SpanTreeOffset', () => {
|
|||
|
||||
it('adds .is-active to correct indentGuide', () => {
|
||||
props.hoverIndentGuideIds = new Set([parentSpanID]);
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />);
|
||||
const styles = getStyles();
|
||||
wrapper = shallow(<SpanTreeOffset {...props} />).dive().dive().dive();
|
||||
const styles = getStyles(defaultTheme);
|
||||
const activeIndentGuide = wrapper.find(`.${styles.indentGuideActive}`);
|
||||
expect(activeIndentGuide.length).toBe(1);
|
||||
expect(activeIndentGuide.prop('data-ancestor-id')).toBe(parentSpanID);
|
||||
|
|
|
|||
|
|
@ -22,19 +22,19 @@ import cx from 'classnames';
|
|||
import { Span } from '../types/trace';
|
||||
import spanAncestorIds from '../utils/span-ancestor-ids';
|
||||
|
||||
import { createStyle } from '../Theme';
|
||||
import { autoColor, createStyle, Theme, withTheme } from '../Theme';
|
||||
|
||||
export const getStyles = createStyle(() => {
|
||||
export const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
SpanTreeOffset: css`
|
||||
label: SpanTreeOffset;
|
||||
color: #000;
|
||||
color: ${autoColor(theme, '#000')};
|
||||
position: relative;
|
||||
`,
|
||||
SpanTreeOffsetParent: css`
|
||||
label: SpanTreeOffsetParent;
|
||||
&:hover {
|
||||
background-color: #e8e8e8;
|
||||
background-color: ${autoColor(theme, '#e8e8e8')};
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
|
|
@ -48,7 +48,7 @@ export const getStyles = createStyle(() => {
|
|||
&::before {
|
||||
content: '';
|
||||
padding-left: 1px;
|
||||
background-color: lightgrey;
|
||||
background-color: ${autoColor(theme, 'lightgrey')};
|
||||
}
|
||||
`,
|
||||
indentGuideActive: css`
|
||||
|
|
@ -58,7 +58,7 @@ export const getStyles = createStyle(() => {
|
|||
&::before {
|
||||
content: '';
|
||||
padding-left: 3px;
|
||||
background-color: darkgrey;
|
||||
background-color: ${autoColor(theme, 'darkgrey')};
|
||||
}
|
||||
`,
|
||||
iconWrapper: css`
|
||||
|
|
@ -78,9 +78,12 @@ type TProps = {
|
|||
hoverIndentGuideIds: Set<string>;
|
||||
addHoverIndentGuideId: (spanID: string) => void;
|
||||
removeHoverIndentGuideId: (spanID: string) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export default class SpanTreeOffset extends React.PureComponent<TProps> {
|
||||
export class UnthemedSpanTreeOffset extends React.PureComponent<TProps> {
|
||||
static displayName = 'UnthemedSpanTreeOffset';
|
||||
|
||||
ancestorIds: string[];
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -134,11 +137,11 @@ export default class SpanTreeOffset extends React.PureComponent<TProps> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { childrenVisible, onClick, showChildrenIcon, span } = this.props;
|
||||
const { childrenVisible, onClick, showChildrenIcon, span, theme } = this.props;
|
||||
const { hasChildren, spanID } = span;
|
||||
const wrapperProps = hasChildren ? { onClick, role: 'switch', 'aria-checked': childrenVisible } : null;
|
||||
const icon = showChildrenIcon && hasChildren && (childrenVisible ? <IoIosArrowDown /> : <IoChevronRight />);
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(theme);
|
||||
return (
|
||||
<span className={cx(styles.SpanTreeOffset, { [styles.SpanTreeOffsetParent]: hasChildren })} {...wrapperProps}>
|
||||
{this.ancestorIds.map(ancestorId => (
|
||||
|
|
@ -167,3 +170,5 @@ export default class SpanTreeOffset extends React.PureComponent<TProps> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedSpanTreeOffset);
|
||||
|
|
|
|||
|
|
@ -18,27 +18,31 @@ import cx from 'classnames';
|
|||
|
||||
import { formatDuration } from './utils';
|
||||
import { TNil } from '../types';
|
||||
import { createStyle } from '../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../Theme';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
Ticks: css`
|
||||
label: Ticks;
|
||||
pointer-events: none;
|
||||
`,
|
||||
tick: css`
|
||||
TicksTick: css`
|
||||
label: TicksTick;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background: #d8d8d8;
|
||||
background: ${autoColor(theme, '#d8d8d8')};
|
||||
&:last-child {
|
||||
width: 0;
|
||||
}
|
||||
`,
|
||||
tickLabel: css`
|
||||
TicksTickLabel: css`
|
||||
label: TicksTickLabel;
|
||||
left: 0.25rem;
|
||||
position: absolute;
|
||||
`,
|
||||
tickLabelEndAnchor: css`
|
||||
TicksTickLabelEndAnchor: css`
|
||||
label: TicksTickLabelEndAnchor;
|
||||
left: initial;
|
||||
right: 0.25rem;
|
||||
`,
|
||||
|
|
@ -64,20 +68,22 @@ export default function Ticks(props: TicksProps) {
|
|||
labels.push(formatDuration(durationAtTick));
|
||||
}
|
||||
}
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
const ticks: React.ReactNode[] = [];
|
||||
for (let i = 0; i < numTicks; i++) {
|
||||
const portion = i / (numTicks - 1);
|
||||
ticks.push(
|
||||
<div
|
||||
key={portion}
|
||||
className={styles.tick}
|
||||
className={styles.TicksTick}
|
||||
style={{
|
||||
left: `${portion * 100}%`,
|
||||
}}
|
||||
>
|
||||
{labels && (
|
||||
<span className={cx(styles.tickLabel, { [styles.tickLabelEndAnchor]: portion >= 1 })}>{labels[i]}</span>
|
||||
<span className={cx(styles.TicksTickLabel, { [styles.TicksTickLabelEndAnchor]: portion >= 1 })}>
|
||||
{labels[i]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ describe('<TimelineHeaderRow>', () => {
|
|||
});
|
||||
|
||||
it('renders the title', () => {
|
||||
expect(wrapper.find('h3').text()).toMatch(/Service.*?Operation/);
|
||||
expect(wrapper.find('h4').text()).toMatch(/Service.*?Operation/);
|
||||
});
|
||||
|
||||
it('renders the TimelineViewingLayer', () => {
|
||||
|
|
@ -88,12 +88,7 @@ describe('<TimelineHeaderRow>', () => {
|
|||
|
||||
it('renders the TimelineColumnResizer', () => {
|
||||
const elm = (
|
||||
<TimelineColumnResizer
|
||||
position={nameColumnWidth}
|
||||
onChange={props.onColummWidthChange}
|
||||
min={0.2}
|
||||
max={0.85}
|
||||
/>
|
||||
<TimelineColumnResizer position={nameColumnWidth} onChange={props.onColummWidthChange} min={0.2} max={0.85} />
|
||||
);
|
||||
expect(wrapper.containsMatchingElement(elm)).toBe(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,27 +22,33 @@ import TimelineViewingLayer from './TimelineViewingLayer';
|
|||
import Ticks from '../Ticks';
|
||||
import TimelineRow from '../TimelineRow';
|
||||
import { TUpdateViewRangeTimeFunction, ViewRangeTime, ViewRangeTimeUpdate } from '../types';
|
||||
import { createStyle } from '../../Theme';
|
||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||
import { ubFlex, ubPx2 } from '../../uberUtilityStyles';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
TimelineHeaderRow: css`
|
||||
background: #ececec;
|
||||
border-bottom: 1px solid #ccc;
|
||||
label: TimelineHeaderRow;
|
||||
background: ${autoColor(theme, '#ececec')};
|
||||
border-bottom: 1px solid ${autoColor(theme, '#ccc')};
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
width: 100%;
|
||||
z-index: 4;
|
||||
position: relative;
|
||||
`,
|
||||
title: css`
|
||||
TimelineHeaderRowTitle: css`
|
||||
label: TimelineHeaderRowTitle;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`,
|
||||
TimelineHeaderWrapper: css`
|
||||
label: TimelineHeaderWrapper;
|
||||
align-items: center;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -77,11 +83,11 @@ export default function TimelineHeaderRow(props: TimelineHeaderRowProps) {
|
|||
columnResizeHandleHeight,
|
||||
} = props;
|
||||
const [viewStart, viewEnd] = viewRangeTime.current;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<TimelineRow className={styles.TimelineHeaderRow} data-test-id="TimelineHeaderRow">
|
||||
<TimelineRow.Cell className={cx(ubFlex, ubPx2)} width={nameColumnWidth}>
|
||||
<h3 className={styles.TimelineHeaderRow}>Service & Operation</h3>
|
||||
<TimelineRow.Cell className={cx(ubFlex, ubPx2, styles.TimelineHeaderWrapper)} width={nameColumnWidth}>
|
||||
<h4 className={styles.TimelineHeaderRowTitle}>Service & Operation</h4>
|
||||
<TimelineCollapser
|
||||
onCollapseAll={onCollapseAll}
|
||||
onExpandAll={onExpandAll}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { createStyle } from '../../Theme';
|
|||
export const getStyles = createStyle(() => {
|
||||
return {
|
||||
TimelineViewingLayer: css`
|
||||
label: TimelineViewingLayer;
|
||||
bottom: 0;
|
||||
cursor: vertical-text;
|
||||
left: 0;
|
||||
|
|
@ -31,7 +32,8 @@ export const getStyles = createStyle(() => {
|
|||
right: 0;
|
||||
top: 0;
|
||||
`,
|
||||
cursorGuide: css`
|
||||
TimelineViewingLayerCursorGuide: css`
|
||||
label: TimelineViewingLayerCursorGuide;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
|
@ -39,26 +41,32 @@ export const getStyles = createStyle(() => {
|
|||
width: 1px;
|
||||
background-color: red;
|
||||
`,
|
||||
dragged: css`
|
||||
TimelineViewingLayerDragged: css`
|
||||
label: TimelineViewingLayerDragged;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
`,
|
||||
draggedDraggingLeft: css`
|
||||
TimelineViewingLayerDraggedDraggingLeft: css`
|
||||
label: TimelineViewingLayerDraggedDraggingLeft;
|
||||
border-left: 1px solid;
|
||||
`,
|
||||
draggedDraggingRight: css`
|
||||
TimelineViewingLayerDraggedDraggingRight: css`
|
||||
label: TimelineViewingLayerDraggedDraggingRight;
|
||||
border-right: 1px solid;
|
||||
`,
|
||||
draggedShiftDrag: css`
|
||||
TimelineViewingLayerDraggedShiftDrag: css`
|
||||
label: TimelineViewingLayerDraggedShiftDrag;
|
||||
background-color: rgba(68, 68, 255, 0.2);
|
||||
border-color: #44f;
|
||||
`,
|
||||
draggedReframeDrag: css`
|
||||
TimelineViewingLayerDraggedReframeDrag: css`
|
||||
label: TimelineViewingLayerDraggedReframeDrag;
|
||||
background-color: rgba(255, 68, 68, 0.2);
|
||||
border-color: #f44;
|
||||
`,
|
||||
fullOverlay: css`
|
||||
TimelineViewingLayerFullOverlay: css`
|
||||
label: TimelineViewingLayerFullOverlay;
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
left: 0;
|
||||
|
|
@ -151,13 +159,13 @@ function getMarkers(viewStart: number, viewEnd: number, from: number, to: number
|
|||
const { isDraggingLeft, left, width } = layout;
|
||||
const styles = getStyles();
|
||||
const cls = cx({
|
||||
[styles.draggedDraggingRight]: !isDraggingLeft,
|
||||
[styles.draggedReframeDrag]: !isShift,
|
||||
[styles.draggedShiftDrag]: isShift,
|
||||
[styles.TimelineViewingLayerDraggedDraggingRight]: !isDraggingLeft,
|
||||
[styles.TimelineViewingLayerDraggedReframeDrag]: !isShift,
|
||||
[styles.TimelineViewingLayerDraggedShiftDrag]: isShift,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.dragged, styles.draggedDraggingLeft, cls)}
|
||||
className={cx(styles.TimelineViewingLayerDragged, styles.TimelineViewingLayerDraggedDraggingLeft, cls)}
|
||||
style={{ left, width }}
|
||||
data-test-id="Dragged"
|
||||
/>
|
||||
|
|
@ -260,7 +268,7 @@ export default class TimelineViewingLayer extends React.PureComponent<TimelineVi
|
|||
>
|
||||
{cusrorPosition != null && (
|
||||
<div
|
||||
className={styles.cursorGuide}
|
||||
className={styles.TimelineViewingLayerCursorGuide}
|
||||
style={{ left: cusrorPosition }}
|
||||
data-test-id="TimelineViewingLayer--cursorGuide"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import SpanDetailRow from './SpanDetailRow';
|
|||
import VirtualizedTraceView, { DEFAULT_HEIGHTS } from './VirtualizedTraceView';
|
||||
import traceGenerator from '../demo/trace-generators';
|
||||
import transformTraceData from '../model/transform-trace-data';
|
||||
import SpanTreeOffset from './SpanTreeOffset';
|
||||
|
||||
jest.mock('./SpanTreeOffset');
|
||||
|
||||
|
|
@ -82,12 +83,16 @@ describe('<VirtualizedTraceViewImpl>', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
SpanTreeOffset.mockReturnValue(<div />);
|
||||
Object.keys(props).forEach(key => {
|
||||
if (typeof props[key] === 'function') {
|
||||
props[key].mockReset();
|
||||
}
|
||||
});
|
||||
wrapper = shallow(<VirtualizedTraceView {...props} />);
|
||||
wrapper = shallow(<VirtualizedTraceView {...props} />)
|
||||
.dive()
|
||||
.dive()
|
||||
.dive();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
|
|
@ -366,9 +371,7 @@ describe('<VirtualizedTraceViewImpl>', () => {
|
|||
|
||||
describe('shouldComponentUpdate', () => {
|
||||
it('returns true if props.shouldScrollToFirstUiFindMatch changes to true', () => {
|
||||
expect(wrapper.instance().shouldComponentUpdate(propsWithTrueShouldScrollToFirstUiFindMatch)).toBe(
|
||||
true
|
||||
);
|
||||
expect(wrapper.instance().shouldComponentUpdate(propsWithTrueShouldScrollToFirstUiFindMatch)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if props.shouldScrollToFirstUiFindMatch changes to false and another props change', () => {
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ import {
|
|||
ViewedBoundsFunctionType,
|
||||
} from './utils';
|
||||
import { Accessors } from '../ScrollManager';
|
||||
import colorGenerator from '../utils/color-generator';
|
||||
import { getColorByKey } from '../utils/color-generator';
|
||||
import { TNil } from '../types';
|
||||
import { Log, Span, Trace, KeyValuePair, Link } from '../types/trace';
|
||||
import TTraceTimeline from '../types/TTraceTimeline';
|
||||
|
||||
import { createStyle } from '../Theme';
|
||||
import { createStyle, Theme, withTheme } from '../Theme';
|
||||
|
||||
type TExtractUiFindFromStateReturn = {
|
||||
uiFind: string | undefined;
|
||||
|
|
@ -63,8 +63,6 @@ type TVirtualizedTraceViewOwnProps = {
|
|||
trace: Trace;
|
||||
focusSpan: (uiFind: string) => void;
|
||||
linksGetter: (span: Span, items: KeyValuePair[], itemIndex: number) => Link[];
|
||||
|
||||
// was from redux
|
||||
childrenToggle: (spanID: string) => void;
|
||||
clearShouldScrollToFirstUiFindMatch: () => void;
|
||||
detailLogItemToggle: (spanID: string, log: Log) => void;
|
||||
|
|
@ -79,6 +77,7 @@ type TVirtualizedTraceViewOwnProps = {
|
|||
hoverIndentGuideIds: Set<string>;
|
||||
addHoverIndentGuideId: (spanID: string) => void;
|
||||
removeHoverIndentGuideId: (spanID: string) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline;
|
||||
|
|
@ -144,7 +143,7 @@ function getClipping(currentViewRange: [number, number]) {
|
|||
}
|
||||
|
||||
// export from tests
|
||||
export default class VirtualizedTraceView extends React.Component<VirtualizedTraceViewProps> {
|
||||
export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTraceViewProps> {
|
||||
clipping: { left: boolean; right: boolean };
|
||||
listView: ListView | TNil;
|
||||
rowStates: RowState[];
|
||||
|
|
@ -329,12 +328,13 @@ export default class VirtualizedTraceView extends React.Component<VirtualizedTra
|
|||
hoverIndentGuideIds,
|
||||
addHoverIndentGuideId,
|
||||
removeHoverIndentGuideId,
|
||||
theme,
|
||||
} = this.props;
|
||||
// to avert flow error
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
const color = colorGenerator.getColorByKey(serviceName);
|
||||
const color = getColorByKey(serviceName, theme);
|
||||
const isCollapsed = childrenHiddenIDs.has(spanID);
|
||||
const isDetailExpanded = detailStates.has(spanID);
|
||||
const isMatchingFilter = findMatchesIDs ? findMatchesIDs.has(spanID) : false;
|
||||
|
|
@ -347,7 +347,7 @@ export default class VirtualizedTraceView extends React.Component<VirtualizedTra
|
|||
if (rpcSpan) {
|
||||
const rpcViewBounds = this.getViewedBounds(rpcSpan.startTime, rpcSpan.startTime + rpcSpan.duration);
|
||||
rpc = {
|
||||
color: colorGenerator.getColorByKey(rpcSpan.process.serviceName),
|
||||
color: getColorByKey(rpcSpan.process.serviceName, theme),
|
||||
operationName: rpcSpan.operationName,
|
||||
serviceName: rpcSpan.process.serviceName,
|
||||
viewEnd: rpcViewBounds.end,
|
||||
|
|
@ -402,12 +402,13 @@ export default class VirtualizedTraceView extends React.Component<VirtualizedTra
|
|||
addHoverIndentGuideId,
|
||||
removeHoverIndentGuideId,
|
||||
linksGetter,
|
||||
theme,
|
||||
} = this.props;
|
||||
const detailState = detailStates.get(spanID);
|
||||
if (!trace || !detailState) {
|
||||
return null;
|
||||
}
|
||||
const color = colorGenerator.getColorByKey(serviceName);
|
||||
const color = getColorByKey(serviceName, theme);
|
||||
const styles = getStyles();
|
||||
return (
|
||||
<div className={styles.row} key={key} style={{ ...style, zIndex: 1 }} {...attrs}>
|
||||
|
|
@ -454,3 +455,5 @@ export default class VirtualizedTraceView extends React.Component<VirtualizedTra
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedVirtualizedTraceView);
|
||||
|
|
|
|||
|
|
@ -46,22 +46,10 @@ describe('<TraceTimelineViewer>', () => {
|
|||
search: null,
|
||||
},
|
||||
};
|
||||
const options = {
|
||||
context: {
|
||||
store: {
|
||||
getState() {
|
||||
return { traceTimeline: { spanNameColumnWidth: 0.25 } };
|
||||
},
|
||||
subscribe() {},
|
||||
dispatch() {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<TraceTimelineViewer {...props} />, options);
|
||||
wrapper = shallow(<TraceTimelineViewer {...props} />).dive().dive().dive();
|
||||
});
|
||||
|
||||
it('it does not explode', () => {
|
||||
|
|
|
|||
|
|
@ -23,18 +23,18 @@ import { TUpdateViewRangeTimeFunction, ViewRange, ViewRangeTimeUpdate } from './
|
|||
import { TNil } from '../types';
|
||||
import { Span, Trace, Log, KeyValuePair, Link } from '../types/trace';
|
||||
import TTraceTimeline from '../types/TTraceTimeline';
|
||||
import { createStyle } from '../Theme';
|
||||
import { autoColor, createStyle, Theme, withTheme } from '../Theme';
|
||||
import ExternalLinkContext from '../url/externalLinkContext';
|
||||
|
||||
type TExtractUiFindFromStateReturn = {
|
||||
uiFind: string | undefined;
|
||||
};
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
TraceTimelineViewer: css`
|
||||
label: TraceTimelineViewer;
|
||||
border-bottom: 1px solid #bbb;
|
||||
border-bottom: 1px solid ${autoColor(theme, '#bbb')};
|
||||
|
||||
& .json-markup {
|
||||
line-height: 17px;
|
||||
|
|
@ -48,19 +48,19 @@ const getStyles = createStyle(() => {
|
|||
}
|
||||
|
||||
& .json-markup-bool {
|
||||
color: firebrick;
|
||||
color: ${autoColor(theme, 'firebrick')};
|
||||
}
|
||||
|
||||
& .json-markup-string {
|
||||
color: teal;
|
||||
color: ${autoColor(theme, 'teal')};
|
||||
}
|
||||
|
||||
& .json-markup-null {
|
||||
color: teal;
|
||||
color: ${autoColor(theme, 'teal')};
|
||||
}
|
||||
|
||||
& .json-markup-number {
|
||||
color: blue;
|
||||
color: ${autoColor(theme, 'blue', 'black')};
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
|
@ -97,6 +97,7 @@ type TProps = TExtractUiFindFromStateReturn & {
|
|||
addHoverIndentGuideId: (spanID: string) => void;
|
||||
removeHoverIndentGuideId: (spanID: string) => void;
|
||||
linksGetter: (span: Span, items: KeyValuePair[], itemIndex: number) => Link[];
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
|
@ -112,7 +113,7 @@ const NUM_TICKS = 5;
|
|||
* re-render the ListView every time the cursor is moved on the trace minimap
|
||||
* or `TimelineHeaderRow`.
|
||||
*/
|
||||
export default class TraceTimelineViewer extends React.PureComponent<TProps, State> {
|
||||
export class UnthemedTraceTimelineViewer extends React.PureComponent<TProps, State> {
|
||||
constructor(props: TProps) {
|
||||
super(props);
|
||||
this.state = { height: 0 };
|
||||
|
|
@ -151,10 +152,11 @@ export default class TraceTimelineViewer extends React.PureComponent<TProps, Sta
|
|||
viewRange,
|
||||
createLinkToExternalSpan,
|
||||
traceTimeline,
|
||||
theme,
|
||||
...rest
|
||||
} = this.props;
|
||||
const { trace } = rest;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<ExternalLinkContext.Provider value={createLinkToExternalSpan}>
|
||||
|
|
@ -187,3 +189,5 @@ export default class TraceTimelineViewer extends React.PureComponent<TProps, Sta
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(UnthemedTraceTimelineViewer);
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import * as React from 'react';
|
|||
import { css } from 'emotion';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { createStyle } from '../Theme';
|
||||
import { createStyle, isLight, Theme, useTheme } from '../Theme';
|
||||
import { UIDivider } from '../uiElementsContext';
|
||||
|
||||
const getStyles = createStyle(() => {
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
return {
|
||||
LabeledList: css`
|
||||
label: LabeledList;
|
||||
|
|
@ -33,7 +33,7 @@ const getStyles = createStyle(() => {
|
|||
`,
|
||||
LabeledListLabel: css`
|
||||
label: LabeledListLabel;
|
||||
color: #999;
|
||||
color: ${isLight(theme) ? '#999' : '#666'};
|
||||
margin-right: 0.25rem;
|
||||
`,
|
||||
};
|
||||
|
|
@ -47,7 +47,7 @@ type LabeledListProps = {
|
|||
|
||||
export default function LabeledList(props: LabeledListProps) {
|
||||
const { className, dividerClassName, items } = props;
|
||||
const styles = getStyles();
|
||||
const styles = getStyles(useTheme());
|
||||
return (
|
||||
<ul className={cx(styles.LabeledList, className)}>
|
||||
{items.map(({ key, label, value }, i) => {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export * from './TraceTimelineViewer/types';
|
|||
export { default as DetailState } from './TraceTimelineViewer/SpanDetail/DetailState';
|
||||
export { default as transformTraceData } from './model/transform-trace-data';
|
||||
export { default as filterSpans } from './utils/filter-spans';
|
||||
export * from './Theme';
|
||||
|
||||
import { onlyUpdateForKeys } from 'recompose';
|
||||
|
||||
|
|
|
|||
|
|
@ -12,26 +12,27 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import colorGenerator from './color-generator';
|
||||
import { getColorByKey, clear } from './color-generator';
|
||||
import { defaultTheme } from '../Theme';
|
||||
|
||||
it('gives the same color for the same key', () => {
|
||||
colorGenerator.clear();
|
||||
const colorOne = colorGenerator.getColorByKey('serviceA');
|
||||
const colorTwo = colorGenerator.getColorByKey('serviceA');
|
||||
clear();
|
||||
const colorOne = getColorByKey('serviceA', defaultTheme);
|
||||
const colorTwo = getColorByKey('serviceA', defaultTheme);
|
||||
expect(colorOne).toBe(colorTwo);
|
||||
});
|
||||
|
||||
it('gives different colors for each for each key', () => {
|
||||
colorGenerator.clear();
|
||||
const colorOne = colorGenerator.getColorByKey('serviceA');
|
||||
const colorTwo = colorGenerator.getColorByKey('serviceB');
|
||||
clear();
|
||||
const colorOne = getColorByKey('serviceA', defaultTheme);
|
||||
const colorTwo = getColorByKey('serviceB', defaultTheme);
|
||||
expect(colorOne).not.toBe(colorTwo);
|
||||
});
|
||||
|
||||
it('should clear cache', () => {
|
||||
colorGenerator.clear();
|
||||
const colorOne = colorGenerator.getColorByKey('serviceA');
|
||||
colorGenerator.clear();
|
||||
const colorTwo = colorGenerator.getColorByKey('serviceB');
|
||||
clear();
|
||||
const colorOne = getColorByKey('serviceA', defaultTheme);
|
||||
clear();
|
||||
const colorTwo = getColorByKey('serviceB', defaultTheme);
|
||||
expect(colorOne).toBe(colorTwo);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { isLight, Theme } from '../Theme';
|
||||
|
||||
const COLORS_HEX = [
|
||||
'#17B8BE',
|
||||
'#F8DCA1',
|
||||
|
|
@ -35,6 +37,27 @@ const COLORS_HEX = [
|
|||
'#776E57',
|
||||
];
|
||||
|
||||
const COLORS_HEX_DARK = [
|
||||
'#17B8BE',
|
||||
'#F8DCA1',
|
||||
'#B7885E',
|
||||
'#FFCB99',
|
||||
'#F89570',
|
||||
'#829AE3',
|
||||
'#E79FD5',
|
||||
'#1E96BE',
|
||||
'#89DAC1',
|
||||
'#B3AD9E',
|
||||
'#12939A',
|
||||
'#DDB27C',
|
||||
'#88572C',
|
||||
'#FF9833',
|
||||
'#EF5D28',
|
||||
'#DA70BF',
|
||||
'#4DC19C',
|
||||
'#776E57',
|
||||
];
|
||||
|
||||
// TS needs the precise return type
|
||||
function strToRgb(s: string): [number, number, number] {
|
||||
if (s.length !== 7) {
|
||||
|
|
@ -46,13 +69,13 @@ function strToRgb(s: string): [number, number, number] {
|
|||
return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
|
||||
}
|
||||
|
||||
export class ColorGenerator {
|
||||
class ColorGenerator {
|
||||
colorsHex: string[];
|
||||
colorsRgb: Array<[number, number, number]>;
|
||||
cache: Map<string, number>;
|
||||
currentIdx: number;
|
||||
|
||||
constructor(colorsHex: string[] = COLORS_HEX) {
|
||||
constructor(colorsHex: string[]) {
|
||||
this.colorsHex = colorsHex;
|
||||
this.colorsRgb = colorsHex.map(strToRgb);
|
||||
this.cache = new Map();
|
||||
|
|
@ -95,4 +118,18 @@ export class ColorGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
export default new ColorGenerator();
|
||||
let darkGenerator = new ColorGenerator(COLORS_HEX_DARK);
|
||||
let lightGenerator = new ColorGenerator(COLORS_HEX);
|
||||
|
||||
export function clear() {
|
||||
darkGenerator = new ColorGenerator(COLORS_HEX_DARK);
|
||||
lightGenerator = new ColorGenerator(COLORS_HEX);
|
||||
}
|
||||
|
||||
export function getColorByKey(key: string, theme: Theme) {
|
||||
return (isLight(theme) ? lightGenerator : darkGenerator).getColorByKey(key);
|
||||
}
|
||||
|
||||
export function getRgbColorByKey(key: string, theme: Theme): [number, number, number] {
|
||||
return (isLight(theme) ? lightGenerator : darkGenerator).getRgbColorByKey(key);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ import {
|
|||
KeyValuePair,
|
||||
Link,
|
||||
Span,
|
||||
Trace,
|
||||
TraceTimelineViewer,
|
||||
TTraceTimeline,
|
||||
UIElementsContext,
|
||||
transformTraceData,
|
||||
SpanData,
|
||||
ThemeProvider,
|
||||
ThemeType,
|
||||
Trace,
|
||||
TraceData,
|
||||
TracePageHeader,
|
||||
TraceTimelineViewer,
|
||||
transformTraceData,
|
||||
TTraceTimeline,
|
||||
UIElementsContext,
|
||||
} from '@jaegertracing/jaeger-ui-components';
|
||||
import { UIElements } from './uiElements';
|
||||
import { useViewRange } from './useViewRange';
|
||||
|
|
@ -18,6 +20,7 @@ import { useSearch } from './useSearch';
|
|||
import { useChildrenState } from './useChildrenState';
|
||||
import { useDetailState } from './useDetailState';
|
||||
import { useHoverIndentGuide } from './useHoverIndentGuide';
|
||||
import { useTheme } from '@grafana/ui';
|
||||
|
||||
type Props = {
|
||||
trace: TraceData & { spans: SpanData[] };
|
||||
|
|
@ -49,71 +52,78 @@ export function TraceView(props: Props) {
|
|||
|
||||
const traceProp = transformTraceData(props.trace);
|
||||
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<UIElementsContext.Provider value={UIElements}>
|
||||
<TracePageHeader
|
||||
canCollapse={true}
|
||||
clearSearch={() => {}}
|
||||
focusUiFindMatches={() => {}}
|
||||
hideMap={false}
|
||||
hideSummary={false}
|
||||
nextResult={() => {}}
|
||||
onSlimViewClicked={() => setSlim(!slim)}
|
||||
onTraceGraphViewClicked={() => {}}
|
||||
prevResult={() => {}}
|
||||
resultCount={0}
|
||||
slimView={slim}
|
||||
textFilter={null}
|
||||
trace={traceProp}
|
||||
traceGraphView={false}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
searchValue={search}
|
||||
onSearchValueChange={setSearch}
|
||||
hideSearchButtons={true}
|
||||
/>
|
||||
<TraceTimelineViewer
|
||||
registerAccessors={() => {}}
|
||||
scrollToFirstVisibleSpan={() => {}}
|
||||
findMatchesIDs={spanFindMatches}
|
||||
trace={traceProp}
|
||||
traceTimeline={
|
||||
{
|
||||
childrenHiddenIDs,
|
||||
detailStates,
|
||||
hoverIndentGuideIds,
|
||||
shouldScrollToFirstUiFindMatch: false,
|
||||
spanNameColumnWidth,
|
||||
traceID: '50b96206cf81dd64',
|
||||
} as TTraceTimeline
|
||||
}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
focusSpan={() => {}}
|
||||
createLinkToExternalSpan={() => ''}
|
||||
setSpanNameColumnWidth={setSpanNameColumnWidth}
|
||||
collapseAll={collapseAll}
|
||||
collapseOne={collapseOne}
|
||||
expandAll={expandAll}
|
||||
expandOne={expandOne}
|
||||
childrenToggle={childrenToggle}
|
||||
clearShouldScrollToFirstUiFindMatch={() => {}}
|
||||
detailLogItemToggle={detailLogItemToggle}
|
||||
detailLogsToggle={detailLogsToggle}
|
||||
detailWarningsToggle={detailWarningsToggle}
|
||||
detailReferencesToggle={detailReferencesToggle}
|
||||
detailProcessToggle={detailProcessToggle}
|
||||
detailTagsToggle={detailTagsToggle}
|
||||
detailToggle={toggleDetail}
|
||||
setTrace={(trace: Trace | null, uiFind: string | null) => {}}
|
||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||
linksGetter={(span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[]}
|
||||
uiFind={search}
|
||||
/>
|
||||
</UIElementsContext.Provider>
|
||||
<ThemeProvider
|
||||
value={{
|
||||
type: theme.isDark ? ThemeType.Dark : ThemeType.Light,
|
||||
}}
|
||||
>
|
||||
<UIElementsContext.Provider value={UIElements}>
|
||||
<TracePageHeader
|
||||
canCollapse={true}
|
||||
clearSearch={() => {}}
|
||||
focusUiFindMatches={() => {}}
|
||||
hideMap={false}
|
||||
hideSummary={false}
|
||||
nextResult={() => {}}
|
||||
onSlimViewClicked={() => setSlim(!slim)}
|
||||
onTraceGraphViewClicked={() => {}}
|
||||
prevResult={() => {}}
|
||||
resultCount={0}
|
||||
slimView={slim}
|
||||
textFilter={null}
|
||||
trace={traceProp}
|
||||
traceGraphView={false}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
searchValue={search}
|
||||
onSearchValueChange={setSearch}
|
||||
hideSearchButtons={true}
|
||||
/>
|
||||
<TraceTimelineViewer
|
||||
registerAccessors={() => {}}
|
||||
scrollToFirstVisibleSpan={() => {}}
|
||||
findMatchesIDs={spanFindMatches}
|
||||
trace={traceProp}
|
||||
traceTimeline={
|
||||
{
|
||||
childrenHiddenIDs,
|
||||
detailStates,
|
||||
hoverIndentGuideIds,
|
||||
shouldScrollToFirstUiFindMatch: false,
|
||||
spanNameColumnWidth,
|
||||
traceID: '50b96206cf81dd64',
|
||||
} as TTraceTimeline
|
||||
}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
focusSpan={() => {}}
|
||||
createLinkToExternalSpan={() => ''}
|
||||
setSpanNameColumnWidth={setSpanNameColumnWidth}
|
||||
collapseAll={collapseAll}
|
||||
collapseOne={collapseOne}
|
||||
expandAll={expandAll}
|
||||
expandOne={expandOne}
|
||||
childrenToggle={childrenToggle}
|
||||
clearShouldScrollToFirstUiFindMatch={() => {}}
|
||||
detailLogItemToggle={detailLogItemToggle}
|
||||
detailLogsToggle={detailLogsToggle}
|
||||
detailWarningsToggle={detailWarningsToggle}
|
||||
detailReferencesToggle={detailReferencesToggle}
|
||||
detailProcessToggle={detailProcessToggle}
|
||||
detailTagsToggle={detailTagsToggle}
|
||||
detailToggle={toggleDetail}
|
||||
setTrace={(trace: Trace | null, uiFind: string | null) => {}}
|
||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||
linksGetter={(span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[]}
|
||||
uiFind={search}
|
||||
/>
|
||||
</UIElementsContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React from 'react';
|
||||
import { ButtonProps, Elements } from '@jaegertracing/jaeger-ui-components';
|
||||
import { Button, Input } from '@grafana/ui';
|
||||
import { Button, Input, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import cx from 'classnames';
|
||||
|
||||
/**
|
||||
* Right now Jaeger components need some UI elements to be injected. This is to get rid of AntD UI library that was
|
||||
|
|
@ -29,17 +32,20 @@ export const UIElements: Elements = {
|
|||
),
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
Divider: css`
|
||||
display: inline-block;
|
||||
background: ${theme.isDark ? '#242424' : '#e8e8e8'};
|
||||
width: 1px;
|
||||
height: 0.9em;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
function Divider({ className }: { className?: string }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
background: '#e8e8e8',
|
||||
width: '1px',
|
||||
height: '0.9em',
|
||||
margin: '0 8px',
|
||||
}}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
const styles = getStyles(useTheme());
|
||||
return <div style={{}} className={cx(styles.Divider, className)} />;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue