Skip to content

Commit 9c07615

Browse files
authored
Merge pull request #1523 from github/koesie10/refactor-common-components
Refactor CodePaths and FileCodeSnippet components
2 parents bbb6f10 + 8a671be commit 9c07615

2 files changed

Lines changed: 356 additions & 208 deletions

File tree

Lines changed: 206 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { XCircleIcon } from '@primer/octicons-react';
2-
import { Overlay } from '@primer/react';
3-
import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTag } from '@vscode/webview-ui-toolkit/react';
41
import * as React from 'react';
5-
import { ChangeEvent, useRef, useState } from 'react';
2+
import { ChangeEvent, SetStateAction, useCallback, useRef, useState } from 'react';
63
import styled from 'styled-components';
7-
import { CodeFlow, AnalysisMessage, ResultSeverity } from '../../../remote-queries/shared/analysis-result';
4+
import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTag } from '@vscode/webview-ui-toolkit/react';
5+
6+
import { Overlay } from '@primer/react';
7+
8+
import { CodeFlow, AnalysisMessage, ResultSeverity, ThreadFlow } from '../../../remote-queries/shared/analysis-result';
89
import { SectionTitle } from '../SectionTitle';
910
import { VerticalSpace } from '../VerticalSpace';
1011
import { FileCodeSnippet } from '../FileCodeSnippet';
@@ -16,13 +17,14 @@ const StyledCloseButton = styled.button`
1617
background-color: var(--vscode-editor-background);
1718
color: var(--vscode-editor-foreground);
1819
border: none;
20+
cursor: pointer;
21+
1922
&:focus-visible {
2023
outline: none
2124
}
2225
`;
2326

