From cf1ebd5a3df752ea9a2b2bb1f9a191c9d9ee6696 Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Tue, 14 Apr 2020 13:26:33 +0200 Subject: [PATCH] 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 --- packages/jaeger-ui-components/package.json | 1 + packages/jaeger-ui-components/src/Theme.tsx | 77 +++++++-- .../SpanGraph/CanvasSpanGraph.test.js | 5 +- .../SpanGraph/CanvasSpanGraph.tsx | 21 +-- .../SpanGraph/ViewingLayer.test.js | 54 +++++-- .../SpanGraph/ViewingLayer.tsx | 22 +-- .../SpanGraph/render-into-canvas.tsx | 6 +- .../src/TracePageHeader/TracePageHeader.tsx | 21 ++- .../TraceTimelineViewer/SpanBarRow.test.js | 7 +- .../src/TraceTimelineViewer/SpanBarRow.tsx | 87 ++++++---- .../SpanDetail/AccordianKeyValues.tsx | 28 ++-- .../SpanDetail/AccordianLogs.tsx | 36 +++-- .../SpanDetail/AccordianText.tsx | 10 +- .../SpanDetail/KeyValuesTable.test.js | 5 +- .../SpanDetail/KeyValuesTable.tsx | 16 +- .../TraceTimelineViewer/SpanDetail/index.tsx | 47 ++++-- .../TraceTimelineViewer/SpanDetailRow.test.js | 2 +- .../src/TraceTimelineViewer/SpanDetailRow.tsx | 16 +- .../SpanTreeOffset.test.js | 9 +- .../TraceTimelineViewer/SpanTreeOffset.tsx | 23 +-- .../src/TraceTimelineViewer/Ticks.tsx | 24 +-- .../TimelineHeaderRow.test.js | 9 +- .../TimelineHeaderRow/TimelineHeaderRow.tsx | 22 ++- .../TimelineViewingLayer.tsx | 32 ++-- .../VirtualizedTraceView.test.js | 11 +- .../VirtualizedTraceView.tsx | 19 ++- .../src/TraceTimelineViewer/index.test.js | 14 +- .../src/TraceTimelineViewer/index.tsx | 22 +-- .../src/common/LabeledList.tsx | 8 +- packages/jaeger-ui-components/src/index.ts | 1 + .../src/utils/color-generator.test.js | 23 +-- .../src/utils/color-generator.tsx | 43 ++++- .../features/explore/TraceView/TraceView.tsx | 148 ++++++++++-------- .../features/explore/TraceView/uiElements.tsx | 32 ++-- 34 files changed, 562 insertions(+), 339 deletions(-) diff --git a/packages/jaeger-ui-components/package.json b/packages/jaeger-ui-components/package.json index 8d20a90e383..15f0195d47a 100644 --- a/packages/jaeger-ui-components/package.json +++ b/packages/jaeger-ui-components/package.json @@ -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" } } diff --git a/packages/jaeger-ui-components/src/Theme.tsx b/packages/jaeger-ui-components/src/Theme.tsx index b3428014001..8888fc7d6e8 100644 --- a/packages/jaeger-ui-components/src/Theme.tsx +++ b/packages/jaeger-ui-components/src/Theme.tsx @@ -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; +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(undefined); ThemeContext.displayName = 'ThemeContext'; @@ -60,14 +72,16 @@ export const withTheme = > = props => { return ( - {(theme: Theme) => ( - - )} + {(theme: Theme) => { + return ( + + ); + }} ); }; @@ -81,6 +95,51 @@ export const withTheme = ; }; +export function useTheme(): Theme { + const theme = useContext(ThemeContext); + return { + ...defaultTheme, + ...theme, + }; +} + export const createStyle = ReturnType>(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(); + } +} diff --git a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.test.js b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.test.js index a3dbab15165..9dafc7bc955 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.test.js +++ b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.test.js @@ -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('', () => { it('renders without exploding', () => { const items = [{ valueWidth: 1, valueOffset: 1, serviceName: 'service-name-0' }]; - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toBeDefined(); wrapper.instance()._setCanvasRef({ getContext: () => ({ diff --git a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.tsx b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.tsx index 4ce62c6988c..f2bf6e17234 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.tsx +++ b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/CanvasSpanGraph.tsx @@ -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 { +export class UnthemedCanvasSpanGraph extends React.PureComponent { _canvasElm: HTMLCanvasElement | TNil; constructor(props: CanvasSpanGraphProps) { @@ -48,6 +47,8 @@ export default class CanvasSpanGraph extends React.PureComponent getRgbColorByKey(key, this.props.theme); + componentDidMount() { this._draw(); } @@ -63,11 +64,13 @@ export default class CanvasSpanGraph extends React.PureComponent; + return ; } } + +export default withTheme(UnthemedCanvasSpanGraph); diff --git a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js index c76f3766bc2..2346c2f66b6 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js +++ b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js @@ -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('', () => { updateViewRangeTime: jest.fn(), viewRange: getViewRange(0, 1), }; - wrapper = shallow(); + wrapper = shallow() + .dive() + .dive() + .dive(); }); describe('_getDraggingBounds()', () => { beforeEach(() => { props = { ...props, viewRange: getViewRange(0.1, 0.9) }; - wrapper = shallow(); + wrapper = shallow() + .dive() + .dive() + .dive(); wrapper.instance()._setRoot({ getBoundingClientRect() { return { left: 10, width: 100 }; @@ -122,7 +129,10 @@ describe('', () => { const anchor = 0.1; const time = { ...props.viewRange.time, reframe: { anchor } }; props = { ...props, viewRange: { time } }; - wrapper = shallow(); + wrapper = shallow() + .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('', () => { const anchor = 0.6; const time = { ...props.viewRange.time, reframe: { anchor } }; props = { ...props, viewRange: { time } }; - wrapper = shallow(); + wrapper = shallow() + .dive() + .dive() + .dive(); wrapper.instance()._handleReframeDragEnd({ manager, value }); expect(manager.resetBounds.mock.calls).toEqual([[]]); @@ -162,7 +175,10 @@ describe('', () => { const anchor = 0.4; const time = { ...props.viewRange.time, reframe: { anchor } }; props = { ...props, viewRange: { time } }; - wrapper = shallow(); + wrapper = shallow() + .dive() + .dive() + .dive(); wrapper.instance()._handleReframeDragEnd({ manager, value }); expect(manager.resetBounds.mock.calls).toEqual([[]]); @@ -258,28 +274,28 @@ describe('', () => { 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('', () => { it('renders a filtering box if leftBound exists', () => { const _props = { ...props, viewRange: getViewRange(0.2, 1) }; - wrapper = shallow(); + wrapper = shallow() + .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('', () => { it('renders a filtering box if rightBound exists', () => { const _props = { ...props, viewRange: getViewRange(0, 0.8) }; - wrapper = shallow(); + wrapper = shallow() + .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)); diff --git a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.tsx b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.tsx index b450bda63af..4371c12ef14 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.tsx +++ b/packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.tsx @@ -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 { +export class UnthemedViewingLayer extends React.PureComponent { state: ViewingLayerState; _root: Element | TNil; @@ -298,7 +300,7 @@ export default class ViewingLayer extends React.PureComponent @@ -406,3 +408,5 @@ export default class ViewingLayer extends React.PureComponent, totalValueWidth: number, - getFillColor: (serviceName: string) => [number, number, number] + getFillColor: (serviceName: string) => [number, number, number], + bgColor: string ) { const fillCache: Map = 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]; diff --git a/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx b/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx index a3f5d2e6bbd..55663948b4c 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx +++ b/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx @@ -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 = (

diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.test.js b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.test.js index 64bc3807f35..1043448e06f 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.test.js +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.test.js @@ -59,6 +59,7 @@ describe('', () => { beforeEach(() => { props.onDetailToggled.mockReset(); props.onChildrenToggled.mockReset(); + SpanTreeOffset.mockReturnValue(() => {}); wrapper = mount(); }); @@ -105,7 +106,7 @@ describe('', () => { props.span ); - const spanRow = shallow(); + const spanRow = shallow().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('', () => { }, props.span ); - const spanRow = shallow(); + const spanRow = shallow().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('', () => { }, props.span ); - const spanRow = shallow(); + const spanRow = shallow().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'); diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.tsx index c1e936ddc1b..7de0d687c59 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBarRow.tsx @@ -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 { +export class UnthemedSpanBarRow extends React.PureComponent { + static displayName = 'UnthemedSpanBarRow'; static defaultProps: Partial = { className: '', rpc: null, @@ -336,6 +356,7 @@ export default class SpanBarRow extends React.PureComponent { removeHoverIndentGuideId, clippingLeft, clippingRight, + theme, } = this.props; const { duration, @@ -347,7 +368,7 @@ export default class SpanBarRow extends React.PureComponent { 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 { ); } } + +export default withTheme(UnthemedSpanBarRow); diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx index 02208ad9201..023190bb3e2 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx @@ -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 (