Skip to content

Commit d69772d

Browse files
authored
Merge pull request #1517 from github/koesie10/variant-analysis-stats
Add variant analysis stats component
2 parents 2fd5f38 + 7b1a93d commit d69772d

24 files changed

Lines changed: 578 additions & 22 deletions

.vscode/launch.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
"runtimeArgs": [
3636
"--inspect=9229"
3737
],
38+
"env": {
39+
"LANG": "en-US"
40+
},
3841
"args": [
3942
"--exit",
4043
"-u",
@@ -43,6 +46,8 @@
4346
"--diff",
4447
"-r",
4548
"ts-node/register",
49+
"-r",
50+
"test/mocha.setup.js",
4651
"test/pure-tests/**/*.ts"
4752
],
4853
"stopOnEntry": false,

extensions/ql-vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@
11921192
"watch:extension": "tsc --watch",
11931193
"watch:webpack": "gulp watchView",
11941194
"test": "npm-run-all -p test:*",
1195-
"test:unit": "mocha --exit -r ts-node/register test/pure-tests/**/*.ts",
1195+
"test:unit": "mocha --exit -r ts-node/register -r test/mocha.setup.js test/pure-tests/**/*.ts",
11961196
"test:view": "jest",
11971197
"preintegration": "rm -rf ./out/vscode-tests && gulp",
11981198
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Contains an assortment of helper constants and functions for working with dates.
3+
*/
4+
5+
const dateWithoutYearFormatter = new Intl.DateTimeFormat(undefined, {
6+
month: 'short',
7+
day: 'numeric',
8+
hour: 'numeric',
9+
minute: '2-digit',
10+
});
11+
12+
const dateFormatter = new Intl.DateTimeFormat(undefined, {
13+
year: 'numeric',
14+
month: 'short',
15+
day: 'numeric',
16+
hour: 'numeric',
17+
minute: '2-digit',
18+
});
19+
20+
export function formatDate(value: Date): string {
21+
if (value.getFullYear() === new Date().getFullYear()) {
22+
return dateWithoutYearFormatter.format(value);
23+
}
24+
25+
return dateFormatter.format(value);
26+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Contains an assortment of helper constants and functions for working with numbers.
3+
*/
4+
5+
const numberFormatter = new Intl.NumberFormat('en-US');
6+
7+
/**
8+
* Formats a number to be human-readable with decimal places and thousands separators.
9+
*
10+
* @param value The number to format.
11+
* @returns The formatted number. For example, "10,000", "1,000,000", or "1,000,000,000".
12+
*/
13+
export function formatDecimal(value: number): string {
14+
return numberFormatter.format(value);
15+
}

extensions/ql-vscode/src/pure/time.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
* Contains an assortment of helper constants and functions for working with time, dates, and durations.
33
*/
44

5-
export const ONE_MINUTE_IN_MS = 1000 * 60;
5+
export const ONE_SECOND_IN_MS = 1000;
6+
export const ONE_MINUTE_IN_MS = ONE_SECOND_IN_MS * 60;
67
export const ONE_HOUR_IN_MS = ONE_MINUTE_IN_MS * 60;
78
export const TWO_HOURS_IN_MS = ONE_HOUR_IN_MS * 2;
89
export const THREE_HOURS_IN_MS = ONE_HOUR_IN_MS * 3;
@@ -43,20 +44,23 @@ export function humanizeRelativeTime(relativeTimeMillis?: number) {
4344

4445
/**
4546
* Converts a number of milliseconds into a human-readable string with units, indicating an amount of time.
46-
* Negative numbers have no meaning and are considered to be "Less than a minute".
47+
* Negative numbers have no meaning and are considered to be "Less than a second".
4748
*
4849
* @param millis The number of milliseconds to convert.
49-
* @returns A humanized duration. For example, "2 minutes", "2 hours", "2 days", or "2 months".
50+
* @returns A humanized duration. For example, "2 seconds", "2 minutes", "2 hours", "2 days", or "2 months".
5051
*/
5152
export function humanizeUnit(millis?: number): string {
5253
// assume a blank or empty string is a zero
5354
// assume anything less than 0 is a zero
54-
if (!millis || millis < ONE_MINUTE_IN_MS) {
55-
return 'Less than a minute';
55+
if (!millis || millis < ONE_SECOND_IN_MS) {
56+
return 'Less than a second';
5657
}
5758
let unit: string;
5859
let unitDiff: number;
59-
if (millis < ONE_HOUR_IN_MS) {
60+
if (millis < ONE_MINUTE_IN_MS) {
61+
unit = 'second';
62+
unitDiff = Math.floor(millis / ONE_SECOND_IN_MS);
63+
} else if (millis < ONE_HOUR_IN_MS) {
6064
unit = 'minute';
6165
unitDiff = Math.floor(millis / ONE_MINUTE_IN_MS);
6266
} else if (millis < ONE_DAY_IN_MS) {

extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisHeader.stories.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ export default {
4747
disable: true,
4848
},
4949
},
50+
onViewLogsClick: {
51+
action: 'view-logs-clicked',
52+
table: {
53+
disable: true,
54+
}
55+
},
5056
}
5157
} as ComponentMeta<typeof VariantAnalysisHeader>;
5258

@@ -59,16 +65,25 @@ InProgress.args = {
5965
queryName: 'Query name',
6066
queryFileName: 'example.ql',
6167
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
68+
totalRepositoryCount: 10,
69+
completedRepositoryCount: 2,
70+
resultCount: 99_999,
6271
};
6372

6473
export const Succeeded = Template.bind({});
6574
Succeeded.args = {
6675
...InProgress.args,
6776
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
77+
totalRepositoryCount: 1000,
78+
completedRepositoryCount: 1000,
79+
duration: 720_000,
80+
completedAt: new Date(1661263446000),
6881
};
6982

7083
export const Failed = Template.bind({});
7184
Failed.args = {
7285
...InProgress.args,
7386
variantAnalysisStatus: VariantAnalysisStatus.Failed,
87+
duration: 10_000,
88+
completedAt: new Date(1661263446000),
7489
};
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from 'react';
2+
3+
import { ComponentStory, ComponentMeta } from '@storybook/react';
4+
5+
import { VariantAnalysisContainer } from '../../view/variant-analysis/VariantAnalysisContainer';
6+
import { VariantAnalysisStats } from '../../view/variant-analysis/VariantAnalysisStats';
7+
import { VariantAnalysisStatus } from '../../remote-queries/shared/variant-analysis';
8+
9+
export default {
10+
title: 'Variant Analysis/Variant Analysis Stats',
11+
component: VariantAnalysisStats,
12+
decorators: [
13+
(Story) => (
14+
<VariantAnalysisContainer>
15+
<Story />
16+
</VariantAnalysisContainer>
17+
)
18+
],
19+
argTypes: {
20+
onViewLogsClick: {
21+
action: 'view-logs-clicked',
22+
table: {
23+
disable: true,
24+
},
25+
},
26+
}
27+
} as ComponentMeta<typeof VariantAnalysisStats>;
28+
29+
const Template: ComponentStory<typeof VariantAnalysisStats> = (args) => (
30+
<VariantAnalysisStats {...args} />
31+
);
32+
33+
export const Starting = Template.bind({});
34+
Starting.args = {
35+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
36+
totalRepositoryCount: 10,
37+
};
38+
39+
export const Started = Template.bind({});
40+
Started.args = {
41+
...Starting.args,
42+
resultCount: 99_999,
43+
completedRepositoryCount: 2,
44+
};
45+
46+
export const StartedWithWarnings = Template.bind({});
47+
StartedWithWarnings.args = {
48+
...Starting.args,
49+
queryResult: 'warning',
50+
};
51+
52+
export const Succeeded = Template.bind({});
53+
Succeeded.args = {
54+
...Started.args,
55+
totalRepositoryCount: 1000,
56+
completedRepositoryCount: 1000,
57+
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
58+
duration: 720_000,
59+
completedAt: new Date(1661263446000),
60+
};
61+
62+
export const SucceededWithWarnings = Template.bind({});
63+
SucceededWithWarnings.args = {
64+
...Succeeded.args,
65+
totalRepositoryCount: 10,
66+
completedRepositoryCount: 2,
67+
queryResult: 'warning',
68+
};
69+
70+
export const Failed = Template.bind({});
71+
Failed.args = {
72+
...Starting.args,
73+
variantAnalysisStatus: VariantAnalysisStatus.Failed,
74+
duration: 10_000,
75+
completedAt: new Date(1661263446000),
76+
};
77+
78+
export const Stopped = Template.bind({});
79+
Stopped.args = {
80+
...SucceededWithWarnings.args,
81+
queryResult: 'stopped',
82+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as React from 'react';
2+
import styled from 'styled-components';
3+
import classNames from 'classnames';
4+
5+
type Props = {
6+
name: string;
7+
label: string;
8+
className?: string;
9+
};
10+
11+
const CodiconIcon = styled.span`
12+
vertical-align: text-bottom;
13+
`;
14+
15+
export const Codicon = ({
16+
name,
17+
label,
18+
className
19+
}: Props) => <CodiconIcon role="img" aria-label={label} className={classNames('codicon', `codicon-${name}`, className)} />;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as React from 'react';
2+
import styled from 'styled-components';
3+
import { Codicon } from './Codicon';
4+
5+
const Icon = styled(Codicon)`
6+
color: var(--vscode-problemsErrorIcon-foreground);
7+
`;
8+
9+
type Props = {
10+
label?: string;
11+
className?: string;
12+
}
13+
14+
export const ErrorIcon = ({
15+
label = 'Error',
16+
className,
17+
}: Props) => <Icon name="error" label={label} className={className} />;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as React from 'react';
2+
import styled from 'styled-components';
3+
import { Codicon } from './Codicon';
4+
5+
const Icon = styled(Codicon)`
6+
color: var(--vscode-testing-iconPassed);
7+
`;
8+
9+
type Props = {
10+
label?: string;
11+
className?: string;
12+
}
13+
14+
export const SuccessIcon = ({
15+
label = 'Success',
16+
className,
17+
}: Props) => <Icon name="pass" label={label} className={className} />;

0 commit comments

Comments
 (0)