Skip to content

Commit d92591a

Browse files
authored
fix(ui): dont mutate searchparams (#38275)
1 parent 53f146a commit d92591a

File tree

8 files changed

+29
-25
lines changed

8 files changed

+29
-25
lines changed

packages/html-reporter/src/headerView.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import './colors.css';
2020
import './common.css';
2121
import './headerView.css';
2222
import * as icons from './icons';
23-
import { Link, navigate, SearchParamsContext } from './links';
23+
import { Link, navigate, useSearchParams } from './links';
2424
import { statusIcon } from './statusIcon';
2525
import { filterWithQuery } from './filter';
2626
import { linkifyText } from '@web/renderUtils';
@@ -48,12 +48,12 @@ export const GlobalFilterView: React.FC<{
4848
filterText: string,
4949
setFilterText: (filterText: string) => void,
5050
}> = ({ stats, filterText, setFilterText }) => {
51-
const searchParams = React.useContext(SearchParamsContext);
51+
const searchParams = useSearchParams();
5252
React.useEffect(() => {
5353
// Add an extra space such that users can easily add to query
5454
const query = searchParams.get('q');
5555
setFilterText(query ? `${query.trim()} ` : '');
56-
}, [searchParams, setFilterText]);
56+
}, [searchParams.toString(), setFilterText]);
5757

5858
return (<>
5959
<div className='pt-3'>
@@ -89,7 +89,7 @@ export const GlobalFilterView: React.FC<{
8989
const StatsNavView: React.FC<{
9090
stats: Stats
9191
}> = ({ stats }) => {
92-
const searchParams = React.useContext(SearchParamsContext);
92+
const searchParams = useSearchParams();
9393

9494
return <nav>
9595
<Link className='subnav-item' href='#?'>
@@ -111,7 +111,7 @@ const NavLink: React.FC<{
111111
token: string,
112112
count: number,
113113
}> = ({ token, count }) => {
114-
const searchParams = new URLSearchParams(React.useContext(SearchParamsContext));
114+
const searchParams = useSearchParams();
115115
searchParams.delete('speedboard');
116116
searchParams.delete('testId');
117117

packages/html-reporter/src/labels.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import * as React from 'react';
1818
import { clsx } from '@web/uiUtils';
1919
import { formatUrl, hashStringToInt } from './utils';
20-
import { navigate, ProjectLink, SearchParamsContext } from './links';
20+
import { navigate, ProjectLink, useSearchParams } from './links';
2121
import { filterWithQuery } from './filter';
2222
import './labels.css';
2323

@@ -55,7 +55,7 @@ export const ProjectAndTagLabelsView: React.FC<{
5555
const LabelsClickView: React.FC<{
5656
labels: string[],
5757
}> = ({ labels }) => {
58-
const searchParams = React.useContext(SearchParamsContext);
58+
const searchParams = useSearchParams();
5959

6060
const onClickHandle = React.useCallback((e: React.MouseEvent, label: string) => {
6161
e.preventDefault();

packages/html-reporter/src/links.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const Route: React.FunctionComponent<{
3737
predicate: (params: URLSearchParams) => boolean,
3838
children: any
3939
}> = ({ predicate, children }) => {
40-
const searchParams = React.useContext(SearchParamsContext);
40+
const searchParams = useSearchParams();
4141
return predicate(searchParams) ? children : null;
4242
};
4343

@@ -64,7 +64,7 @@ export const ProjectLink: React.FunctionComponent<{
6464
projectNames: string[],
6565
projectName: string,
6666
}> = ({ projectNames, projectName }) => {
67-
const searchParams = React.useContext(SearchParamsContext);
67+
const searchParams = useSearchParams();
6868
if (searchParams.has('testId'))
6969
searchParams.delete('speedboard');
7070
searchParams.delete('testId');
@@ -153,7 +153,11 @@ export const TraceLink: React.FC<{ test: TestCaseSummary, trailingSeparator?: bo
153153
);
154154
};
155155

156-
export const SearchParamsContext = React.createContext<URLSearchParams>(new URLSearchParams(window.location.hash.slice(1)));
156+
const SearchParamsContext = React.createContext<URLSearchParams>(new URLSearchParams(window.location.hash.slice(1)));
157+
158+
export function useSearchParams() {
159+
return new URLSearchParams(React.useContext(SearchParamsContext));
160+
}
157161

158162
export const SearchParamsProvider: React.FunctionComponent<React.PropsWithChildren> = ({ children }) => {
159163
const [searchParams, setSearchParams] = React.useState<URLSearchParams>(new URLSearchParams(window.location.hash.slice(1)));
@@ -185,7 +189,7 @@ const kMissingContentType = 'x-playwright/missing';
185189
export type AnchorID = string | string[] | ((id: string) => boolean) | undefined;
186190

187191
export function useAnchor(id: AnchorID, onReveal: React.EffectCallback) {
188-
const searchParams = React.useContext(SearchParamsContext);
192+
const searchParams = useSearchParams();
189193
const isAnchored = useIsAnchored(id);
190194
React.useEffect(() => {
191195
if (isAnchored)
@@ -194,7 +198,7 @@ export function useAnchor(id: AnchorID, onReveal: React.EffectCallback) {
194198
}
195199

196200
export function useIsAnchored(id: AnchorID) {
197-
const searchParams = React.useContext(SearchParamsContext);
201+
const searchParams = useSearchParams();
198202
const anchor = searchParams.get('anchor');
199203
if (anchor === null)
200204
return false;

packages/html-reporter/src/metadataView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import type { Metadata } from '@playwright/test';
2323
import type { CIInfo, GitCommitInfo, MetadataWithCommitInfo } from '@testIsomorphic/types';
2424
import { CopyToClipboardContainer } from './copyToClipboard';
2525
import { linkifyText } from '@web/renderUtils';
26-
import { SearchParamsContext } from './links';
2726
import { formatUrl } from './utils';
27+
import { useSearchParams } from './links';
2828

2929
class ErrorBoundary extends React.Component<React.PropsWithChildren<{}>, { error: Error | null, errorInfo: React.ErrorInfo | null }> {
3030
override state: { error: Error | null, errorInfo: React.ErrorInfo | null } = {
@@ -57,7 +57,7 @@ export const MetadataView: React.FC<{ metadata: Metadata }> = params => {
5757
};
5858

5959
const InnerMetadataView: React.FC<{ metadata: Metadata }> = params => {
60-
const searchParams = React.useContext(SearchParamsContext);
60+
const searchParams = useSearchParams();
6161
const commitInfo = params.metadata as MetadataWithCommitInfo;
6262
const otherEntries = searchParams.has('show-metadata-other') ? Object.entries(params.metadata).filter(([key]) => !ignoreKeys.has(key)) : [];
6363
const hasMetadata = commitInfo.ci || commitInfo.gitCommit || otherEntries.length > 0;

packages/html-reporter/src/reportView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import './colors.css';
2020
import './common.css';
2121
import { Filter, filterWithQuery } from './filter';
2222
import { HeaderView, GlobalFilterView } from './headerView';
23-
import { navigate, Route, SearchParamsContext, testResultHref } from './links';
23+
import { navigate, Route, testResultHref, useSearchParams } from './links';
2424
import type { LoadedReport } from './loadedReport';
2525
import './reportView.css';
2626
import { TestCaseView } from './testCaseView';
@@ -48,7 +48,7 @@ type TestModelSummary = {
4848
export const ReportView: React.FC<{
4949
report: LoadedReport | undefined,
5050
}> = ({ report }) => {
51-
const searchParams = React.useContext(SearchParamsContext);
51+
const searchParams = useSearchParams();
5252
const [expandedFiles, setExpandedFiles] = React.useState<Map<string, boolean>>(new Map());
5353
const [filterText, setFilterText] = React.useState(searchParams.get('q') || '');
5454
const [metadataVisible, setMetadataVisible] = React.useState(false);
@@ -165,7 +165,7 @@ const TestCaseViewLoader: React.FC<{
165165
prev?: TestCaseSummary,
166166
testIdToFileIdMap: Map<string, string>,
167167
}> = ({ report, testIdToFileIdMap, next, prev, testId }) => {
168-
const searchParams = React.useContext(SearchParamsContext);
168+
const searchParams = useSearchParams();
169169
const [test, setTest] = React.useState<TestCase | 'loading' | 'not-found'>('loading');
170170
const run = +(searchParams.get('run') || '0');
171171

packages/html-reporter/src/testCaseView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import * as React from 'react';
2020
import { TabbedPane } from './tabbedPane';
2121
import { AutoChip } from './chip';
2222
import './common.css';
23-
import { Link, SearchParamsContext, testResultHref, TraceLink } from './links';
23+
import { Link, testResultHref, TraceLink, useSearchParams } from './links';
2424
import { statusIcon } from './statusIcon';
2525
import './testCaseView.css';
2626
import { TestResultView } from './testResultView';
@@ -42,7 +42,7 @@ export const TestCaseView: React.FC<{
4242
options?: HTMLReportOptions,
4343
}> = ({ projectNames, test, testRunMetadata, run, next, prev, options }) => {
4444
const [selectedResultIndex, setSelectedResultIndex] = React.useState(run);
45-
const searchParams = React.useContext(SearchParamsContext);
45+
const searchParams = useSearchParams();
4646

4747
const visibleTestAnnotations = test.annotations.filter(a => !a.type.startsWith('_')) ?? [];
4848

packages/html-reporter/src/testFileView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { TestCaseSummary, TestFileSummary } from './types';
1818
import * as React from 'react';
1919
import { msToString } from './utils';
2020
import { Chip } from './chip';
21-
import { Link, LinkBadge, SearchParamsContext, testResultHref, TraceLink } from './links';
21+
import { Link, LinkBadge, testResultHref, TraceLink, useSearchParams } from './links';
2222
import { statusIcon } from './statusIcon';
2323
import './testFileView.css';
2424
import { video, image } from './icons';
@@ -32,7 +32,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
3232
setFileExpanded?: (fileId: string, expanded: boolean) => void;
3333
footer?: React.JSX.Element | string;
3434
}>> = ({ file, projectNames, isFileExpanded, setFileExpanded, footer }) => {
35-
const searchParams = React.useContext(SearchParamsContext);
35+
const searchParams = useSearchParams();
3636
return <Chip
3737
expanded={isFileExpanded ? isFileExpanded(file.fileId) : undefined}
3838
noInsets={true}
@@ -74,7 +74,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
7474
};
7575

7676
function ImageDiffBadge({ test }: { test: TestCaseSummary }) {
77-
const searchParams = React.useContext(SearchParamsContext);
77+
const searchParams = useSearchParams();
7878
for (const result of test.results) {
7979
for (const attachment of result.attachments) {
8080
if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/))
@@ -84,7 +84,7 @@ function ImageDiffBadge({ test }: { test: TestCaseSummary }) {
8484
}
8585

8686
function VideoBadge({ test }: { test: TestCaseSummary }) {
87-
const searchParams = React.useContext(SearchParamsContext);
87+
const searchParams = useSearchParams();
8888
const resultWithVideo = test.results.find(result => result.attachments.some(attachment => attachment.name === 'video'));
8989
return resultWithVideo ? <LinkBadge href={testResultHref({ test, result: resultWithVideo, anchor: 'attachment-video' }, searchParams)} title='View video' dim={true}>{video()}</LinkBadge> : undefined;
9090
}

packages/html-reporter/src/testResultView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { TreeItem } from './treeItem';
2020
import { formatUrl, msToString } from './utils';
2121
import { AutoChip } from './chip';
2222
import { traceImage } from './images';
23-
import { Anchor, AttachmentLink, generateTraceUrl, SearchParamsContext, testResultHref } from './links';
23+
import { Anchor, AttachmentLink, generateTraceUrl, testResultHref, useSearchParams } from './links';
2424
import { statusIcon } from './statusIcon';
2525
import type { ImageDiff } from '@web/shared/imageDiffView';
2626
import { ImageDiffView } from '@web/shared/imageDiffView';
@@ -193,7 +193,7 @@ const StepTreeItem: React.FC<{
193193
step: TestStep;
194194
depth: number,
195195
}> = ({ test, step, result, depth }) => {
196-
const searchParams = React.useContext(SearchParamsContext);
196+
const searchParams = useSearchParams();
197197
return <TreeItem title={<span aria-label={step.title}>
198198
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
199199
{step.attachments.length > 0 && <a

0 commit comments

Comments
 (0)