Merge pull request #2073 from github/koesie10/remove-remote-queries-react

Remove remote queries React components
This commit is contained in:
Koen Vlaswinkel
2023-02-15 11:15:31 +01:00
committed by GitHub
28 changed files with 9 additions and 1077 deletions

View File

@@ -2,7 +2,7 @@ import * as React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import TextButtonComponent from "../../view/remote-queries/TextButton";
import TextButtonComponent from "../../view/common/TextButton";
export default {
title: "Text Button",

View File

@@ -1,27 +0,0 @@
import * as React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import DownloadButtonComponent from "../../view/remote-queries/DownloadButton";
export default {
title: "Download Button",
component: DownloadButtonComponent,
argTypes: {
onClick: {
action: "clicked",
table: {
disable: true,
},
},
},
} as ComponentMeta<typeof DownloadButtonComponent>;
const Template: ComponentStory<typeof DownloadButtonComponent> = (args) => (
<DownloadButtonComponent {...args} />
);
export const DownloadButton = Template.bind({});
DownloadButton.args = {
text: "Download",
};

View File

@@ -1,12 +0,0 @@
import * as React from "react";
import { ComponentMeta } from "@storybook/react";
import DownloadSpinnerComponent from "../../view/remote-queries/DownloadSpinner";
export default {
title: "Download Spinner",
component: DownloadSpinnerComponent,
} as ComponentMeta<typeof DownloadSpinnerComponent>;
export const DownloadSpinner = <DownloadSpinnerComponent />;

View File

@@ -1,20 +0,0 @@
import * as React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import LastUpdatedComponent from "../../view/remote-queries/LastUpdated";
export default {
title: "MRVA/Last Updated",
component: LastUpdatedComponent,
} as ComponentMeta<typeof LastUpdatedComponent>;
const Template: ComponentStory<typeof LastUpdatedComponent> = (args) => (
<LastUpdatedComponent {...args} />
);
export const LastUpdated = Template.bind({});
LastUpdated.args = {
lastUpdated: -3_600_000, // 1 hour ago
};

View File

@@ -1,25 +0,0 @@
import * as React from "react";
import { useEffect } from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { RemoteQueries } from "../../view/remote-queries/RemoteQueries";
import * as remoteQueryResult from "./data/remoteQueryResultMessage.json";
import * as analysesResults from "./data/analysesResultsMessage.json";
export default {
title: "MRVA/Remote Queries",
component: RemoteQueries,
} as ComponentMeta<typeof RemoteQueries>;
const Template: ComponentStory<typeof RemoteQueries> = () => {
useEffect(() => {
window.postMessage(remoteQueryResult);
window.postMessage(analysesResults);
});
return <RemoteQueries />;
};
export const Top10JavaScript = Template.bind({});

View File

@@ -1,29 +0,0 @@
import * as React from "react";
import { useState } from "react";
import { ComponentMeta } from "@storybook/react";
import RepositoriesSearchComponent from "../../view/remote-queries/RepositoriesSearch";
export default {
title: "MRVA/Repositories Search",
component: RepositoriesSearchComponent,
argTypes: {
filterValue: {
control: {
disable: true,
},
},
},
} as ComponentMeta<typeof RepositoriesSearchComponent>;
export const RepositoriesSearch = () => {
const [filterValue, setFilterValue] = useState("");
return (
<RepositoriesSearchComponent
filterValue={filterValue}
setFilterValue={setFilterValue}
/>
);
};

View File

@@ -2,11 +2,11 @@ import * as React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import AnalysisAlertResult from "../../view/remote-queries/AnalysisAlertResult";
import AnalysisAlertResult from "../../view/variant-analysis/AnalysisAlertResult";
import type { AnalysisAlert } from "../../remote-queries/shared/analysis-result";
export default {
title: "Analysis Alert Result",
title: "Variant Analysis/Analysis Alert Result",
component: AnalysisAlertResult,
} as ComponentMeta<typeof AnalysisAlertResult>;

View File

@@ -13,8 +13,8 @@ import {
} from "../../remote-queries/shared/analysis-result";
import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository";
import * as analysesResults from "../remote-queries/data/analysesResultsMessage.json";
import * as rawResults from "../remote-queries/data/rawResults.json";
import * as analysesResults from "../data/analysesResultsMessage.json";
import * as rawResults from "../data/rawResults.json";
import { RepoRow, RepoRowProps } from "../../view/variant-analysis/RepoRow";
export default {

View File

@@ -16,7 +16,7 @@ import { createMockVariantAnalysis } from "../../../test/factories/remote-querie
import { createMockRepositoryWithMetadata } from "../../../test/factories/remote-queries/shared/repository";
import { createMockScannedRepo } from "../../../test/factories/remote-queries/shared/scanned-repositories";
import * as analysesResults from "../remote-queries/data/analysesResultsMessage.json";
import * as analysesResults from "../data/analysesResultsMessage.json";
export default {
title: "Variant Analysis/Analyzed Repos",

View File

@@ -1,50 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import { ChevronDownIcon, ChevronRightIcon } from "@primer/octicons-react";
import { useState } from "react";
const Container = styled.div`
display: block;
vertical-align: middle;
cursor: pointer;
`;
const TitleContainer = styled.span`
display: inline-block;
`;
const Button = styled.button`
display: inline-block;
background-color: transparent;
color: var(--vscode-editor-foreground);
border: none;
padding-left: 0;
padding-right: 0.1em;
`;
const CollapsibleItem = ({
title,
children,
}: {
title: React.ReactNode;
children: React.ReactNode;
}) => {
const [isExpanded, setExpanded] = useState(false);
return (
<>
<Container onClick={() => setExpanded(!isExpanded)}>
<Button>
{isExpanded ? (
<ChevronDownIcon size={16} />
) : (
<ChevronRightIcon size={16} />
)}
</Button>
<TitleContainer>{title}</TitleContainer>
</Container>
{isExpanded && children}
</>
);
};
export default CollapsibleItem;

View File

@@ -1,30 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import { DownloadIcon } from "@primer/octicons-react";
const ButtonLink = styled.a`
display: inline-block;
font-size: x-small;
text-decoration: none;
cursor: pointer;
vertical-align: middle;
svg {
fill: var(--vscode-textLink-foreground);
}
`;
const DownloadButton = ({
text,
onClick,
}: {
text: string;
onClick: () => void;
}) => (
<ButtonLink onClick={onClick}>
<DownloadIcon size={16} />
{text}
</ButtonLink>
);
export default DownloadButton;

View File

@@ -1,16 +0,0 @@
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react";
import * as React from "react";
import styled from "styled-components";
const SpinnerContainer = styled.span`
vertical-align: middle;
display: inline-block;
`;
const DownloadSpinner = () => (
<SpinnerContainer>
<VSCodeProgressRing style={{ height: "0.8em", width: "0.8em" }} />
</SpinnerContainer>
);
export default DownloadSpinner;

View File

@@ -1,53 +0,0 @@
import * as React from "react";
import { createPortal } from "react-dom";
import styled from "styled-components";
import { XCircleIcon } from "@primer/octicons-react";
const Container = styled.div`
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
opacity: 1;
background-color: var(--vscode-editor-background);
z-index: 5000;
padding-top: 1em;
`;
const CloseButton = styled.button`
position: absolute;
top: 1em;
right: 1em;
background-color: var(--vscode-editor-background);
border: none;
`;
const FullScreenModal = ({
setOpen,
containerElementId,
children,
}: {
setOpen: (open: boolean) => void;
containerElementId: string;
children: React.ReactNode;
}) => {
const containerElement = document.getElementById(containerElementId);
if (!containerElement) {
throw Error(`Could not find container element. Id: ${containerElementId}`);
}
return createPortal(
<>
<Container>
<CloseButton onClick={() => setOpen(false)}>
<XCircleIcon size={24} />
</CloseButton>
{children}
</Container>
</>,
containerElement,
);
};
export default FullScreenModal;

View File

@@ -1,35 +0,0 @@
import * as React from "react";
import { RepoPushIcon } from "@primer/octicons-react";
import styled from "styled-components";
import { humanizeRelativeTime } from "../../pure/time";
const IconContainer = styled.span`
flex-grow: 0;
text-align: right;
margin-right: 0;
`;
const Duration = styled.span`
text-align: left;
width: 8em;
margin-left: 0.5em;
`;
type Props = { lastUpdated?: number };
const LastUpdated = ({ lastUpdated }: Props) =>
// lastUpdated will be undefined for older results that were
// created before the lastUpdated field was added.
Number.isFinite(lastUpdated) ? (
<>
<IconContainer>
<RepoPushIcon size={16} />
</IconContainer>
<Duration>{humanizeRelativeTime(lastUpdated)}</Duration>
</>
) : (
<></>
);
export default LastUpdated;

View File

@@ -1,551 +0,0 @@
import * as React from "react";
import { useEffect, useState } from "react";
import { Flash, ThemeProvider } from "@primer/react";
import { ToRemoteQueriesMessage } from "../../pure/interface-types";
import {
AnalysisSummary,
RemoteQueryResult,
} from "../../remote-queries/shared/remote-query-result";
import { MAX_RAW_RESULTS } from "../../remote-queries/shared/result-limits";
import { vscode } from "../vscode-api";
import { VSCodeBadge, VSCodeButton } from "@vscode/webview-ui-toolkit/react";
import {
HorizontalSpace,
SectionTitle,
VerticalSpace,
ViewTitle,
} from "../common";
import DownloadButton from "./DownloadButton";
import {
AnalysisResults,
getAnalysisResultCount,
} from "../../remote-queries/shared/analysis-result";
import DownloadSpinner from "./DownloadSpinner";
import CollapsibleItem from "./CollapsibleItem";
import {
AlertIcon,
CodeSquareIcon,
FileCodeIcon,
RepoIcon,
TerminalIcon,
} from "@primer/octicons-react";
import AnalysisAlertResult from "./AnalysisAlertResult";
import RawResultsTable from "./RawResultsTable";
import RepositoriesSearch from "./RepositoriesSearch";
import StarCount from "../common/StarCount";
import SortRepoFilter, { Sort, sorter } from "./SortRepoFilter";
import LastUpdated from "./LastUpdated";
import RepoListCopyButton from "./RepoListCopyButton";
import "./baseStyles.css";
import "./remoteQueries.css";
const numOfReposInContractedMode = 10;
const emptyQueryResult: RemoteQueryResult = {
queryId: "",
queryTitle: "",
queryFileName: "",
queryFilePath: "",
queryText: "",
language: "",
workflowRunUrl: "",
totalRepositoryCount: 0,
affectedRepositoryCount: 0,
totalResultCount: 0,
executionTimestamp: "",
executionDuration: "",
analysisSummaries: [],
analysisFailures: [],
};
const downloadAnalysisResults = (analysisSummary: AnalysisSummary) => {
vscode.postMessage({
t: "remoteQueryDownloadAnalysisResults",
analysisSummary,
});
};
const downloadAllAnalysesResults = (query: RemoteQueryResult) => {
vscode.postMessage({
t: "remoteQueryDownloadAllAnalysesResults",
analysisSummaries: query.analysisSummaries,
});
};
const openQueryFile = (queryResult: RemoteQueryResult) => {
vscode.postMessage({
t: "openFile",
filePath: queryResult.queryFilePath,
});
};
const openQueryTextVirtualFile = (queryResult: RemoteQueryResult) => {
vscode.postMessage({
t: "openVirtualFile",
queryText: queryResult.queryText,
});
};
function createResultsDescription(queryResult: RemoteQueryResult) {
const reposCount = `${queryResult.totalRepositoryCount} ${
queryResult.totalRepositoryCount === 1 ? "repository" : "repositories"
}`;
return `${queryResult.totalResultCount} results from running against ${reposCount} (${queryResult.executionDuration}), ${queryResult.executionTimestamp}`;
}
const sumAnalysesResults = (analysesResults: AnalysisResults[]) =>
analysesResults.reduce((acc, curr) => acc + getAnalysisResultCount(curr), 0);
const QueryInfo = (queryResult: RemoteQueryResult) => (
<>
<VerticalSpace size={1} />
{createResultsDescription(queryResult)}
<VerticalSpace size={1} />
<span>
<a
className="vscode-codeql__query-info-link"
href="#"
onClick={() => openQueryFile(queryResult)}
>
<span>
{" "}
<FileCodeIcon size={16} />{" "}
</span>
{queryResult.queryFileName}
</a>
</span>
<span>
<a
className="vscode-codeql__query-info-link"
href="#"
onClick={() => openQueryTextVirtualFile(queryResult)}
>
<span>
{" "}
<CodeSquareIcon size={16} />{" "}
</span>
Query
</a>
</span>
<span>
<a
className="vscode-codeql__query-info-link"
href={queryResult.workflowRunUrl}
>
<span>
{" "}
<TerminalIcon size={16} />{" "}
</span>
Logs
</a>
</span>
</>
);
const Failures = (queryResult: RemoteQueryResult) => {
if (queryResult.analysisFailures.length === 0) {
return <></>;
}
return (
<>
<VerticalSpace size={3} />
<Flash variant="danger">
{queryResult.analysisFailures.map((f, i) => (
<div key={i}>
<p className="vscode-codeql__analysis-failure">
<AlertIcon size={16} />
<b>{f.nwo}: </b>
{f.error}
</p>
{i === queryResult.analysisFailures.length - 1 ? (
<></>
) : (
<VerticalSpace size={1} />
)}
</div>
))}
</Flash>
</>
);
};
const SummaryTitleWithResults = ({
queryResult,
analysesResults,
sort,
setSort,
}: {
queryResult: RemoteQueryResult;
analysesResults: AnalysisResults[];
sort: Sort;
setSort: (sort: Sort) => void;
}) => {
const showDownloadButton =
queryResult.totalResultCount !== sumAnalysesResults(analysesResults);
return (
<div className="vscode-codeql__query-summary-container">
<SectionTitle>
Repositories with results ({queryResult.affectedRepositoryCount}):
</SectionTitle>
{showDownloadButton && (
<DownloadButton
text="Download all"
onClick={() => downloadAllAnalysesResults(queryResult)}
/>
)}
<div style={{ flexGrow: 2, textAlign: "right" }}>
<RepoListCopyButton queryResult={queryResult} />
<HorizontalSpace size={1} />
<SortRepoFilter sort={sort} setSort={setSort} />
</div>
</div>
);
};
const SummaryTitleNoResults = () => (
<div className="vscode-codeql__query-summary-container">
<SectionTitle>No results found</SectionTitle>
</div>
);
const SummaryItemDownload = ({
analysisSummary,
analysisResults,
}: {
analysisSummary: AnalysisSummary;
analysisResults: AnalysisResults | undefined;
}) => {
if (!analysisResults || analysisResults.status === "Failed") {
return (
<DownloadButton
text={analysisSummary.fileSize}
onClick={() => downloadAnalysisResults(analysisSummary)}
/>
);
}
if (analysisResults.status === "InProgress") {
return (
<>
<HorizontalSpace size={2} />
<DownloadSpinner />
</>
);
}
return <></>;
};
const SummaryItem = ({
analysisSummary,
analysisResults,
}: {
analysisSummary: AnalysisSummary;
analysisResults: AnalysisResults | undefined;
}) => (
<>
<span className="vscode-codeql__analysis-item">
<RepoIcon size={16} />
</span>
<span className="vscode-codeql__analysis-item">{analysisSummary.nwo}</span>
<HorizontalSpace size={1} />
<span className="vscode-codeql__analysis-item">
<VSCodeBadge>{analysisSummary.resultCount.toString()}</VSCodeBadge>
</span>
<span className="vscode-codeql__analysis-item">
<SummaryItemDownload
analysisSummary={analysisSummary}
analysisResults={analysisResults}
/>
</span>
<StarCount starCount={analysisSummary.starCount} />
<LastUpdated lastUpdated={analysisSummary.lastUpdated} />
</>
);
const Summary = ({
queryResult,
analysesResults,
sort,
setSort,
}: {
queryResult: RemoteQueryResult;
analysesResults: AnalysisResults[];
sort: Sort;
setSort: (sort: Sort) => void;
}) => {
const [repoListExpanded, setRepoListExpanded] = useState(false);
const numOfReposToShow = repoListExpanded
? queryResult.analysisSummaries.length
: numOfReposInContractedMode;
return (
<>
{queryResult.affectedRepositoryCount === 0 ? (
<SummaryTitleNoResults />
) : (
<SummaryTitleWithResults
queryResult={queryResult}
analysesResults={analysesResults}
sort={sort}
setSort={setSort}
/>
)}
<ul className="vscode-codeql__flat-list">
{queryResult.analysisSummaries
.slice(0, numOfReposToShow)
.sort(sorter(sort))
.map((summary, i) => (
<li
key={summary.nwo}
className="vscode-codeql__analysis-summaries-list-item"
>
<SummaryItem
analysisSummary={summary}
analysisResults={analysesResults.find(
(a) => a.nwo === summary.nwo,
)}
/>
</li>
))}
</ul>
{queryResult.analysisSummaries.length > numOfReposInContractedMode && (
<button
className="vscode-codeql__expand-button"
onClick={() => setRepoListExpanded(!repoListExpanded)}
>
{repoListExpanded ? <span>View less</span> : <span>View all</span>}
</button>
)}
</>
);
};
const AnalysesResultsTitle = ({
totalAnalysesResults,
totalResults,
}: {
totalAnalysesResults: number;
totalResults: number;
}) => {
if (totalAnalysesResults === totalResults) {
return <SectionTitle>{totalAnalysesResults} results</SectionTitle>;
}
return (
<SectionTitle>
{totalAnalysesResults}/{totalResults} results
</SectionTitle>
);
};
const exportResults = (queryResult: RemoteQueryResult) => {
vscode.postMessage({
t: "remoteQueryExportResults",
queryId: queryResult.queryId,
});
};
const AnalysesResultsDescription = ({
queryResult,
analysesResults,
}: {
queryResult: RemoteQueryResult;
analysesResults: AnalysisResults[];
}) => {
const showDownloadsMessage = queryResult.analysisSummaries.some(
(s) =>
!analysesResults.some((a) => a.nwo === s.nwo && a.status === "Completed"),
);
const downloadsMessage = (
<>
<VerticalSpace size={1} />
Some results haven&apos;t been downloaded automatically because of their
size or because enough were downloaded already. Download them manually
from the list above if you want to see them here.
</>
);
const showMaxResultsMessage = analysesResults.some(
(a) => a.rawResults?.capped,
);
const maxRawResultsMessage = (
<>
<VerticalSpace size={1} />
Some repositories have more than {MAX_RAW_RESULTS} results. We will only
show you up to&nbsp;
{MAX_RAW_RESULTS} results for each repository.
</>
);
return (
<>
{showDownloadsMessage && downloadsMessage}
{showMaxResultsMessage && maxRawResultsMessage}
</>
);
};
const RepoAnalysisResults = (analysisResults: AnalysisResults) => {
const numOfResults = getAnalysisResultCount(analysisResults);
const title = (
<>
{analysisResults.nwo}
<HorizontalSpace size={1} />
<VSCodeBadge>{numOfResults.toString()}</VSCodeBadge>
</>
);
return (
<CollapsibleItem title={title}>
<ul className="vscode-codeql__flat-list">
{analysisResults.interpretedResults.map((r, i) => (
<li key={i}>
<AnalysisAlertResult alert={r} />
<VerticalSpace size={2} />
</li>
))}
</ul>
{analysisResults.rawResults && (
<RawResultsTable
schema={analysisResults.rawResults.schema}
results={analysisResults.rawResults.resultSet}
fileLinkPrefix={analysisResults.rawResults.fileLinkPrefix}
sourceLocationPrefix={analysisResults.rawResults.sourceLocationPrefix}
/>
)}
</CollapsibleItem>
);
};
const AnalysesResults = ({
queryResult,
analysesResults,
totalResults,
sort,
}: {
queryResult: RemoteQueryResult;
analysesResults: AnalysisResults[];
totalResults: number;
sort: Sort;
}) => {
const totalAnalysesResults = sumAnalysesResults(analysesResults);
const [filterValue, setFilterValue] = useState("");
if (totalResults === 0) {
return <></>;
}
return (
<>
<VerticalSpace size={2} />
<div style={{ display: "flex" }}>
<div style={{ flexGrow: 1 }}>
<AnalysesResultsTitle
totalAnalysesResults={totalAnalysesResults}
totalResults={totalResults}
/>
</div>
<div>
<VSCodeButton onClick={() => exportResults(queryResult)}>
Export all
</VSCodeButton>
</div>
</div>
<AnalysesResultsDescription
queryResult={queryResult}
analysesResults={analysesResults}
/>
<VerticalSpace size={2} />
<RepositoriesSearch
filterValue={filterValue}
setFilterValue={setFilterValue}
/>
<ul className="vscode-codeql__flat-list">
{analysesResults
.filter(
(a) =>
a.interpretedResults.length ||
a.rawResults?.resultSet?.rows?.length,
)
.filter((a) =>
a.nwo.toLowerCase().includes(filterValue.toLowerCase()),
)
.sort(sorter(sort))
.map((r) => (
<li
key={r.nwo}
className="vscode-codeql__analyses-results-list-item"
>
<RepoAnalysisResults {...r} />
</li>
))}
</ul>
</>
);
};
export function RemoteQueries(): JSX.Element {
const [queryResult, setQueryResult] =
useState<RemoteQueryResult>(emptyQueryResult);
const [analysesResults, setAnalysesResults] = useState<AnalysisResults[]>([]);
const [sort, setSort] = useState<Sort>("name");
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToRemoteQueriesMessage = evt.data;
if (msg.t === "setRemoteQueryResult") {
setQueryResult(msg.queryResult);
} else if (msg.t === "setAnalysesResults") {
setAnalysesResults(msg.analysesResults);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
}, []);
if (!queryResult) {
return <div>Waiting for results to load.</div>;
}
try {
return (
<div className="vscode-codeql__remote-queries">
<ThemeProvider colorMode="auto">
<ViewTitle>{queryResult.queryTitle}</ViewTitle>
<QueryInfo {...queryResult} />
<Failures {...queryResult} />
<Summary
queryResult={queryResult}
analysesResults={analysesResults}
sort={sort}
setSort={setSort}
/>
<AnalysesResults
queryResult={queryResult}
analysesResults={analysesResults}
totalResults={queryResult.totalResultCount}
sort={sort}
/>
</ThemeProvider>
</div>
);
} catch (err) {
console.error(err);
return <div>There was an error displaying the view.</div>;
}
}

View File

@@ -1,29 +0,0 @@
import * as React from "react";
import { vscode } from "../vscode-api";
import { RemoteQueryResult } from "../../remote-queries/shared/remote-query-result";
import { CopyIcon } from "@primer/octicons-react";
import { IconButton } from "@primer/react";
const copyRepositoryList = (queryResult: RemoteQueryResult) => {
vscode.postMessage({
t: "copyRepoList",
queryId: queryResult.queryId,
});
};
const RepoListCopyButton = ({
queryResult,
}: {
queryResult: RemoteQueryResult;
}) => (
<IconButton
aria-label="Copy repository list"
icon={CopyIcon}
variant="invisible"
size="small"
sx={{ "text-align": "right" }}
onClick={() => copyRepositoryList(queryResult)}
/>
);
export default RepoListCopyButton;

View File

@@ -1,31 +0,0 @@
import * as React from "react";
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react";
interface RepositoriesSearchProps {
filterValue: string;
setFilterValue: (value: string) => void;
}
const RepositoriesSearch = ({
filterValue,
setFilterValue,
}: RepositoriesSearchProps) => {
return (
<>
<VSCodeTextField
style={{ width: "100%" }}
placeholder="Filter by repository owner/name"
ariaLabel="Repository search"
name="repository-search"
value={filterValue}
onInput={(e: InputEvent) =>
setFilterValue((e.target as HTMLInputElement).value)
}
>
<span slot="start" className="codicon codicon-search"></span>
</VSCodeTextField>
</>
);
};
export default RepositoriesSearch;

View File

@@ -1,94 +0,0 @@
import * as React from "react";
import { FilterIcon } from "@primer/octicons-react";
import { ActionList, ActionMenu, IconButton } from "@primer/react";
import styled from "styled-components";
const SortWrapper = styled.span`
flex-grow: 2;
text-align: right;
margin-right: 0;
`;
export type Sort = "name" | "stars" | "results" | "lastUpdated";
type Props = {
sort: Sort;
setSort: (sort: Sort) => void;
};
type Sortable = {
nwo: string;
starCount?: number;
resultCount?: number;
lastUpdated?: number;
};
const sortBy = [
{ name: "Sort by Name", sort: "name" },
{ name: "Sort by Results", sort: "results" },
{ name: "Sort by Stars", sort: "stars" },
{ name: "Sort by Last Updated", sort: "lastUpdated" },
];
export function sorter(
sort: Sort,
): (left: Sortable, right: Sortable) => number {
// stars and results are highest to lowest
// name is alphabetical
return (left: Sortable, right: Sortable) => {
if (sort === "stars") {
const stars = (right.starCount || 0) - (left.starCount || 0);
if (stars !== 0) {
return stars;
}
}
if (sort === "lastUpdated") {
const lastUpdated = (right.lastUpdated || 0) - (left.lastUpdated || 0);
if (lastUpdated !== 0) {
return lastUpdated;
}
}
if (sort === "results") {
const results = (right.resultCount || 0) - (left.resultCount || 0);
if (results !== 0) {
return results;
}
}
// Fall back on name compare if results, stars, or lastUpdated are equal
return left.nwo.localeCompare(right.nwo, undefined, {
sensitivity: "base",
});
};
}
const SortRepoFilter = ({ sort, setSort }: Props) => {
return (
<SortWrapper>
<ActionMenu>
<ActionMenu.Anchor>
<IconButton
icon={FilterIcon}
variant="invisible"
aria-label="Sort results"
/>
</ActionMenu.Anchor>
<ActionMenu.Overlay width="small" anchorSide="outside-bottom">
<ActionList selectionVariant="single">
{sortBy.map((type, index) => (
<ActionList.Item
key={index}
selected={type.sort === sort}
onSelect={() => setSort(type.sort as Sort)}
>
{type.name}
</ActionList.Item>
))}
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</SortWrapper>
);
};
export default SortRepoFilter;

View File

@@ -1,4 +0,0 @@
body {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji;
}

View File

@@ -1,9 +0,0 @@
import * as React from "react";
import { WebviewDefinition } from "../webview-definition";
import { RemoteQueries } from "./RemoteQueries";
const definition: WebviewDefinition = {
component: <RemoteQueries />,
};
export default definition;

View File

@@ -1,53 +0,0 @@
.vscode-codeql__remote-queries {
max-width: 55em;
}
.vscode-codeql__query-info-link {
text-decoration: none;
padding-right: 1em;
color: var(--vscode-editor-foreground);
}
.vscode-codeql__query-info-link:hover {
color: var(--vscode-editor-foreground);
}
.vscode-codeql__query-summary-container {
padding-top: 1.5em;
display: flex;
}
.vscode-codeql__analysis-summaries-list-item {
margin-top: 0.5em;
display: flex;
}
.vscode-codeql__analyses-results-list-item {
padding-top: 0.5em;
}
.vscode-codeql__analysis-item {
padding-right: 0.1em;
}
.vscode-codeql__expand-button {
background: none;
color: var(--vscode-textLink-foreground);
border: none;
cursor: pointer;
padding-top: 1em;
font-size: x-small;
}
.vscode-codeql__analysis-failure {
margin: 0;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
color: var(--vscode-editor-foreground);
}
.vscode-codeql__flat-list {
list-style-type: none;
margin: 0;
padding: 0.5em 0 0 0;
}

View File

@@ -4,8 +4,8 @@ import {
AnalysisAlert,
AnalysisRawResults,
} from "../../remote-queries/shared/analysis-result";
import AnalysisAlertResult from "../remote-queries/AnalysisAlertResult";
import RawResultsTable from "../remote-queries/RawResultsTable";
import AnalysisAlertResult from "./AnalysisAlertResult";
import RawResultsTable from "./RawResultsTable";
import {
VariantAnalysisRepoStatus,
VariantAnalysisScannedRepositoryDownloadStatus,

View File

@@ -8,7 +8,7 @@ import {
ResultSetSchema,
} from "../../pure/bqrs-cli-types";
import { tryGetRemoteLocation } from "../../pure/bqrs-utils";
import TextButton from "./TextButton";
import TextButton from "../common/TextButton";
import { convertNonPrintableChars } from "../../text-utils";
import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry";