Skip to content

Commit a6a1fbc

Browse files
chore(compass-explain-plan, compass-components): explain plan visual improvements follow up (#4511)
Co-authored-by: Sergey Petushkov <[email protected]>
1 parent 32645da commit a6a1fbc

File tree

4 files changed

+220
-64
lines changed

4 files changed

+220
-64
lines changed

packages/compass-editor/src/json-editor.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,10 @@ const multilineEditorContainerStyle = css({
11481148
},
11491149
});
11501150

1151+
const multilineEditorContainerWithActionsStyle = css({
1152+
minHeight: spacing[5] - 2,
1153+
});
1154+
11511155
const multilineEditorContainerDarkModeStyle = css({
11521156
backgroundColor: editorPalette.dark.backgroundColor,
11531157
});
@@ -1274,6 +1278,7 @@ const MultilineEditor = React.forwardRef<EditorRef, MultilineEditorProps>(
12741278
className={cx(
12751279
multilineEditorContainerStyle,
12761280
darkMode && multilineEditorContainerDarkModeStyle,
1281+
!!actions.length && multilineEditorContainerWithActionsStyle,
12771282
className
12781283
)}
12791284
// We want folks to be able to click into the container element

packages/compass-explain-plan/src/components/explain-tree/clock.tsx

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { palette, css, cx, useDarkMode } from '@mongodb-js/compass-components';
22
import d3 from 'd3';
33
import React, { useEffect, useMemo, useRef } from 'react';
4+
import { milliSecondsToNormalisedValue } from './explain-tree-stage';
45

56
const lightModeColors = {
67
clockBackgroundColor: palette.white,
@@ -199,31 +200,30 @@ function drawClockFace({
199200
});
200201
}
201202

202-
type ClockProps = {
203+
export type ClockProps = {
203204
totalExecTimeMS: number;
204205
curStageExecTimeMS: number;
205206
prevStageExecTimeMS: number;
206-
width: number;
207-
height: number;
208-
strokeWidth: number;
209207
className?: string;
210208
};
211209

210+
const CLOCK_WIDTH = 50;
211+
const CLOCK_HEIGHT = 50;
212+
const ARC_STROKE_WIDTH = 5;
213+
212214
const Clock: React.FunctionComponent<ClockProps> = ({
213215
totalExecTimeMS,
214216
curStageExecTimeMS,
215217
prevStageExecTimeMS,
216218
className,
217-
width,
218-
height,
219-
strokeWidth,
220219
}) => {
221220
const svgRef = useRef<SVGSVGElement>(null);
222221

223-
const deltaExecTime = useMemo(
224-
() => curStageExecTimeMS - prevStageExecTimeMS,
225-
[curStageExecTimeMS, prevStageExecTimeMS]
226-
);
222+
const { value: normalisedDeltaExecTime, unit: normalisedDeltaExecTimeUnit } =
223+
useMemo(() => {
224+
const deltaExecTime = curStageExecTimeMS - prevStageExecTimeMS;
225+
return milliSecondsToNormalisedValue(deltaExecTime);
226+
}, [curStageExecTimeMS, prevStageExecTimeMS]);
227227

228228
const darkmode = useDarkMode();
229229

@@ -248,17 +248,17 @@ const Clock: React.FunctionComponent<ClockProps> = ({
248248
};
249249

250250
drawClockFace({
251-
width: width,
252-
height: height,
251+
width: CLOCK_WIDTH,
252+
height: CLOCK_HEIGHT,
253253
strokeColor: clockFaceColor,
254254
fillColor: clockBackgroundColor,
255255
svgElement,
256256
});
257257

258258
drawElapsedTimes({
259-
width: width,
260-
height: height,
261-
strokeWidth,
259+
width: CLOCK_WIDTH,
260+
height: CLOCK_HEIGHT,
261+
strokeWidth: ARC_STROKE_WIDTH,
262262
svgElement,
263263
curStageExecTimeMS,
264264
prevStageExecTimeMS,
@@ -274,9 +274,6 @@ const Clock: React.FunctionComponent<ClockProps> = ({
274274
curStageExecTimeMS,
275275
prevStageExecTimeMS,
276276
totalExecTimeMS,
277-
width,
278-
height,
279-
strokeWidth,
280277
clockBackgroundColor,
281278
clockFaceColor,
282279
previousElapsedArcColor,
@@ -290,12 +287,15 @@ const Clock: React.FunctionComponent<ClockProps> = ({
290287
containerStyles,
291288
darkmode ? containerStylesDarkMode : containerStylesLightMode
292289
)}
293-
style={{ width, height }}
290+
style={{ width: CLOCK_WIDTH, height: CLOCK_HEIGHT }}
294291
>
295-
<div className={faceContainerStyle} style={{ width, height }}>
292+
<div
293+
className={faceContainerStyle}
294+
style={{ width: CLOCK_WIDTH, height: CLOCK_HEIGHT }}
295+
>
296296
<svg
297-
width={width}
298-
height={height}
297+
width={CLOCK_WIDTH}
298+
height={CLOCK_HEIGHT}
299299
ref={svgRef}
300300
className={svgStyles}
301301
// Our svg clock has additional arcs that expands outside of the clock
@@ -305,16 +305,19 @@ const Clock: React.FunctionComponent<ClockProps> = ({
305305
// to exact center
306306
viewBox="-3 -3 56 56"
307307
></svg>
308-
<div className={executionTimeStyles} style={{ width, height }}>
308+
<div
309+
className={executionTimeStyles}
310+
style={{ width: CLOCK_WIDTH, height: CLOCK_HEIGHT }}
311+
>
309312
<span
310313
className={cx(
311314
msecsStyles,
312315
darkmode ? msecsStylesDarkMode : msecsStylesLightMode
313316
)}
314317
>
315-
{deltaExecTime}
318+
{normalisedDeltaExecTime}
316319
</span>
317-
<span className={msStyles}>ms</span>
320+
<span className={msStyles}>{normalisedDeltaExecTimeUnit}</span>
318321
</div>
319322
</div>
320323
</div>

packages/compass-explain-plan/src/components/explain-tree/explain-tree-stage.spec.tsx

Lines changed: 120 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,128 @@ import React from 'react';
22
import { render, cleanup } from '@testing-library/react';
33
import { expect } from 'chai';
44

5-
import { ExplainTreeStage } from './explain-tree-stage';
6-
7-
describe('ExplainStage [Component]', function () {
8-
let component: ReturnType<typeof render>;
9-
const name = '';
10-
const nReturned = 1;
11-
const highlights = {};
12-
const curStageExecTimeMS = 2;
13-
const prevStageExecTimeMS = 1;
14-
const totalExecTimeMS = 3;
15-
const isShard = false;
16-
const details = {};
17-
18-
beforeEach(function () {
19-
component = render(
20-
<ExplainTreeStage
21-
name={name}
22-
nReturned={nReturned}
23-
highlights={highlights}
24-
curStageExecTimeMS={curStageExecTimeMS}
25-
prevStageExecTimeMS={prevStageExecTimeMS}
26-
totalExecTimeMS={totalExecTimeMS}
27-
isShard={isShard}
28-
details={details}
29-
onToggleDetailsClick={() => {}}
30-
detailsOpen={false}
31-
/>
32-
);
5+
import {
6+
ExplainTreeStage,
7+
milliSecondsToNormalisedValue,
8+
trimInMiddle,
9+
} from './explain-tree-stage';
10+
11+
describe('ExplainTreeStage', function () {
12+
describe('ExplainStage [Component]', function () {
13+
let component: ReturnType<typeof render>;
14+
const name = '';
15+
const nReturned = 1;
16+
const highlights = {};
17+
const curStageExecTimeMS = 2;
18+
const prevStageExecTimeMS = 1;
19+
const totalExecTimeMS = 3;
20+
const isShard = false;
21+
const details = {};
22+
23+
beforeEach(function () {
24+
component = render(
25+
<ExplainTreeStage
26+
name={name}
27+
nReturned={nReturned}
28+
highlights={highlights}
29+
curStageExecTimeMS={curStageExecTimeMS}
30+
prevStageExecTimeMS={prevStageExecTimeMS}
31+
totalExecTimeMS={totalExecTimeMS}
32+
isShard={isShard}
33+
details={details}
34+
onToggleDetailsClick={() => {}}
35+
detailsOpen={false}
36+
/>
37+
);
38+
});
39+
40+
afterEach(cleanup);
41+
42+
it('renders', function () {
43+
expect(component.getByTestId('explain-stage')).to.exist;
44+
});
3345
});
3446

35-
afterEach(cleanup);
47+
describe('Helpers', function () {
48+
describe('trimInMiddle', function () {
49+
it('will not trim the text if the text length is less than provided threshold', function () {
50+
expect(trimInMiddle('Compass', 10)).to.equal('Compass');
51+
});
52+
53+
context(
54+
'when text length is more than the provided threshold',
55+
function () {
56+
it('will trim text and keep only the specified number of chars in front and in back', function () {
57+
const trimmedValue = trimInMiddle(
58+
'Devtools Product - Compass',
59+
10,
60+
5,
61+
5
62+
);
63+
expect(trimmedValue).to.equal('Devto…mpass');
64+
});
65+
66+
it('will trim the text, while making sure that the chars that are already at front do not come at the back of ellipsis', function () {
67+
const trimmedValue = trimInMiddle('Devtools', 5, 5, 5);
68+
expect(trimmedValue).to.equal('Devto…ols');
69+
});
70+
}
71+
);
72+
});
73+
74+
describe('milliSecondsToNormalisedValue', function () {
75+
it('returns the ms values itself when the value is less than a second', function () {
76+
expect(milliSecondsToNormalisedValue(975)).to.deep.equal({
77+
value: '975',
78+
unit: 'ms',
79+
});
80+
});
81+
82+
it('correctly normalises a seconds equivalent value', function () {
83+
expect(milliSecondsToNormalisedValue(1000)).to.deep.equal({
84+
value: '1',
85+
unit: 's',
86+
});
87+
88+
expect(milliSecondsToNormalisedValue(1500)).to.deep.equal({
89+
value: '1.5',
90+
unit: 's',
91+
});
92+
});
93+
94+
it('correctly normalises a minutes equivalent value', function () {
95+
expect(milliSecondsToNormalisedValue(60 * 1000)).to.deep.equal({
96+
value: '1',
97+
unit: 'min',
98+
});
99+
100+
expect(milliSecondsToNormalisedValue(90 * 1000)).to.deep.equal({
101+
value: '1.5',
102+
unit: 'min',
103+
});
104+
105+
// Below cases are for rounded values
106+
expect(milliSecondsToNormalisedValue(134 * 1000)).to.deep.equal({
107+
value: '2.2',
108+
unit: 'min',
109+
});
110+
111+
expect(milliSecondsToNormalisedValue(135 * 1000)).to.deep.equal({
112+
value: '2.3',
113+
unit: 'min',
114+
});
115+
});
116+
117+
it('correctly normalises an hour equivalent value', function () {
118+
expect(milliSecondsToNormalisedValue(60 * 60 * 1000)).to.deep.equal({
119+
value: '1',
120+
unit: 'h',
121+
});
36122

37-
it('renders', function () {
38-
expect(component.getByTestId('explain-stage')).to.exist;
123+
expect(
124+
milliSecondsToNormalisedValue(1.5 * 60 * 60 * 1000)
125+
).to.deep.equal({ value: '1.5', unit: 'h' });
126+
});
127+
});
39128
});
40129
});

0 commit comments

Comments
 (0)