Skip to content

Commit 892f052

Browse files
Move ResultTablesHeader to its own component
1 parent 9ed6b01 commit 892f052

File tree

2 files changed

+147
-107
lines changed

2 files changed

+147
-107
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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+
changePage(e.currentTarget.value);
77+
},
78+
[changePage],
79+
);
80+
81+
const prevPageHandler = useCallback(() => {
82+
vscode.postMessage({
83+
t: "changePage",
84+
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
85+
selectedTable,
86+
});
87+
sendResultsPageChangedTelemetry();
88+
}, [parsedResultSets.pageNumber, selectedTable]);
89+
90+
const nextPageHandler = useCallback(() => {
91+
vscode.postMessage({
92+
t: "changePage",
93+
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
94+
selectedTable,
95+
});
96+
sendResultsPageChangedTelemetry();
97+
}, [numPages, parsedResultSets.pageNumber, selectedTable]);
98+
99+
const openQueryHandler = useCallback(() => {
100+
openFile(queryPath);
101+
sendTelemetry("local-results-open-query-file");
102+
}, [queryPath]);
103+
104+
return (
105+
<span className="vscode-codeql__table-selection-pagination">
106+
<button onClick={prevPageHandler}>&#xab;</button>
107+
<input
108+
type="number"
109+
size={3}
110+
value={selectedPage}
111+
min="1"
112+
max={numPages}
113+
onChange={onChangeHandler}
114+
onBlur={onBlurHandler}
115+
onKeyDown={onKeyDownHandler}
116+
/>
117+
<span>/&nbsp;{numPages}</span>
118+
<button value=">" onClick={nextPageHandler}>
119+
&#xbb;
120+
</button>
121+
<div className={tableHeaderItemClassName}>{queryName}</div>
122+
<div className={tableHeaderItemClassName}>
123+
{/*
124+
eslint-disable-next-line
125+
jsx-a11y/anchor-is-valid
126+
*/}
127+
<a
128+
href="#"
129+
onClick={openQueryHandler}
130+
className="vscode-codeql__result-table-location-link"
131+
>
132+
Open {basename(queryPath)}
133+
</a>
134+
</div>
135+
</span>
136+
);
137+
}
138+
139+
function sendResultsPageChangedTelemetry() {
140+
sendTelemetry("local-results-alert-table-page-changed");
141+
}

extensions/ql-vscode/src/view/results/result-tables.tsx

Lines changed: 6 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ import {
2323
tableHeaderItemClassName,
2424
toggleDiagnosticsClassName,
2525
alertExtrasClassName,
26-
openFile,
2726
} from "./result-table-utils";
2827
import { vscode } from "../vscode-api";
2928
import { sendTelemetry } from "../common/telemetry";
30-
import { basename } from "../../common/path";
29+
import { ResultTablesHeader } from "./ResultTablesHeader";
3130

3231
/**
3332
* Properties for the `ResultTables` component.
@@ -52,7 +51,6 @@ interface ResultTablesProps {
5251
*/
5352
interface ResultTablesState {
5453
selectedTable: string; // name of selected result set
55-
selectedPage: string; // stringified selected page
5654
problemsViewSelected: boolean;
5755
}
5856

@@ -138,10 +136,8 @@ export class ResultTables extends React.Component<
138136
getDefaultResultSet(
139137
getResultSets(props.rawResultSets, props.interpretation),
140138
);
141-
const selectedPage = `${props.parsedResultSets.pageNumber + 1}`;
142139
this.state = {
143140
selectedTable,
144-
selectedPage,
145141
problemsViewSelected: false,
146142
};
147143
}
@@ -168,10 +164,7 @@ export class ResultTables extends React.Component<
168164
getResultSets(props.rawResultSets, props.interpretation),
169165
);
170166

