mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 13:11:49 +00:00
web: upgrade react-router to v6 (#47595)
- Closes #33834 - Upgraded react-router to v6 - Migrated the web application to [the data-aware router introduced in v6.4.0](https://reactrouter.com/en/main/routers/picking-a-router#using-v64-data-apis). - Migrated `history.block` usages to the `unstable_useBlock` hook [introduced in v6.7.0](https://github.com/remix-run/react-router/issues/8139). - Removed explicit history reference from the `renderWithBrandedContext` utility used in unit tests. - Migrated the search-query state observer from `history.listen` to `useLocation` API. ## Test plan CI and manually visiting all the pages.
This commit is contained in:
parent
39488a9491
commit
77541f2c30
@ -34,8 +34,8 @@ describe('TabbedPanel', () => {
|
||||
const panelButton = await renderResult.findByRole('tab', { name: panelToSelect.title })
|
||||
fireEvent.click(panelButton)
|
||||
|
||||
expect(renderResult.history.location.pathname).toEqual(location.pathname)
|
||||
expect(renderResult.history.location.search).toEqual(location.search)
|
||||
expect(renderResult.history.location.hash).toEqual(`#tab=${panelToSelect.id}`)
|
||||
expect(renderResult.locationRef.current?.pathname).toEqual(location.pathname)
|
||||
expect(renderResult.locationRef.current?.search).toEqual(location.search)
|
||||
expect(renderResult.locationRef.current?.hash).toEqual(`#tab=${panelToSelect.id}`)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { BehaviorSubject, Observable } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, KeyboardEvent, MouseEvent } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useCallback, useState } from 'react'
|
||||
|
||||
import { mdiOpenInNew } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { EditorHint, QueryState } from '@sourcegraph/shared/src/search'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, KeyboardEvent, MouseEvent } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { MouseEvent, KeyboardEvent } from 'react'
|
||||
|
||||
import { NavigateFunction } from 'react-router-dom-v5-compat'
|
||||
import { NavigateFunction } from 'react-router-dom'
|
||||
|
||||
/**
|
||||
* A helper function to replicate browser behavior when clicking on links.
|
||||
|
||||
@ -26,7 +26,7 @@ import {
|
||||
WidgetType,
|
||||
} from '@codemirror/view'
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { renderMarkdown } from '@sourcegraph/common'
|
||||
import { TraceSpanProvider } from '@sourcegraph/observability-client'
|
||||
|
||||
@ -46,7 +46,7 @@ import {
|
||||
mdiWrench,
|
||||
} from '@mdi/js'
|
||||
import { isEqual, startCase } from 'lodash'
|
||||
import { NavigateFunction } from 'react-router-dom-v5-compat'
|
||||
import { NavigateFunction } from 'react-router-dom'
|
||||
|
||||
import { isDefined } from '@sourcegraph/common'
|
||||
import { SymbolKind } from '@sourcegraph/shared/src/graphql-operations'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeSpec, EditorState, Extension } from '@codemirror/state'
|
||||
import { EditorView, ViewUpdate } from '@codemirror/view'
|
||||
import { NavigateFunction } from 'react-router-dom-v5-compat'
|
||||
import { NavigateFunction } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { createCancelableFetchSuggestions } from '@sourcegraph/shared/src/search/query/providers-utils'
|
||||
|
||||
@ -4,7 +4,7 @@ import { defaultKeymap, historyKeymap, history as codemirrorHistory } from '@cod
|
||||
import { Compartment, EditorState, Extension, Prec } from '@codemirror/state'
|
||||
import { EditorView, keymap, drawSelection } from '@codemirror/view'
|
||||
import inRange from 'lodash/inRange'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import useResizeObserver from 'use-resize-observer'
|
||||
import * as uuid from 'uuid'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { TraceSpanProvider } from '@sourcegraph/observability-client'
|
||||
|
||||
@ -2,7 +2,6 @@ import bitbucketStyles from '@atlassian/aui/dist/aui/css/aui.css'
|
||||
import { DecoratorFn, Meta, Story } from '@storybook/react'
|
||||
import classNames from 'classnames'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
|
||||
import { registerHighlightContributions } from '@sourcegraph/common'
|
||||
import { NotificationType } from '@sourcegraph/shared/src/api/extension/extensionHostApi'
|
||||
@ -58,18 +57,16 @@ const BITBUCKET_CLASS_PROPS: HoverOverlayClassProps = {
|
||||
|
||||
export const BitbucketStyles: Story = (props = {}) => (
|
||||
<BrowserRouter>
|
||||
<CompatRouter>
|
||||
<HoverOverlay
|
||||
{...commonProps()}
|
||||
{...BITBUCKET_CLASS_PROPS}
|
||||
{...props}
|
||||
hoverOrError={{
|
||||
contents: [FIXTURE_CONTENT],
|
||||
aggregatedBadges: [FIXTURE_SEMANTIC_BADGE],
|
||||
}}
|
||||
actionsOrError={FIXTURE_ACTIONS}
|
||||
/>
|
||||
</CompatRouter>
|
||||
<HoverOverlay
|
||||
{...commonProps()}
|
||||
{...BITBUCKET_CLASS_PROPS}
|
||||
{...props}
|
||||
hoverOrError={{
|
||||
contents: [FIXTURE_CONTENT],
|
||||
aggregatedBadges: [FIXTURE_SEMANTIC_BADGE],
|
||||
}}
|
||||
actionsOrError={FIXTURE_ACTIONS}
|
||||
/>
|
||||
</BrowserRouter>
|
||||
)
|
||||
BitbucketStyles.storyName = 'Bitbucket styles'
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { Falsey } from 'utility-types'
|
||||
|
||||
/**
|
||||
* Returns true if `val` is not `null` or `undefined`
|
||||
*/
|
||||
export const isDefined = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null
|
||||
|
||||
/**
|
||||
* Returns true if `val` is truthy.
|
||||
*/
|
||||
export const isTruthy = <T>(value: T | Falsey): value is T => !!value
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { History } from 'history'
|
||||
import { NavigateFunction, NavigateOptions, To } from 'react-router-dom-v5-compat'
|
||||
import { NavigateFunction, NavigateOptions, To } from 'react-router-dom'
|
||||
|
||||
export type HistoryOrNavigate = History | NavigateFunction
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ import { useEffect, useRef } from 'react'
|
||||
|
||||
import { DecoratorFn, Meta, Story } from '@storybook/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { EMPTY, NEVER } from 'rxjs'
|
||||
import { useDarkMode } from 'storybook-dark-mode'
|
||||
|
||||
@ -45,45 +44,43 @@ export const JetBrainsSearchBoxStory: Story = () => {
|
||||
return (
|
||||
<WildcardThemeContext.Provider value={{ isBranded: true }}>
|
||||
<BrowserRouter>
|
||||
<CompatRouter>
|
||||
<div ref={rootElementRef}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<div className="mx-6">
|
||||
<JetBrainsSearchBox
|
||||
caseSensitive={true}
|
||||
setCaseSensitivity={() => {}}
|
||||
patternType={SearchPatternType.regexp}
|
||||
setPatternType={() => {}}
|
||||
isSourcegraphDotCom={false}
|
||||
structuralSearchDisabled={false}
|
||||
queryState={{ query: 'type:file test AND test repo:contains.file(CHANGELOG)' }}
|
||||
onChange={() => {}}
|
||||
onSubmit={() => {}}
|
||||
authenticatedUser={null}
|
||||
searchContextsEnabled={true}
|
||||
showSearchContext={true}
|
||||
showSearchContextManagement={false}
|
||||
setSelectedSearchContextSpec={() => {}}
|
||||
selectedSearchContextSpec={undefined}
|
||||
fetchSearchContexts={() => {
|
||||
throw new Error('fetchSearchContexts')
|
||||
}}
|
||||
getUserSearchContextNamespaces={() => []}
|
||||
fetchStreamSuggestions={() => NEVER}
|
||||
settingsCascade={EMPTY_SETTINGS_CASCADE}
|
||||
globbing={false}
|
||||
isLightTheme={!isDarkTheme}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
platformContext={{ requestGraphQL: () => EMPTY }}
|
||||
className=""
|
||||
containerClassName=""
|
||||
autoFocus={true}
|
||||
hideHelpButton={true}
|
||||
/>
|
||||
</div>
|
||||
<div ref={rootElementRef}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<div className="mx-6">
|
||||
<JetBrainsSearchBox
|
||||
caseSensitive={true}
|
||||
setCaseSensitivity={() => {}}
|
||||
patternType={SearchPatternType.regexp}
|
||||
setPatternType={() => {}}
|
||||
isSourcegraphDotCom={false}
|
||||
structuralSearchDisabled={false}
|
||||
queryState={{ query: 'type:file test AND test repo:contains.file(CHANGELOG)' }}
|
||||
onChange={() => {}}
|
||||
onSubmit={() => {}}
|
||||
authenticatedUser={null}
|
||||
searchContextsEnabled={true}
|
||||
showSearchContext={true}
|
||||
showSearchContextManagement={false}
|
||||
setSelectedSearchContextSpec={() => {}}
|
||||
selectedSearchContextSpec={undefined}
|
||||
fetchSearchContexts={() => {
|
||||
throw new Error('fetchSearchContexts')
|
||||
}}
|
||||
getUserSearchContextNamespaces={() => []}
|
||||
fetchStreamSuggestions={() => NEVER}
|
||||
settingsCascade={EMPTY_SETTINGS_CASCADE}
|
||||
globbing={false}
|
||||
isLightTheme={!isDarkTheme}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
platformContext={{ requestGraphQL: () => EMPTY }}
|
||||
className=""
|
||||
containerClassName=""
|
||||
autoFocus={true}
|
||||
hideHelpButton={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CompatRouter>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</WildcardThemeContext.Provider>
|
||||
)
|
||||
|
||||
@ -5,8 +5,7 @@ import React, { useMemo } from 'react'
|
||||
import { VSCodeProgressRing } from '@vscode/webview-ui-toolkit/react'
|
||||
import * as Comlink from 'comlink'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
|
||||
import { wrapRemoteObservable } from '@sourcegraph/shared/src/api/client/api/common'
|
||||
import { ShortcutProvider } from '@sourcegraph/shared/src/react-shortcuts'
|
||||
@ -117,9 +116,7 @@ root.render(
|
||||
<WildcardThemeContext.Provider value={{ isBranded: true }}>
|
||||
{/* Required for shared components that depend on `location`. */}
|
||||
<MemoryRouter>
|
||||
<CompatRouter>
|
||||
<Main />
|
||||
</CompatRouter>
|
||||
<Main />
|
||||
</MemoryRouter>
|
||||
</WildcardThemeContext.Provider>
|
||||
</ShortcutProvider>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { FC, ReactElement, ReactNode, useCallback, useMemo } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect'
|
||||
import create from 'zustand'
|
||||
|
||||
|
||||
@ -49,7 +49,8 @@ export function getAPIProxySettings(options: GetAPIProxySettingsOptions): ProxyS
|
||||
// the index.html generated by `getLocalIndexHTML`.
|
||||
if (
|
||||
getLocalIndexHTML &&
|
||||
proxyRes.statusCode === 200 &&
|
||||
// router.go is not up to date with client routes and still serves index.html with 404
|
||||
(proxyRes.statusCode === 200 || proxyRes.statusCode === 404) &&
|
||||
proxyRes.headers['content-type'] &&
|
||||
proxyRes.headers['content-type'].includes('text/html')
|
||||
) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { Suspense, useCallback, useRef, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation, Navigate, Outlet } from 'react-router-dom-v5-compat'
|
||||
import { Outlet, useLocation, Navigate } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { TabbedPanelContent } from '@sourcegraph/branded/src/components/panel/TabbedPanelContent'
|
||||
@ -40,6 +40,7 @@ import type { NotebookProps } from './notebooks'
|
||||
import { EnterprisePageRoutes, PageRoutes } from './routes.constants'
|
||||
import { parseSearchURLQuery, SearchAggregationProps, SearchStreamingProps } from './search'
|
||||
import { NotepadContainer } from './search/Notepad'
|
||||
import { SearchQueryStateObserver } from './SearchQueryStateObserver'
|
||||
import { useExperimentalFeatures } from './stores'
|
||||
import { ThemePreferenceProps, useTheme } from './theme'
|
||||
import { getExperimentalFeatures } from './util/get-experimental-features'
|
||||
@ -274,6 +275,12 @@ export const Layout: React.FC<LegacyLayoutProps> = props => {
|
||||
userHistory={userHistory}
|
||||
/>
|
||||
)}
|
||||
<SearchQueryStateObserver
|
||||
platformContext={props.platformContext}
|
||||
searchContextsEnabled={props.searchAggregationEnabled}
|
||||
setSelectedSearchContextSpec={props.setSelectedSearchContextSpec}
|
||||
selectedSearchContextSpec={props.selectedSearchContextSpec}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { Suspense, useCallback, useRef, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { matchPath, useLocation, Route, Routes, Navigate } from 'react-router-dom-v5-compat'
|
||||
import { matchPath, useLocation, Route, Routes, Navigate } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { TabbedPanelContent } from '@sourcegraph/branded/src/components/panel/TabbedPanelContent'
|
||||
@ -51,6 +51,7 @@ import type { LegacyLayoutRouteComponentProps, LayoutRouteProps } from './routes
|
||||
import { EnterprisePageRoutes, PageRoutes } from './routes.constants'
|
||||
import { parseSearchURLQuery, SearchAggregationProps, SearchStreamingProps } from './search'
|
||||
import { NotepadContainer } from './search/Notepad'
|
||||
import { SearchQueryStateObserver } from './SearchQueryStateObserver'
|
||||
import type { SiteAdminAreaRoute } from './site-admin/SiteAdminArea'
|
||||
import type { SiteAdminSideBarGroups } from './site-admin/SiteAdminSidebar'
|
||||
import { useExperimentalFeatures } from './stores'
|
||||
@ -328,6 +329,12 @@ export const LegacyLayout: React.FunctionComponent<React.PropsWithChildren<Legac
|
||||
userHistory={userHistory}
|
||||
/>
|
||||
)}
|
||||
<SearchQueryStateObserver
|
||||
platformContext={props.platformContext}
|
||||
searchContextsEnabled={props.searchAggregationEnabled}
|
||||
setSelectedSearchContextSpec={props.setSelectedSearchContextSpec}
|
||||
selectedSearchContextSpec={props.selectedSearchContextSpec}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,10 +4,9 @@ import * as React from 'react'
|
||||
|
||||
import { ApolloProvider } from '@apollo/client'
|
||||
import ServerIcon from 'mdi-react/ServerIcon'
|
||||
import { Router } from 'react-router'
|
||||
import { CompatRouter, Routes, Route } from 'react-router-dom-v5-compat'
|
||||
import { RouterProvider, createBrowserRouter, createRoutesFromElements, Route } from 'react-router-dom'
|
||||
import { combineLatest, from, Subscription, fromEvent, of, Subject, Observable } from 'rxjs'
|
||||
import { first, startWith, switchMap, map, distinctUntilChanged } from 'rxjs/operators'
|
||||
import { startWith, switchMap } from 'rxjs/operators'
|
||||
|
||||
import { logger } from '@sourcegraph/common'
|
||||
import { GraphQLClient, HTTPStatusError } from '@sourcegraph/http-client'
|
||||
@ -36,7 +35,6 @@ import {
|
||||
getDefaultSearchContextSpec,
|
||||
} from '@sourcegraph/shared/src/search'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { omitFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { filterExists } from '@sourcegraph/shared/src/search/query/validate'
|
||||
import { aggregateStreamingSearch } from '@sourcegraph/shared/src/search/stream'
|
||||
import { EMPTY_SETTINGS_CASCADE, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
@ -68,8 +66,9 @@ import type { RepoRevisionContainerRoute } from './repo/RepoRevisionContainer'
|
||||
import type { RepoSettingsAreaRoute } from './repo/settings/RepoSettingsArea'
|
||||
import type { RepoSettingsSideBarGroup } from './repo/settings/RepoSettingsSidebar'
|
||||
import type { LayoutRouteProps } from './routes'
|
||||
import { parseSearchURL, getQueryStateFromLocation, SearchAggregationProps } from './search'
|
||||
import { parseSearchURL, SearchAggregationProps } from './search'
|
||||
import { SearchResultsCacheProvider } from './search/results/SearchResultsCacheProvider'
|
||||
import { GLOBAL_SEARCH_CONTEXT_SPEC } from './SearchQueryStateObserver'
|
||||
import type { SiteAdminAreaRoute } from './site-admin/SiteAdminArea'
|
||||
import type { SiteAdminSideBarGroups } from './site-admin/SiteAdminSidebar'
|
||||
import {
|
||||
@ -77,18 +76,13 @@ import {
|
||||
setExperimentalFeaturesFromSettings,
|
||||
getExperimentalFeatures,
|
||||
useNavbarQueryState,
|
||||
observeStore,
|
||||
useExperimentalFeatures,
|
||||
} from './stores'
|
||||
import { setQueryStateFromURL } from './stores/navbarSearchQueryState'
|
||||
import { eventLogger } from './tracking/eventLogger'
|
||||
import type { UserAreaRoute } from './user/area/UserArea'
|
||||
import type { UserAreaHeaderNavItem } from './user/area/UserAreaHeader'
|
||||
import type { UserSettingsAreaRoute } from './user/settings/UserSettingsArea'
|
||||
import type { UserSettingsSidebarItems } from './user/settings/UserSettingsSidebar'
|
||||
import { UserSessionStores } from './UserSessionStores'
|
||||
import { globalHistory } from './util/globalHistory'
|
||||
import { observeLocation } from './util/location'
|
||||
import { siteSubjectNoAdmin, viewerSubjectFromSettings } from './util/settings'
|
||||
|
||||
import styles from './LegacySourcegraphWebApp.module.scss'
|
||||
@ -160,8 +154,6 @@ const WILDCARD_THEME: WildcardTheme = {
|
||||
isBranded: true,
|
||||
}
|
||||
|
||||
const GLOBAL_SEARCH_CONTEXT_SPEC = 'global'
|
||||
|
||||
setLinkComponent(RouterLink)
|
||||
|
||||
/**
|
||||
@ -268,58 +260,6 @@ export class LegacySourcegraphWebApp extends React.Component<
|
||||
logger.error('Error sending search context to extensions!', error)
|
||||
})
|
||||
|
||||
// Update search query state whenever the URL changes
|
||||
this.subscriptions.add(
|
||||
combineLatest([
|
||||
observeStore(useExperimentalFeatures).pipe(
|
||||
map(([features]) => features.searchQueryInput === 'experimental'),
|
||||
// This ensures that the query stays unmodified until we know
|
||||
// whether the feature flag is set or not.
|
||||
startWith(true),
|
||||
distinctUntilChanged()
|
||||
),
|
||||
getQueryStateFromLocation({
|
||||
location: observeLocation(globalHistory).pipe(startWith(globalHistory.location)),
|
||||
isSearchContextAvailable: (searchContext: string) =>
|
||||
this.props.searchContextsEnabled
|
||||
? isSearchContextSpecAvailable({
|
||||
spec: searchContext,
|
||||
platformContext: this.platformContext,
|
||||
})
|
||||
.pipe(first())
|
||||
.toPromise()
|
||||
: Promise.resolve(false),
|
||||
}),
|
||||
]).subscribe(([enableExperimentalSearchInput, parsedSearchURLAndContext]) => {
|
||||
if (parsedSearchURLAndContext.query) {
|
||||
// Only override filters and update query from URL if there
|
||||
// is a search query.
|
||||
if (!parsedSearchURLAndContext.searchContextSpec) {
|
||||
// If no search context is present we have to fall back
|
||||
// to the global search context to match the server
|
||||
// behavior.
|
||||
this.setSelectedSearchContextSpec(GLOBAL_SEARCH_CONTEXT_SPEC)
|
||||
} else if (
|
||||
parsedSearchURLAndContext.searchContextSpec.spec !== this.state.selectedSearchContextSpec
|
||||
) {
|
||||
this.setSelectedSearchContextSpec(parsedSearchURLAndContext.searchContextSpec.spec)
|
||||
}
|
||||
|
||||
const processedQuery =
|
||||
!enableExperimentalSearchInput &&
|
||||
parsedSearchURLAndContext.searchContextSpec &&
|
||||
this.props.searchContextsEnabled
|
||||
? omitFilter(
|
||||
parsedSearchURLAndContext.query,
|
||||
parsedSearchURLAndContext.searchContextSpec.filter
|
||||
)
|
||||
: parsedSearchURLAndContext.query
|
||||
|
||||
setQueryStateFromURL(parsedSearchURLAndContext, processedQuery)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
this.userRepositoriesUpdates.next()
|
||||
}
|
||||
|
||||
@ -359,6 +299,45 @@ export class LegacySourcegraphWebApp extends React.Component<
|
||||
return null
|
||||
}
|
||||
|
||||
const router = createBrowserRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<LegacyLayout
|
||||
{...this.props}
|
||||
authenticatedUser={authenticatedUser}
|
||||
viewerSubject={this.state.viewerSubject}
|
||||
settingsCascade={this.state.settingsCascade}
|
||||
batchChangesEnabled={this.props.batchChangesEnabled}
|
||||
batchChangesExecutionEnabled={isBatchChangesExecutionEnabled(this.state.settingsCascade)}
|
||||
batchChangesWebhookLogsEnabled={window.context.batchChangesWebhookLogsEnabled}
|
||||
// Search query
|
||||
fetchHighlightedFileLineRanges={this.fetchHighlightedFileLineRanges}
|
||||
// Extensions
|
||||
platformContext={this.platformContext}
|
||||
extensionsController={this.extensionsController}
|
||||
telemetryService={eventLogger}
|
||||
isSourcegraphDotCom={window.context.sourcegraphDotComMode}
|
||||
searchContextsEnabled={this.props.searchContextsEnabled}
|
||||
selectedSearchContextSpec={this.getSelectedSearchContextSpec()}
|
||||
setSelectedSearchContextSpec={this.setSelectedSearchContextSpec}
|
||||
getUserSearchContextNamespaces={getUserSearchContextNamespaces}
|
||||
fetchSearchContexts={fetchSearchContexts}
|
||||
fetchSearchContextBySpec={fetchSearchContextBySpec}
|
||||
fetchSearchContext={fetchSearchContext}
|
||||
createSearchContext={createSearchContext}
|
||||
updateSearchContext={updateSearchContext}
|
||||
deleteSearchContext={deleteSearchContext}
|
||||
isSearchContextSpecAvailable={isSearchContextSpecAvailable}
|
||||
globbing={this.state.globbing}
|
||||
streamSearch={aggregateStreamingSearch}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<ComponentsComposer
|
||||
components={[
|
||||
@ -376,48 +355,7 @@ export class LegacySourcegraphWebApp extends React.Component<
|
||||
/* eslint-enable react/no-children-prop, react/jsx-key */
|
||||
]}
|
||||
>
|
||||
<Router history={globalHistory}>
|
||||
<CompatRouter>
|
||||
<Routes>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<LegacyLayout
|
||||
{...this.props}
|
||||
authenticatedUser={authenticatedUser}
|
||||
viewerSubject={this.state.viewerSubject}
|
||||
settingsCascade={this.state.settingsCascade}
|
||||
batchChangesEnabled={this.props.batchChangesEnabled}
|
||||
batchChangesExecutionEnabled={isBatchChangesExecutionEnabled(
|
||||
this.state.settingsCascade
|
||||
)}
|
||||
batchChangesWebhookLogsEnabled={window.context.batchChangesWebhookLogsEnabled}
|
||||
// Search query
|
||||
fetchHighlightedFileLineRanges={this.fetchHighlightedFileLineRanges}
|
||||
// Extensions
|
||||
platformContext={this.platformContext}
|
||||
extensionsController={this.extensionsController}
|
||||
telemetryService={eventLogger}
|
||||
isSourcegraphDotCom={window.context.sourcegraphDotComMode}
|
||||
searchContextsEnabled={this.props.searchContextsEnabled}
|
||||
selectedSearchContextSpec={this.getSelectedSearchContextSpec()}
|
||||
setSelectedSearchContextSpec={this.setSelectedSearchContextSpec}
|
||||
getUserSearchContextNamespaces={getUserSearchContextNamespaces}
|
||||
fetchSearchContexts={fetchSearchContexts}
|
||||
fetchSearchContextBySpec={fetchSearchContextBySpec}
|
||||
fetchSearchContext={fetchSearchContext}
|
||||
createSearchContext={createSearchContext}
|
||||
updateSearchContext={updateSearchContext}
|
||||
deleteSearchContext={deleteSearchContext}
|
||||
isSearchContextSpecAvailable={isSearchContextSpecAvailable}
|
||||
globbing={this.state.globbing}
|
||||
streamSearch={aggregateStreamingSearch}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
<RouterProvider router={router} />
|
||||
{this.extensionsController !== null && window.context.enableLegacyExtensions ? (
|
||||
<Notifications
|
||||
key={2}
|
||||
|
||||
99
client/web/src/SearchQueryStateObserver.tsx
Normal file
99
client/web/src/SearchQueryStateObserver.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { Location, useLocation } from 'react-router-dom'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { first } from 'rxjs/operators'
|
||||
|
||||
import { PlatformContext } from '@sourcegraph/shared/src/platform/context'
|
||||
import { isSearchContextSpecAvailable } from '@sourcegraph/shared/src/search'
|
||||
import { omitFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
|
||||
import { getQueryStateFromLocation } from './search'
|
||||
import { useExperimentalFeatures } from './stores'
|
||||
import { setQueryStateFromURL } from './stores/navbarSearchQueryState'
|
||||
|
||||
export const GLOBAL_SEARCH_CONTEXT_SPEC = 'global'
|
||||
|
||||
interface SearchQueryStateObserverProps {
|
||||
searchContextsEnabled: boolean
|
||||
platformContext: PlatformContext
|
||||
selectedSearchContextSpec?: string
|
||||
setSelectedSearchContextSpec: (spec: string) => void
|
||||
}
|
||||
|
||||
// Update search query state whenever the URL changes
|
||||
export const SearchQueryStateObserver: FC<SearchQueryStateObserverProps> = props => {
|
||||
const { searchContextsEnabled, platformContext, setSelectedSearchContextSpec, selectedSearchContextSpec } = props
|
||||
|
||||
const location = useLocation()
|
||||
|
||||
const selectedSearchContextSpecRef = useRef(selectedSearchContextSpec)
|
||||
selectedSearchContextSpecRef.current = selectedSearchContextSpec
|
||||
|
||||
const { searchQueryInput, isInitialized } = useExperimentalFeatures()
|
||||
|
||||
// This ensures that the query stays unmodified until we know
|
||||
// whether the feature flag is set or not.
|
||||
const enableExperimentalSearchInput = isInitialized ? searchQueryInput === 'experimental' : true
|
||||
const enableExperimentalSearchInputRef = useRef(enableExperimentalSearchInput)
|
||||
enableExperimentalSearchInputRef.current = enableExperimentalSearchInput
|
||||
|
||||
// Create `locationSubject` once on mount. New values are provided in the `useEffect` hook.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const [locationSubject] = useState(() => new BehaviorSubject<Location>(location))
|
||||
|
||||
useEffect(() => {
|
||||
locationSubject.next(location)
|
||||
}, [location, locationSubject])
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = getQueryStateFromLocation({
|
||||
location: locationSubject,
|
||||
isSearchContextAvailable: (searchContext: string) =>
|
||||
searchContextsEnabled
|
||||
? isSearchContextSpecAvailable({
|
||||
spec: searchContext,
|
||||
platformContext,
|
||||
})
|
||||
.pipe(first())
|
||||
.toPromise()
|
||||
: Promise.resolve(false),
|
||||
}).subscribe(parsedSearchURLAndContext => {
|
||||
if (parsedSearchURLAndContext.query) {
|
||||
// Only override filters and update query from URL if there
|
||||
// is a search query.
|
||||
if (!parsedSearchURLAndContext.searchContextSpec) {
|
||||
// If no search context is present we have to fall back
|
||||
// to the global search context to match the server
|
||||
// behavior.
|
||||
setSelectedSearchContextSpec(GLOBAL_SEARCH_CONTEXT_SPEC)
|
||||
} else if (parsedSearchURLAndContext.searchContextSpec.spec !== selectedSearchContextSpecRef.current) {
|
||||
setSelectedSearchContextSpec(parsedSearchURLAndContext.searchContextSpec.spec)
|
||||
}
|
||||
|
||||
const processedQuery =
|
||||
!enableExperimentalSearchInputRef.current &&
|
||||
parsedSearchURLAndContext.searchContextSpec &&
|
||||
searchContextsEnabled
|
||||
? omitFilter(
|
||||
parsedSearchURLAndContext.query,
|
||||
parsedSearchURLAndContext.searchContextSpec.filter
|
||||
)
|
||||
: parsedSearchURLAndContext.query
|
||||
|
||||
setQueryStateFromURL(parsedSearchURLAndContext, processedQuery)
|
||||
}
|
||||
})
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [
|
||||
locationSubject,
|
||||
platformContext,
|
||||
searchContextsEnabled,
|
||||
selectedSearchContextSpecRef,
|
||||
enableExperimentalSearchInputRef,
|
||||
setSelectedSearchContextSpec,
|
||||
])
|
||||
|
||||
return null
|
||||
}
|
||||
@ -5,12 +5,11 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { ApolloProvider } from '@apollo/client'
|
||||
import ServerIcon from 'mdi-react/ServerIcon'
|
||||
import { Router } from 'react-router'
|
||||
import { CompatRouter, Routes, Route } from 'react-router-dom-v5-compat'
|
||||
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
||||
import { combineLatest, from, Subscription, fromEvent, of, Subject, Observable } from 'rxjs'
|
||||
import { distinctUntilChanged, first, map, startWith, switchMap } from 'rxjs/operators'
|
||||
import { startWith, switchMap } from 'rxjs/operators'
|
||||
|
||||
import { isMacPlatform, logger } from '@sourcegraph/common'
|
||||
import { isTruthy, isMacPlatform, logger } from '@sourcegraph/common'
|
||||
import { GraphQLClient, HTTPStatusError } from '@sourcegraph/http-client'
|
||||
import { SharedSpanName, TraceSpanProvider } from '@sourcegraph/observability-client'
|
||||
import { FetchFileParameters, fetchHighlightedFileLineRanges } from '@sourcegraph/shared/src/backend/file'
|
||||
@ -30,7 +29,6 @@ import {
|
||||
getDefaultSearchContextSpec,
|
||||
} from '@sourcegraph/shared/src/search'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { omitFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { filterExists } from '@sourcegraph/shared/src/search/query/validate'
|
||||
import { aggregateStreamingSearch } from '@sourcegraph/shared/src/search/stream'
|
||||
import {
|
||||
@ -68,8 +66,9 @@ import type { RepoRevisionContainerRoute } from './repo/RepoRevisionContainer'
|
||||
import type { RepoSettingsAreaRoute } from './repo/settings/RepoSettingsArea'
|
||||
import type { RepoSettingsSideBarGroup } from './repo/settings/RepoSettingsSidebar'
|
||||
import type { LayoutRouteProps, LegacyLayoutRouteComponentProps } from './routes'
|
||||
import { parseSearchURL, getQueryStateFromLocation, SearchAggregationProps } from './search'
|
||||
import { parseSearchURL, SearchAggregationProps } from './search'
|
||||
import { SearchResultsCacheProvider } from './search/results/SearchResultsCacheProvider'
|
||||
import { GLOBAL_SEARCH_CONTEXT_SPEC } from './SearchQueryStateObserver'
|
||||
import type { SiteAdminAreaRoute } from './site-admin/SiteAdminArea'
|
||||
import type { SiteAdminSideBarGroups } from './site-admin/SiteAdminSidebar'
|
||||
import {
|
||||
@ -77,10 +76,7 @@ import {
|
||||
setExperimentalFeaturesFromSettings,
|
||||
getExperimentalFeatures,
|
||||
useNavbarQueryState,
|
||||
observeStore,
|
||||
useExperimentalFeatures,
|
||||
} from './stores'
|
||||
import { setQueryStateFromURL } from './stores/navbarSearchQueryState'
|
||||
import { useThemeProps } from './theme'
|
||||
import { eventLogger } from './tracking/eventLogger'
|
||||
import type { UserAreaRoute } from './user/area/UserArea'
|
||||
@ -88,8 +84,6 @@ import type { UserAreaHeaderNavItem } from './user/area/UserAreaHeader'
|
||||
import type { UserSettingsAreaRoute } from './user/settings/UserSettingsArea'
|
||||
import type { UserSettingsSidebarItems } from './user/settings/UserSettingsSidebar'
|
||||
import { UserSessionStores } from './UserSessionStores'
|
||||
import { globalHistory } from './util/globalHistory'
|
||||
import { observeLocation } from './util/location'
|
||||
import { siteSubjectNoAdmin, viewerSubjectFromSettings } from './util/settings'
|
||||
|
||||
import styles from './LegacySourcegraphWebApp.module.scss'
|
||||
@ -125,8 +119,6 @@ const WILDCARD_THEME: WildcardTheme = {
|
||||
isBranded: true,
|
||||
}
|
||||
|
||||
const GLOBAL_SEARCH_CONTEXT_SPEC = 'global'
|
||||
|
||||
setLinkComponent(RouterLink)
|
||||
|
||||
export const SourcegraphWebApp: React.FC<SourcegraphWebAppProps> = props => {
|
||||
@ -298,55 +290,6 @@ export const SourcegraphWebApp: React.FC<SourcegraphWebAppProps> = props => {
|
||||
|
||||
setWorkspaceSearchContext(selectedSearchContextSpec)
|
||||
|
||||
// Update search query state whenever the URL changes
|
||||
subscriptions.add(
|
||||
combineLatest([
|
||||
observeStore(useExperimentalFeatures).pipe(
|
||||
map(([features]) => features.searchQueryInput === 'experimental'),
|
||||
// This ensures that the query stays unmodified until we know
|
||||
// whether the feature flag is set or not.
|
||||
startWith(true),
|
||||
distinctUntilChanged()
|
||||
),
|
||||
getQueryStateFromLocation({
|
||||
location: observeLocation(globalHistory).pipe(startWith(globalHistory.location)),
|
||||
isSearchContextAvailable: (searchContext: string) =>
|
||||
props.searchContextsEnabled
|
||||
? isSearchContextSpecAvailable({ spec: searchContext, platformContext })
|
||||
.pipe(first())
|
||||
.toPromise()
|
||||
: Promise.resolve(false),
|
||||
}),
|
||||
]).subscribe(([enableExperimentalQueryInput, parsedSearchURLAndContext]) => {
|
||||
if (parsedSearchURLAndContext.query) {
|
||||
// Only override filters and update query from URL if there
|
||||
// is a search query.
|
||||
if (!parsedSearchURLAndContext.searchContextSpec) {
|
||||
// If no search context is present we have to fall back
|
||||
// to the global search context to match the server
|
||||
// behavior.
|
||||
setSelectedSearchContextSpec(GLOBAL_SEARCH_CONTEXT_SPEC)
|
||||
} else if (
|
||||
parsedSearchURLAndContext.searchContextSpec.spec !== selectedSearchContextSpecRef.current
|
||||
) {
|
||||
setSelectedSearchContextSpec(parsedSearchURLAndContext.searchContextSpec.spec)
|
||||
}
|
||||
|
||||
const processedQuery =
|
||||
!enableExperimentalQueryInput &&
|
||||
parsedSearchURLAndContext.searchContextSpec &&
|
||||
props.searchContextsEnabled
|
||||
? omitFilter(
|
||||
parsedSearchURLAndContext.query,
|
||||
parsedSearchURLAndContext.searchContextSpec.filter
|
||||
)
|
||||
: parsedSearchURLAndContext.query
|
||||
|
||||
setQueryStateFromURL(parsedSearchURLAndContext, processedQuery)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
userRepositoriesUpdates.next()
|
||||
|
||||
return () => subscriptions.unsubscribe()
|
||||
@ -421,6 +364,55 @@ export const SourcegraphWebApp: React.FC<SourcegraphWebAppProps> = props => {
|
||||
return null
|
||||
}
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
element: (
|
||||
<Layout
|
||||
authenticatedUser={resolvedAuthenticatedUser}
|
||||
viewerSubject={viewerSubject}
|
||||
settingsCascade={settingsCascade}
|
||||
batchChangesEnabled={props.batchChangesEnabled}
|
||||
batchChangesExecutionEnabled={isBatchChangesExecutionEnabled(settingsCascade)}
|
||||
batchChangesWebhookLogsEnabled={window.context.batchChangesWebhookLogsEnabled}
|
||||
// Search query
|
||||
fetchHighlightedFileLineRanges={_fetchHighlightedFileLineRanges}
|
||||
// Extensions
|
||||
platformContext={platformContext}
|
||||
extensionsController={null}
|
||||
telemetryService={eventLogger}
|
||||
isSourcegraphDotCom={window.context.sourcegraphDotComMode}
|
||||
searchContextsEnabled={props.searchContextsEnabled}
|
||||
selectedSearchContextSpec={getSelectedSearchContextSpec()}
|
||||
setSelectedSearchContextSpec={setSelectedSearchContextSpec}
|
||||
getUserSearchContextNamespaces={getUserSearchContextNamespaces}
|
||||
fetchSearchContexts={fetchSearchContexts}
|
||||
fetchSearchContextBySpec={fetchSearchContextBySpec}
|
||||
fetchSearchContext={fetchSearchContext}
|
||||
createSearchContext={createSearchContext}
|
||||
updateSearchContext={updateSearchContext}
|
||||
deleteSearchContext={deleteSearchContext}
|
||||
isSearchContextSpecAvailable={isSearchContextSpecAvailable}
|
||||
globbing={globbing}
|
||||
streamSearch={aggregateStreamingSearch}
|
||||
codeIntelligenceEnabled={!!props.codeInsightsEnabled}
|
||||
notebooksEnabled={props.notebooksEnabled}
|
||||
codeMonitoringEnabled={props.codeMonitoringEnabled}
|
||||
searchAggregationEnabled={props.searchAggregationEnabled}
|
||||
themeProps={themeProps}
|
||||
/>
|
||||
),
|
||||
children: props.routes
|
||||
.map(
|
||||
({ condition = () => true, render, path }) =>
|
||||
condition(context) && {
|
||||
path: path.slice(1), // remove leading slash
|
||||
element: render(context),
|
||||
}
|
||||
)
|
||||
.filter(isTruthy),
|
||||
},
|
||||
])
|
||||
|
||||
return (
|
||||
<ComponentsComposer
|
||||
components={[
|
||||
@ -438,61 +430,7 @@ export const SourcegraphWebApp: React.FC<SourcegraphWebAppProps> = props => {
|
||||
/* eslint-enable react/no-children-prop, react/jsx-key */
|
||||
]}
|
||||
>
|
||||
<Router history={globalHistory}>
|
||||
<CompatRouter>
|
||||
<Routes>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<Layout
|
||||
authenticatedUser={resolvedAuthenticatedUser}
|
||||
viewerSubject={viewerSubject}
|
||||
settingsCascade={settingsCascade}
|
||||
batchChangesEnabled={props.batchChangesEnabled}
|
||||
batchChangesExecutionEnabled={isBatchChangesExecutionEnabled(settingsCascade)}
|
||||
batchChangesWebhookLogsEnabled={window.context.batchChangesWebhookLogsEnabled}
|
||||
// Search query
|
||||
fetchHighlightedFileLineRanges={_fetchHighlightedFileLineRanges}
|
||||
// Extensions
|
||||
platformContext={platformContext}
|
||||
extensionsController={null}
|
||||
telemetryService={eventLogger}
|
||||
isSourcegraphDotCom={window.context.sourcegraphDotComMode}
|
||||
searchContextsEnabled={props.searchContextsEnabled}
|
||||
selectedSearchContextSpec={getSelectedSearchContextSpec()}
|
||||
setSelectedSearchContextSpec={setSelectedSearchContextSpec}
|
||||
getUserSearchContextNamespaces={getUserSearchContextNamespaces}
|
||||
fetchSearchContexts={fetchSearchContexts}
|
||||
fetchSearchContextBySpec={fetchSearchContextBySpec}
|
||||
fetchSearchContext={fetchSearchContext}
|
||||
createSearchContext={createSearchContext}
|
||||
updateSearchContext={updateSearchContext}
|
||||
deleteSearchContext={deleteSearchContext}
|
||||
isSearchContextSpecAvailable={isSearchContextSpecAvailable}
|
||||
globbing={globbing}
|
||||
streamSearch={aggregateStreamingSearch}
|
||||
codeIntelligenceEnabled={!!props.codeInsightsEnabled}
|
||||
notebooksEnabled={props.notebooksEnabled}
|
||||
codeMonitoringEnabled={props.codeMonitoringEnabled}
|
||||
searchAggregationEnabled={props.searchAggregationEnabled}
|
||||
themeProps={themeProps}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{props.routes.map(
|
||||
({ condition = () => true, ...route }) =>
|
||||
condition(context) && (
|
||||
<Route
|
||||
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
|
||||
path={route.path.slice(1)} // remove leading slash
|
||||
element={route.render(context)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Route>
|
||||
</Routes>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
<RouterProvider router={router} />
|
||||
<UserSessionStores />
|
||||
</ComponentsComposer>
|
||||
)
|
||||
|
||||
@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
|
||||
import * as _graphiqlModule from 'graphiql' // type only
|
||||
import * as H from 'history'
|
||||
import { useNavigate, useLocation, type NavigateFunction } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation, type NavigateFunction } from 'react-router-dom'
|
||||
import { from as fromPromise, Subject, Subscription } from 'rxjs'
|
||||
import { catchError, debounceTime } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { asError, ErrorLike, isErrorLike, logger } from '@sourcegraph/common'
|
||||
import { Button, Link, LoadingSpinner, Alert, Text, Input, ErrorAlert, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { within } from '@testing-library/dom'
|
||||
import { Route, Routes } from 'react-router-dom-v5-compat'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
|
||||
import { renderWithBrandedContext } from '@sourcegraph/wildcard/src/testing'
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import { mdiBitbucket, mdiGithub, mdiGitlab, mdiEmail } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { partition } from 'lodash'
|
||||
import { Navigate, useLocation, useSearchParams } from 'react-router-dom-v5-compat'
|
||||
import { Navigate, useLocation, useSearchParams } from 'react-router-dom'
|
||||
|
||||
import { Alert, Icon, Text, Link, Button, ErrorAlert, AnchorLink } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Route, Routes } from 'react-router-dom-v5-compat'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { MockedTestProvider } from '@sourcegraph/shared/src/testing/apollo'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { Navigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { Navigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { Navigate, useLocation, useParams } from 'react-router-dom-v5-compat'
|
||||
import { Navigate, useLocation, useParams } from 'react-router-dom'
|
||||
|
||||
import { Alert, Link, LoadingSpinner, ErrorAlert } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { asError, logger } from '@sourcegraph/common'
|
||||
import { Label, Button, LoadingSpinner, Link, Text, Input, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Navigate } from 'react-router-dom-v5-compat'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
|
||||
import { AuthenticatedUser } from '../auth'
|
||||
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { LinkWithIcon } from '../components/LinkWithIcon'
|
||||
|
||||
import { CodeMonitoringLogo } from './CodeMonitoringLogo'
|
||||
|
||||
export const CodeMonitoringNavItem: React.FunctionComponent<React.PropsWithChildren<unknown>> = () => (
|
||||
<LinkWithIcon
|
||||
to="/code-monitoring"
|
||||
text="Monitoring"
|
||||
icon={CodeMonitoringLogo}
|
||||
className="nav-link text-decoration-none"
|
||||
activeClassName="active"
|
||||
/>
|
||||
)
|
||||
@ -1,4 +1,5 @@
|
||||
import { within, fireEvent } from '@testing-library/react'
|
||||
import { createPath } from 'react-router-dom'
|
||||
|
||||
import { MockedTestProvider, waitForNextApolloResponse } from '@sourcegraph/shared/src/testing/apollo'
|
||||
import '@sourcegraph/shared/dev/mockReactVisibilitySensor'
|
||||
@ -53,7 +54,7 @@ describe('ReferencesPanel', () => {
|
||||
})
|
||||
|
||||
it('renders a code view when clicking on a location', async () => {
|
||||
const { history, ...result } = await renderReferencesPanel()
|
||||
const { locationRef, ...result } = await renderReferencesPanel()
|
||||
|
||||
const definitionsList = result.getByTestId('definitions')
|
||||
const referencesList = result.getByTestId('references')
|
||||
@ -95,11 +96,11 @@ describe('ReferencesPanel', () => {
|
||||
expect(codeView).toHaveTextContent('package diff import')
|
||||
|
||||
// Assert the current URL points at the reference panel
|
||||
expect(history.createHref(history.location)).toBe(
|
||||
expect(createPath(locationRef.current!)).toBe(
|
||||
'/github.com/sourcegraph/go-diff@9d1f353a285b3094bc33bdae277a19aedabe8b71/-/blob/diff/diff.go?L16:2&subtree=true#tab=references'
|
||||
)
|
||||
// Click on reference the second time promotes the active location to the URL (and main blob view)
|
||||
fireEvent.click(referenceButton)
|
||||
expect(history.createHref(history.location)).toBe(fullReferenceURL)
|
||||
expect(createPath(locationRef.current!)).toBe(fullReferenceURL)
|
||||
})
|
||||
})
|
||||
|
||||
@ -4,7 +4,7 @@ import { mdiArrowCollapseRight, mdiChevronDown, mdiChevronUp, mdiFilterOutline,
|
||||
import classNames from 'classnames'
|
||||
import * as H from 'history'
|
||||
import { capitalize, uniqBy } from 'lodash'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { Observable, of } from 'rxjs'
|
||||
import { map } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { mdiSourceRepositoryMultiple, mdiGithub, mdiGitlab, mdiBitbucket } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { catchError, startWith } from 'rxjs/operators'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from '@sourcegraph/branded'
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
import { FC, useEffect, useState, useRef } from 'react'
|
||||
import { FC, useState, useCallback } from 'react'
|
||||
|
||||
import * as H from 'history'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { Location, useNavigate, unstable_useBlocker as useBlocker, unstable_BlockerFunction } from 'react-router-dom'
|
||||
|
||||
import { Button, Modal, H3 } from '@sourcegraph/wildcard'
|
||||
|
||||
import { globalHistory } from '../util/globalHistory'
|
||||
|
||||
type Func = () => void
|
||||
interface Props {
|
||||
message: string
|
||||
when: Func | boolean
|
||||
when: boolean | (() => boolean)
|
||||
header?: string
|
||||
button_ok_text?: string
|
||||
button_cancel_text?: string
|
||||
@ -22,42 +18,42 @@ export const AwayPrompt: FC<Props> = props => {
|
||||
const { message, when, header = 'Navigate away?', button_ok_text = 'OK', button_cancel_text = 'Cancel' } = props
|
||||
|
||||
const navigate = useNavigate()
|
||||
const [pendingLocation, setPendingLocation] = useState<H.Location>()
|
||||
const unblock = useRef<() => void>()
|
||||
const [pendingLocation, setPendingLocation] = useState<Location>()
|
||||
|
||||
const blocker = useBlocker(
|
||||
useCallback<unstable_BlockerFunction>(
|
||||
({ currentLocation, nextLocation }) => {
|
||||
if (nextLocation.state === ALLOW_NAVIGATION) {
|
||||
return false
|
||||
}
|
||||
|
||||
const shouldBlock = typeof when === 'boolean' ? when : when()
|
||||
|
||||
if (shouldBlock) {
|
||||
setPendingLocation(nextLocation)
|
||||
|
||||
// prevent navigation for now - pop-up is shown
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
[when]
|
||||
)
|
||||
)
|
||||
|
||||
const closeModal = (shouldNavigate: boolean): void => {
|
||||
// close modal
|
||||
setPendingLocation(undefined)
|
||||
|
||||
if (pendingLocation && shouldNavigate) {
|
||||
unblock.current?.()
|
||||
if (blocker.state === 'blocked') {
|
||||
blocker.reset()
|
||||
}
|
||||
navigate(pendingLocation)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
unblock.current = globalHistory.block(location => {
|
||||
if (location.state === ALLOW_NAVIGATION) {
|
||||
return unblock.current?.()
|
||||
}
|
||||
|
||||
const shouldBlock = typeof when === 'boolean' ? when : when()
|
||||
|
||||
if (shouldBlock) {
|
||||
setPendingLocation(location)
|
||||
|
||||
// prevent navigation for now - pop-up is shown
|
||||
return false
|
||||
}
|
||||
|
||||
return unblock.current?.()
|
||||
})
|
||||
|
||||
return () => {
|
||||
unblock.current?.()
|
||||
}
|
||||
}, [when])
|
||||
|
||||
return pendingLocation ? (
|
||||
<Modal aria-labelledby={header}>
|
||||
<H3 className="text-dark mb-4">{header}</H3>
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { FC, useState, useEffect, useMemo, useCallback } from 'react'
|
||||
import { mdiChevronRight } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { sortBy } from 'lodash'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Unsubscribable } from 'rxjs'
|
||||
|
||||
import { isDefined } from '@sourcegraph/common'
|
||||
|
||||
@ -2,7 +2,7 @@ import { useEffect } from 'react'
|
||||
|
||||
import { cleanup, fireEvent, render, screen, waitFor, act } from '@testing-library/react'
|
||||
import * as H from 'history'
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import sinon from 'sinon'
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
|
||||
import * as H from 'history'
|
||||
import { isEqual, uniq } from 'lodash'
|
||||
import { NavigateFunction, useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { NavigateFunction, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { combineLatest, merge, Observable, of, Subject, Subscription } from 'rxjs'
|
||||
import {
|
||||
catchError,
|
||||
|
||||
@ -251,7 +251,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe('')
|
||||
expect(page.locationRef.current?.search).toBe('')
|
||||
})
|
||||
|
||||
it('supports forward pagination', async () => {
|
||||
@ -270,7 +270,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?after=${getCursorForId('3')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?after=${getCursorForId('3')}`)
|
||||
|
||||
await goToNextPage(page)
|
||||
|
||||
@ -285,7 +285,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?after=${getCursorForId('6')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?after=${getCursorForId('6')}`)
|
||||
|
||||
await goToNextPage(page)
|
||||
|
||||
@ -298,7 +298,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(() => page.getByText('Next page')).toThrowError(/Unable to find an element/)
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?after=${getCursorForId('9')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?after=${getCursorForId('9')}`)
|
||||
})
|
||||
|
||||
it('supports restoration from forward pagination URL', async () => {
|
||||
@ -332,7 +332,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe('')
|
||||
expect(page.locationRef.current?.search).toBe('')
|
||||
})
|
||||
|
||||
it('supports jumping to the last page', async () => {
|
||||
@ -351,7 +351,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(() => page.getByText('Next page')).toThrowError(/Unable to find an element/)
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe('?last=3')
|
||||
expect(page.locationRef.current?.search).toBe('?last=3')
|
||||
})
|
||||
|
||||
it('supports restoration from last page URL', async () => {
|
||||
@ -386,7 +386,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?before=${getCursorForId('8')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?before=${getCursorForId('8')}`)
|
||||
|
||||
await goToPreviousPage(page)
|
||||
|
||||
@ -401,7 +401,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?before=${getCursorForId('5')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?before=${getCursorForId('5')}`)
|
||||
|
||||
await goToPreviousPage(page)
|
||||
|
||||
@ -414,7 +414,7 @@ describe('usePageSwitcherPagination', () => {
|
||||
expect(page.getByText('Next page')).toBeVisible()
|
||||
expect(page.getByText('Last page')).toBeVisible()
|
||||
|
||||
expect(page.history.location.search).toBe(`?before=${getCursorForId('2')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?before=${getCursorForId('2')}`)
|
||||
})
|
||||
|
||||
it('supports restoration from backward pagination URL', async () => {
|
||||
@ -448,6 +448,6 @@ describe('usePageSwitcherPagination', () => {
|
||||
|
||||
await goToLastPage(page)
|
||||
|
||||
expect(page.history.location.search).toBe(`?after=${getCursorForId('6')}`)
|
||||
expect(page.locationRef.current?.search).toBe(`?after=${getCursorForId('6')}`)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import { ApolloError, WatchQueryFetchPolicy } from '@apollo/client'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { GraphQLResult, useQuery } from '@sourcegraph/http-client'
|
||||
|
||||
|
||||
@ -177,7 +177,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('repo-A')).toBeVisible()
|
||||
expect(queries.getByText('Total count: 4')).toBeVisible()
|
||||
expect(queries.getByText('Fetch more')).toBeVisible()
|
||||
expect(queries.history.location.search).toBe('')
|
||||
expect(queries.locationRef.current?.search).toBe('')
|
||||
})
|
||||
|
||||
it('fetches next page of results correctly', async () => {
|
||||
@ -194,7 +194,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('Fetch more')).toBeVisible()
|
||||
|
||||
// URL updates to match visible results
|
||||
expect(queries.history.location.search).toBe('?visible=2')
|
||||
expect(queries.locationRef.current?.search).toBe('?visible=2')
|
||||
})
|
||||
|
||||
it('fetches final page of results correctly', async () => {
|
||||
@ -217,7 +217,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.queryByText('Fetch more')).not.toBeInTheDocument()
|
||||
|
||||
// URL updates to match visible results
|
||||
expect(queries.history.location.search).toBe('?visible=4')
|
||||
expect(queries.locationRef.current?.search).toBe('?visible=4')
|
||||
})
|
||||
|
||||
it('fetches correct amount of results when navigating directly with a URL', async () => {
|
||||
@ -249,7 +249,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('Total count: 4')).toBeVisible()
|
||||
|
||||
// URL should be overidden
|
||||
expect(queries.history.location.search).toBe('?visible=4')
|
||||
expect(queries.locationRef.current?.search).toBe('?visible=4')
|
||||
})
|
||||
})
|
||||
|
||||
@ -290,7 +290,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('repo-A')).toBeVisible()
|
||||
expect(queries.getByText('Total count: 4')).toBeVisible()
|
||||
expect(queries.getByText('Fetch more')).toBeVisible()
|
||||
expect(queries.history.location.search).toBe('')
|
||||
expect(queries.locationRef.current?.search).toBe('')
|
||||
})
|
||||
|
||||
it('fetches next page of results correctly', async () => {
|
||||
@ -307,7 +307,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('Fetch more')).toBeVisible()
|
||||
|
||||
// URL updates to match the new request
|
||||
expect(queries.history.location.search).toBe('?first=2')
|
||||
expect(queries.locationRef.current?.search).toBe('?first=2')
|
||||
})
|
||||
|
||||
it('fetches final page of results correctly', async () => {
|
||||
@ -329,7 +329,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.queryByText('Fetch more')).not.toBeInTheDocument()
|
||||
|
||||
// URL updates to match the new request
|
||||
expect(queries.history.location.search).toBe('?first=4')
|
||||
expect(queries.locationRef.current?.search).toBe('?first=4')
|
||||
})
|
||||
|
||||
it('fetches correct amount of results when navigating directly with a URL', async () => {
|
||||
@ -349,7 +349,7 @@ describe('useShowMorePagination', () => {
|
||||
expect(queries.getByText('Total count: 4')).toBeVisible()
|
||||
|
||||
// URL should be overidden
|
||||
expect(queries.history.location.search).toBe('?first=4')
|
||||
expect(queries.locationRef.current?.search).toBe('?first=4')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { getUrlQuery, GetUrlQueryParameters } from '../utils'
|
||||
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
.link-with-icon {
|
||||
padding: var(--nav-link-padding-y) var(--nav-link-padding-x);
|
||||
|
||||
&:global(.active):not(:global(.focus)):not(:focus) {
|
||||
// To override default active btn shadow
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { kebabCase } from 'lodash'
|
||||
import { NavLink, NavLinkProps } from 'react-router-dom'
|
||||
|
||||
import { Button, Icon } from '@sourcegraph/wildcard'
|
||||
|
||||
import styles from './LinkWithIcon.module.scss'
|
||||
|
||||
interface LinkWithIconProps extends NavLinkProps {
|
||||
text: string
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
}
|
||||
|
||||
/**
|
||||
* A link displaying an icon along with text.
|
||||
*/
|
||||
export const LinkWithIcon: React.FunctionComponent<React.PropsWithChildren<LinkWithIconProps>> = props => {
|
||||
const { to, exact, text, className, activeClassName, icon: linkIcon } = props
|
||||
|
||||
return (
|
||||
<Button
|
||||
as={NavLink}
|
||||
to={to}
|
||||
exact={exact}
|
||||
className={classNames('d-flex', 'align-items-center', styles.linkWithIcon, className)}
|
||||
activeClassName={activeClassName}
|
||||
variant="link"
|
||||
data-testid={kebabCase(text)}
|
||||
>
|
||||
<Icon className="mr-1" as={linkIcon} aria-hidden={true} />
|
||||
<span className="inline-block">{text}</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Location, Navigate, Params, useLocation, useParams } from 'react-router-dom-v5-compat'
|
||||
import { Location, Navigate, Params, useLocation, useParams } from 'react-router-dom'
|
||||
|
||||
interface RedirectRouteProps {
|
||||
getRedirectURL: (data: { location: Location; params: Params }) => string
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import React, { FC, PropsWithChildren } from 'react'
|
||||
|
||||
import { mdiMenuDown, mdiMenuUp } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
import { useRouteMatch } from 'react-router-dom'
|
||||
import { useMatch } from 'react-router-dom'
|
||||
|
||||
import {
|
||||
AnchorLink,
|
||||
@ -19,20 +19,28 @@ import {
|
||||
|
||||
import styles from './Sidebar.module.scss'
|
||||
|
||||
interface SidebarNavItemProps {
|
||||
to: string
|
||||
className?: string
|
||||
source?: string
|
||||
onClick?: () => void
|
||||
exact?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Item of `SideBarGroup`.
|
||||
*/
|
||||
export const SidebarNavItem: React.FunctionComponent<
|
||||
React.PropsWithChildren<{
|
||||
to: string
|
||||
className?: string
|
||||
exact?: boolean
|
||||
source?: string
|
||||
onClick?: () => void
|
||||
}>
|
||||
> = ({ children, className, to, exact, source, onClick }) => {
|
||||
export const SidebarNavItem: FC<PropsWithChildren<SidebarNavItemProps>> = ({
|
||||
children,
|
||||
className,
|
||||
to,
|
||||
source,
|
||||
onClick,
|
||||
exact = false,
|
||||
}) => {
|
||||
// Match nested routes too.
|
||||
const routeMatch = useMatch(to + (exact ? '' : '/*'))
|
||||
const buttonClassNames = classNames('text-left d-flex', styles.linkInactive, className)
|
||||
const routeMatch = useRouteMatch({ path: to, exact })
|
||||
|
||||
if (source === 'server') {
|
||||
return (
|
||||
@ -43,12 +51,7 @@ export const SidebarNavItem: React.FunctionComponent<
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonLink
|
||||
to={to}
|
||||
className={buttonClassNames}
|
||||
variant={routeMatch?.isExact ? 'primary' : undefined}
|
||||
onClick={onClick}
|
||||
>
|
||||
<ButtonLink to={to} className={buttonClassNames} variant={routeMatch ? 'primary' : undefined} onClick={onClick}>
|
||||
{children}
|
||||
</ButtonLink>
|
||||
)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { Location, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { Location, useLocation } from 'react-router-dom'
|
||||
import { fromEvent, Observable } from 'rxjs'
|
||||
import { finalize, tap } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { MemoryRouter, MemoryRouterProps } from 'react-router'
|
||||
import { Routes, Route, CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { RouterProvider, createMemoryRouter, MemoryRouterProps } from 'react-router-dom'
|
||||
|
||||
import { MockedStoryProvider, MockedStoryProviderProps } from '@sourcegraph/shared/src/stories'
|
||||
import { NOOP_TELEMETRY_SERVICE, TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
@ -40,7 +39,8 @@ export const WebStory: FC<WebStoryProps> = ({
|
||||
mocks,
|
||||
path = '*',
|
||||
useStrictMocking,
|
||||
...memoryRouterProps
|
||||
initialEntries = ['/'],
|
||||
initialIndex = 1,
|
||||
}) => {
|
||||
const isLightTheme = useTheme()
|
||||
const breadcrumbSetters = useBreadcrumbs()
|
||||
@ -48,25 +48,28 @@ export const WebStory: FC<WebStoryProps> = ({
|
||||
usePrependStyles('web-styles', webStyles)
|
||||
setExperimentalFeaturesForTesting()
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path,
|
||||
element: (
|
||||
<Children
|
||||
{...breadcrumbSetters}
|
||||
isLightTheme={isLightTheme}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const router = createMemoryRouter(routes, {
|
||||
initialEntries,
|
||||
initialIndex,
|
||||
})
|
||||
|
||||
return (
|
||||
<MockedStoryProvider mocks={mocks} useStrictMocking={useStrictMocking}>
|
||||
<WildcardThemeContext.Provider value={{ isBranded: true }}>
|
||||
<MemoryRouter {...memoryRouterProps}>
|
||||
<CompatRouter>
|
||||
<Routes>
|
||||
<Route
|
||||
path={path}
|
||||
element={
|
||||
<Children
|
||||
{...breadcrumbSetters}
|
||||
isLightTheme={isLightTheme}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</CompatRouter>
|
||||
</MemoryRouter>
|
||||
<RouterProvider router={router} />
|
||||
</WildcardThemeContext.Provider>
|
||||
</MockedStoryProvider>
|
||||
)
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { createMemoryHistory } from 'history'
|
||||
import { Router } from 'react-router-dom'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
|
||||
import { DiffHunkLineType, FileDiffHunkFields } from '../../graphql-operations'
|
||||
|
||||
import { DiffHunk } from './DiffHunk'
|
||||
|
||||
describe('DiffHunk', () => {
|
||||
const history = createMemoryHistory()
|
||||
const hunk: FileDiffHunkFields = {
|
||||
oldRange: { startLine: 159, lines: 7 },
|
||||
oldNoNewlineAt: false,
|
||||
@ -56,15 +53,13 @@ describe('DiffHunk', () => {
|
||||
it('renders a unified diff view for the given diff hunk', () => {
|
||||
expect(
|
||||
render(
|
||||
<Router history={history}>
|
||||
<CompatRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<DiffHunk hunk={hunk} lineNumbers={true} fileDiffAnchor="anchor_" />
|
||||
</tbody>
|
||||
</table>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<DiffHunk hunk={hunk} lineNumbers={true} fileDiffAnchor="anchor_" />
|
||||
</tbody>
|
||||
</table>
|
||||
</MemoryRouter>
|
||||
).asFragment()
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
|
||||
@ -3,7 +3,7 @@ import * as React from 'react'
|
||||
|
||||
import { VisuallyHidden } from '@reach/visually-hidden'
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { createLinkUrl, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { cleanup, fireEvent, render, RenderResult } from '@testing-library/react'
|
||||
import { createMemoryHistory } from 'history'
|
||||
import { Router } from 'react-router'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
|
||||
import { DiffHunkLineType, FileDiffHunkFields } from '../../graphql-operations'
|
||||
|
||||
@ -54,19 +52,16 @@ describe('DiffSplitHunk', () => {
|
||||
},
|
||||
}
|
||||
|
||||
const history = createMemoryHistory()
|
||||
let queries: RenderResult
|
||||
const renderWithProps = (props: DiffHunkProps): RenderResult =>
|
||||
render(
|
||||
<Router history={history}>
|
||||
<CompatRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<DiffSplitHunk {...props} />
|
||||
</tbody>
|
||||
</table>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
<MemoryRouter>
|
||||
<table>
|
||||
<tbody>
|
||||
<DiffSplitHunk {...props} />
|
||||
</tbody>
|
||||
</table>
|
||||
</MemoryRouter>
|
||||
)
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { DiffHunkLineType, FileDiffHunkFields } from '../../graphql-operations'
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { useState, useCallback } from 'react'
|
||||
import { mdiChevronDown, mdiChevronUp } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { Button, Badge, Link, Icon, Text, createLinkUrl, Tooltip } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FC, useEffect, useCallback, useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { asError, isErrorLike, logger, renderMarkdown } from '@sourcegraph/common'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { mdiInformation } from '@mdi/js'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { FC, useEffect, useState, useCallback } from 'react'
|
||||
|
||||
import { mdiCog } from '@mdi/js'
|
||||
import { Navigate, useParams } from 'react-router-dom-v5-compat'
|
||||
import { Navigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { useQuery } from '@sourcegraph/http-client'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback, useMemo, FC } from 'react'
|
||||
|
||||
import { mdiCog, mdiConnection, mdiDelete } from '@mdi/js'
|
||||
import MapSearchIcon from 'mdi-react/MapSearchIcon'
|
||||
import { useNavigate, useParams } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Subject } from 'rxjs'
|
||||
|
||||
import { asError, isErrorLike } from '@sourcegraph/common'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FC, useEffect } from 'react'
|
||||
|
||||
import { mdiPlus } from '@mdi/js'
|
||||
import { Navigate } from 'react-router-dom-v5-compat'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { Link, ButtonLink, Icon, PageHeader, Container } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo } from 'react'
|
||||
|
||||
import * as H from 'history'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { parseBrowserRepoURL } from '../util/url'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Subscription } from 'rxjs'
|
||||
|
||||
import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { FC, useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
|
||||
import { useNavigate, useParams } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { useQuery } from '@sourcegraph/http-client'
|
||||
import { Settings, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useState, useCallback } from 'react'
|
||||
|
||||
import { mdiChevronDoubleLeft, mdiChevronDoubleRight, mdiOpenInNew } from '@mdi/js'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
|
||||
import { Button, useLocalStorage, H3, H4, Icon, Link, Text, VIEWPORT_XL } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { useMutation } from '@sourcegraph/http-client'
|
||||
import { Scalars } from '@sourcegraph/shared/src/graphql-operations'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { FC, useCallback, useRef, useState } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Input, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useCallback, useState } from 'react'
|
||||
|
||||
import { mdiChevronDown, mdiAlertCircle, mdiPencil, mdiClose, mdiSync, mdiCloseCircleOutline } from '@mdi/js'
|
||||
import { noop } from 'lodash'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { useMutation } from '@sourcegraph/http-client'
|
||||
import {
|
||||
|
||||
@ -4,7 +4,7 @@ import { mdiProgressClock } from '@mdi/js'
|
||||
import { VisuallyHidden } from '@reach/visually-hidden'
|
||||
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
|
||||
import MapSearchIcon from 'mdi-react/MapSearchIcon'
|
||||
import { Navigate, Route, Routes, useParams } from 'react-router-dom-v5-compat'
|
||||
import { Navigate, Route, Routes, useParams } from 'react-router-dom'
|
||||
|
||||
import { Timestamp } from '@sourcegraph/branded/src/components/Timestamp'
|
||||
import { useQuery } from '@sourcegraph/http-client'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { FC, useMemo, useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { useMutation } from '@sourcegraph/http-client'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import { VisuallyHidden } from '@reach/visually-hidden'
|
||||
import { useNavigate, useParams } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
import { Card, CardBody, H3, H1, Icon, Text, Code, ErrorAlert } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { lowerCase, upperFirst } from 'lodash'
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Input, Select, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FC, useCallback } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { DiffStat } from '../../../../../components/diff/DiffStat'
|
||||
import {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { isErrorLike, asError, pluralize } from '@sourcegraph/common'
|
||||
import { Button, AlertLink, CardBody, Card, Alert, Checkbox, Text, ErrorAlert } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useState, useMemo, useCallback } from 'react'
|
||||
|
||||
import { subDays } from 'date-fns'
|
||||
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
|
||||
import { useParams } from 'react-router-dom-v5-compat'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import { ErrorLike, isErrorLike } from '@sourcegraph/common'
|
||||
import { PageHeader, LoadingSpinner, useObservable } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { useCallback, useState } from 'react'
|
||||
import { mdiInformationOutline, mdiLock } from '@mdi/js'
|
||||
import classNames from 'classnames'
|
||||
import { noop } from 'lodash'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { useMutation } from '@sourcegraph/http-client'
|
||||
import { UserSettingFields, OrgSettingFields } from '@sourcegraph/shared/src/graphql-operations'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { Settings, SettingsCascadeOrError } from '@sourcegraph/shared/src/settings/settings'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import helloWorldSample from '../batch-spec/edit/library/hello-world.batch.yaml'
|
||||
import { insertQueryIntoLibraryItem, insertNameIntoLibraryItem } from '../batch-spec/yaml-util'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import { mdiInformation, mdiClose, mdiDelete, mdiPencil } from '@mdi/js'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { isErrorLike, asError } from '@sourcegraph/common'
|
||||
import { Settings } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from 'react'
|
||||
|
||||
import { subDays, startOfDay } from 'date-fns'
|
||||
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
|
||||
import { useParams } from 'react-router-dom-v5-compat'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import { useQuery } from '@sourcegraph/http-client'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import { mdiSourceBranch, mdiChartLineVariant, mdiFileDocument, mdiArchive, mdiMonitorStar } from '@mdi/js'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { Settings, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { pluralize } from '@sourcegraph/common'
|
||||
import { BulkOperationState } from '@sourcegraph/shared/src/graphql-operations'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { mdiArchive } from '@mdi/js'
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { pluralize } from '@sourcegraph/common'
|
||||
import { Link, Icon } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Input, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { Link, H3, Text } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Routes, Route } from 'react-router-dom-v5-compat'
|
||||
import { Routes, Route } from 'react-router-dom'
|
||||
|
||||
import { Scalars } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useCallback, useState, useMemo } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { pluralize } from '@sourcegraph/common'
|
||||
import { dataOrThrowErrors, useQuery } from '@sourcegraph/http-client'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { LegacyBatchChangesFilter } from '@sourcegraph/shared/src/settings/temporary/TemporarySettings'
|
||||
import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary/useTemporarySetting'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FC, useEffect } from 'react'
|
||||
|
||||
import AlertCircleIcon from 'mdi-react/AlertCircleIcon'
|
||||
import { useParams } from 'react-router-dom-v5-compat'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import { useQuery } from '@sourcegraph/http-client'
|
||||
import { Alert, LoadingSpinner, PageHeader } from '@sourcegraph/wildcard'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { mdiSourceBranch, mdiFileDocument } from '@mdi/js'
|
||||
import { useNavigate, useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useContext, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { isErrorLike } from '@sourcegraph/common'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { FC, useCallback, useContext, useEffect, useRef } from 'react'
|
||||
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Input, Form } from '@sourcegraph/wildcard'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { of } from 'rxjs'
|
||||
|
||||
import { buildCloudTrialURL } from '@sourcegraph/shared/src/util/url'
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { render, fireEvent } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router'
|
||||
import { CompatRouter } from 'react-router-dom-v5-compat'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { of } from 'rxjs'
|
||||
import sinon from 'sinon'
|
||||
|
||||
@ -50,9 +49,7 @@ describe('CodeMonitoringListPage', () => {
|
||||
test('Clicking enabled toggle calls toggleCodeMonitorEnabled', () => {
|
||||
const component = render(
|
||||
<MemoryRouter initialEntries={['/code-monitoring']}>
|
||||
<CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(1)} />
|
||||
</CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(1)} />
|
||||
</MemoryRouter>
|
||||
)
|
||||
const toggle = component.getByTestId('toggle-monitor-enabled')
|
||||
@ -63,9 +60,7 @@ describe('CodeMonitoringListPage', () => {
|
||||
test('Switching tabs from getting started to empty list works', () => {
|
||||
const component = render(
|
||||
<MemoryRouter initialEntries={['/code-monitoring']}>
|
||||
<CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(0)} />
|
||||
</CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(0)} />
|
||||
</MemoryRouter>
|
||||
)
|
||||
const codeMonitorsButton = component.getByRole('button', { name: 'Code monitors' })
|
||||
@ -78,9 +73,7 @@ describe('CodeMonitoringListPage', () => {
|
||||
test('Switching tabs from list to getting started works', () => {
|
||||
const component = render(
|
||||
<MemoryRouter initialEntries={['/code-monitoring']}>
|
||||
<CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(0)} />
|
||||
</CompatRouter>
|
||||
<CodeMonitoringPage {...additionalProps} fetchUserCodeMonitors={generateMockFetchMonitors(0)} />
|
||||
</MemoryRouter>
|
||||
)
|
||||
const gettingStartedButton = component.getByRole('button', { name: 'Getting started' })
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { Route, Routes } from 'react-router-dom-v5-compat'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import { NEVER, of } from 'rxjs'
|
||||
import sinon from 'sinon'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||
|
||||
import { VisuallyHidden } from '@reach/visually-hidden'
|
||||
import { useLocation } from 'react-router-dom-v5-compat'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { Route, Routes } from 'react-router-dom-v5-compat/'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import { of } from 'rxjs'
|
||||
import sinon from 'sinon'
|
||||
|
||||
@ -204,8 +204,7 @@ describe('ManageCodeMonitorPage', () => {
|
||||
})
|
||||
|
||||
test('Clicking delete code monitor opens deletion confirmation modal', () => {
|
||||
let currentPathname = ''
|
||||
renderWithBrandedContext(
|
||||
const { locationRef } = renderWithBrandedContext(
|
||||
<MockedTestProvider>
|
||||
<Routes>
|
||||
<Route path="/code-monitoring/:id" element={<ManageCodeMonitorPage {...props} />} />
|
||||
@ -214,7 +213,6 @@ describe('ManageCodeMonitorPage', () => {
|
||||
</MockedTestProvider>,
|
||||
{
|
||||
route: '/code-monitoring/test-monitor-id',
|
||||
onLocationChange: location => (currentPathname = location.pathname),
|
||||
}
|
||||
)
|
||||
userEvent.click(screen.getByTestId('delete-monitor'))
|
||||
@ -225,6 +223,6 @@ describe('ManageCodeMonitorPage', () => {
|
||||
userEvent.click(confirmDeleteButton)
|
||||
|
||||
sinon.assert.calledOnce(props.deleteCodeMonitor)
|
||||
expect(currentPathname).toEqual('/code-monitoring')
|
||||
expect(locationRef.current?.pathname).toEqual('/code-monitoring')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import { VisuallyHidden } from '@reach/visually-hidden'
|
||||
import { useParams } from 'react-router-dom-v5-compat'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
import { startWith, catchError, tap } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { isEqual } from 'lodash'
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Observable } from 'rxjs'
|
||||
import { mergeMap, startWith, catchError, tap, filter } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { useNavigate } from 'react-router-dom-v5-compat'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Observable, throwError } from 'rxjs'
|
||||
import { mergeMap, startWith, tap, catchError } from 'rxjs/operators'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Routes, Route } from 'react-router-dom-v5-compat'
|
||||
import { Routes, Route } from 'react-router-dom'
|
||||
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user