Skip to content
This repository has been archived by the owner on Nov 10, 2022. It is now read-only.

Commit

Permalink
clean up webviews
Browse files Browse the repository at this point in the history
  • Loading branch information
jodyheavener committed Oct 11, 2020
1 parent f2c7811 commit f0106d2
Show file tree
Hide file tree
Showing 33 changed files with 2,307 additions and 397 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"vscode-nls": "^5.0.0"
},
"devDependencies": {
"@svgr/webpack": "^5.4.0",
"@types/follow-redirects": "^1.13.0",
"@types/node": "^14.11.2",
"@types/react": "^16.9.50",
Expand Down
15 changes: 14 additions & 1 deletion src/assets/dark/status-dots-blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion src/assets/dark/status-dots-grey.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,13 @@ export type Asset = {
};

export type PostMessagePayload = { event: string; data?: any };

export type JobTestDetails = {
jobName: string;
jobNumber: number;
pipelineNumber: number;
workflowId: string;
vcs: string;
user: string;
repo: string;
};
4 changes: 3 additions & 1 deletion src/views/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export default class Workflow extends ResourcesItem {
: l('retryingFailedJobs', 'Retrying failed Workflow Jobs')
);
// Retry adds *new* jobs, so reload the whole pipeline
setTimeout(this.pipeline.reload.bind(this), 1000);
setTimeout(() => {
this.pipeline.reload();
}, 1000);
}

private setContextValue(): void {
Expand Down
16 changes: 16 additions & 0 deletions src/webviews/assets/components/CTAButton/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.cta-button {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
padding: 6px 15px;
outline: none;
display: inline-block;
text-decoration: none;
line-height: 1.3rem;

&:hover,
&:focus {
background-color: var(--vscode-button-hoverBackground);
color: var(--vscode-button-foreground);
}
}
28 changes: 28 additions & 0 deletions src/webviews/assets/components/CTAButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import './index.scss';

const CTAButton = ({
text,
href,
onClick
}: {
text: string;
href?: string;
onClick?: () => void;
}): JSX.Element => {
if (href) {
return (
<a className="cta-button" {...{ href }}>
{text}
</a>
);
} else {
return (
<button className="cta-button" {...{ onClick }}>
{text}
</button>
);
}
};

export default CTAButton;
8 changes: 8 additions & 0 deletions src/webviews/assets/components/JobTests/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
body {
padding: 10px 20px;
line-height: 22px;
max-width: 882px;
margin: 0 auto;
min-height: 100vh;
box-sizing: border-box;
}
81 changes: 81 additions & 0 deletions src/webviews/assets/components/JobTests/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useEffect, useState } from 'react';
import { JobTest } from 'circle-client';
import constants from '../../../../lib/constants';
import { JobTestDetails, PostMessagePayload } from '../../../../lib/types';
import TestsHeader from '../TestsHeader';
import TestResults from '../TestResults';
import Loading from '../Loading';
import './index.scss';

const JobTests = ({ vscode, rootPath }: { vscode: any; rootPath: string }): JSX.Element => {
const [jobDetails, setJobDetails] = useState<JobTestDetails | null>(null);
const [initLoaded, setInitLoaded] = useState<boolean>(false);
const [initHasTests, setInitHasTests] = useState<boolean>(false);
const [tests, setTests] = useState<JobTest[]>([]);
const [hasMore, setHasMore] = useState<boolean>(true);
const [query, setQuery] = useState<string | null>(null);
const [sortBy, setSortBy] = useState<'status' | 'alphabetically' | 'duration'>('status');

useEffect(() => {
if (!jobDetails) {
window.addEventListener(
'message',
({ data }: { data: PostMessagePayload }) => {
switch (data.event) {
case constants.TEST_DATA_WEBVIEW_EVENT:
if (!initLoaded) {
setInitLoaded(true);
setInitHasTests(!!(data.data.tests.length));
}

setTests((existing) => existing.concat(data.data.tests));
setHasMore(data.data.hasMore);
break;
case constants.JOB_DATA_WEBVIEW_EVENT:
setJobDetails(data.data as JobTestDetails);
break;
}
}
);

vscode.postMessage({
event: constants.REQUEST_JOB_WEBVIEW_EVENT,
});
}
}, [jobDetails]);

let filteredTests = tests;
if (query) {
// filter anything test-related against the query
filteredTests = tests.filter((test) =>
Object.values(test).some(
(value) => typeof value === 'string' && value.includes(query)
)
);
}

if (sortBy === 'status') {
filteredTests.sort((a, b) => a.result.localeCompare(b.result))
}

if (sortBy === 'alphabetically') {
filteredTests.sort((a, b) => a.classname.localeCompare(b.classname))
}

if (sortBy === 'duration') {
filteredTests.sort((a, b) => { return b.run_time - a.run_time })
}

if (!jobDetails) {
return <Loading />;
}

return (
<>
<TestsHeader {...{ jobDetails, query, setQuery, hasTests: initHasTests, sortBy, setSortBy }} />
<TestResults {...{ vscode, query, setQuery, tests: filteredTests, hasMore, loaded: initLoaded, hasTests: initHasTests } }/>
</>
);
};

