Merge pull request #2732 from github/robertbrignull/AlertTable-decompose
Split AlertTable into smaller components
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import * as Keys from "./result-keys";
|
||||
import { info, listUnordered } from "./octicons";
|
||||
import {
|
||||
className,
|
||||
ResultTableProps,
|
||||
selectableZebraStripe,
|
||||
jumpToLocation,
|
||||
} from "./result-table-utils";
|
||||
import { onNavigation } from "./ResultsApp";
|
||||
@@ -19,11 +17,9 @@ import { parseSarifLocation, isNoLocation } from "../../common/sarif-utils";
|
||||
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
||||
import { sendTelemetry } from "../common/telemetry";
|
||||
import { AlertTableHeader } from "./AlertTableHeader";
|
||||
import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations";
|
||||
import { SarifLocation } from "./locations/SarifLocation";
|
||||
import { EmptyQueryResultsMessage } from "./EmptyQueryResultsMessage";
|
||||
import TextButton from "../common/TextButton";
|
||||
import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCell";
|
||||
import { AlertTableNoResults } from "./AlertTableNoResults";
|
||||
import { AlertTableTruncatedMessage } from "./AlertTableTruncatedMessage";
|
||||
import { AlertTableResultRow } from "./AlertTableResultRow";
|
||||
|
||||
type AlertTableProps = ResultTableProps & {
|
||||
resultSet: InterpretedResultSet<SarifInterpretationData>;
|
||||
@@ -70,263 +66,50 @@ export class AlertTable extends React.Component<
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
renderNoResults(): JSX.Element {
|
||||
if (this.props.nonemptyRawResults) {
|
||||
return (
|
||||
<span>
|
||||
No Alerts. See{" "}
|
||||
<TextButton onClick={this.props.showRawResults}>
|
||||
raw results
|
||||
</TextButton>
|
||||
.
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return <EmptyQueryResultsMessage />;
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { databaseUri, resultSet } = this.props;
|
||||
|
||||
const rows: JSX.Element[] = [];
|
||||
const { numTruncatedResults, sourceLocationPrefix } =
|
||||
resultSet.interpretation;
|
||||
|
||||
const updateSelectionCallback = (
|
||||
resultKey: Keys.PathNode | Keys.Result | undefined,
|
||||
) => {
|
||||
return () => {
|
||||
this.setState((previousState) => ({
|
||||
...previousState,
|
||||
selectedItem: resultKey,
|
||||
}));
|
||||
sendTelemetry("local-results-alert-table-path-selected");
|
||||
};
|
||||
};
|
||||
|
||||
const toggler: (keys: Keys.ResultKey[]) => (e: React.MouseEvent) => void = (
|
||||
indices,
|
||||
) => {
|
||||
return (e) => this.toggle(e, indices);
|
||||
this.setState((previousState) => ({
|
||||
...previousState,
|
||||
selectedItem: resultKey,
|
||||
}));
|
||||
sendTelemetry("local-results-alert-table-path-selected");
|
||||
};
|
||||
|
||||
if (!resultSet.interpretation.data.runs?.[0]?.results?.length) {
|
||||
return this.renderNoResults();
|
||||
}
|
||||
|
||||
resultSet.interpretation.data.runs[0].results.forEach(
|
||||
(result, resultIndex) => {
|
||||
const resultKey: Keys.Result = { resultIndex };
|
||||
const text = result.message.text || "[no text]";
|
||||
const msg =
|
||||
result.relatedLocations === undefined ? (
|
||||
<span key="0">{text}</span>
|
||||
) : (
|
||||
<SarifMessageWithLocations
|
||||
msg={text}
|
||||
relatedLocations={result.relatedLocations}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={updateSelectionCallback(resultKey)}
|
||||
/>
|
||||
);
|
||||
|
||||
const currentResultExpanded = this.state.expanded.has(
|
||||
Keys.keyToString(resultKey),
|
||||
);
|
||||
const location = result.locations !== undefined &&
|
||||
result.locations.length > 0 && (
|
||||
<SarifLocation
|
||||
loc={result.locations[0]}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={updateSelectionCallback(resultKey)}
|
||||
/>
|
||||
);
|
||||
const locationCells = (
|
||||
<td className="vscode-codeql__location-cell">{location}</td>
|
||||
);
|
||||
|
||||
const selectedItem = this.state.selectedItem;
|
||||
const resultRowIsSelected =
|
||||
selectedItem?.resultIndex === resultIndex &&
|
||||
selectedItem.pathIndex === undefined;
|
||||
|
||||
if (result.codeFlows === undefined) {
|
||||
rows.push(
|
||||
<tr
|
||||
ref={this.scroller.ref(resultRowIsSelected)}
|
||||
key={resultIndex}
|
||||
{...selectableZebraStripe(resultRowIsSelected, resultIndex)}
|
||||
>
|
||||
<td className="vscode-codeql__icon-cell">{info}</td>
|
||||
<td colSpan={3}>{msg}</td>
|
||||
{locationCells}
|
||||
</tr>,
|
||||
);
|
||||
} else {
|
||||
const paths: Sarif.ThreadFlow[] = Keys.getAllPaths(result);
|
||||
|
||||
const indices =
|
||||
paths.length === 1
|
||||
? [resultKey, { ...resultKey, pathIndex: 0 }]
|
||||
: /* if there's exactly one path, auto-expand
|
||||
* the path when expanding the result */
|
||||
[resultKey];
|
||||
|
||||
rows.push(
|
||||
<tr
|
||||
ref={this.scroller.ref(resultRowIsSelected)}
|
||||
{...selectableZebraStripe(resultRowIsSelected, resultIndex)}
|
||||
key={resultIndex}
|
||||
>
|
||||
<AlertTableDropdownIndicatorCell
|
||||
expanded={currentResultExpanded}
|
||||
onClick={toggler(indices)}
|
||||
/>
|
||||
<td className="vscode-codeql__icon-cell">{listUnordered}</td>
|
||||
<td colSpan={2}>{msg}</td>
|
||||
{locationCells}
|
||||
</tr>,
|
||||
);
|
||||
|
||||
paths.forEach((path, pathIndex) => {
|
||||
const pathKey = { resultIndex, pathIndex };
|
||||
const currentPathExpanded = this.state.expanded.has(
|
||||
Keys.keyToString(pathKey),
|
||||
);
|
||||
if (currentResultExpanded) {
|
||||
const isPathSpecificallySelected = Keys.equalsNotUndefined(
|
||||
pathKey,
|
||||
selectedItem,
|
||||
);
|
||||
rows.push(
|
||||
<tr
|
||||
ref={this.scroller.ref(isPathSpecificallySelected)}
|
||||
{...selectableZebraStripe(
|
||||
isPathSpecificallySelected,
|
||||
resultIndex,
|
||||
)}
|
||||
key={`${resultIndex}-${pathIndex}`}
|
||||
>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<AlertTableDropdownIndicatorCell
|
||||
expanded={currentPathExpanded}
|
||||
onClick={toggler([pathKey])}
|
||||
/>
|
||||
<td className="vscode-codeql__text-center" colSpan={3}>
|
||||
Path
|
||||
</td>
|
||||
</tr>,
|
||||
);
|
||||
}
|
||||
|
||||
if (currentResultExpanded && currentPathExpanded) {
|
||||
const pathNodes = path.locations;
|
||||
for (
|
||||
let pathNodeIndex = 0;
|
||||
pathNodeIndex < pathNodes.length;
|
||||
++pathNodeIndex
|
||||
) {
|
||||
const pathNodeKey: Keys.PathNode = {
|
||||
...pathKey,
|
||||
pathNodeIndex,
|
||||
};
|
||||
const step = pathNodes[pathNodeIndex];
|
||||
const msg =
|
||||
step.location !== undefined &&
|
||||
step.location.message !== undefined ? (
|
||||
<SarifLocation
|
||||
text={step.location.message.text}
|
||||
loc={step.location}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={updateSelectionCallback(pathNodeKey)}
|
||||
/>
|
||||
) : (
|
||||
"[no location]"
|
||||
);
|
||||
const additionalMsg =
|
||||
step.location !== undefined ? (
|
||||
<SarifLocation
|
||||
loc={step.location}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={updateSelectionCallback(pathNodeKey)}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
const isSelected = Keys.equalsNotUndefined(
|
||||
this.state.selectedItem,
|
||||
pathNodeKey,
|
||||
);
|
||||
const stepIndex = pathNodeIndex + 1; // Convert to 1-based
|
||||
const zebraIndex = resultIndex + stepIndex;
|
||||
rows.push(
|
||||
<tr
|
||||
ref={this.scroller.ref(isSelected)}
|
||||
className={
|
||||
isSelected
|
||||
? "vscode-codeql__selected-path-node"
|
||||
: undefined
|
||||
}
|
||||
key={`${resultIndex}-${pathIndex}-${pathNodeIndex}`}
|
||||
>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<td
|
||||
{...selectableZebraStripe(
|
||||
isSelected,
|
||||
zebraIndex,
|
||||
"vscode-codeql__path-index-cell",
|
||||
)}
|
||||
>
|
||||
{stepIndex}
|
||||
</td>
|
||||
<td {...selectableZebraStripe(isSelected, zebraIndex)}>
|
||||
{msg}{" "}
|
||||
</td>
|
||||
<td
|
||||
{...selectableZebraStripe(
|
||||
isSelected,
|
||||
zebraIndex,
|
||||
"vscode-codeql__location-cell",
|
||||
)}
|
||||
>
|
||||
{additionalMsg}
|
||||
</td>
|
||||
</tr>,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (numTruncatedResults > 0) {
|
||||
rows.push(
|
||||
<tr key="truncatd-message">
|
||||
<td colSpan={5} style={{ textAlign: "center", fontStyle: "italic" }}>
|
||||
Too many results to show at once. {numTruncatedResults} result(s)
|
||||
omitted.
|
||||
</td>
|
||||
</tr>,
|
||||
);
|
||||
return <AlertTableNoResults {...this.props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<table className={className}>
|
||||
<AlertTableHeader sortState={resultSet.interpretation.data.sortState} />
|
||||
<tbody>{rows}</tbody>
|
||||
<tbody>
|
||||
{resultSet.interpretation.data.runs[0].results.map(
|
||||
(result, resultIndex) => (
|
||||
<AlertTableResultRow
|
||||
key={resultIndex}
|
||||
result={result}
|
||||
resultIndex={resultIndex}
|
||||
expanded={this.state.expanded}
|
||||
selectedItem={this.state.selectedItem}
|
||||
databaseUri={databaseUri}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
updateSelectionCallback={updateSelectionCallback}
|
||||
toggleExpanded={this.toggle.bind(this)}
|
||||
scroller={this.scroller}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AlertTableTruncatedMessage
|
||||
numTruncatedResults={numTruncatedResults}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as React from "react";
|
||||
import { EmptyQueryResultsMessage } from "./EmptyQueryResultsMessage";
|
||||
import TextButton from "../common/TextButton";
|
||||
|
||||
interface Props {
|
||||
nonemptyRawResults: boolean;
|
||||
showRawResults: () => void;
|
||||
}
|
||||
|
||||
export function AlertTableNoResults(props: Props): JSX.Element {
|
||||
if (props.nonemptyRawResults) {
|
||||
return (
|
||||
<span>
|
||||
No Alerts. See{" "}
|
||||
<TextButton onClick={props.showRawResults}>raw results</TextButton>.
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return <EmptyQueryResultsMessage />;
|
||||
}
|
||||
}
|
||||
103
extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx
Normal file
103
extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import * as Keys from "./result-keys";
|
||||
import { SarifLocation } from "./locations/SarifLocation";
|
||||
import { selectableZebraStripe } from "./result-table-utils";
|
||||
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
interface Props {
|
||||
step: Sarif.ThreadFlowLocation;
|
||||
pathNodeIndex: number;
|
||||
pathIndex: number;
|
||||
resultIndex: number;
|
||||
selectedItem: undefined | Keys.ResultKey;
|
||||
databaseUri: string;
|
||||
sourceLocationPrefix: string;
|
||||
updateSelectionCallback: (
|
||||
resultKey: Keys.PathNode | Keys.Result | undefined,
|
||||
) => void;
|
||||
scroller: ScrollIntoViewHelper;
|
||||
}
|
||||
|
||||
export function AlertTablePathNodeRow(props: Props) {
|
||||
const {
|
||||
step,
|
||||
pathNodeIndex,
|
||||
pathIndex,
|
||||
resultIndex,
|
||||
selectedItem,
|
||||
databaseUri,
|
||||
sourceLocationPrefix,
|
||||
updateSelectionCallback,
|
||||
scroller,
|
||||
} = props;
|
||||
|
||||
const pathNodeKey: Keys.PathNode = useMemo(
|
||||
() => ({
|
||||
resultIndex,
|
||||
pathIndex,
|
||||
pathNodeIndex,
|
||||
}),
|
||||
[pathIndex, pathNodeIndex, resultIndex],
|
||||
);
|
||||
const handleSarifLocationClicked = useCallback(
|
||||
() => updateSelectionCallback(pathNodeKey),
|
||||
[pathNodeKey, updateSelectionCallback],
|
||||
);
|
||||
|
||||
const isSelected = Keys.equalsNotUndefined(selectedItem, pathNodeKey);
|
||||
const stepIndex = pathNodeIndex + 1; // Convert to 1-based
|
||||
const zebraIndex = resultIndex + stepIndex;
|
||||
return (
|
||||
<tr
|
||||
ref={scroller.ref(isSelected)}
|
||||
className={isSelected ? "vscode-codeql__selected-path-node" : undefined}
|
||||
>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<td
|
||||
{...selectableZebraStripe(
|
||||
isSelected,
|
||||
zebraIndex,
|
||||
"vscode-codeql__path-index-cell",
|
||||
)}
|
||||
>
|
||||
{stepIndex}
|
||||
</td>
|
||||
<td {...selectableZebraStripe(isSelected, zebraIndex)}>
|
||||
{step.location && step.location.message ? (
|
||||
<SarifLocation
|
||||
text={step.location.message.text}
|
||||
loc={step.location}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={handleSarifLocationClicked}
|
||||
/>
|
||||
) : (
|
||||
"[no location]"
|
||||
)}
|
||||
</td>
|
||||
<td
|
||||
{...selectableZebraStripe(
|
||||
isSelected,
|
||||
zebraIndex,
|
||||
"vscode-codeql__location-cell",
|
||||
)}
|
||||
>
|
||||
{step.location && (
|
||||
<SarifLocation
|
||||
loc={step.location}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={handleSarifLocationClicked}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
78
extensions/ql-vscode/src/view/results/AlertTablePathRow.tsx
Normal file
78
extensions/ql-vscode/src/view/results/AlertTablePathRow.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import * as Keys from "./result-keys";
|
||||
import { selectableZebraStripe } from "./result-table-utils";
|
||||
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
||||
import { AlertTablePathNodeRow } from "./AlertTablePathNodeRow";
|
||||
import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCell";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
interface Props {
|
||||
path: Sarif.ThreadFlow;
|
||||
pathIndex: number;
|
||||
resultIndex: number;
|
||||
currentPathExpanded: boolean;
|
||||
selectedItem: undefined | Keys.ResultKey;
|
||||
databaseUri: string;
|
||||
sourceLocationPrefix: string;
|
||||
updateSelectionCallback: (
|
||||
resultKey: Keys.PathNode | Keys.Result | undefined,
|
||||
) => void;
|
||||
toggleExpanded: (e: React.MouseEvent, keys: Keys.ResultKey[]) => void;
|
||||
scroller: ScrollIntoViewHelper;
|
||||
}
|
||||
|
||||
export function AlertTablePathRow(props: Props) {
|
||||
const {
|
||||
path,
|
||||
pathIndex,
|
||||
resultIndex,
|
||||
currentPathExpanded,
|
||||
selectedItem,
|
||||
toggleExpanded,
|
||||
scroller,
|
||||
} = props;
|
||||
|
||||
const pathKey = useMemo(
|
||||
() => ({ resultIndex, pathIndex }),
|
||||
[pathIndex, resultIndex],
|
||||
);
|
||||
const handleDropdownClick = useCallback(
|
||||
(e: React.MouseEvent) => toggleExpanded(e, [pathKey]),
|
||||
[pathKey, toggleExpanded],
|
||||
);
|
||||
|
||||
const isPathSpecificallySelected = Keys.equalsNotUndefined(
|
||||
pathKey,
|
||||
selectedItem,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
ref={scroller.ref(isPathSpecificallySelected)}
|
||||
{...selectableZebraStripe(isPathSpecificallySelected, resultIndex)}
|
||||
>
|
||||
<td className="vscode-codeql__icon-cell">
|
||||
<span className="vscode-codeql__vertical-rule"></span>
|
||||
</td>
|
||||
<AlertTableDropdownIndicatorCell
|
||||
expanded={currentPathExpanded}
|
||||
onClick={handleDropdownClick}
|
||||
/>
|
||||
<td className="vscode-codeql__text-center" colSpan={3}>
|
||||
Path
|
||||
</td>
|
||||
</tr>
|
||||
{currentPathExpanded &&
|
||||
path.locations.map((step, pathNodeIndex) => (
|
||||
<AlertTablePathNodeRow
|
||||
key={`${resultIndex}-${pathIndex}-${pathNodeIndex}`}
|
||||
{...props}
|
||||
step={step}
|
||||
pathNodeIndex={pathNodeIndex}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
128
extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx
Normal file
128
extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import * as Keys from "./result-keys";
|
||||
import { info, listUnordered } from "./octicons";
|
||||
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
||||
import { selectableZebraStripe } from "./result-table-utils";
|
||||
import { AlertTableDropdownIndicatorCell } from "./AlertTableDropdownIndicatorCell";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { SarifLocation } from "./locations/SarifLocation";
|
||||
import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations";
|
||||
import { AlertTablePathRow } from "./AlertTablePathRow";
|
||||
|
||||
interface Props {
|
||||
result: Sarif.Result;
|
||||
resultIndex: number;
|
||||
expanded: Set<string>;
|
||||
selectedItem: undefined | Keys.ResultKey;
|
||||
databaseUri: string;
|
||||
sourceLocationPrefix: string;
|
||||
updateSelectionCallback: (
|
||||
resultKey: Keys.PathNode | Keys.Result | undefined,
|
||||
) => void;
|
||||
toggleExpanded: (e: React.MouseEvent, keys: Keys.ResultKey[]) => void;
|
||||
scroller: ScrollIntoViewHelper;
|
||||
}
|
||||
|
||||
export function AlertTableResultRow(props: Props) {
|
||||
const {
|
||||
result,
|
||||
resultIndex,
|
||||
expanded,
|
||||
selectedItem,
|
||||
databaseUri,
|
||||
sourceLocationPrefix,
|
||||
updateSelectionCallback,
|
||||
toggleExpanded,
|
||||
scroller,
|
||||
} = props;
|
||||
|
||||
const resultKey: Keys.Result = useMemo(
|
||||
() => ({ resultIndex }),
|
||||
[resultIndex],
|
||||
);
|
||||
|
||||
const handleSarifLocationClicked = useCallback(
|
||||
() => updateSelectionCallback(resultKey),
|
||||
[resultKey, updateSelectionCallback],
|
||||
);
|
||||
const handleDropdownClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
const indices =
|
||||
Keys.getAllPaths(result).length === 1
|
||||
? [resultKey, { ...resultKey, pathIndex: 0 }]
|
||||
: /* if there's exactly one path, auto-expand
|
||||
* the path when expanding the result */
|
||||
[resultKey];
|
||||
toggleExpanded(e, indices);
|
||||
},
|
||||
[result, resultKey, toggleExpanded],
|
||||
);
|
||||
|
||||
const resultRowIsSelected =
|
||||
selectedItem?.resultIndex === resultIndex &&
|
||||
selectedItem.pathIndex === undefined;
|
||||
|
||||
const text = result.message.text || "[no text]";
|
||||
const msg =
|
||||
result.relatedLocations === undefined ? (
|
||||
<span key="0">{text}</span>
|
||||
) : (
|
||||
<SarifMessageWithLocations
|
||||
msg={text}
|
||||
relatedLocations={result.relatedLocations}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={handleSarifLocationClicked}
|
||||
/>
|
||||
);
|
||||
|
||||
const currentResultExpanded = expanded.has(Keys.keyToString(resultKey));
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
ref={scroller.ref(resultRowIsSelected)}
|
||||
{...selectableZebraStripe(resultRowIsSelected, resultIndex)}
|
||||
>
|
||||
{result.codeFlows === undefined ? (
|
||||
<>
|
||||
<td className="vscode-codeql__icon-cell">{info}</td>
|
||||
<td colSpan={3}>{msg}</td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AlertTableDropdownIndicatorCell
|
||||
expanded={currentResultExpanded}
|
||||
onClick={handleDropdownClick}
|
||||
/>
|
||||
<td className="vscode-codeql__icon-cell">{listUnordered}</td>
|
||||
<td colSpan={2}>{msg}</td>
|
||||
</>
|
||||
)}
|
||||
<td className="vscode-codeql__location-cell">
|
||||
{result.locations && result.locations.length > 0 && (
|
||||
<SarifLocation
|
||||
loc={result.locations[0]}
|
||||
sourceLocationPrefix={sourceLocationPrefix}
|
||||
databaseUri={databaseUri}
|
||||
onClick={handleSarifLocationClicked}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
{currentResultExpanded &&
|
||||
result.codeFlows &&
|
||||
Keys.getAllPaths(result).map((path, pathIndex) => (
|
||||
<AlertTablePathRow
|
||||
key={`${resultIndex}-${pathIndex}`}
|
||||
{...props}
|
||||
path={path}
|
||||
pathIndex={pathIndex}
|
||||
currentPathExpanded={expanded.has(
|
||||
Keys.keyToString({ resultIndex, pathIndex }),
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as React from "react";
|
||||
|
||||
interface Props {
|
||||
numTruncatedResults: number;
|
||||
}
|
||||
|
||||
export function AlertTableTruncatedMessage(props: Props): JSX.Element | null {
|
||||
if (props.numTruncatedResults === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<tr key="truncatd-message">
|
||||
<td colSpan={5} style={{ textAlign: "center", fontStyle: "italic" }}>
|
||||
Too many results to show at once. {props.numTruncatedResults} result(s)
|
||||
omitted.
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user