Skip to content

Commit dd0534b

Browse files
Merge pull request #2666 from github/robertbrignull/ResultTables-Header
Move ResultTablesHeader to its own component
2 parents 3b7f7e5 + dd89f0d commit dd0534b

File tree

2 files changed

+149
-107
lines changed

2 files changed

+149
-107
lines changed

extensions/ql-vscode/src/view/results/ResultTables.tsx

Lines changed: 6 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ import {
1919
tableHeaderItemClassName,
2020
toggleDiagnosticsClassName,
2121
alertExtrasClassName,
22-
openFile,
2322
} from "./result-table-utils";
2423
import { vscode } from "../vscode-api";
2524
import { sendTelemetry } from "../common/telemetry";
26-
import { basename } from "../../common/path";
2725
import { ResultTable } from "./ResultTable";
26+
import { ResultTablesHeader } from "./ResultTablesHeader";
2827

2928
/**
3029
* Properties for the `ResultTables` component.
@@ -49,7 +48,6 @@ interface ResultTablesProps {
4948
*/
5049
interface ResultTablesState {
5150
selectedTable: string; // name of selected result set
52-
selectedPage: string; // stringified selected page
5351
problemsViewSelected: boolean;
5452
}
5553

@@ -135,10 +133,8 @@ export class ResultTables extends React.Component<
135133
getDefaultResultSet(
136134
getResultSets(props.rawResultSets, props.interpretation),
137135
);
138-
const selectedPage = `${props.parsedResultSets.pageNumber + 1}`;
139136
this.state = {
140137
selectedTable,
141-
selectedPage,
142138
problemsViewSelected: false,
143139
};
144140
}
@@ -165,10 +161,7 @@ export class ResultTables extends React.Component<
165161
getResultSets(props.rawResultSets, props.interpretation),
166162
);
167163

168-
return {
169-
selectedTable,
170-
selectedPage: `${props.parsedResultSets.pageNumber + 1}`,
171-
};
164+
return { selectedTable };
172165
});
173166
}
174167
}
@@ -236,103 +229,6 @@ export class ResultTables extends React.Component<
236229
sendTelemetry("local-results-alert-table-page-changed");
237230
}
238231