171-
return {
172-
selectedTable,
173-
selectedPage: `${props.parsedResultSets.pageNumber + 1}`,
174-
};
167+
return { selectedTable };
175168
});
176169
}
177170
}
@@ -245,103 +238,6 @@ export class ResultTables extends React.Component<
245238
sendTelemetry("local-results-alert-table-page-changed");
246239
}
247240

248-
renderPageButtons(): JSX.Element {
249-
const { parsedResultSets } = this.props;
250-
const selectedTable = this.state.selectedTable;
251-
252-
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
253-
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
254-
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
255-
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
256-
// instead.
257-
const numPages = Math.max(
258-
selectedTable === ALERTS_TABLE_NAME
259-
? parsedResultSets.numInterpretedPages
260-
: parsedResultSets.numPages,
261-
1,
262-
);
263-
264-
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
265-
this.setState({ selectedPage: e.target.value });
266-
this.sendResultsPageChangedTelemetry();
267-
};
268-
const choosePage = (input: string) => {
269-
const pageNumber = parseInt(input);
270-
if (pageNumber !== undefined && !isNaN(pageNumber)) {
271-
const actualPageNumber = Math.max(
272-
0,
273-
Math.min(pageNumber - 1, numPages - 1),
274-
);
275-
vscode.postMessage({
276-
t: "changePage",
277-
pageNumber: actualPageNumber,
278-
selectedTable,
279-
});
280-
}
281-
};
282-
283-
const prevPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
284-
vscode.postMessage({
285-
t: "changePage",
286-
pageNumber: Math.max(parsedResultSets.pageNumber - 1, 0),
287-
selectedTable,
288-
});
289-
this.sendResultsPageChangedTelemetry();
290-
};
291-
const nextPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
292-
vscode.postMessage({
293-
t: "changePage",
294-
pageNumber: Math.min(parsedResultSets.pageNumber + 1, numPages - 1),
295-
selectedTable,
296-
});
297-
this.sendResultsPageChangedTelemetry();
298-
};
299-
300-
const openQuery = () => {
301-
openFile(this.props.queryPath);
302-
sendTelemetry("local-results-open-query-file");
303-
};
304-
const fileName = basename(this.props.queryPath);
305-
306-
return (
307-
<span className="vscode-codeql__table-selection-pagination">
308-
<button onClick={prevPage}>&#xab;</button>
309-
<input
310-
type="number"
311-
size={3}
312-
value={this.state.selectedPage}
313-
min="1"
314-
max={numPages}
315-
onChange={onChange}
316-
onBlur={(e) => choosePage(e.target.value)}
317-
onKeyDown={(e) => {
318-
if (e.keyCode === 13) {
319-
choosePage((e.target as HTMLInputElement).value);
320-
}
321-
}}
322-
/>
323-
<span>/&nbsp;{numPages}</span>
324-
<button value=">" onClick={nextPage}>
325-
&#xbb;
326-
</button>
327-
<div className={tableHeaderItemClassName}>{this.props.queryName}</div>
328-
<div className={tableHeaderItemClassName}>
329-
{/*
330-
eslint-disable-next-line
331-
jsx-a11y/anchor-is-valid
332-
*/}
333-
<a
334-
href="#"
335-
onClick={openQuery}
336-
className="vscode-codeql__result-table-location-link"
337-
>
338-
Open {fileName}
339-
</a>
340-
</div>
341-
</span>
342-
);
343-
}
344-
345241
render(): React.ReactNode {
346242
const { selectedTable } = this.state;
347243
const resultSets = getResultSets(
@@ -369,7 +265,10 @@ export class ResultTables extends React.Component<
369265
));
370266
return (
371267
<div>
372-
{this.renderPageButtons()}
268+
<ResultTablesHeader
269+
{...this.props}
270+
selectedTable={this.state.selectedTable}
271+
/>
373272
<div className={tableHeaderClassName}></div>
374273
<div className={tableHeaderClassName}>
375274
<select value={selectedTable} onChange={this.onTableSelectionChange}>

0 commit comments

Comments
 (0)