2427
const OverlayContainer = styled.div`
25-
padding: 1em;
2628
height: 100%;
2729
width: 100%;
2830
padding: 2em;
@@ -35,142 +37,246 @@ const OverlayContainer = styled.div`
3537
`;
3638

3739
const CloseButton = ({ onClick }: { onClick: () => void }) => (
38-
<StyledCloseButton onClick={onClick} tabIndex={-1} >
39-
<XCircleIcon size={24} />
40+
<StyledCloseButton onClick={onClick} tabIndex={-1}>
41+
<span className="codicon codicon-chrome-close" />
4042
</StyledCloseButton>
4143
);
4244

43-
const CodePath = ({
44-
codeFlow,
45+
const PathsContainer = styled.div`
46+
display: flex;
47+
justify-content: center;
48+
align-items: center;
49+
`;
50+
51+
const PathDetailsContainer = styled.div`
52+
padding: 0;
53+
border: 0;
54+
`;
55+
56+
const PathDropdownContainer = styled.div`
57+
flex-grow: 1;
58+
padding: 0 0 0 0.2em;
59+
border: none;
60+
`;
61+
62+
const Container = styled.div`
63+
max-width: 55em;
64+
margin-bottom: 1.5em;
65+
`;
66+
67+
const HeaderContainer = styled.div`
68+
display: flex;
69+
justify-content: center;
70+
align-items: center;
71+
margin-bottom: 1em;
72+
`;
73+
74+
const TitleContainer = styled.div`
75+
flex-grow: 1;
76+
padding: 0;
77+
border: none;
78+
`;
79+
80+
const TagContainer = styled.div`
81+
padding: 0;
82+
border: none;
83+
`;
84+
85+
const ShowPathsLink = styled(VSCodeLink)`
86+
cursor: pointer;
87+
`;
88+
89+
type ThreadPathProps = {
90+
threadFlow: ThreadFlow;
91+
step: number;
92+
message: AnalysisMessage;
93+
severity: ResultSeverity;
94+
isSource?: boolean;
95+
isSink?: boolean;
96+
}
97+
98+
const ThreadPath = ({
99+
threadFlow,
100+
step,
45101
message,
46-
severity
47-
}: {
102+
severity,
103+
isSource,
104+
isSink,
105+
}: ThreadPathProps) => (
106+
<Container>
107+
<HeaderContainer>
108+
<TitleContainer>
109+
<SectionTitle>Step {step}</SectionTitle>
110+
</TitleContainer>
111+
{isSource &&
112+
<TagContainer>
113+
<VSCodeTag>Source</VSCodeTag>
114+
</TagContainer>
115+
}
116+
{isSink &&
117+
<TagContainer>
118+
<VSCodeTag>Sink</VSCodeTag>
119+
</TagContainer>
120+
}
121+
</HeaderContainer>
122+
123+
<FileCodeSnippet
124+
fileLink={threadFlow.fileLink}
125+
codeSnippet={threadFlow.codeSnippet}
126+
highlightedRegion={threadFlow.highlightedRegion}
127+
severity={severity}
128+
message={isSink ? message : threadFlow.message}
129+
/>
130+
</Container>
131+
);
132+
133+
type CodePathProps = {
48134
codeFlow: CodeFlow;
49135
message: AnalysisMessage;
50136
severity: ResultSeverity;
51-
}) => {
52-
return <>
137+
}
138+
139+
const CodePath = ({
140+
codeFlow,
141+
message,
142+
severity
143+
}: CodePathProps) => (
144+
<>
53145
{codeFlow.threadFlows.map((threadFlow, index) =>
54-
<div key={`thread-flow-${index}`} style={{ maxWidth: '55em' }}>
55-
{index !== 0 && <VerticalSpace size={3} />}
56-
57-
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
58-
<div style={{ flexGrow: 1, padding: 0, border: 'none' }}>
59-
<SectionTitle>Step {index + 1}</SectionTitle>
60-
</div>
61-
{index === 0 &&
62-
<div style={{ padding: 0, border: 'none' }}>
63-
<VSCodeTag>Source</VSCodeTag>
64-
</div>
65-
}
66-
{index === codeFlow.threadFlows.length - 1 &&
67-
<div style={{ padding: 0, border: 'none' }}>
68-
<VSCodeTag>Sink</VSCodeTag>
69-
</div>
70-
}
71-
</div>
72-
73-
<VerticalSpace size={2} />
74-
<FileCodeSnippet
75-
fileLink={threadFlow.fileLink}
76-
codeSnippet={threadFlow.codeSnippet}
77-
highlightedRegion={threadFlow.highlightedRegion}
78-
severity={severity}
79-
message={index === codeFlow.threadFlows.length - 1 ? message : threadFlow.message} />
80-
</div>
146+
<ThreadPath
147+
key={index}
148+
threadFlow={threadFlow}
149+
step={index + 1}
150+
message={message}
151+
severity={severity}
152+
isSource={index === 0}
153+
isSink={index === codeFlow.threadFlows.length - 1}
154+
/>
81155
)}
82-
</>;
83-
};
156+
</>
157+
);
84158

85159
const getCodeFlowName = (codeFlow: CodeFlow) => {
86160
const filePath = codeFlow.threadFlows[codeFlow.threadFlows.length - 1].fileLink.filePath;
87161
return filePath.substring(filePath.lastIndexOf('/') + 1);
88162
};
89163

90-
const Menu = ({
164+
type CodeFlowsDropdownProps = {
165+
codeFlows: CodeFlow[];
166+
setSelectedCodeFlow: (value: SetStateAction<CodeFlow>) => void;
167+
}
168+
169+
const CodeFlowsDropdown = ({
91170
codeFlows,
92171
setSelectedCodeFlow
93-
}: {
94-
codeFlows: CodeFlow[],
95-
setSelectedCodeFlow: (value: React.SetStateAction<CodeFlow>) => void
96-
}) => {
97-
return <VSCodeDropdown
98-
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
99-
const selectedOption = event.target;
100-
const selectedIndex = selectedOption.value as unknown as number;
101-
setSelectedCodeFlow(codeFlows[selectedIndex]);
102-
}}
103-
>
104-
{codeFlows.map((codeFlow, index) =>
105-
<VSCodeOption
106-
key={`codeflow-${index}'`}
107-
value={index}
108-
>
109-
{getCodeFlowName(codeFlow)}
110-
</VSCodeOption>
111-
)}
112-
</VSCodeDropdown>;
172+
}: CodeFlowsDropdownProps) => {
173+
const handleChange = useCallback((e: ChangeEvent<HTMLSelectElement>) => {
174+
const selectedOption = e.target;
175+
const selectedIndex = selectedOption.value as unknown as number;
176+
setSelectedCodeFlow(codeFlows[selectedIndex]);
177+
}, [setSelectedCodeFlow, codeFlows]);
178+
179+
return (
180+
<VSCodeDropdown onChange={handleChange}>
181+
{codeFlows.map((codeFlow, index) =>
182+
<VSCodeOption
183+
key={index}
184+
value={index}
185+
>
186+
{getCodeFlowName(codeFlow)}
187+
</VSCodeOption>
188+
)}
189+
</VSCodeDropdown>
190+
);
113191
};
114192