239-
renderPageButtons(): JSX.Element {
240-
const { parsedResultSets } = this.props;
241-
const selectedTable = this.state.selectedTable;
242-
243-
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
244-
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
245-
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
246-
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
247-
// instead.
248-
const numPages = Math.max(
249-
selectedTable === ALERTS_TABLE_NAME
250-
? parsedResultSets.numInterpretedPages
251-
: parsedResultSets.numPages,
252-
1,
253-
);
254-
255-
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
256-
this.setState({ selectedPage: e.target.value });
257-
this.sendResultsPageChangedTelemetry();
258-
};
259-
const choosePage = (input: string) => {
260-
const pageNumber = parseInt(input);
261-
if (pageNumber !== undefined && !isNaN(pageNumber)) {
262-
const actualPageNumber = Math.max(
263-
0,
264-
Math.min(pageNumber - 1, numPages - 1),
265-
);
266-
vscode.postMessage({
267-
t: "changePage",
268-
pageNumber: actualPageNumber,
269-
selectedTable,
270-
});
271-
}
272-
};
273-
274-
const prevPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
275-
vscode.postMessage({
276-
t: "changePage",
277-
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
278-
selectedTable,
279-
});
280-
this.sendResultsPageChangedTelemetry();
281-
};
282-
const nextPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
283-
vscode.postMessage({
284-
t: "changePage",
285-
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
286-
selectedTable,
287-
});
288-
this.sendResultsPageChangedTelemetry();
289-
};
290-
291-
const openQuery = () => {
292-
openFile(this.props.queryPath);
293-
sendTelemetry("local-results-open-query-file");
294-
};
295-
const fileName = basename(this.props.queryPath);
296-
297-
return (
298-
<span className="vscode-codeql__table-selection-pagination">
299-
<button onClick={prevPage}>&#xab;</button>
300-
<input
301-
type="number"
302-
size={3}
303-
value={this.state.selectedPage}
304-
min="1"
305-
max={numPages}
306-
onChange={onChange}
307-
onBlur={(e) => choosePage(e.target.value)}
308-
onKeyDown={(e) => {
309-
if (e.keyCode === 13) {
310-
choosePage((e.target as HTMLInputElement).value);
311-
}
312-
}}
313-
/>
314-
<span>/&nbsp;{numPages}</span>
315-
<button value=">" onClick={nextPage}>
316-
&#xbb;
317-
</button>
318-
<div className={tableHeaderItemClassName}>{this.props.queryName}</div>
319-
<div className={tableHeaderItemClassName}>
320-
{/*
321-
eslint-disable-next-line
322-
jsx-a11y/anchor-is-valid
323-
*/}
324-
<a
325-
href="#"
326-
onClick={openQuery}
327-
className="vscode-codeql__result-table-location-link"
328-
>
329-
Open {fileName}
330-
</a>
331-
</div>
332-
</span>
333-
);
334-
}
335-
336232
render(): React.ReactNode {
337233
const { selectedTable } = this.state;
338234
const resultSets = getResultSets(
@@ -360,7 +256,10 @@ export class ResultTables extends React.Component<
360256
));
361257
return (
362258
<div>
363-
{this.renderPageButtons()}
259+
<ResultTablesHeader
260+
{...this.props}
261+
selectedTable={this.state.selectedTable}
262+
/>
364263
<div className={tableHeaderClassName}></div>
365264
<div className={tableHeaderClassName}>
366265
<select value={selectedTable} onChange={this.onTableSelectionChange}>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import * as React from "react";
2+
import { useCallback, useEffect } from "react";
3+
import { vscode } from "../vscode-api";
4+
import { openFile, tableHeaderItemClassName } from "./result-table-utils";
5+
import { sendTelemetry } from "../common/telemetry";
6+
import {
7+
ALERTS_TABLE_NAME,
8+
ParsedResultSets,
9+
} from "../../common/interface-types";
10+
import { basename } from "../../common/path";
11+
12+
interface Props {
13+
queryName: string;
14+
queryPath: string;
15+
parsedResultSets: ParsedResultSets;
16+
selectedTable: string;
17+
}
18+
19+
export function ResultTablesHeader(props: Props) {
20+
const { queryPath, queryName, parsedResultSets, selectedTable } = props;
21+
22+
const [selectedPage, setSelectedPage] = React.useState(
23+
`${parsedResultSets.pageNumber + 1}`,
24+
);
25+
useEffect(() => {
26+
setSelectedPage(`${parsedResultSets.pageNumber + 1}`);
27+
}, [parsedResultSets.pageNumber]);
28+
29+
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
30+
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
31+
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
32+
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
33+
// instead.
34+
const numPages = Math.max(
35+
selectedTable === ALERTS_TABLE_NAME
36+
? parsedResultSets.numInterpretedPages
37+
: parsedResultSets.numPages,
38+
1,
39+
);
40+
41+
const onChangeHandler = useCallback(
42+
(e: React.ChangeEvent<HTMLInputElement>) => {
43+
setSelectedPage(e.target.value);
44+
sendResultsPageChangedTelemetry();
45+
},
46+
[],
47+
);
48+
49+
const changePage = useCallback(
50+
(value: string) => {
51+
const pageNumber = parseInt(value);
52+
if (pageNumber !== undefined && !isNaN(pageNumber)) {
53+
const actualPageNumber = Math.max(
54+
0,
55+
Math.min(pageNumber - 1, numPages - 1),
56+
);
57+
vscode.postMessage({
58+
t: "changePage",
59+
pageNumber: actualPageNumber,
60+
selectedTable,
61+
});
62+
}
63+
},
64+
[numPages, selectedTable],
65+
);
66+
67+
const onBlurHandler = useCallback(
68+
(e: React.FocusEvent<HTMLInputElement, Element>) => {
69+
changePage(e.target.value);
70+
},
71+
[changePage],
72+
);
73+
74+
const onKeyDownHandler = useCallback(
75+
(e: React.KeyboardEvent<HTMLInputElement>) => {
76+
if (e.key === "Enter") {
77+
changePage(e.currentTarget.value);
78+
}
79+
},
80+
[changePage],
81+
);
82+
83+
const prevPageHandler = useCallback(() => {
84+
vscode.postMessage({
85+
t: "changePage",
86+
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
87+
selectedTable,
88+
});
89+
sendResultsPageChangedTelemetry();
90+
}, [parsedResultSets.pageNumber, selectedTable]);
91+
92+
const nextPageHandler = useCallback(() => {
93+
vscode.postMessage({
94+
t: "changePage",
95+
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
96+
selectedTable,
97+
});
98+
sendResultsPageChangedTelemetry();
99+
}, [numPages, parsedResultSets.pageNumber, selectedTable]);
100+
101+
const openQueryHandler = useCallback(() => {
102+
openFile(queryPath);
103+
sendTelemetry("local-results-open-query-file");
104+
}, [queryPath]);
105+
106+
return (
107+
<span className="vscode-codeql__table-selection-pagination">
108+
<button onClick={prevPageHandler}>&#xab;</button>
109+
<input
110+
type="number"
111+
size={3}
112+
value={selectedPage}
113+
min="1"
114+
max={numPages}
115+
onChange={onChangeHandler}
116+
onBlur={onBlurHandler}
117+
onKeyDown={onKeyDownHandler}
118+
/>
119+
<span>/&nbsp;{numPages}</span>
120+
<button value=">" onClick={nextPageHandler}>
121+
&#xbb;
122+
</button>
123+
<div className={tableHeaderItemClassName}>{queryName}</div>
124+
<div className={tableHeaderItemClassName}>
125+
{/*
126+
eslint-disable-next-line
127+
jsx-a11y/anchor-is-valid
128+
*/}
129+
<a
130+
href="#"
131+
onClick={openQueryHandler}
132+
className="vscode-codeql__result-table-location-link"
133+
>
134+
Open {basename(queryPath)}
135+
</a>
136+
</div>
137+
</span>
138+
);
139+
}
140+
141+
function sendResultsPageChangedTelemetry() {
142+
sendTelemetry("local-results-alert-table-page-changed");
143+
}

0 commit comments

Comments
 (0)