Merge pull request #2666 from github/robertbrignull/ResultTables-Header

Move ResultTablesHeader to its own component
This commit is contained in:
Robert
2023-08-11 15:45:41 +01:00
committed by GitHub
2 changed files with 149 additions and 107 deletions

View File

@@ -19,12 +19,11 @@ import {
tableHeaderItemClassName,
toggleDiagnosticsClassName,
alertExtrasClassName,
openFile,
} from "./result-table-utils";
import { vscode } from "../vscode-api";
import { sendTelemetry } from "../common/telemetry";
import { basename } from "../../common/path";
import { ResultTable } from "./ResultTable";
import { ResultTablesHeader } from "./ResultTablesHeader";
/**
* Properties for the `ResultTables` component.
@@ -49,7 +48,6 @@ interface ResultTablesProps {
*/
interface ResultTablesState {
selectedTable: string; // name of selected result set
selectedPage: string; // stringified selected page
problemsViewSelected: boolean;
}
@@ -135,10 +133,8 @@ export class ResultTables extends React.Component<
getDefaultResultSet(
getResultSets(props.rawResultSets, props.interpretation),
);
const selectedPage = `${props.parsedResultSets.pageNumber + 1}`;
this.state = {
selectedTable,
selectedPage,
problemsViewSelected: false,
};
}
@@ -165,10 +161,7 @@ export class ResultTables extends React.Component<
getResultSets(props.rawResultSets, props.interpretation),
);
return {
selectedTable,
selectedPage: `${props.parsedResultSets.pageNumber + 1}`,
};
return { selectedTable };
});
}
}
@@ -236,103 +229,6 @@ export class ResultTables extends React.Component<
sendTelemetry("local-results-alert-table-page-changed");
}
renderPageButtons(): JSX.Element {
const { parsedResultSets } = this.props;
const selectedTable = this.state.selectedTable;
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
// instead.
const numPages = Math.max(
selectedTable === ALERTS_TABLE_NAME
? parsedResultSets.numInterpretedPages
: parsedResultSets.numPages,
1,
);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ selectedPage: e.target.value });
this.sendResultsPageChangedTelemetry();
};
const choosePage = (input: string) => {
const pageNumber = parseInt(input);
if (pageNumber !== undefined && !isNaN(pageNumber)) {
const actualPageNumber = Math.max(
0,
Math.min(pageNumber - 1, numPages - 1),
);
vscode.postMessage({
t: "changePage",
pageNumber: actualPageNumber,
selectedTable,
});
}
};
const prevPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
vscode.postMessage({
t: "changePage",
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
selectedTable,
});
this.sendResultsPageChangedTelemetry();
};
const nextPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
vscode.postMessage({
t: "changePage",
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
selectedTable,
});
this.sendResultsPageChangedTelemetry();
};
const openQuery = () => {
openFile(this.props.queryPath);
sendTelemetry("local-results-open-query-file");
};
const fileName = basename(this.props.queryPath);
return (
<span className="vscode-codeql__table-selection-pagination">
<button onClick={prevPage}>&#xab;</button>
<input
type="number"
size={3}
value={this.state.selectedPage}
min="1"
max={numPages}
onChange={onChange}
onBlur={(e) => choosePage(e.target.value)}
onKeyDown={(e) => {
if (e.keyCode === 13) {
choosePage((e.target as HTMLInputElement).value);
}
}}
/>
<span>/&nbsp;{numPages}</span>
<button value=">" onClick={nextPage}>
&#xbb;
</button>
<div className={tableHeaderItemClassName}>{this.props.queryName}</div>
<div className={tableHeaderItemClassName}>
{/*
eslint-disable-next-line
jsx-a11y/anchor-is-valid
*/}
<a
href="#"
onClick={openQuery}
className="vscode-codeql__result-table-location-link"
>
Open {fileName}
</a>
</div>
</span>
);
}
render(): React.ReactNode {
const { selectedTable } = this.state;
const resultSets = getResultSets(
@@ -360,7 +256,10 @@ export class ResultTables extends React.Component<
));
return (
<div>
{this.renderPageButtons()}
<ResultTablesHeader
{...this.props}
selectedTable={this.state.selectedTable}
/>
<div className={tableHeaderClassName}></div>
<div className={tableHeaderClassName}>
<select value={selectedTable} onChange={this.onTableSelectionChange}>

View File

@@ -0,0 +1,143 @@
import * as React from "react";
import { useCallback, useEffect } from "react";
import { vscode } from "../vscode-api";
import { openFile, tableHeaderItemClassName } from "./result-table-utils";
import { sendTelemetry } from "../common/telemetry";
import {
ALERTS_TABLE_NAME,
ParsedResultSets,
} from "../../common/interface-types";
import { basename } from "../../common/path";
interface Props {
queryName: string;
queryPath: string;
parsedResultSets: ParsedResultSets;
selectedTable: string;
}
export function ResultTablesHeader(props: Props) {
const { queryPath, queryName, parsedResultSets, selectedTable } = props;
const [selectedPage, setSelectedPage] = React.useState(
`${parsedResultSets.pageNumber + 1}`,
);
useEffect(() => {
setSelectedPage(`${parsedResultSets.pageNumber + 1}`);
}, [parsedResultSets.pageNumber]);
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
// instead.
const numPages = Math.max(
selectedTable === ALERTS_TABLE_NAME
? parsedResultSets.numInterpretedPages
: parsedResultSets.numPages,
1,
);
const onChangeHandler = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setSelectedPage(e.target.value);
sendResultsPageChangedTelemetry();
},
[],
);
const changePage = useCallback(
(value: string) => {
const pageNumber = parseInt(value);
if (pageNumber !== undefined && !isNaN(pageNumber)) {
const actualPageNumber = Math.max(
0,
Math.min(pageNumber - 1, numPages - 1),
);
vscode.postMessage({
t: "changePage",
pageNumber: actualPageNumber,
selectedTable,
});
}
},
[numPages, selectedTable],
);
const onBlurHandler = useCallback(
(e: React.FocusEvent<HTMLInputElement, Element>) => {
changePage(e.target.value);
},
[changePage],
);
const onKeyDownHandler = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
changePage(e.currentTarget.value);
}
},
[changePage],
);
const prevPageHandler = useCallback(() => {
vscode.postMessage({
t: "changePage",
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
selectedTable,
});
sendResultsPageChangedTelemetry();
}, [parsedResultSets.pageNumber, selectedTable]);
const nextPageHandler = useCallback(() => {
vscode.postMessage({
t: "changePage",
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
selectedTable,
});
sendResultsPageChangedTelemetry();
}, [numPages, parsedResultSets.pageNumber, selectedTable]);
const openQueryHandler = useCallback(() => {
openFile(queryPath);
sendTelemetry("local-results-open-query-file");
}, [queryPath]);
return (
<span className="vscode-codeql__table-selection-pagination">
<button onClick={prevPageHandler}>&#xab;</button>
<input
type="number"
size={3}
value={selectedPage}
min="1"
max={numPages}
onChange={onChangeHandler}
onBlur={onBlurHandler}
onKeyDown={onKeyDownHandler}
/>
<span>/&nbsp;{numPages}</span>
<button value=">" onClick={nextPageHandler}>
&#xbb;
</button>
<div className={tableHeaderItemClassName}>{queryName}</div>
<div className={tableHeaderItemClassName}>
{/*
eslint-disable-next-line
jsx-a11y/anchor-is-valid
*/}
<a
href="#"
onClick={openQueryHandler}
className="vscode-codeql__result-table-location-link"
>
Open {basename(queryPath)}
</a>
</div>
</span>
);
}
function sendResultsPageChangedTelemetry() {
sendTelemetry("local-results-alert-table-page-changed");
}