From dc8f2d4522c7c18b6a8812eab51225c723bae770 Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Thu, 14 Sep 2017 01:42:50 -0700 Subject: [PATCH 1/6] Fix #63 Left column adjustable in trace detail --- .../TracePage/TraceTimelineViewer/SpanBar.js | 12 +- .../TraceTimelineViewer/SpanBarRow.js | 21 +- .../TraceTimelineViewer/SpanDetailRow.js | 10 +- .../TracePage/TraceTimelineViewer/Ticks.js | 69 ++++--- .../TraceTimelineViewer/TimelineHeaderRow.css | 124 ++++++++++++ .../TraceTimelineViewer/TimelineHeaderRow.js | 190 ++++++++++++++++++ .../TraceTimelineViewer/TimelineRow.js | 28 +-- .../VirtualizedTraceView.css | 10 - .../VirtualizedTraceView.js | 44 ++-- .../TracePage/TraceTimelineViewer/duck.js | 17 +- 10 files changed, 425 insertions(+), 100 deletions(-) create mode 100644 src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css create mode 100644 src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBar.js b/src/components/TracePage/TraceTimelineViewer/SpanBar.js index 49847ad63b..917e0bd4ff 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBar.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanBar.js @@ -60,17 +60,17 @@ function SpanBar(props) { } SpanBar.propTypes = { + color: PropTypes.string.isRequired, + hintSide: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + onClick: PropTypes.func, + viewEnd: PropTypes.number.isRequired, + viewStart: PropTypes.number.isRequired, rpc: PropTypes.shape({ viewStart: PropTypes.number, viewEnd: PropTypes.number, color: PropTypes.string, }), - viewStart: PropTypes.number.isRequired, - viewEnd: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - hintSide: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - onClick: PropTypes.func, setLongLabel: PropTypes.func, setShortLabel: PropTypes.func, }; diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js index 7a2bd5b091..541bc29960 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js @@ -32,6 +32,7 @@ export default function SpanBarRow(props) { const { className, color, + columnDivision, depth, isChildrenExpanded, isDetailExapnded, @@ -40,11 +41,11 @@ export default function SpanBarRow(props) { label, onDetailToggled, onChildrenToggled, + numTicks, operationName, rpc, serviceName, showErrorIcon, - ticks, viewEnd, viewStart, } = props; @@ -75,7 +76,7 @@ export default function SpanBarRow(props) { ${isFilteredOut ? 'is-filtered-out' : ''} `} > - +
-
- - + + + - + ); } @@ -128,6 +134,7 @@ export default function SpanBarRow(props) { SpanBarRow.propTypes = { className: PropTypes.string, color: PropTypes.string.isRequired, + columnDivision: PropTypes.number.isRequired, depth: PropTypes.number.isRequired, isChildrenExpanded: PropTypes.bool.isRequired, isDetailExapnded: PropTypes.bool.isRequired, @@ -137,6 +144,7 @@ SpanBarRow.propTypes = { onDetailToggled: PropTypes.func.isRequired, onChildrenToggled: PropTypes.func.isRequired, operationName: PropTypes.string.isRequired, + numTicks: PropTypes.number.isRequired, rpc: PropTypes.shape({ viewStart: PropTypes.number, viewEnd: PropTypes.number, @@ -146,7 +154,6 @@ SpanBarRow.propTypes = { }), serviceName: PropTypes.string.isRequired, showErrorIcon: PropTypes.bool.isRequired, - ticks: PropTypes.arrayOf(PropTypes.number).isRequired, viewEnd: PropTypes.number.isRequired, viewStart: PropTypes.number.isRequired, }; diff --git a/src/components/TracePage/TraceTimelineViewer/SpanDetailRow.js b/src/components/TracePage/TraceTimelineViewer/SpanDetailRow.js index 8e77ef788b..29ee87ccf7 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanDetailRow.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanDetailRow.js @@ -32,6 +32,7 @@ import './SpanDetailRow.css'; type SpanDetailRowProps = { color: string, + columnDivision: number, detailState: DetailState, detailToggle: string => void, isFilteredOut: boolean, @@ -46,6 +47,7 @@ type SpanDetailRowProps = { export default function SpanDetailRow(props: SpanDetailRowProps) { const { color, + columnDivision, detailState, detailToggle, isFilteredOut, @@ -58,7 +60,7 @@ export default function SpanDetailRow(props: SpanDetailRowProps) { } = props; return ( - +
@@ -69,8 +71,8 @@ export default function SpanDetailRow(props: SpanDetailRowProps) { />
-
- + +
-
+
); } diff --git a/src/components/TracePage/TraceTimelineViewer/Ticks.js b/src/components/TracePage/TraceTimelineViewer/Ticks.js index c9601c5d45..9b643c566d 100644 --- a/src/components/TracePage/TraceTimelineViewer/Ticks.js +++ b/src/components/TracePage/TraceTimelineViewer/Ticks.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,39 +20,52 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import PropTypes from 'prop-types'; -import React from 'react'; +import * as React from 'react'; + +import { formatDuration } from './utils'; import './Ticks.css'; -export default function Ticks(props) { - const { labels, ticks } = props; +type TicksProps = { + endTime: number, + numTicks: number, + showLabels?: boolean, + startTime: number, +}; + +export default function Ticks(props: TicksProps) { + const { endTime, numTicks, showLabels, startTime } = props; + let labels: string[]; + if (showLabels) { + labels = []; + const viewingDuration = endTime - startTime; + for (let i = 0; i < numTicks; i++) { + const durationAtTick = startTime + i / (numTicks - 1) * viewingDuration; + labels.push(formatDuration(durationAtTick)); + } + } + const ticks: React.Node[] = []; + for (let i = 0; i < numTicks; i++) { + const portion = i / (numTicks - 1); + ticks.push( +
+ {labels && + = 1 ? 'is-end-anchor' : ''}`}> + {labels[i]} + } +
+ ); + } return (
- {ticks.map((tick, i) => -
- {labels && - = 1 ? 'is-end-anchor' : ''}`}> - {labels[i]} - } -
- )} + {ticks}
); } - -Ticks.propTypes = { - ticks: PropTypes.arrayOf(PropTypes.number).isRequired, - labels: PropTypes.arrayOf(PropTypes.string), -}; - -Ticks.defaultProps = { - labels: null, -}; diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css new file mode 100644 index 0000000000..5349555012 --- /dev/null +++ b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css @@ -0,0 +1,124 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +.TimelineColumnResizer { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +.TimelineColumnResizer--wrapper { + bottom: 0; + position: absolute; + top: 0; +} + +.TimelineColumnResizer--dragger { + border-left: 1px solid transparent; + bottom: 0; + cursor: ew-resize; + position: fixed; + top: 0; + width: 5px; +} + +.TimelineColumnResizer--dragger:hover { + border-left: 1px solid #999; +} + +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--dragger { + border-left: 1px solid #808; + /* #808 === rgb(136, 0, 136) */ + background: rgba(136, 0, 136, 0.05); +} + +/* .TimelineColumnResizer--dragger.isDraggingRight, +.TimelineColumnResizer--dragger.isDraggingRight:hover { */ + +.TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--dragger { + border-right: 1px solid #808; + /* #808 === rgb(136, 0, 136) */ + background: rgba(136, 0, 136, 0.03); +} + +.TimelineColumnResizer--dragger::before { + position: absolute; + top: 0; + bottom: 0; + left: -8px; + right: -5px; + content: " "; +} + +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--dragger::before, +.TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--dragger::before { + left: -2000px; + right: -2000px; +} + +.TimelineColumnResizer--gripIcon { + position: absolute; + top: 0; + bottom: 0; +} + +.TimelineColumnResizer--gripIcon::before, +.TimelineColumnResizer--gripIcon::after { + position: absolute; + content: " "; + top: 60%; + bottom: 10%; + right: 8px; + border-right: 1px solid #ccc; +} + +.TimelineColumnResizer--gripIcon::after { + right: 4px; +} + +.TimelineColumnResizer--wrapper:hover > .TimelineColumnResizer--gripIcon::before, +.TimelineColumnResizer--wrapper:hover > .TimelineColumnResizer--gripIcon::after { + border-right: 1px solid #999; +} +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--gripIcon::before, +.TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--gripIcon::before, +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--gripIcon::after, +.TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--gripIcon::after { + border-right: 1px solid rgba(136, 0, 136, 0.5); +} + +.TimelineHeaderRow { + background: #e8e8e8; + border-bottom: 1px solid #ccc; + height: 38px; + overflow: hidden; + position: fixed; + width: 100%; + z-index: 2; +} + +.TimelineHeaderRow--title { + padding: 0.5rem; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js new file mode 100644 index 0000000000..01954b906f --- /dev/null +++ b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js @@ -0,0 +1,190 @@ +// @flow + +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import * as React from 'react'; +import cx from 'classnames'; + +import Ticks from './Ticks'; +import TimelineRow from './TimelineRow'; + +import './TimelineHeaderRow.css'; + +type TimelineHeaderRowProps = { + endTime: number, + nameColumnWidth: number, + numTicks: number, + onColummWidthChange: number => void, + startTime: number, +}; + +type TimelineColumnResizerProps = { + min: number, + max: number, + onChange: number => void, + position: number, +}; + +type TimelineColumnResizerState = { + currentDragPosition: ?number, + rootX: ?number, +}; + +const LEFT_MOUSE_BUTTON = 0; + +class TimelineColumnResizer extends React.PureComponent< + TimelineColumnResizerProps, + TimelineColumnResizerState +> { + props: TimelineColumnResizerProps; + state: TimelineColumnResizerState; + _rootElm: ?Element; + _isDragging: boolean; + + constructor(props) { + super(props); + this._rootElm = undefined; + this._isDragging = false; + this.state = { + currentDragPosition: null, + rootX: null, + }; + this._setRootElm = this._setRootElm.bind(this); + this._onDraggerMouseDown = this._onDraggerMouseDown.bind(this); + this._onWindowMouseMove = this._onWindowMouseMove.bind(this); + this._onWindowMouseUp = this._onWindowMouseUp.bind(this); + } + + componentWillUnmount() { + if (this._isDragging) { + window.removeEventListener('mousemove', this._onWindowMouseMove); + window.removeEventListener('mouseup', this._onWindowMouseUp); + this._isDragging = false; + } + } + + _setRootElm = function _setRootElm(elm) { + this._rootElm = elm; + }; + + _onDraggerMouseDown = function _onDraggerMouseDown({ button, clientX }) { + if (this._isDragging || button !== LEFT_MOUSE_BUTTON || !this._rootElm) { + return; + } + const rootX = this._rootElm.getBoundingClientRect().left; + this.setState({ + rootX, + currentDragPosition: clientX - rootX, + }); + window.addEventListener('mousemove', this._onWindowMouseMove); + window.addEventListener('mouseup', this._onWindowMouseUp); + this._isDragging = true; + if (document && document.body && document.body.style) { + (document.body.style: any).userSelect = 'none'; + } + }; + + _onWindowMouseMove = function _onWindowMouseMove({ clientX }) { + const { rootX } = this.state; + this.setState({ rootX, currentDragPosition: clientX - (rootX || 0) }); + }; + + _onWindowMouseUp = function _onWindowMouseUp({ clientX }) { + window.removeEventListener('mousemove', this._onWindowMouseMove); + window.removeEventListener('mouseup', this._onWindowMouseUp); + if (document && document.body && document.body.style) { + (document.body.style: any).userSelect = undefined; + } + this._isDragging = false; + const { rootX } = this.state; + this.setState({ rootX: null, currentDragPosition: null }); + const { min, max } = this.props; + if (this._rootElm) { + let value = (clientX - (rootX || 0)) / this._rootElm.clientWidth; + if (value < min) { + value = min; + } else if (value > max) { + value = max; + } + this.props.onChange(value); + } + }; + + render() { + let left; + let draggerStyle; + let draggerStateCls = ''; + if (this._isDragging && this._rootElm) { + const { min, max, position } = this.props; + const { currentDragPosition } = this.state; + let newPosition = (currentDragPosition || 0) / this._rootElm.clientWidth; + if (newPosition < min) { + newPosition = min; + } else if (newPosition > max) { + newPosition = max; + } + left = `${newPosition * 100}%`; + const draggLeft = `${Math.min(position, newPosition) * 100}%`; + const width = `${Math.abs(position - newPosition) * 100}%`; + draggerStyle = { width, left: draggLeft }; + draggerStateCls = cx({ + isDraggingLeft: newPosition < position, + isDraggingRight: newPosition > position, + }); + } else { + const { position } = this.props; + left = `${position * 100}%`; + draggerStyle = { left }; + } + return ( +
+
+
+
+
+
+ ); + } +} + +export default function TimelineHeaderRow(props: TimelineHeaderRowProps) { + const { endTime, nameColumnWidth, numTicks, onColummWidthChange, startTime } = props; + return ( + + +

Service & Operation

+
+ + + + +
+ ); +} diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineRow.js b/src/components/TracePage/TraceTimelineViewer/TimelineRow.js index e64b9af94d..b90dbb9dd4 100644 --- a/src/components/TracePage/TraceTimelineViewer/TimelineRow.js +++ b/src/components/TracePage/TraceTimelineViewer/TimelineRow.js @@ -26,6 +26,7 @@ import './TimelineRow.css'; const propTypes = { children: PropTypes.node, className: PropTypes.string, + style: PropTypes.object, }; const defaultProps = { @@ -44,28 +45,17 @@ export default function TimelineRow(props) { TimelineRow.propTypes = { ...propTypes }; TimelineRow.defaultProps = { ...defaultProps }; -function TimelineRowLeft(props) { - const { children, className, ...rest } = props; - return ( -
- {children} -
- ); -} -TimelineRowLeft.propTypes = { ...propTypes }; -TimelineRowLeft.defaultProps = { ...defaultProps }; - -TimelineRow.Left = TimelineRowLeft; - -function TimelineRowRight(props) { - const { children, className, ...rest } = props; +function TimelineRowCell(props) { + const { children, className, width, style, ...rest } = props; + const widthPercent = `${width * 100}%`; + const mergedStyle = { ...style, flexBasis: widthPercent, maxWidth: widthPercent }; return ( -
+
{children}
); } -TimelineRowRight.propTypes = { ...propTypes }; -TimelineRowRight.defaultProps = { ...defaultProps }; +TimelineRowCell.propTypes = { ...propTypes, width: PropTypes.number.isRequired }; +TimelineRowCell.defaultProps = { ...defaultProps }; -TimelineRow.Right = TimelineRowRight; +TimelineRow.Cell = TimelineRowCell; diff --git a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.css b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.css index e909c66e62..9790ee2688 100644 --- a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.css +++ b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.css @@ -20,16 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.VirtualizedTraceView--headerRow { - background: #e8e8e8; - border-bottom: 1px solid #ccc; - height: 38px; - overflow: hidden; - position: fixed; - width: 100%; - z-index: 2; -} - .VirtualizedTraceView--spans { padding-top: 40px; position: relative; diff --git a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js index 8b944a7366..8904e7c3e0 100644 --- a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js +++ b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js @@ -30,8 +30,7 @@ import ListView from './ListView'; import SpanBarRow from './SpanBarRow'; import DetailState from './SpanDetail/DetailState'; import SpanDetailRow from './SpanDetailRow'; -import Ticks from './Ticks'; -import TimelineRow from './TimelineRow'; +import TimelineHeaderRow from './TimelineHeaderRow'; import { findServerChildSpan, formatDuration, @@ -62,8 +61,9 @@ type VirtualizedTraceViewProps = { find: (?Trace, ?string) => void, findMatchesIDs: Set, setTrace: (?string) => void, + setSpanNameColumnWidth: number => void, + spanNameColumnWidth: number, textFilter: ?string, - ticks: number[], trace?: Trace, zoomEnd: number, zoomStart: number, @@ -75,6 +75,8 @@ const DEFAULT_HEIGHTS = { detailWithLogs: 223, }; +const NUM_TICKS = 5; + function generateRowStates( spans: ?(Span[]), childrenHiddenIDs: Set, @@ -215,11 +217,11 @@ class VirtualizedTraceView extends React.PureComponent childrenToggle(spanID)} operationName={span.operationName} rpc={rpc} serviceName={span.process.serviceName} showErrorIcon={showErrorIcon} - ticks={ticks} viewEnd={viewBounds.end} viewStart={viewBounds.start} /> @@ -303,6 +306,7 @@ class VirtualizedTraceView extends React.PureComponent detailToggle(spanID)} detailState={detailState} isFilteredOut={isFilteredOut} @@ -330,30 +335,21 @@ class VirtualizedTraceView extends React.PureComponent - - -

Span Name

-
- - (tick > 0 ? formatDuration(getDuationAtTick(tick)) : ''))} - ticks={ticks} - /> -

Timeline

-
-
+
, // findMatches: ?Set, // detailStates: Map // } -// -export function newInitialState(traceID = null) { +export function newInitialState({ spanNameColumnWidth = null, traceID = null } = {}) { return { traceID, + spanNameColumnWidth: spanNameColumnWidth || 0.25, childrenHiddenIDs: new Set(), detailStates: new Map(), findMatchesIDs: null, @@ -52,6 +53,7 @@ export function newInitialState(traceID = null) { const actionTypes = generateActionTypes('@jaeger-ui/trace-timeline-viewer', [ 'SET_TRACE', + 'SET_SPAN_NAME_COLUMN_WIDTH', 'CHILDREN_TOGGLE', 'DETAIL_TOGGLE', 'DETAIL_TAGS_TOGGLE', @@ -63,6 +65,7 @@ const actionTypes = generateActionTypes('@jaeger-ui/trace-timeline-viewer', [ const fullActions = createActions({ [actionTypes.SET_TRACE]: traceID => ({ traceID }), + [actionTypes.SET_SPAN_NAME_COLUMN_WIDTH]: width => ({ width }), [actionTypes.CHILDREN_TOGGLE]: spanID => ({ spanID }), [actionTypes.DETAIL_TOGGLE]: spanID => ({ spanID }), [actionTypes.DETAIL_TAGS_TOGGLE]: spanID => ({ spanID }), @@ -79,7 +82,14 @@ function setTrace(state, { payload }) { if (traceID === state.traceID) { return state; } - return newInitialState(traceID); + // preserve spanNameColumnWidth when resetting state + const { spanNameColumnWidth } = state; + return newInitialState({ spanNameColumnWidth, traceID }); +} + +function setColumnWidth(state, { payload }) { + const { width } = payload; + return { ...state, spanNameColumnWidth: width }; } function childrenToggle(state, { payload }) { @@ -143,6 +153,7 @@ function find(state, { payload }) { export default handleActions( { [actionTypes.SET_TRACE]: setTrace, + [actionTypes.SET_SPAN_NAME_COLUMN_WIDTH]: setColumnWidth, [actionTypes.CHILDREN_TOGGLE]: childrenToggle, [actionTypes.DETAIL_TOGGLE]: detailToggle, [actionTypes.DETAIL_TAGS_TOGGLE]: detailTagsToggle, From 00e83d37a3f558f931415725783a8267c7e688ec Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Sat, 16 Sep 2017 03:24:42 -0700 Subject: [PATCH 2/6] Simplify span-name column drag code --- .../TraceTimelineViewer/TimelineHeaderRow.css | 19 +++-- .../TraceTimelineViewer/TimelineHeaderRow.js | 72 ++++++++++--------- 2 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css index 5349555012..b8a69fa2f9 100644 --- a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css +++ b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.css @@ -47,19 +47,21 @@ THE SOFTWARE. border-left: 1px solid #999; } -.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--dragger { - border-left: 1px solid #808; +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--dragger, +.TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--dragger { /* #808 === rgb(136, 0, 136) */ background: rgba(136, 0, 136, 0.05); + width: auto; } -/* .TimelineColumnResizer--dragger.isDraggingRight, -.TimelineColumnResizer--dragger.isDraggingRight:hover { */ +.TimelineColumnResizer--wrapper.isDraggingLeft > .TimelineColumnResizer--dragger { + border-left: 2px solid #808; + border-right: 1px solid #999; +} .TimelineColumnResizer--wrapper.isDraggingRight > .TimelineColumnResizer--dragger { - border-right: 1px solid #808; - /* #808 === rgb(136, 0, 136) */ - background: rgba(136, 0, 136, 0.03); + border-left: 1px solid #999; + border-right: 2px solid #808; } .TimelineColumnResizer--dragger::before { @@ -119,6 +121,9 @@ THE SOFTWARE. } .TimelineHeaderRow--title { + overflow: hidden; padding: 0.5rem; + padding-right: 0.1rem; + text-overflow: ellipsis; white-space: nowrap; } \ No newline at end of file diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js index 01954b906f..93f568bd41 100644 --- a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js +++ b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js @@ -22,6 +22,7 @@ import * as React from 'react'; import cx from 'classnames'; +import _clamp from 'lodash/clamp'; import Ticks from './Ticks'; import TimelineRow from './TimelineRow'; @@ -44,7 +45,7 @@ type TimelineColumnResizerProps = { }; type TimelineColumnResizerState = { - currentDragPosition: ?number, + dragPosition: ?number, rootX: ?number, }; @@ -64,7 +65,7 @@ class TimelineColumnResizer extends React.PureComponent< this._rootElm = undefined; this._isDragging = false; this.state = { - currentDragPosition: null, + dragPosition: null, rootX: null, }; this._setRootElm = this._setRootElm.bind(this); @@ -85,15 +86,25 @@ class TimelineColumnResizer extends React.PureComponent< this._rootElm = elm; }; + _getDraggedPosition(clientX: number, rootX: ?number = null) { + if (!this._rootElm) { + return null; + } + const { min, max } = this.props; + const rx = rootX == null ? this.state.rootX : rootX; + // pos is position of cursor in the horizontal portion of the bounding box, + // in range [0, 1] + const pos = (clientX - (rx || 0)) / this._rootElm.clientWidth; + return _clamp(pos, min, max); + } + _onDraggerMouseDown = function _onDraggerMouseDown({ button, clientX }) { if (this._isDragging || button !== LEFT_MOUSE_BUTTON || !this._rootElm) { return; } const rootX = this._rootElm.getBoundingClientRect().left; - this.setState({ - rootX, - currentDragPosition: clientX - rootX, - }); + const dragPosition = this._getDraggedPosition(clientX, rootX); + this.setState({ rootX, dragPosition }); window.addEventListener('mousemove', this._onWindowMouseMove); window.addEventListener('mouseup', this._onWindowMouseUp); this._isDragging = true; @@ -103,8 +114,8 @@ class TimelineColumnResizer extends React.PureComponent< }; _onWindowMouseMove = function _onWindowMouseMove({ clientX }) { - const { rootX } = this.state; - this.setState({ rootX, currentDragPosition: clientX - (rootX || 0) }); + const dragPosition = this._getDraggedPosition(clientX); + this.setState({ ...this.state, dragPosition }); }; _onWindowMouseUp = function _onWindowMouseUp({ clientX }) { @@ -114,41 +125,33 @@ class TimelineColumnResizer extends React.PureComponent< (document.body.style: any).userSelect = undefined; } this._isDragging = false; - const { rootX } = this.state; - this.setState({ rootX: null, currentDragPosition: null }); - const { min, max } = this.props; - if (this._rootElm) { - let value = (clientX - (rootX || 0)) / this._rootElm.clientWidth; - if (value < min) { - value = min; - } else if (value > max) { - value = max; - } - this.props.onChange(value); + const dragPosition = this._getDraggedPosition(clientX); + if (dragPosition != null) { + this.props.onChange(dragPosition); } + this.setState({ rootX: null, dragPosition: null }); }; render() { let left; let draggerStyle; let draggerStateCls = ''; - if (this._isDragging && this._rootElm) { - const { min, max, position } = this.props; - const { currentDragPosition } = this.state; - let newPosition = (currentDragPosition || 0) / this._rootElm.clientWidth; - if (newPosition < min) { - newPosition = min; - } else if (newPosition > max) { - newPosition = max; - } - left = `${newPosition * 100}%`; - const draggLeft = `${Math.min(position, newPosition) * 100}%`; - const width = `${Math.abs(position - newPosition) * 100}%`; - draggerStyle = { width, left: draggLeft }; + const { dragPosition } = this.state; + if (this._isDragging && this._rootElm && dragPosition != null) { + const { position } = this.props; draggerStateCls = cx({ - isDraggingLeft: newPosition < position, - isDraggingRight: newPosition > position, + isDraggingLeft: dragPosition < position, + isDraggingRight: dragPosition > position, }); + left = `${dragPosition * 100}%`; + // Draw a highlight from the current dragged position back to the original + // position, e.g. highlight the change. Draw the highlight via `left` and + // `right` css styles (simpler than using `width`). + const draggerLeft = `${Math.min(position, dragPosition) * 100}%`; + // subtract 1px for draggerRight to deal with the right border being off + // by 1px when dragging left + const draggerRight = `calc(${(1 - Math.max(position, dragPosition)) * 100}% - 1px)`; + draggerStyle = { left: draggerLeft, right: draggerRight }; } else { const { position } = this.props; left = `${position * 100}%`; @@ -159,6 +162,7 @@ class TimelineColumnResizer extends React.PureComponent<
Date: Sat, 16 Sep 2017 03:27:43 -0700 Subject: [PATCH 3/6] Remove unused hint styling --- .../TracePage/TraceTimelineViewer/SpanBar.css | 1 - .../TracePage/TraceTimelineViewer/index.css | 643 ------------------ 2 files changed, 644 deletions(-) diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBar.css b/src/components/TracePage/TraceTimelineViewer/SpanBar.css index 5d9cc54d1a..1761b430e1 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBar.css +++ b/src/components/TracePage/TraceTimelineViewer/SpanBar.css @@ -37,7 +37,6 @@ THE SOFTWARE. opacity: 1; } -/* Add the hint related selector to override the hint styling (via specificity) */ .SpanBar--bar { border-radius: 3px; min-width: 2px; diff --git a/src/components/TracePage/TraceTimelineViewer/index.css b/src/components/TracePage/TraceTimelineViewer/index.css index ef020eb226..baa520b10a 100644 --- a/src/components/TracePage/TraceTimelineViewer/index.css +++ b/src/components/TracePage/TraceTimelineViewer/index.css @@ -64,646 +64,3 @@ THE SOFTWARE. .json-markup-number { color: blue; } -/*! Hint.css - v2.4.1 - 2016-11-08 -* http://kushagragour.in/lab/hint/ -* Copyright (c) 2016 Kushagra Gour */ - -/*-------------------------------------* HINT.css - A CSS tooltip library -\*-------------------------------------*/ -/** - * HINT.css is a tooltip library made in pure CSS. - * - * Source: https://github.com/chinchang/hint.css - * Demo: http://kushagragour.in/lab/hint/ - * - * Release under The MIT License - * - */ -/** - * source: hint-core.scss - * - * Defines the basic styling for the tooltip. - * Each tooltip is made of 2 parts: - * 1) body (:after) - * 2) arrow (:before) - * - * Classes added: - * 1) hint - */ -[class*="hint--"] { - position: relative; - display: inline-block; - /** - * tooltip arrow - */ - /** - * tooltip body - */ } - [class*="hint--"]:before, [class*="hint--"]:after { - position: absolute; - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - visibility: hidden; - opacity: 0; - z-index: 1000000; - pointer-events: none; - -webkit-transition: 0.3s ease; - -moz-transition: 0.3s ease; - transition: 0.3s ease; - -webkit-transition-delay: 0ms; - -moz-transition-delay: 0ms; - transition-delay: 0ms; } - [class*="hint--"]:hover:before, [class*="hint--"]:hover:after { - visibility: visible; - opacity: 1; } - [class*="hint--"]:hover:before, [class*="hint--"]:hover:after { - -webkit-transition-delay: 100ms; - -moz-transition-delay: 100ms; - transition-delay: 100ms; } - [class*="hint--"]:before { - content: ''; - position: absolute; - background: transparent; - z-index: 1000001; } - [class*="hint--"]:after { - color: black; - padding: 8px 0px; - font-size: 12px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - line-height: 12px; - white-space: nowrap; } - [class*="hint--"][aria-label]:after { - content: attr(aria-label); } - [class*="hint--"][data-hint]:after { - content: attr(data-hint); } - -[aria-label='']:before, [aria-label='']:after, -[data-hint='']:before, -[data-hint='']:after { - display: none !important; } - -/** - * source: hint-position.scss - * - * Defines the positoning logic for the tooltips. - * - * Classes added: - * 1) hint--top - * 2) hint--bottom - * 3) hint--left - * 4) hint--right - */ -/** - * set default color for tooltip arrows - */ -.hint--top-left:before { - border-top-color: #383838; } - -.hint--top-right:before { - border-top-color: #383838; } - -.hint--top:before { - border-top-color: #383838; } - -.hint--bottom-left:before { - border-bottom-color: #383838; } - -.hint--bottom-right:before { - border-bottom-color: #383838; } - -.hint--bottom:before { - border-bottom-color: #383838; } - -.hint--left:before { - border-left-color: #383838; } - -.hint--right:before { - border-right-color: #383838; } - -/** - * top tooltip - */ -.hint--top:before { - margin-bottom: -11px; } - -.hint--top:before, .hint--top:after { - bottom: 100%; - left: 50%; } - -.hint--top:before { - left: calc(50% - 6px); } - -.hint--top:after { - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - transform: translateX(-50%); } - -.hint--top:hover:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--top:hover:after { - -webkit-transform: translateX(-50%) translateY(-8px); - -moz-transform: translateX(-50%) translateY(-8px); - transform: translateX(-50%) translateY(-8px); } - -/** - * bottom tooltip - */ -.hint--bottom:before { - margin-top: -11px; } - -.hint--bottom:before, .hint--bottom:after { - top: 100%; - left: 50%; } - -.hint--bottom:before { - left: calc(50% - 6px); } - -.hint--bottom:after { - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - transform: translateX(-50%); } - -.hint--bottom:hover:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--bottom:hover:after { - -webkit-transform: translateX(-50%) translateY(8px); - -moz-transform: translateX(-50%) translateY(8px); - transform: translateX(-50%) translateY(8px); } - -/** - * right tooltip - */ -.hint--right:before { - margin-left: -11px; - margin-bottom: -6px; } - -.hint--right:after { - margin-bottom: -14px; } - -.hint--right:before, .hint--right:after { - left: 100%; - bottom: 50%; } - -.hint--right:hover:before { - -webkit-transform: translateX(8px); - -moz-transform: translateX(8px); - transform: translateX(8px); } - -.hint--right:hover:after { - -webkit-transform: translateX(8px); - -moz-transform: translateX(8px); - transform: translateX(8px); } - -/** - * left tooltip - */ -.hint--left:before { - margin-right: -11px; - margin-bottom: -6px; } - -.hint--left:after { - margin-bottom: -14px; } - -.hint--left:before, .hint--left:after { - right: 100%; - bottom: 50%; } - -.hint--left:hover:before { - -webkit-transform: translateX(-8px); - -moz-transform: translateX(-8px); - transform: translateX(-8px); } - -.hint--left:hover:after { - -webkit-transform: translateX(-8px); - -moz-transform: translateX(-8px); - transform: translateX(-8px); } - -/** - * top-left tooltip - */ -.hint--top-left:before { - margin-bottom: -11px; } - -.hint--top-left:before, .hint--top-left:after { - bottom: 100%; - left: 50%; } - -.hint--top-left:before { - left: calc(50% - 6px); } - -.hint--top-left:after { - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - transform: translateX(-100%); } - -.hint--top-left:after { - margin-left: 12px; } - -.hint--top-left:hover:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--top-left:hover:after { - -webkit-transform: translateX(-100%) translateY(-8px); - -moz-transform: translateX(-100%) translateY(-8px); - transform: translateX(-100%) translateY(-8px); } - -/** - * top-right tooltip - */ -.hint--top-right:before { - margin-bottom: -11px; } - -.hint--top-right:before, .hint--top-right:after { - bottom: 100%; - left: 50%; } - -.hint--top-right:before { - left: calc(50% - 6px); } - -.hint--top-right:after { - -webkit-transform: translateX(0); - -moz-transform: translateX(0); - transform: translateX(0); } - -.hint--top-right:after { - margin-left: -12px; } - -.hint--top-right:hover:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--top-right:hover:after { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -/** - * bottom-left tooltip - */ -.hint--bottom-left:before { - margin-top: -11px; } - -.hint--bottom-left:before, .hint--bottom-left:after { - top: 100%; - left: 50%; } - -.hint--bottom-left:before { - left: calc(50% - 6px); } - -.hint--bottom-left:after { - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - transform: translateX(-100%); } - -.hint--bottom-left:after { - margin-left: 12px; } - -.hint--bottom-left:hover:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--bottom-left:hover:after { - -webkit-transform: translateX(-100%) translateY(8px); - -moz-transform: translateX(-100%) translateY(8px); - transform: translateX(-100%) translateY(8px); } - -/** - * bottom-right tooltip - */ -.hint--bottom-right:before { - margin-top: -11px; } - -.hint--bottom-right:before, .hint--bottom-right:after { - top: 100%; - left: 50%; } - -.hint--bottom-right:before { - left: calc(50% - 6px); } - -.hint--bottom-right:after { - -webkit-transform: translateX(0); - -moz-transform: translateX(0); - transform: translateX(0); } - -.hint--bottom-right:after { - margin-left: -12px; } - -.hint--bottom-right:hover:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--bottom-right:hover:after { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -/** - * source: hint-sizes.scss - * - * Defines width restricted tooltips that can span - * across multiple lines. - * - * Classes added: - * 1) hint--small - * 2) hint--medium - * 3) hint--large - * - */ -.hint--small:after, -.hint--medium:after, -.hint--large:after { - white-space: normal; - line-height: 1.4em; - word-wrap: break-word; } - -.hint--small:after { - width: 80px; } - -.hint--medium:after { - width: 150px; } - -.hint--large:after { - width: 300px; } - -/** - * source: hint-theme.scss - * - * Defines basic theme for tooltips. - * - */ -[class*="hint--"] { - /** - * tooltip body - */ } -/** - * source: hint-color-types.scss - * - * Contains tooltips of various types based on color differences. - * - * Classes added: - * 1) hint--error - * 2) hint--warning - * 3) hint--info - * 4) hint--success - * - */ -/** - * Error - */ -.hint--error:after { - background-color: #b34e4d; - text-shadow: 0 -1px 0px #592726; } - -.hint--error.hint--top-left:before { - border-top-color: #b34e4d; } - -.hint--error.hint--top-right:before { - border-top-color: #b34e4d; } - -.hint--error.hint--top:before { - border-top-color: #b34e4d; } - -.hint--error.hint--bottom-left:before { - border-bottom-color: #b34e4d; } - -.hint--error.hint--bottom-right:before { - border-bottom-color: #b34e4d; } - -.hint--error.hint--bottom:before { - border-bottom-color: #b34e4d; } - -.hint--error.hint--left:before { - border-left-color: #b34e4d; } - -.hint--error.hint--right:before { - border-right-color: #b34e4d; } - -/** - * Warning - */ -.hint--warning:after { - background-color: #c09854; - text-shadow: 0 -1px 0px #6c5328; } - -.hint--warning.hint--top-left:before { - border-top-color: #c09854; } - -.hint--warning.hint--top-right:before { - border-top-color: #c09854; } - -.hint--warning.hint--top:before { - border-top-color: #c09854; } - -.hint--warning.hint--bottom-left:before { - border-bottom-color: #c09854; } - -.hint--warning.hint--bottom-right:before { - border-bottom-color: #c09854; } - -.hint--warning.hint--bottom:before { - border-bottom-color: #c09854; } - -.hint--warning.hint--left:before { - border-left-color: #c09854; } - -.hint--warning.hint--right:before { - border-right-color: #c09854; } - -/** - * Info - */ -.hint--info:after { - background-color: #3986ac; - text-shadow: 0 -1px 0px #1a3c4d; } - -.hint--info.hint--top-left:before { - border-top-color: #3986ac; } - -.hint--info.hint--top-right:before { - border-top-color: #3986ac; } - -.hint--info.hint--top:before { - border-top-color: #3986ac; } - -.hint--info.hint--bottom-left:before { - border-bottom-color: #3986ac; } - -.hint--info.hint--bottom-right:before { - border-bottom-color: #3986ac; } - -.hint--info.hint--bottom:before { - border-bottom-color: #3986ac; } - -.hint--info.hint--left:before { - border-left-color: #3986ac; } - -.hint--info.hint--right:before { - border-right-color: #3986ac; } - -/** - * Success - */ -.hint--success:after { - background-color: #458746; - text-shadow: 0 -1px 0px #1a321a; } - -.hint--success.hint--top-left:before { - border-top-color: #458746; } - -.hint--success.hint--top-right:before { - border-top-color: #458746; } - -.hint--success.hint--top:before { - border-top-color: #458746; } - -.hint--success.hint--bottom-left:before { - border-bottom-color: #458746; } - -.hint--success.hint--bottom-right:before { - border-bottom-color: #458746; } - -.hint--success.hint--bottom:before { - border-bottom-color: #458746; } - -.hint--success.hint--left:before { - border-left-color: #458746; } - -.hint--success.hint--right:before { - border-right-color: #458746; } - -/** - * source: hint-always.scss - * - * Defines a persisted tooltip which shows always. - * - * Classes added: - * 1) hint--always - * - */ -.hint--always:after, .hint--always:before { - opacity: 1; - visibility: visible; } - -.hint--always.hint--top:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--always.hint--top:after { - -webkit-transform: translateX(-50%) translateY(-8px); - -moz-transform: translateX(-50%) translateY(-8px); - transform: translateX(-50%) translateY(-8px); } - -.hint--always.hint--top-left:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--always.hint--top-left:after { - -webkit-transform: translateX(-100%) translateY(-8px); - -moz-transform: translateX(-100%) translateY(-8px); - transform: translateX(-100%) translateY(-8px); } - -.hint--always.hint--top-right:before { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--always.hint--top-right:after { - -webkit-transform: translateY(-8px); - -moz-transform: translateY(-8px); - transform: translateY(-8px); } - -.hint--always.hint--bottom:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--always.hint--bottom:after { - -webkit-transform: translateX(-50%) translateY(8px); - -moz-transform: translateX(-50%) translateY(8px); - transform: translateX(-50%) translateY(8px); } - -.hint--always.hint--bottom-left:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--always.hint--bottom-left:after { - -webkit-transform: translateX(-100%) translateY(8px); - -moz-transform: translateX(-100%) translateY(8px); - transform: translateX(-100%) translateY(8px); } - -.hint--always.hint--bottom-right:before { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--always.hint--bottom-right:after { - -webkit-transform: translateY(8px); - -moz-transform: translateY(8px); - transform: translateY(8px); } - -.hint--always.hint--left:before { - -webkit-transform: translateX(-8px); - -moz-transform: translateX(-8px); - transform: translateX(-8px); } - -.hint--always.hint--left:after { - -webkit-transform: translateX(-8px); - -moz-transform: translateX(-8px); - transform: translateX(-8px); } - -.hint--always.hint--right:before { - -webkit-transform: translateX(8px); - -moz-transform: translateX(8px); - transform: translateX(8px); } - -.hint--always.hint--right:after { - -webkit-transform: translateX(8px); - -moz-transform: translateX(8px); - transform: translateX(8px); } - -/** - * source: hint-rounded.scss - * - * Defines rounded corner tooltips. - * - * Classes added: - * 1) hint--rounded - * - */ -.hint--rounded:after { - border-radius: 4px; } - -/** - * source: hint-effects.scss - * - * Defines various transition effects for the tooltips. - * - * Classes added: - * 1) hint--no-animate - * 2) hint--bounce - * - */ -.hint--no-animate:before, .hint--no-animate:after { - -webkit-transition-duration: 0ms; - -moz-transition-duration: 0ms; - transition-duration: 0ms; } - -.hint--bounce:before, .hint--bounce:after { - -webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); - -moz-transition: opacity 0.3s ease, visibility 0.3s ease, -moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); - transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); } From b76d79e124d19c4bffd27914023d34033abd4fe4 Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Sun, 17 Sep 2017 05:27:35 -0700 Subject: [PATCH 4/6] Trace view Loading indicator, handle next trace --- .../TraceTimelineViewer/SpanBarRow.css | 1 - .../VirtualizedTraceView.js | 12 +++-- .../TracePage/TraceTimelineViewer/index.js | 50 ++++++++----------- src/components/TracePage/index.js | 9 +++- src/components/TracePage/index.test.js | 6 +++ 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.css b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.css index edfeed23e8..972a9fab27 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.css +++ b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.css @@ -79,7 +79,6 @@ THE SOFTWARE. .span-name:hover > .endpoint-name { color: #000; - font-weight: bold; } .span-svc-name { diff --git a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js index 8904e7c3e0..eb766d6765 100644 --- a/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js +++ b/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.js @@ -164,9 +164,15 @@ class VirtualizedTraceView extends React.PureComponent - -
- ); - } +export default function TraceTimelineViewer(props: TraceTimelineViewerProps) { + const { timeRangeFilter: zoomRange, textFilter, trace } = props; + const { startTime, endTime } = trace || {}; + return ( +
+ +
+ ); } -TraceTimelineViewer.propTypes = { - trace: PropTypes.object, - timeRangeFilter: PropTypes.array, - textFilter: PropTypes.string, -}; diff --git a/src/components/TracePage/index.js b/src/components/TracePage/index.js index 59d76e585c..0d5f2c5a1e 100644 --- a/src/components/TracePage/index.js +++ b/src/components/TracePage/index.js @@ -138,10 +138,17 @@ export default class TracePage extends Component { } render() { - const { id, trace } = this.props; + const { id, loading, trace } = this.props; const { slimView, headerHeight } = this.state; if (!trace) { + if (loading) { + return ( +
+
+
+ ); + } return
; } diff --git a/src/components/TracePage/index.test.js b/src/components/TracePage/index.test.js index 737a4d93c4..09605d3a20 100644 --- a/src/components/TracePage/index.test.js +++ b/src/components/TracePage/index.test.js @@ -57,6 +57,12 @@ describe('', () => { expect(isEmpty).toBe(true); }); + it('renders a loading indicator when loading', () => { + wrapper = shallow(); + const loading = wrapper.find('.loader'); + expect(loading.length).toBe(1); + }); + // can't do mount tests in standard tape run. it('fetches the trace if necessary', () => { const fetchTrace = sinon.spy(); From ae4c69ee2d0d08b1ce98e3bc3a59ceaabd2cd40d Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Sun, 17 Sep 2017 06:54:28 -0700 Subject: [PATCH 5/6] Flow for remaining TraceTimelineViewer components --- .../TracePage/TraceTimelineViewer/SpanBar.js | 46 +++++++-------- .../TraceTimelineViewer/SpanBarRow.js | 59 ++++++++++--------- .../TraceTimelineViewer/SpanTreeOffset.js | 20 ++++--- .../TracePage/TraceTimelineViewer/Ticks.js | 6 +- .../TraceTimelineViewer/TimelineRow.js | 34 ++++++----- 5 files changed, 83 insertions(+), 82 deletions(-) diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBar.js b/src/components/TracePage/TraceTimelineViewer/SpanBar.js index 917e0bd4ff..4cd47544bb 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBar.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanBar.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,17 +20,32 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import PropTypes from 'prop-types'; import React from 'react'; import { onlyUpdateForKeys, compose, withState, withProps } from 'recompose'; import './SpanBar.css'; -function toPercent(value) { +type SpanBarProps = { + color: string, + hintSide: string, + label: string, + onClick: (SyntheticMouseEvent) => void, + viewEnd: number, + viewStart: number, + rpc: { + viewStart: number, + viewEnd: number, + color: string, + }, + setLongLabel: () => void, + setShortLabel: () => void, +}; + +function toPercent(value: number) { return `${value * 100}%`; } -function SpanBar(props) { +function SpanBar(props: SpanBarProps) { const { viewEnd, viewStart, color, label, hintSide, onClick, setLongLabel, setShortLabel, rpc } = props; return ( @@ -59,29 +76,6 @@ function SpanBar(props) { ); } -SpanBar.propTypes = { - color: PropTypes.string.isRequired, - hintSide: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - onClick: PropTypes.func, - viewEnd: PropTypes.number.isRequired, - viewStart: PropTypes.number.isRequired, - rpc: PropTypes.shape({ - viewStart: PropTypes.number, - viewEnd: PropTypes.number, - color: PropTypes.string, - }), - setLongLabel: PropTypes.func, - setShortLabel: PropTypes.func, -}; - -SpanBar.defaultProps = { - rpc: null, - onClick: null, - onMouseOver: null, - onMouseOut: null, -}; - export default compose( withState('label', 'setLabel', props => props.shortLabel), withProps(({ setLabel, shortLabel, longLabel }) => ({ diff --git a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js index 11690eb0e6..5ccae96224 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanBarRow.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -19,7 +21,6 @@ // THE SOFTWARE. import React from 'react'; -import PropTypes from 'prop-types'; import TimelineRow from './TimelineRow'; import SpanTreeOffset from './SpanTreeOffset'; @@ -28,7 +29,34 @@ import Ticks from './Ticks'; import './SpanBarRow.css'; -export default function SpanBarRow(props) { +type SpanBarRowProps = { + className: string, + color: string, + columnDivision: number, + depth: number, + isChildrenExpanded: boolean, + isDetailExapnded: boolean, + isFilteredOut: boolean, + isParent: boolean, + label: string, + onDetailToggled: () => void, + onChildrenToggled: () => void, + operationName: string, + numTicks: number, + rpc: ?{ + viewStart: number, + viewEnd: number, + color: string, + operationName: string, + serviceName: string, + }, + serviceName: string, + showErrorIcon: boolean, + viewEnd: number, + viewStart: number, +}; + +export default function SpanBarRow(props: SpanBarRowProps) { const { className, color, @@ -124,33 +152,6 @@ export default function SpanBarRow(props) { ); } -SpanBarRow.propTypes = { - className: PropTypes.string, - color: PropTypes.string.isRequired, - columnDivision: PropTypes.number.isRequired, - depth: PropTypes.number.isRequired, - isChildrenExpanded: PropTypes.bool.isRequired, - isDetailExapnded: PropTypes.bool.isRequired, - isFilteredOut: PropTypes.bool.isRequired, - isParent: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, - onDetailToggled: PropTypes.func.isRequired, - onChildrenToggled: PropTypes.func.isRequired, - operationName: PropTypes.string.isRequired, - numTicks: PropTypes.number.isRequired, - rpc: PropTypes.shape({ - viewStart: PropTypes.number, - viewEnd: PropTypes.number, - color: PropTypes.string, - operationName: PropTypes.string, - serviceName: PropTypes.string, - }), - serviceName: PropTypes.string.isRequired, - showErrorIcon: PropTypes.bool.isRequired, - viewEnd: PropTypes.number.isRequired, - viewStart: PropTypes.number.isRequired, -}; - SpanBarRow.defaultProps = { className: '', rpc: null, diff --git a/src/components/TracePage/TraceTimelineViewer/SpanTreeOffset.js b/src/components/TracePage/TraceTimelineViewer/SpanTreeOffset.js index e0593c0c86..0da6823eb1 100644 --- a/src/components/TracePage/TraceTimelineViewer/SpanTreeOffset.js +++ b/src/components/TracePage/TraceTimelineViewer/SpanTreeOffset.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,12 +20,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import PropTypes from 'prop-types'; import React from 'react'; import './SpanTreeOffset.css'; -export default function SpanTreeOffset({ level, hasChildren, childrenVisible, onClick }) { +type SpanTreeOffsetProps = { + level: number, + hasChildren: boolean, + childrenVisible: boolean, + onClick: ?() => void, +}; + +export default function SpanTreeOffset(props: SpanTreeOffsetProps) { + const { level, hasChildren, childrenVisible, onClick } = props; const className = hasChildren ? 'span-kids-toggle' : ''; const icon = hasChildren ? @@ -36,13 +45,6 @@ export default function SpanTreeOffset({ level, hasChildren, childrenVisible, on ); } -SpanTreeOffset.propTypes = { - level: PropTypes.number.isRequired, - hasChildren: PropTypes.bool, - childrenVisible: PropTypes.bool, - onClick: PropTypes.func, -}; - SpanTreeOffset.defaultProps = { hasChildren: false, childrenVisible: false, diff --git a/src/components/TracePage/TraceTimelineViewer/Ticks.js b/src/components/TracePage/TraceTimelineViewer/Ticks.js index 9b643c566d..8188a88f11 100644 --- a/src/components/TracePage/TraceTimelineViewer/Ticks.js +++ b/src/components/TracePage/TraceTimelineViewer/Ticks.js @@ -27,10 +27,10 @@ import { formatDuration } from './utils'; import './Ticks.css'; type TicksProps = { - endTime: number, + endTime?: number, numTicks: number, showLabels?: boolean, - startTime: number, + startTime?: ?number, }; export default function Ticks(props: TicksProps) { @@ -39,7 +39,7 @@ export default function Ticks(props: TicksProps) { let labels: string[]; if (showLabels) { labels = []; - const viewingDuration = endTime - startTime; + const viewingDuration = (endTime || 0) - (startTime || 0); for (let i = 0; i < numTicks; i++) { const durationAtTick = startTime + i / (numTicks - 1) * viewingDuration; labels.push(formatDuration(durationAtTick)); diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineRow.js b/src/components/TracePage/TraceTimelineViewer/TimelineRow.js index b90dbb9dd4..80519f11ea 100644 --- a/src/components/TracePage/TraceTimelineViewer/TimelineRow.js +++ b/src/components/TracePage/TraceTimelineViewer/TimelineRow.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,23 +20,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import './TimelineRow.css'; -const propTypes = { - children: PropTypes.node, - className: PropTypes.string, - style: PropTypes.object, +type TimelineRowProps = { + children?: React.Node, + className: string, }; -const defaultProps = { - children: null, - className: '', +type TimelineRowCellProps = { + children?: React.Node, + className: string, + width: number, + style?: Object, }; -export default function TimelineRow(props) { +export default function TimelineRow(props: TimelineRowProps) { const { children, className, ...rest } = props; return (
@@ -42,10 +44,12 @@ export default function TimelineRow(props) {
); } -TimelineRow.propTypes = { ...propTypes }; -TimelineRow.defaultProps = { ...defaultProps }; -function TimelineRowCell(props) { +TimelineRow.defaultProps = { + className: '', +}; + +function TimelineRowCell(props: TimelineRowCellProps) { const { children, className, width, style, ...rest } = props; const widthPercent = `${width * 100}%`; const mergedStyle = { ...style, flexBasis: widthPercent, maxWidth: widthPercent }; @@ -55,7 +59,7 @@ function TimelineRowCell(props) {
); } -TimelineRowCell.propTypes = { ...propTypes, width: PropTypes.number.isRequired }; -TimelineRowCell.defaultProps = { ...defaultProps }; + +TimelineRowCell.defaultProps = { className: '' }; TimelineRow.Cell = TimelineRowCell; From b83deddb04b6458a45c4a7f5f78ccf796d43b6d6 Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Mon, 18 Sep 2017 22:43:27 -0400 Subject: [PATCH 6/6] Use lodash get to simplify property access --- .../TraceTimelineViewer/TimelineHeaderRow.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js index 93f568bd41..3fe0e5a013 100644 --- a/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js +++ b/src/components/TracePage/TraceTimelineViewer/TimelineHeaderRow.js @@ -23,6 +23,7 @@ import * as React from 'react'; import cx from 'classnames'; import _clamp from 'lodash/clamp'; +import _get from 'lodash/get'; import Ticks from './Ticks'; import TimelineRow from './TimelineRow'; @@ -108,8 +109,9 @@ class TimelineColumnResizer extends React.PureComponent< window.addEventListener('mousemove', this._onWindowMouseMove); window.addEventListener('mouseup', this._onWindowMouseUp); this._isDragging = true; - if (document && document.body && document.body.style) { - (document.body.style: any).userSelect = 'none'; + const style = _get(document, 'body.style'); + if (style) { + (style: any).userSelect = 'none'; } }; @@ -121,8 +123,9 @@ class TimelineColumnResizer extends React.PureComponent< _onWindowMouseUp = function _onWindowMouseUp({ clientX }) { window.removeEventListener('mousemove', this._onWindowMouseMove); window.removeEventListener('mouseup', this._onWindowMouseUp); - if (document && document.body && document.body.style) { - (document.body.style: any).userSelect = undefined; + const style = _get(document, 'body.style'); + if (style) { + (style: any).userSelect = undefined; } this._isDragging = false; const dragPosition = this._getDraggedPosition(clientX);