export default JobTests;
15 changes: 15 additions & 0 deletions src/webviews/assets/components/Loading/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.loading-container {
text-align: center;
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);

svg {
display: inline-block;
}

p {
opacity: 0.7;
}
}
15 changes: 15 additions & 0 deletions src/webviews/assets/components/Loading/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import CircleLogo from './logo.svg';
import { l } from '../../lib/utils';
import './index.scss';

const Loading = (): JSX.Element => {
return (
<div className="loading-container">
<CircleLogo />
<p>{l('loadingLabel', 'Loading...')}</p>
</div>
);
};

export default Loading;
7 changes: 7 additions & 0 deletions src/webviews/assets/components/Loading/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/webviews/assets/components/NoTests/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { l } from '../../lib/utils';
import CTAButton from '../CTAButton';

const NoTests = (): JSX.Element => {
return (
<div className="no-tests">
<h3>{l('noTestMetadataTitle', `This Job doesn't have any test metadata.`)}</h3>
<p>{l('noTestMetadataExplain', `You can set up test metadata collection by setting the store_test_results key in your config.`)}</p>
<p><CTAButton text={l('metadataLink', 'Learn more')} href="https://circleci.com/docs/2.0/collect-test-data/" /></p>
</div>
);
};

export default NoTests;
1 change: 1 addition & 0 deletions src/webviews/assets/components/Search/ex.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions src/webviews/assets/components/Search/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
form {
position: relative;
}

.search-form {
flex: 1 0 0;
}

.search-input {
background-color: var(--vscode-input-background);
color: var(--vscode-input-foreground);
border: none;
padding: 7px 10px;
width: 100%;
box-sizing: border-box;

&.has-query {
padding-right: 30px;
}
}

.search-clear {
position: absolute;
right: 0;
top: 50%;
background: none;
border: none;
cursor: pointer;
transform: translateY(-50%);
margin-top: 1px;

svg {
fill: var(--vscode-icon-foreground);
}

&:focus {
// Fix this
outline: none;
}
}
51 changes: 51 additions & 0 deletions src/webviews/assets/components/Search/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useCallback, useRef } from 'react';
import { l } from '../../lib/utils';
import ExIcon from './ex.svg';
import './index.scss';

const Search = ({
query,
setQuery
}: {
query: string | null;
setQuery: (value: React.SetStateAction<string | null>) => void;
}): JSX.Element => {
let typeTimer: NodeJS.Timeout | null;
let searchField = useRef<HTMLInputElement | null>(null);

const onSearch = useCallback((value: string) => {
clearTimeout(typeTimer!);
// @ts-ignore
typeTimer = setTimeout(() => {
setQuery(value);
}, 300);
}, [query]);

const onClear = useCallback(() => {
setQuery(null);
typeTimer = null;
}, [query]);

return (
<form className="search-form" onSubmit={(event) => { event.preventDefault(); }}>
<input
ref={searchField}
className={`search-input ${query ? 'has-query' : ''}`}
type="text"
placeholder={l('searchTestsPlaceholder', 'Search tests')}
defaultValue={query ? query : ''}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
onSearch(event.target.value);
}}
/>

{query && (
<button type="button" onClick={onClear} className="search-clear">
<ExIcon />
</button>
)}
</form>
);
};

export default Search;
40 changes: 40 additions & 0 deletions src/webviews/assets/components/TestResult/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.test-result {
border-bottom: 1px solid var(--vscode-panel-border);
padding: 9px 10px 11px;

&:first-child {
border-top: 1px solid var(--vscode-panel-border);
}

&:hover {
background-color: rgba(0,0,0,0.1);
}
}

.test-classname {
margin: 0;
opacity: 0.8;
display: flex;
justify-content: space-between;
}

.test-duration {
display: flex;
align-items: center;

svg {
margin-right: 3px;
fill: var(--vscode-icon-foreground);
}
}

.test-name {
margin: 3px 0 0;
font-size: 0.95rem;

svg {
margin-right: 6px;
position: relative;
top: 1px;
}
}
Loading

0 comments on commit f0106d2

Please sign in to comment.