Skip to content

Commit 9b0fe4d

Browse files
authored
Merge pull request #1755 from github/koesie10/checkbox-state
Keep track of checkbox state in view
2 parents fca2faf + 3098899 commit 9b0fe4d

File tree

5 files changed

+109
-5
lines changed

5 files changed

+109
-5
lines changed

extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useCallback, useEffect, useState } from 'react';
2+
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
33
import styled from 'styled-components';
44
import { VSCodeBadge, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
55
import {
@@ -80,6 +80,9 @@ export type RepoRowProps = {
8080

8181
interpretedResults?: AnalysisAlert[];
8282
rawResults?: AnalysisRawResults;
83+
84+
selected?: boolean;
85+
onSelectedChange?: (repositoryId: number, selected: boolean) => void;
8386
}
8487

8588
const canExpand = (
@@ -101,6 +104,11 @@ const canExpand = (
101104
return downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Succeeded || downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Failed;
102105
};
103106

107+
const canSelect = (
108+
status: VariantAnalysisRepoStatus | undefined,
109+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus | undefined,
110+
) => status == VariantAnalysisRepoStatus.Succeeded && downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
111+
104112
const isExpandableContentLoaded = (
105113
status: VariantAnalysisRepoStatus | undefined,
106114
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus | undefined,
@@ -133,6 +141,8 @@ export const RepoRow = ({
133141
resultCount,
134142
interpretedResults,
135143
rawResults,
144+
selected,
145+
onSelectedChange,
136146
}: RepoRowProps) => {
137147
const [isExpanded, setExpanded] = useState(false);
138148
const resultsLoaded = !!interpretedResults || !!rawResults;
@@ -163,13 +173,35 @@ export const RepoRow = ({
163173
}
164174
}, [resultsLoaded, resultsLoading]);
165175

176+
const onClickCheckbox = useCallback((e: React.MouseEvent) => {
177+
// Prevent calling the onClick event of the container, which would toggle the expanded state
178+
e.stopPropagation();
179+
}, []);
180+
const onChangeCheckbox = useCallback((e: ChangeEvent<HTMLInputElement>) => {
181+
// This is called on first render, but we don't really care about this value
182+
if (e.target.checked === undefined) {
183+
return;
184+
}
185+
186+
if (!repository.id) {
187+
return;
188+
}
189+
190+
onSelectedChange?.(repository.id, e.target.checked);
191+
}, [onSelectedChange, repository]);
192+
166193
const disabled = !canExpand(status, downloadStatus);
167194
const expandableContentLoaded = isExpandableContentLoaded(status, downloadStatus, resultsLoaded);
168195

169196
return (
170197
<div>
171198
<TitleContainer onClick={toggleExpanded} disabled={disabled} aria-expanded={isExpanded}>
172-
<VSCodeCheckbox disabled />
199+
<VSCodeCheckbox
200+
onChange={onChangeCheckbox}
201+
onClick={onClickCheckbox}
202+
checked={selected}
203+
disabled={!repository.id || !canSelect(status, downloadStatus)}
204+
/>
173205
{isExpanded ? <ExpandCollapseCodicon name="chevron-down" label="Collapse" /> :
174206
<ExpandCollapseCodicon name="chevron-right" label="Expand" />}
175207
<VSCodeBadge>{resultCount === undefined ? '-' : formatDecimal(resultCount)}</VSCodeBadge>

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export function VariantAnalysis({
5757
const [repoStates, setRepoStates] = useState<VariantAnalysisScannedRepositoryState[]>(initialRepoStates);
5858
const [repoResults, setRepoResults] = useState<VariantAnalysisScannedRepositoryResult[]>(initialRepoResults);
5959

60+
const [selectedRepositoryIds, setSelectedRepositoryIds] = useState<number[]>([]);
61+
6062
useEffect(() => {
6163
const listener = (evt: MessageEvent) => {
6264
if (evt.origin === window.origin) {
@@ -109,6 +111,8 @@ export function VariantAnalysis({
109111
variantAnalysis={variantAnalysis}
110112
repositoryStates={repoStates}
111113
repositoryResults={repoResults}
114+
selectedRepositoryIds={selectedRepositoryIds}
115+
setSelectedRepositoryIds={setSelectedRepositoryIds}
112116
/>
113117
</>
114118
);

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisAnalyzedRepos.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useMemo } from 'react';
2+
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
33
import styled from 'styled-components';
44
import { RepoRow } from './RepoRow';
55
import {
@@ -22,13 +22,18 @@ export type VariantAnalysisAnalyzedReposProps = {
2222
repositoryResults?: VariantAnalysisScannedRepositoryResult[];
2323

2424
filterSortState?: RepositoriesFilterSortState;
25+
26+
selectedRepositoryIds?: number[];
27+
setSelectedRepositoryIds?: Dispatch<SetStateAction<number[]>>;
2528
}
2629

2730
export const VariantAnalysisAnalyzedRepos = ({
2831
variantAnalysis,
2932
repositoryStates,
3033
repositoryResults,
3134
filterSortState,
35+
selectedRepositoryIds,
36+
setSelectedRepositoryIds,
3237
}: VariantAnalysisAnalyzedReposProps) => {
3338
const repositoryStateById = useMemo(() => {
3439
const map = new Map<number, VariantAnalysisScannedRepositoryState>();
@@ -52,6 +57,20 @@ export const VariantAnalysisAnalyzedRepos = ({
5257
})?.sort(compareWithResults(filterSortState));
5358
}, [filterSortState, variantAnalysis.scannedRepos]);
5459

60+
const onSelectedChange = useCallback((repositoryId: number, selected: boolean) => {
61+
setSelectedRepositoryIds?.((prevSelectedRepositoryIds) => {
62+
if (selected) {
63+
if (prevSelectedRepositoryIds.includes(repositoryId)) {
64+
return prevSelectedRepositoryIds;
65+
}
66+
67+
return [...prevSelectedRepositoryIds, repositoryId];
68+
} else {
69+
return prevSelectedRepositoryIds.filter((id) => id !== repositoryId);
70+
}
71+
});
72+
}, [setSelectedRepositoryIds]);
73+
5574
return (
5675
<Container>
5776
{repositories?.map(repository => {
@@ -67,6 +86,8 @@ export const VariantAnalysisAnalyzedRepos = ({
6786
resultCount={repository.resultCount}
6887
interpretedResults={results?.interpretedResults}
6988
rawResults={results?.rawResults}
89+
selected={selectedRepositoryIds?.includes(repository.repository.id)}
90+
onSelectedChange={onSelectedChange}
7091
/>
7192
);
7293
})}

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisOutcomePanels.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useState } from 'react';
2+
import { Dispatch, SetStateAction, useState } from 'react';
33
import styled from 'styled-components';
44
import { VSCodeBadge, VSCodePanels, VSCodePanelTab, VSCodePanelView } from '@vscode/webview-ui-toolkit/react';
55
import { formatDecimal } from '../../pure/number';
@@ -20,6 +20,9 @@ export type VariantAnalysisOutcomePanelProps = {
2020
variantAnalysis: VariantAnalysis;
2121
repositoryStates?: VariantAnalysisScannedRepositoryState[];
2222
repositoryResults?: VariantAnalysisScannedRepositoryResult[];
23+
24+
selectedRepositoryIds?: number[];
25+
setSelectedRepositoryIds?: Dispatch<SetStateAction<number[]>>;
2326
};
2427

2528
const Tab = styled(VSCodePanelTab)`
@@ -46,6 +49,8 @@ export const VariantAnalysisOutcomePanels = ({
4649
variantAnalysis,
4750
repositoryStates,
4851
repositoryResults,
52+
selectedRepositoryIds,
53+
setSelectedRepositoryIds,
4954
}: VariantAnalysisOutcomePanelProps) => {
5055
const [filterSortState, setFilterSortState] = useState<RepositoriesFilterSortState>(defaultFilterSortState);
5156

@@ -94,6 +99,8 @@ export const VariantAnalysisOutcomePanels = ({
9499
repositoryStates={repositoryStates}
95100
repositoryResults={repositoryResults}
96101
filterSortState={filterSortState}
102+
selectedRepositoryIds={selectedRepositoryIds}
103+
setSelectedRepositoryIds={setSelectedRepositoryIds}
97104
/>
98105
</>
99106
);
@@ -126,6 +133,8 @@ export const VariantAnalysisOutcomePanels = ({
126133
repositoryStates={repositoryStates}
127134
repositoryResults={repositoryResults}
128135
filterSortState={filterSortState}
136+
selectedRepositoryIds={selectedRepositoryIds}
137+
setSelectedRepositoryIds={setSelectedRepositoryIds}
129138
/>
130139
</VSCodePanelView>
131140
{notFoundRepos?.repositoryCount &&

extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { render as reactRender, screen } from '@testing-library/react';
2+
import { render as reactRender, screen, waitFor } from '@testing-library/react';
33
import {
44
VariantAnalysisRepoStatus,
55
VariantAnalysisScannedRepositoryDownloadStatus
@@ -330,4 +330,42 @@ describe(RepoRow.name, () => {
330330
expanded: false
331331
})).toBeDisabled();
332332
});
333+
334+
it('does not allow selecting the item if the item has not succeeded', async () => {
335+
render({
336+
status: VariantAnalysisRepoStatus.InProgress,
337+
});
338+
339+
expect(screen.getByRole('checkbox')).toBeDisabled();
340+
});
341+
342+
it('does not allow selecting the item if the item has not been downloaded', async () => {
343+
render({
344+
status: VariantAnalysisRepoStatus.Succeeded,
345+
});
346+
347+
expect(screen.getByRole('checkbox')).toBeDisabled();
348+
});
349+
350+
it('does not allow selecting the item if the item has not been downloaded successfully', async () => {
351+
render({
352+
status: VariantAnalysisRepoStatus.Succeeded,
353+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Failed,
354+
});
355+
356+
// It seems like sometimes the first render doesn't have the checkbox disabled
357+
// Might be related to https://github.com/microsoft/vscode-webview-ui-toolkit/issues/404
358+
await waitFor(() => {
359+
expect(screen.getByRole('checkbox')).toBeDisabled();
360+
});
361+
});
362+
363+
it('allows selecting the item if the item has been downloaded', async () => {
364+
render({
365+
status: VariantAnalysisRepoStatus.Succeeded,
366+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded,
367+
});
368+
369+
expect(screen.getByRole('checkbox')).toBeEnabled();
370+
});
333371
});

0 commit comments

Comments
 (0)