@samitouri / QOS-React-2 / commits / cb88572227

Improve DevTools Profiler commit-selector UX (#20943)

* Improve DevTools Profiler commit-selector UX 1. Use natural log of durations (rather than linear) when calculating bar height. This reduces the impact of one (or few) outlier times on more common smaller durations. (Continue to use linear for bar color though.) 2. Decrease the minimum bar height to make the differences in height more noticeable. 3. Add a background hover highlight to increase contrast. 4. Add hover tooltip with commit duration and timestamp.

Brian Vaughn committed Mar 8, 2021 at 11:09 UTC cb8857222789136f99b56c8109863fdee7cab691
5 files changed +60 -19
packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotCommitList.js
+36 -13
@@ -13,6 +13,8 @@ import AutoSizer from 'react-virtualized-auto-sizer';
13 import {FixedSizeList} from 'react-window';
14 import SnapshotCommitListItem from './SnapshotCommitListItem';
15 import {minBarWidth} from './constants';
16 +import {formatDuration, formatTime} from './utils';
17 +import Tooltip from './Tooltip';
18
19 import styles from './SnapshotCommitList.css';
20
@@ -24,6 +26,7 @@ export type ItemData = {|
26 selectedCommitIndex: number | null,
27 selectedFilteredCommitIndex: number | null,
28 selectCommitIndex: (index: number) => void,
29 + setHoveredCommitIndex: (index: number) => void,
30 startCommitDrag: (newDragState: DragState) => void,
31 |};
32
@@ -166,6 +169,10 @@ function List({
169 }
170 }, [dragState]);
171
172 + const [hoveredCommitIndex, setHoveredCommitIndex] = useState<number | null>(
173 + null,
174 + );
175 +
176 // Pass required contextual data down to the ListItem renderer.
177 const itemData = useMemo<ItemData>(
178 () => ({
@@ -176,6 +183,7 @@ function List({
183 selectedCommitIndex,
184 selectedFilteredCommitIndex,
185 selectCommitIndex,
186 + setHoveredCommitIndex,
187 startCommitDrag: setDragState,
188 }),
189 [
@@ -186,22 +194,37 @@ function List({
194 selectedCommitIndex,
195 selectedFilteredCommitIndex,
196 selectCommitIndex,
197 + setHoveredCommitIndex,
198 ],
199 );
200
201 + let tooltipLabel = null;
202 + if (hoveredCommitIndex !== null) {
203 + const commitDuration = commitDurations[hoveredCommitIndex];
204 + const commitTime = commitTimes[hoveredCommitIndex];
205 + tooltipLabel = `${formatDuration(commitDuration)}ms at ${formatTime(
206 + commitTime,
207 + )}s`;
208 + }
209 +
210 return (
193 - <div ref={divRef} style={{height, width}}>
194 - <FixedSizeList
195 - className={styles.List}
196 - layout="horizontal"
197 - height={height}
198 - itemCount={filteredCommitIndices.length}
199 - itemData={itemData}
200 - itemSize={itemSize}
201 - ref={(listRef: any) /* Flow bug? */}
202 - width={width}>
203 - {SnapshotCommitListItem}
204 - </FixedSizeList>
205 - </div>
211 + <Tooltip label={tooltipLabel}>
212 + <div
213 + ref={divRef}
214 + style={{height, width}}
215 + onMouseLeave={() => setHoveredCommitIndex(null)}>
216 + <FixedSizeList
217 + className={styles.List}
218 + layout="horizontal"
219 + height={height}
220 + itemCount={filteredCommitIndices.length}
221 + itemData={itemData}
222 + itemSize={itemSize}
223 + ref={(listRef: any) /* Flow bug? */}
224 + width={width}>
225 + {SnapshotCommitListItem}
226 + </FixedSizeList>
227 + </div>
228 + </Tooltip>
229 );
230 }
packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotCommitListItem.css
+4 -1
@@ -7,10 +7,13 @@
7 display: flex;
8 align-items: flex-end;
9 }
10 +.Outer:hover {
11 + background-color: var(--color-background);
12 +}
13
14 .Inner {
15 width: 100%;
13 - min-height: 5px;
16 + min-height: 2px;
17 background-color: var(--color-commit-did-not-render-fill);
18 color: var(--color-commit-did-not-render-fill-text);
19 }
packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotCommitListItem.js
+18 -4
@@ -31,6 +31,7 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
31 maxDuration,
32 selectedCommitIndex,
33 selectCommitIndex,
34 + setHoveredCommitIndex,
35 startCommitDrag,
36 } = itemData;
37
@@ -39,9 +40,21 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
40 const commitDuration = commitDurations[index];
41 const commitTime = commitTimes[index];
42
42 - // Guard against commits with duration 0
43 - const percentage =
43 + // Use natural log for bar height.
44 + // This prevents one (or a few) outliers from squishing the majority of other commits.
45 + // So rather than e.g. _█_ we get something more like e.g. ▄█_
46 + const heightScale =
47 + Math.min(
48 + 1,
49 + Math.max(0, Math.log(commitDuration) / Math.log(maxDuration)),
50 + ) || 0;
51 +
52 + // Use a linear scale for color.
53 + // This gives some visual contrast between cheaper and more expensive commits
54 + // and somewhat compensates for the log scale height.
55 + const colorScale =
56 Math.min(1, Math.max(0, commitDuration / maxDuration)) || 0;
57 +
58 const isSelected = selectedCommitIndex === index;
59
60 // Leave a 1px gap between snapshots
@@ -62,6 +75,7 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
75 <div
76 className={styles.Outer}
77 onMouseDown={handleMouseDown}
78 + onMouseEnter={() => setHoveredCommitIndex(index)}
79 style={{
80 ...style,
81 width,
@@ -75,9 +89,9 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
89 <div
90 className={styles.Inner}
91 style={{
78 - height: `${Math.round(percentage * 100)}%`,
92 + height: `${Math.round(heightScale * 100)}%`,
93 backgroundColor:
80 - percentage > 0 ? getGradientColor(percentage) : undefined,
94 + commitDuration > 0 ? getGradientColor(colorScale) : undefined,
95 }}
96 />
97 </div>
packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotSelector.css
+1 -1
@@ -7,7 +7,7 @@
7 height: 100%;
8 min-width: 30px;
9 margin-left: 0.25rem;
10 - overflow: hidden;
10 + overflow: visible;
11 }
12 .Commits:focus {
13 outline: none;
packages/react-devtools-shared/src/devtools/views/Profiler/Tooltip.css
+1
@@ -9,6 +9,7 @@
9 background-color: var(--color-tooltip-background);
10 color: var(--color-tooltip-text);
11 opacity: 1;
12 + white-space: nowrap;
13 /* Make sure this is above the DevTools, which are above the Overlay */
14 z-index: 10000002;
15 }