115-
export const CodePaths = ({
193+
type CodePathsOverlayProps = {
194+
codeFlows: CodeFlow[];
195+
ruleDescription: string;
196+
message: AnalysisMessage;
197+
severity: ResultSeverity;
198+
onClose: () => void;
199+
}
200+
201+
const CodePathsOverlay = ({
116202
codeFlows,
117203
ruleDescription,
118204
message,
119-
severity
120-
}: {
205+
severity,
206+
onClose,
207+
}: CodePathsOverlayProps) => {
208+
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
209+
210+
return (
211+
<OverlayContainer>
212+
<CloseButton onClick={onClose} />
213+
214+
<SectionTitle>{ruleDescription}</SectionTitle>
215+
<VerticalSpace size={2} />
216+
217+
<PathsContainer>
218+
<PathDetailsContainer>
219+
{codeFlows.length} paths available: {selectedCodeFlow.threadFlows.length} steps in
220+
</PathDetailsContainer>
221+
<PathDropdownContainer>
222+
<CodeFlowsDropdown codeFlows={codeFlows} setSelectedCodeFlow={setSelectedCodeFlow} />
223+
</PathDropdownContainer>
224+
</PathsContainer>
225+
226+
<VerticalSpace size={2} />
227+
<CodePath
228+
codeFlow={selectedCodeFlow}
229+
severity={severity}
230+
message={message}
231+
/>
232+
<VerticalSpace size={3} />
233+
</OverlayContainer>
234+
);
235+
};
236+
237+
type Props = {
121238
codeFlows: CodeFlow[],
122239
ruleDescription: string,
123240
message: AnalysisMessage,
124241
severity: ResultSeverity
125-
}) => {
242+
};
243+
244+
export const CodePaths = ({
245+
codeFlows,
246+
ruleDescription,
247+
message,
248+
severity
249+
}: Props) => {
126250
const [isOpen, setIsOpen] = useState(false);
127-
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
128251

129-
const anchorRef = useRef<HTMLDivElement>(null);
130252
const linkRef = useRef<HTMLAnchorElement>(null);
131253

132254
const closeOverlay = () => setIsOpen(false);
133255

134256
return (
135-
<div ref={anchorRef}>
136-
<VSCodeLink
257+
<>
258+
<ShowPathsLink
137259
onClick={() => setIsOpen(true)}
138260
ref={linkRef}
139-
sx={{ cursor: 'pointer' }}>
261+
>
140262
Show paths
141-
</VSCodeLink>
263+
</ShowPathsLink>
142264
{isOpen && (
143265
<Overlay
144266
returnFocusRef={linkRef}
145267
onEscape={closeOverlay}
146268
onClickOutside={closeOverlay}
147-
anchorSide="outside-top">
148-
<OverlayContainer>
149-
<CloseButton onClick={closeOverlay} />
150-
151-
<SectionTitle>{ruleDescription}</SectionTitle>
152-
<VerticalSpace size={2} />
153-
154-
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
155-
<div style={{ padding: 0, border: 0 }}>
156-
{codeFlows.length} paths available: {selectedCodeFlow.threadFlows.length} steps in
157-
</div>
158-
<div style={{ flexGrow: 1, padding: 0, paddingLeft: '0.2em', border: 'none' }}>
159-
<Menu codeFlows={codeFlows} setSelectedCodeFlow={setSelectedCodeFlow} />
160-
</div>
161-
</div>
162-
163-
<VerticalSpace size={2} />
164-
<CodePath
165-
codeFlow={selectedCodeFlow}
166-
severity={severity}
167-
message={message} />
168-
169-
<VerticalSpace size={3} />
170-
171-
</OverlayContainer>
269+
anchorSide="outside-top"
270+
>
271+
<CodePathsOverlay
272+
codeFlows={codeFlows}
273+
ruleDescription={ruleDescription}
274+
message={message}
275+
severity={severity}
276+
onClose={closeOverlay}
277+
/>
172278
</Overlay>
173279
)}
174-
</div>
280+
</>
175281
);
176282
};

0 commit comments

Comments
 (0)