mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:51:59 +00:00
Create search and search-ui packages (#29773)
Co-authored-by: Juliana Peña <juliana@sourcegraph.com>
This commit is contained in:
parent
c7f9aeaf38
commit
29ef1da2a9
2
.github/workflows/lsif-ts.yml
vendored
2
.github/workflows/lsif-ts.yml
vendored
@ -21,6 +21,8 @@ jobs:
|
||||
- client/browser
|
||||
- client/build-config
|
||||
- client/shared
|
||||
- client/search
|
||||
- client/search-ui
|
||||
- client/web
|
||||
- client/wildcard
|
||||
- client/common
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -104,7 +104,7 @@ package-lock.json
|
||||
coverage/
|
||||
out/
|
||||
client/shared/src/schema.ts
|
||||
client/web/src/schema/*
|
||||
client/shared/src/schema/*
|
||||
puppeteer/
|
||||
package-lock.json
|
||||
/dist
|
||||
|
||||
@ -16,7 +16,7 @@ vendor/
|
||||
out/
|
||||
dist/
|
||||
client/shared/src/graphqlschema.ts
|
||||
client/web/src/schema/
|
||||
client/shared/src/schema/
|
||||
ts-node-*
|
||||
testdata
|
||||
.github/*
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -61,6 +61,8 @@
|
||||
"./client/browser",
|
||||
"./client/build-config",
|
||||
"./client/shared",
|
||||
"./client/search",
|
||||
"./client/search-ui",
|
||||
"./client/http-client",
|
||||
"./client/branded",
|
||||
"./client/wildcard",
|
||||
|
||||
1
client/search-ui/.eslintignore
Normal file
1
client/search-ui/.eslintignore
Normal file
@ -0,0 +1 @@
|
||||
out/
|
||||
12
client/search-ui/.eslintrc.js
Normal file
12
client/search-ui/.eslintrc.js
Normal file
@ -0,0 +1,12 @@
|
||||
// @ts-check
|
||||
|
||||
const baseConfig = require('../../.eslintrc.js')
|
||||
|
||||
module.exports = {
|
||||
extends: '../../.eslintrc.js',
|
||||
parserOptions: {
|
||||
...baseConfig.parserOptions,
|
||||
project: [__dirname + '/tsconfig.json'],
|
||||
},
|
||||
overrides: baseConfig.overrides,
|
||||
}
|
||||
3
client/search-ui/README.md
Normal file
3
client/search-ui/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Search UI package
|
||||
|
||||
This package contains search UI components with branded styling that are shared between clients. For example, the `<SearchBox>` component that is used in both the web application and VS Code extension.
|
||||
5
client/search-ui/babel.config.js
Normal file
5
client/search-ui/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
module.exports = {
|
||||
extends: '../../babel.config.js',
|
||||
}
|
||||
13
client/search-ui/jest.config.js
Normal file
13
client/search-ui/jest.config.js
Normal file
@ -0,0 +1,13 @@
|
||||
// @ts-check
|
||||
|
||||
const config = require('../../jest.config.base')
|
||||
|
||||
const exportedConfig = {
|
||||
...config,
|
||||
displayName: 'common',
|
||||
rootDir: __dirname,
|
||||
roots: ['<rootDir>'],
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
module.exports = exportedConfig
|
||||
14
client/search-ui/package.json
Normal file
14
client/search-ui/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@sourcegraph/search-ui",
|
||||
"version": "0.0.1",
|
||||
"description": "Branded Sourcegraph search UI components",
|
||||
"main": "./src/index.ts",
|
||||
"sideEffects": false,
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"storybook": "STORIES_GLOB=client/search-ui/src/**/*.story.tsx yarn workspace @sourcegraph/storybook run start",
|
||||
"eslint": "eslint --cache 'src/**/*.[jt]s?(x)'",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
@ -6,18 +6,18 @@ import { combineLatest, of, Subject, Subscription } from 'rxjs'
|
||||
import { catchError, distinctUntilChanged, filter, switchMap } from 'rxjs/operators'
|
||||
import sanitizeHtml from 'sanitize-html'
|
||||
|
||||
import { highlightCode } from '@sourcegraph/search'
|
||||
import { LastSyncedIcon } from '@sourcegraph/shared/src/components/LastSyncedIcon'
|
||||
import { Markdown } from '@sourcegraph/shared/src/components/Markdown'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { CommitMatch } from '@sourcegraph/shared/src/search/stream'
|
||||
import { highlightNode } from '@sourcegraph/shared/src/util/dom'
|
||||
import { LoadingSpinner, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { highlightCode } from '../search/backend'
|
||||
|
||||
import styles from './CommitSearchResultMatch.module.scss'
|
||||
import searchResultStyles from './SearchResult.module.scss'
|
||||
|
||||
interface CommitSearchResultMatchProps {
|
||||
interface CommitSearchResultMatchProps extends PlatformContextProps<'requestGraphQL'> {
|
||||
item: CommitMatch
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ export class CommitSearchResultMatch extends React.Component<
|
||||
code: codeContent,
|
||||
fuzzyLanguage: lang,
|
||||
disableTimeout: false,
|
||||
platformContext: props.platformContext,
|
||||
}).pipe(
|
||||
// Return the rendered markdown if highlighting fails.
|
||||
catchError(error => {
|
||||
@ -9,21 +9,21 @@ import { Markdown } from '@sourcegraph/shared/src/components/Markdown'
|
||||
import { RepoIcon } from '@sourcegraph/shared/src/components/RepoIcon'
|
||||
import { ResultContainer } from '@sourcegraph/shared/src/components/ResultContainer'
|
||||
import { SearchResultStar } from '@sourcegraph/shared/src/components/SearchResultStar'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { CommitMatch, getMatchTitle, RepositoryMatch } from '@sourcegraph/shared/src/search/stream'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { renderMarkdown } from '@sourcegraph/shared/src/util/markdown'
|
||||
import { formatRepositoryStarCount } from '@sourcegraph/shared/src/util/stars'
|
||||
|
||||
import { CommitSearchResultMatch } from './CommitSearchResultMatch'
|
||||
import styles from './SearchResult.module.scss'
|
||||
|
||||
interface Props extends TelemetryProps {
|
||||
interface Props extends PlatformContextProps<'requestGraphQL'> {
|
||||
result: CommitMatch | RepositoryMatch
|
||||
repoName: string
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
}
|
||||
|
||||
export const SearchResult: React.FunctionComponent<Props> = ({ result, icon, repoName, telemetryService }) => {
|
||||
export const SearchResult: React.FunctionComponent<Props> = ({ result, icon, repoName, platformContext }) => {
|
||||
const renderTitle = (): JSX.Element => {
|
||||
const formattedRepositoryStarCount = formatRepositoryStarCount(result.repoStars)
|
||||
return (
|
||||
@ -117,7 +117,7 @@ export const SearchResult: React.FunctionComponent<Props> = ({ result, icon, rep
|
||||
)
|
||||
}
|
||||
|
||||
return <CommitSearchResultMatch key={result.url} item={result} />
|
||||
return <CommitSearchResultMatch key={result.url} item={result} platformContext={platformContext} />
|
||||
}
|
||||
|
||||
return (
|
||||
@ -1,15 +1,16 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from './SyntaxHighlightedSearchQuery'
|
||||
import { WebStory } from './WebStory'
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
|
||||
const { add } = storiesOf('web/SyntaxHighlightedSearchQuery', module).addParameters({
|
||||
import { SyntaxHighlightedSearchQuery } from './SyntaxHighlightedSearchQuery'
|
||||
|
||||
const { add } = storiesOf('search-ui/SyntaxHighlightedSearchQuery', module).addParameters({
|
||||
chromatic: { viewports: [480] },
|
||||
})
|
||||
|
||||
add('Examples', () => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<p>
|
||||
<SyntaxHighlightedSearchQuery query="test AND spec" />
|
||||
@ -19,5 +20,5 @@ add('Examples', () => (
|
||||
<SyntaxHighlightedSearchQuery query="test -lang:ts" />
|
||||
</p>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
))
|
||||
2
client/search-ui/src/components/index.ts
Normal file
2
client/search-ui/src/components/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './SyntaxHighlightedSearchQuery'
|
||||
export * from './SearchResult'
|
||||
@ -14,6 +14,7 @@ interface ModalVideoProps {
|
||||
onToggle?: (isOpen: boolean) => void
|
||||
showCaption?: boolean
|
||||
className?: string
|
||||
assetsRoot?: string
|
||||
}
|
||||
|
||||
export const ModalVideo: React.FunctionComponent<ModalVideoProps> = ({
|
||||
@ -24,8 +25,8 @@ export const ModalVideo: React.FunctionComponent<ModalVideoProps> = ({
|
||||
onToggle,
|
||||
showCaption = false,
|
||||
className,
|
||||
assetsRoot = '',
|
||||
}) => {
|
||||
const assetsRoot = window.context?.assetsRoot || ''
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const toggleDialog = useCallback(
|
||||
isOpen => {
|
||||
9
client/search-ui/src/index.ts
Normal file
9
client/search-ui/src/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export * from './components'
|
||||
export * from './input/toggles'
|
||||
export * from './input/SearchBox'
|
||||
export * from './documentation/ModalVideo'
|
||||
export * from './results/StreamingSearchResultsList'
|
||||
export * from './results/progress/StreamingProgress'
|
||||
export * from './results/sidebar/revisions'
|
||||
export * from './results/sidebar/FilterLink'
|
||||
export * from './results/sidebar/SearchSidebar'
|
||||
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { Suspense } from 'react'
|
||||
|
||||
import { lazyComponent } from '../../util/lazyComponent'
|
||||
import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent'
|
||||
|
||||
import styles from './LazyMonacoQueryInput.module.scss'
|
||||
import { MonacoQueryInputProps } from './MonacoQueryInput'
|
||||
@ -1,12 +1,12 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
|
||||
import { WebStory } from '../../components/WebStory'
|
||||
import { SearchPatternType } from '../../graphql-operations'
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/schema'
|
||||
|
||||
import { MonacoQueryInput, MonacoQueryInputProps } from './MonacoQueryInput'
|
||||
|
||||
const { add } = storiesOf('web/search/input/MonacoQueryInput', module)
|
||||
const { add } = storiesOf('search-ui/search/input/MonacoQueryInput', module)
|
||||
.addParameters({ chromatic: { viewports: [700] } })
|
||||
.addDecorator(story => (
|
||||
<div className="p-3" style={{ height: 'calc(34px + 1rem + 1rem)', display: 'flex' }}>
|
||||
@ -29,6 +29,8 @@ const defaultProps: MonacoQueryInputProps = {
|
||||
|
||||
add(
|
||||
'default',
|
||||
() => <WebStory>{props => <MonacoQueryInput {...defaultProps} isLightTheme={props.isLightTheme} />}</WebStory>,
|
||||
() => (
|
||||
<BrandedStory>{props => <MonacoQueryInput {...defaultProps} isLightTheme={props.isLightTheme} />}</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -3,20 +3,25 @@ import { isPlainObject, noop } from 'lodash'
|
||||
import * as Monaco from 'monaco-editor'
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react'
|
||||
|
||||
import {
|
||||
QueryChangeSource,
|
||||
QueryState,
|
||||
CaseSensitivityProps,
|
||||
SearchPatternTypeProps,
|
||||
SearchContextProps,
|
||||
useQueryIntelligence,
|
||||
useQueryDiagnostics,
|
||||
} from '@sourcegraph/search'
|
||||
import { MonacoEditor } from '@sourcegraph/shared/src/components/MonacoEditor'
|
||||
import { KeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts'
|
||||
import { KEYBOARD_SHORTCUT_FOCUS_SEARCHBAR } from '@sourcegraph/shared/src/keyboardShortcuts/keyboardShortcuts'
|
||||
import { toMonacoRange } from '@sourcegraph/shared/src/search/query/monaco'
|
||||
import { appendContextFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { fetchStreamSuggestions } from '@sourcegraph/shared/src/search/suggestions'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
import { observeResize } from '@sourcegraph/shared/src/util/dom'
|
||||
import { hasProperty } from '@sourcegraph/shared/src/util/types'
|
||||
|
||||
import { CaseSensitivityProps, SearchPatternTypeProps, SearchContextProps } from '..'
|
||||
import { MonacoEditor } from '../../components/MonacoEditor'
|
||||
import { KEYBOARD_SHORTCUT_FOCUS_SEARCHBAR } from '../../keyboardShortcuts/keyboardShortcuts'
|
||||
import { observeResize } from '../../util/dom'
|
||||
import { QueryChangeSource, QueryState } from '../helpers'
|
||||
import { useQueryIntelligence, useQueryDiagnostics } from '../useQueryIntelligence'
|
||||
|
||||
import styles from './MonacoQueryInput.module.scss'
|
||||
|
||||
export const DEFAULT_MONACO_OPTIONS: Monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
@ -1,19 +1,19 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/schema'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import {
|
||||
mockFetchAutoDefinedSearchContexts,
|
||||
mockFetchSearchContexts,
|
||||
mockGetUserSearchContextNamespaces,
|
||||
} from '@sourcegraph/shared/src/testing/searchContexts/testHelpers'
|
||||
|
||||
import { WebStory } from '../../components/WebStory'
|
||||
import { SearchPatternType } from '../../graphql-operations'
|
||||
import { NOOP_PLATFORM_CONTEXT } from '@sourcegraph/shared/src/testing/searchTestHelpers'
|
||||
|
||||
import { SearchBox, SearchBoxProps } from './SearchBox'
|
||||
|
||||
const { add } = storiesOf('web/search/input/SearchBox', module)
|
||||
const { add } = storiesOf('search-ui/search/input/SearchBox', module)
|
||||
.addParameters({ chromatic: { viewports: [575, 700] } })
|
||||
.addDecorator(story => <div className="w-100 d-flex">{story()}</div>)
|
||||
|
||||
@ -45,22 +45,23 @@ const defaultProps: SearchBoxProps = {
|
||||
authenticatedUser: null,
|
||||
hasUserAddedExternalServices: false,
|
||||
getUserSearchContextNamespaces: mockGetUserSearchContextNamespaces,
|
||||
platformContext: NOOP_PLATFORM_CONTEXT,
|
||||
}
|
||||
|
||||
add(
|
||||
'default',
|
||||
() => <WebStory>{props => <SearchBox {...defaultProps} isLightTheme={props.isLightTheme} />}</WebStory>,
|
||||
() => <BrandedStory>{props => <SearchBox {...defaultProps} isLightTheme={props.isLightTheme} />}</BrandedStory>,
|
||||
{}
|
||||
)
|
||||
|
||||
add(
|
||||
'regexp enabled',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => (
|
||||
<SearchBox {...defaultProps} patternType={SearchPatternType.regexp} isLightTheme={props.isLightTheme} />
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -68,7 +69,7 @@ add(
|
||||
add(
|
||||
'structural enabled',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => (
|
||||
<SearchBox
|
||||
{...defaultProps}
|
||||
@ -76,7 +77,7 @@ add(
|
||||
isLightTheme={props.isLightTheme}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -84,9 +85,9 @@ add(
|
||||
add(
|
||||
'case sensitivity enabled',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => <SearchBox {...defaultProps} caseSensitive={true} isLightTheme={props.isLightTheme} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -94,7 +95,7 @@ add(
|
||||
add(
|
||||
'with search contexts',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => (
|
||||
<SearchBox
|
||||
{...defaultProps}
|
||||
@ -103,7 +104,7 @@ add(
|
||||
selectedSearchContextSpec="global"
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -111,7 +112,7 @@ add(
|
||||
add(
|
||||
'with search contexts, user context selected',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => (
|
||||
<SearchBox
|
||||
{...defaultProps}
|
||||
@ -120,7 +121,7 @@ add(
|
||||
selectedSearchContextSpec="@username/test-version-1.5"
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -128,7 +129,7 @@ add(
|
||||
add(
|
||||
'with search contexts, disabled based on query',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{props => (
|
||||
<SearchBox
|
||||
{...defaultProps}
|
||||
@ -138,7 +139,7 @@ add(
|
||||
selectedSearchContextSpec="@username"
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -2,25 +2,25 @@ import classNames from 'classnames'
|
||||
import * as Monaco from 'monaco-editor'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
import { SearchContextInputProps, QueryState, SubmitSearchProps } from '@sourcegraph/search'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { KeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
import { SearchContextInputProps } from '..'
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { QueryState, SubmitSearchProps } from '../helpers'
|
||||
|
||||
import { LazyMonacoQueryInput } from './LazyMonacoQueryInput'
|
||||
import styles from './SearchBox.module.scss'
|
||||
import { SearchButton } from './SearchButton'
|
||||
import { SearchContextDropdown } from './SearchContextDropdown'
|
||||
import { Toggles, TogglesProps } from './toggles/Toggles'
|
||||
import { Toggles, TogglesProps } from './toggles'
|
||||
|
||||
export interface SearchBoxProps
|
||||
extends Omit<TogglesProps, 'navbarSearchQuery' | 'submitSearch'>,
|
||||
ThemeProps,
|
||||
SearchContextInputProps,
|
||||
TelemetryProps {
|
||||
TelemetryProps,
|
||||
PlatformContextProps<'requestGraphQL'> {
|
||||
authenticatedUser: AuthenticatedUser | null
|
||||
isSourcegraphDotCom: boolean // significant for query suggestions
|
||||
showSearchContext: boolean
|
||||
@ -46,6 +46,9 @@ export interface SearchBoxProps
|
||||
hideHelpButton?: boolean
|
||||
|
||||
onHandleFuzzyFinder?: React.Dispatch<React.SetStateAction<boolean>>
|
||||
|
||||
/** Set in JSContext only available to the web app. */
|
||||
isExternalServicesUserModeAll?: boolean
|
||||
}
|
||||
|
||||
export const SearchBox: React.FunctionComponent<SearchBoxProps> = props => {
|
||||
@ -84,7 +87,12 @@ export const SearchBox: React.FunctionComponent<SearchBoxProps> = props => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SearchButton hideHelpButton={props.hideHelpButton} className={styles.searchBoxButton} />
|
||||
<SearchButton
|
||||
hideHelpButton={props.hideHelpButton}
|
||||
className={styles.searchBoxButton}
|
||||
telemetryService={props.telemetryService}
|
||||
isSourcegraphDotCom={props.isSourcegraphDotCom}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -2,22 +2,29 @@ import classNames from 'classnames'
|
||||
import SearchIcon from 'mdi-react/SearchIcon'
|
||||
import React from 'react'
|
||||
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { Button } from '@sourcegraph/wildcard'
|
||||
|
||||
import styles from './SearchButton.module.scss'
|
||||
import { SearchHelpDropdownButton } from './SearchHelpDropdownButton'
|
||||
|
||||
interface Props {
|
||||
interface Props extends TelemetryProps {
|
||||
/** Hide the "help" icon and dropdown. */
|
||||
hideHelpButton?: boolean
|
||||
className?: string
|
||||
isSourcegraphDotCom?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A search button with a dropdown with related links. It must be wrapped in a form whose onSubmit
|
||||
* handler performs the search.
|
||||
*/
|
||||
export const SearchButton: React.FunctionComponent<Props> = ({ hideHelpButton, className }) => (
|
||||
export const SearchButton: React.FunctionComponent<Props> = ({
|
||||
hideHelpButton,
|
||||
className,
|
||||
isSourcegraphDotCom,
|
||||
telemetryService,
|
||||
}) => (
|
||||
<div className={className}>
|
||||
<Button
|
||||
data-search-button={true}
|
||||
@ -28,6 +35,8 @@ export const SearchButton: React.FunctionComponent<Props> = ({ hideHelpButton, c
|
||||
>
|
||||
<SearchIcon className="icon-inline" aria-hidden="true" />
|
||||
</Button>
|
||||
{!hideHelpButton && <SearchHelpDropdownButton />}
|
||||
{!hideHelpButton && (
|
||||
<SearchHelpDropdownButton isSourcegraphDotCom={isSourcegraphDotCom} telemetryService={telemetryService} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@ -1,14 +1,13 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { WebStory } from '../../components/WebStory'
|
||||
|
||||
import { SearchContextCtaPrompt } from './SearchContextCtaPrompt'
|
||||
|
||||
const { add } = storiesOf('web/searchContexts/SearchContextCtaPrompt', module)
|
||||
const { add } = storiesOf('search-ui/searchContexts/SearchContextCtaPrompt', module)
|
||||
.addParameters({
|
||||
chromatic: { viewports: [500] },
|
||||
})
|
||||
@ -41,7 +40,7 @@ const authUser: AuthenticatedUser = {
|
||||
add(
|
||||
'not authenticated',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextCtaPrompt
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
@ -50,7 +49,7 @@ add(
|
||||
onDismiss={() => {}}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -58,7 +57,7 @@ add(
|
||||
add(
|
||||
'authenticated without added external services',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextCtaPrompt
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
@ -67,7 +66,7 @@ add(
|
||||
onDismiss={() => {}}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -75,7 +74,7 @@ add(
|
||||
add(
|
||||
'authenticated with added external services',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextCtaPrompt
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
@ -84,7 +83,7 @@ add(
|
||||
onDismiss={() => {}}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -92,7 +91,7 @@ add(
|
||||
add(
|
||||
'authenticated with private code',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextCtaPrompt
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
@ -101,7 +100,7 @@ add(
|
||||
onDismiss={() => {}}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -1,28 +1,29 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { Button, ProductStatusBadge, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
|
||||
import styles from './SearchContextCtaPrompt.module.scss'
|
||||
|
||||
export interface SearchContextCtaPromptProps extends TelemetryProps {
|
||||
authenticatedUser: AuthenticatedUser | null
|
||||
hasUserAddedExternalServices: boolean
|
||||
onDismiss: () => void
|
||||
/** Set in JSContext so only available to the web app. */
|
||||
isExternalServicesUserModeAll?: boolean
|
||||
}
|
||||
|
||||
export const SearchContextCtaPrompt: React.FunctionComponent<SearchContextCtaPromptProps> = ({
|
||||
authenticatedUser,
|
||||
hasUserAddedExternalServices,
|
||||
telemetryService,
|
||||
isExternalServicesUserModeAll,
|
||||
onDismiss,
|
||||
}) => {
|
||||
const repositoriesVisibility =
|
||||
window.context.externalServicesUserMode === 'all' ||
|
||||
authenticatedUser?.tags.includes('AllowUserExternalServicePrivate')
|
||||
isExternalServicesUserModeAll || authenticatedUser?.tags.includes('AllowUserExternalServicePrivate')
|
||||
? 'repositories'
|
||||
: 'public repositories'
|
||||
|
||||
@ -4,6 +4,8 @@ import React from 'react'
|
||||
import { act } from 'react-dom/test-utils'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { MockTemporarySettings } from '@sourcegraph/shared/src/settings/temporary/testUtils'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { MockIntersectionObserver } from '@sourcegraph/shared/src/testing/MockIntersectionObserver'
|
||||
import { renderWithRouter } from '@sourcegraph/shared/src/testing/render-with-router'
|
||||
@ -12,10 +14,7 @@ import {
|
||||
mockFetchSearchContexts,
|
||||
mockGetUserSearchContextNamespaces,
|
||||
} from '@sourcegraph/shared/src/testing/searchContexts/testHelpers'
|
||||
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { SourcegraphContext } from '../../jscontext'
|
||||
import { MockTemporarySettings } from '../../settings/temporary/testUtils'
|
||||
import { NOOP_PLATFORM_CONTEXT } from '@sourcegraph/shared/src/testing/searchTestHelpers'
|
||||
|
||||
import { SearchContextDropdown, SearchContextDropdownProps } from './SearchContextDropdown'
|
||||
|
||||
@ -35,6 +34,7 @@ describe('SearchContextDropdown', () => {
|
||||
isSourcegraphDotCom: false,
|
||||
authenticatedUser: null,
|
||||
searchContextsEnabled: true,
|
||||
platformContext: NOOP_PLATFORM_CONTEXT,
|
||||
}
|
||||
const RealIntersectionObserver = window.IntersectionObserver
|
||||
let clock: sinon.SinonFakeTimers
|
||||
@ -110,24 +110,12 @@ describe('SearchContextDropdown', () => {
|
||||
})
|
||||
|
||||
describe('with CTA', () => {
|
||||
let oldContext: SourcegraphContext & Mocha.SuiteFunction
|
||||
beforeEach(() => {
|
||||
oldContext = window.context
|
||||
window.context = { externalServicesUserMode: 'all' } as SourcegraphContext & Mocha.SuiteFunction
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
window.context = oldContext
|
||||
})
|
||||
const props = { ...defaultProps, isExternalServicesUserModeAll: true }
|
||||
|
||||
it('should not display CTA if not on Sourcegraph.com', () => {
|
||||
render(
|
||||
<MockTemporarySettings settings={{ 'search.contexts.ctaDismissed': false }}>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
isSourcegraphDotCom={false}
|
||||
hasUserAddedRepositories={false}
|
||||
/>
|
||||
<SearchContextDropdown {...props} isSourcegraphDotCom={false} hasUserAddedRepositories={false} />
|
||||
</MockTemporarySettings>
|
||||
)
|
||||
|
||||
@ -139,11 +127,7 @@ describe('SearchContextDropdown', () => {
|
||||
it('should display CTA on Sourcegraph.com if no repos have been added and not permanently dismissed', () => {
|
||||
renderWithRouter(
|
||||
<MockTemporarySettings settings={{ 'search.contexts.ctaDismissed': false }}>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
isSourcegraphDotCom={true}
|
||||
hasUserAddedRepositories={false}
|
||||
/>
|
||||
<SearchContextDropdown {...props} isSourcegraphDotCom={true} hasUserAddedRepositories={false} />
|
||||
</MockTemporarySettings>
|
||||
)
|
||||
|
||||
@ -162,7 +146,7 @@ describe('SearchContextDropdown', () => {
|
||||
render(
|
||||
<MockTemporarySettings settings={{ 'search.contexts.ctaDismissed': false }}>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
isSourcegraphDotCom={true}
|
||||
hasUserAddedRepositories={false}
|
||||
authenticatedUser={mockUserWithOrg}
|
||||
@ -178,11 +162,7 @@ describe('SearchContextDropdown', () => {
|
||||
it('should not display CTA on Sourcegraph.com if repos have been added', () => {
|
||||
render(
|
||||
<MockTemporarySettings settings={{ 'search.contexts.ctaDismissed': false }}>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
isSourcegraphDotCom={true}
|
||||
hasUserAddedRepositories={true}
|
||||
/>
|
||||
<SearchContextDropdown {...props} isSourcegraphDotCom={true} hasUserAddedRepositories={true} />
|
||||
</MockTemporarySettings>
|
||||
)
|
||||
|
||||
@ -194,11 +174,7 @@ describe('SearchContextDropdown', () => {
|
||||
it('should not display CTA on Sourcegraph.com if dimissed', () => {
|
||||
renderWithRouter(
|
||||
<MockTemporarySettings settings={{ 'search.contexts.ctaDismissed': true }}>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
isSourcegraphDotCom={true}
|
||||
hasUserAddedRepositories={false}
|
||||
/>
|
||||
<SearchContextDropdown {...props} isSourcegraphDotCom={true} hasUserAddedRepositories={false} />
|
||||
</MockTemporarySettings>
|
||||
)
|
||||
|
||||
@ -215,11 +191,7 @@ describe('SearchContextDropdown', () => {
|
||||
settings={{ 'search.contexts.ctaDismissed': false }}
|
||||
onSettingsChanged={onSettingsChanged}
|
||||
>
|
||||
<SearchContextDropdown
|
||||
{...defaultProps}
|
||||
isSourcegraphDotCom={true}
|
||||
hasUserAddedRepositories={false}
|
||||
/>
|
||||
<SearchContextDropdown {...props} isSourcegraphDotCom={true} hasUserAddedRepositories={false} />
|
||||
</MockTemporarySettings>
|
||||
)
|
||||
|
||||
@ -2,15 +2,14 @@ import classNames from 'classnames'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Dropdown, DropdownMenu, DropdownToggle } from 'reactstrap'
|
||||
|
||||
import { SearchContextInputProps, SubmitSearchProps } from '@sourcegraph/search'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { filterExists } from '@sourcegraph/shared/src/search/query/validate'
|
||||
import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary/useTemporarySetting'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
import { SearchContextInputProps } from '..'
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { useTemporarySetting } from '../../settings/temporary/useTemporarySetting'
|
||||
import { SubmitSearchProps } from '../helpers'
|
||||
|
||||
import { SearchContextCtaPrompt } from './SearchContextCtaPrompt'
|
||||
import styles from './SearchContextDropdown.module.scss'
|
||||
import { SearchContextMenu } from './SearchContextMenu'
|
||||
@ -18,13 +17,15 @@ import { SearchContextMenu } from './SearchContextMenu'
|
||||
export interface SearchContextDropdownProps
|
||||
extends SearchContextInputProps,
|
||||
TelemetryProps,
|
||||
Partial<Pick<SubmitSearchProps, 'submitSearch'>> {
|
||||
Partial<Pick<SubmitSearchProps, 'submitSearch'>>,
|
||||
PlatformContextProps<'requestGraphQL'> {
|
||||
isSourcegraphDotCom: boolean
|
||||
showSearchContextManagement: boolean
|
||||
authenticatedUser: AuthenticatedUser | null
|
||||
query: string
|
||||
className?: string
|
||||
onEscapeMenuClose?: () => void
|
||||
isExternalServicesUserModeAll?: boolean
|
||||
}
|
||||
|
||||
export const SearchContextDropdown: React.FunctionComponent<SearchContextDropdownProps> = props => {
|
||||
@ -43,6 +44,7 @@ export const SearchContextDropdown: React.FunctionComponent<SearchContextDropdow
|
||||
telemetryService,
|
||||
onEscapeMenuClose,
|
||||
showSearchContextManagement,
|
||||
isExternalServicesUserModeAll,
|
||||
} = props
|
||||
|
||||
const [contextCtaDismissed, setContextCtaDismissed] = useTemporarySetting('search.contexts.ctaDismissed', false)
|
||||
@ -147,6 +149,7 @@ export const SearchContextDropdown: React.FunctionComponent<SearchContextDropdow
|
||||
authenticatedUser={authenticatedUser}
|
||||
hasUserAddedExternalServices={hasUserAddedExternalServices}
|
||||
onDismiss={onCtaDismissed}
|
||||
isExternalServicesUserModeAll={isExternalServicesUserModeAll}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
@ -2,18 +2,19 @@ import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
import { Observable, of } from 'rxjs'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { ListSearchContextsResult } from '@sourcegraph/search'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import {
|
||||
mockFetchAutoDefinedSearchContexts,
|
||||
mockFetchSearchContexts,
|
||||
mockGetUserSearchContextNamespaces,
|
||||
} from '@sourcegraph/shared/src/testing/searchContexts/testHelpers'
|
||||
|
||||
import { WebStory } from '../../components/WebStory'
|
||||
import { ListSearchContextsResult } from '../../graphql-operations'
|
||||
import { NOOP_PLATFORM_CONTEXT } from '@sourcegraph/shared/src/testing/searchTestHelpers'
|
||||
|
||||
import { SearchContextMenu, SearchContextMenuProps } from './SearchContextMenu'
|
||||
|
||||
const { add } = storiesOf('web/search/input/SearchContextMenu', module)
|
||||
const { add } = storiesOf('search-ui/search/input/SearchContextMenu', module)
|
||||
.addParameters({
|
||||
chromatic: { viewports: [500] },
|
||||
design: {
|
||||
@ -73,6 +74,8 @@ const defaultProps: SearchContextMenuProps = {
|
||||
closeMenu: () => {},
|
||||
getUserSearchContextNamespaces: mockGetUserSearchContextNamespaces,
|
||||
searchContextsEnabled: true,
|
||||
platformContext: NOOP_PLATFORM_CONTEXT,
|
||||
telemetryService: NOOP_TELEMETRY_SERVICE,
|
||||
}
|
||||
|
||||
const emptySearchContexts = {
|
||||
@ -80,12 +83,18 @@ const emptySearchContexts = {
|
||||
fetchSearchContexts: mockFetchSearchContexts,
|
||||
}
|
||||
|
||||
add('default', () => <WebStory>{() => <SearchContextMenu {...defaultProps} />}</WebStory>, {})
|
||||
add('default', () => <BrandedStory>{() => <SearchContextMenu {...defaultProps} />}</BrandedStory>, {})
|
||||
|
||||
add('empty', () => <WebStory>{() => <SearchContextMenu {...defaultProps} {...emptySearchContexts} />}</WebStory>, {})
|
||||
add(
|
||||
'empty',
|
||||
() => <BrandedStory>{() => <SearchContextMenu {...defaultProps} {...emptySearchContexts} />}</BrandedStory>,
|
||||
{}
|
||||
)
|
||||
|
||||
add(
|
||||
'with manage link',
|
||||
() => <WebStory>{() => <SearchContextMenu {...defaultProps} showSearchContextManagement={true} />}</WebStory>,
|
||||
() => (
|
||||
<BrandedStory>{() => <SearchContextMenu {...defaultProps} showSearchContextManagement={true} />}</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -6,11 +6,12 @@ import { DropdownMenu, UncontrolledDropdown } from 'reactstrap'
|
||||
import { Observable, of, throwError } from 'rxjs'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import { ListSearchContextsResult, SearchContextFields } from '@sourcegraph/search'
|
||||
import { ISearchContext } from '@sourcegraph/shared/src/schema'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { MockIntersectionObserver } from '@sourcegraph/shared/src/testing/MockIntersectionObserver'
|
||||
import { mockGetUserSearchContextNamespaces } from '@sourcegraph/shared/src/testing/searchContexts/testHelpers'
|
||||
|
||||
import { ListSearchContextsResult, SearchContextFields } from '../../graphql-operations'
|
||||
import { NOOP_PLATFORM_CONTEXT } from '@sourcegraph/shared/src/testing/searchTestHelpers'
|
||||
|
||||
import { SearchContextMenu, SearchContextMenuProps } from './SearchContextMenu'
|
||||
|
||||
@ -112,6 +113,8 @@ describe('SearchContextMenu', () => {
|
||||
closeMenu: () => {},
|
||||
getUserSearchContextNamespaces: mockGetUserSearchContextNamespaces,
|
||||
searchContextsEnabled: true,
|
||||
platformContext: NOOP_PLATFORM_CONTEXT,
|
||||
telemetryService: NOOP_TELEMETRY_SERVICE,
|
||||
}
|
||||
|
||||
const RealIntersectionObserver = window.IntersectionObserver
|
||||
@ -14,31 +14,42 @@ import { BehaviorSubject, combineLatest, of, timer } from 'rxjs'
|
||||
import { catchError, debounce, switchMap, tap } from 'rxjs/operators'
|
||||
|
||||
import { asError, isErrorLike } from '@sourcegraph/common'
|
||||
import { SearchContextInputProps, SearchContextFields } from '@sourcegraph/search'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import { ISearchContext } from '@sourcegraph/shared/src/schema'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { Badge, Button, useObservable, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { SearchContextInputProps } from '..'
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { SearchContextFields } from '../../graphql-operations'
|
||||
import { eventLogger } from '../../tracking/eventLogger'
|
||||
|
||||
import { HighlightedSearchContextSpec } from './HighlightedSearchContextSpec'
|
||||
import styles from './SearchContextMenu.module.scss'
|
||||
|
||||
export const SearchContextMenuItem: React.FunctionComponent<{
|
||||
spec: string
|
||||
description: string
|
||||
query: string
|
||||
selected: boolean
|
||||
isDefault: boolean
|
||||
selectSearchContextSpec: (spec: string) => void
|
||||
searchFilter: string
|
||||
onKeyDown: (key: string) => void
|
||||
}> = ({ spec, description, query, selected, isDefault, selectSearchContextSpec, searchFilter, onKeyDown }) => {
|
||||
export const SearchContextMenuItem: React.FunctionComponent<
|
||||
{
|
||||
spec: string
|
||||
description: string
|
||||
query: string
|
||||
selected: boolean
|
||||
isDefault: boolean
|
||||
selectSearchContextSpec: (spec: string) => void
|
||||
searchFilter: string
|
||||
onKeyDown: (key: string) => void
|
||||
} & TelemetryProps
|
||||
> = ({
|
||||
spec,
|
||||
description,
|
||||
query,
|
||||
selected,
|
||||
isDefault,
|
||||
selectSearchContextSpec,
|
||||
searchFilter,
|
||||
onKeyDown,
|
||||
telemetryService,
|
||||
}) => {
|
||||
const setContext = useCallback(() => {
|
||||
eventLogger.log('SearchContextSelected')
|
||||
telemetryService.log('SearchContextSelected')
|
||||
selectSearchContextSpec(spec)
|
||||
}, [selectSearchContextSpec, spec])
|
||||
}, [selectSearchContextSpec, spec, telemetryService])
|
||||
|
||||
const descriptionOrQuery = description.length > 0 ? description : query
|
||||
|
||||
@ -72,9 +83,11 @@ export const SearchContextMenuItem: React.FunctionComponent<{
|
||||
|
||||
export interface SearchContextMenuProps
|
||||
extends Omit<
|
||||
SearchContextInputProps,
|
||||
'setSelectedSearchContextSpec' | 'hasUserAddedRepositories' | 'hasUserAddedExternalServices'
|
||||
> {
|
||||
SearchContextInputProps,
|
||||
'setSelectedSearchContextSpec' | 'hasUserAddedRepositories' | 'hasUserAddedExternalServices'
|
||||
>,
|
||||
PlatformContextProps<'requestGraphQL'>,
|
||||
TelemetryProps {
|
||||
showSearchContextManagement: boolean
|
||||
authenticatedUser: AuthenticatedUser | null
|
||||
closeMenu: (isEscapeKey?: boolean) => void
|
||||
@ -108,6 +121,8 @@ export const SearchContextMenu: React.FunctionComponent<SearchContextMenuProps>
|
||||
fetchSearchContexts,
|
||||
closeMenu,
|
||||
showSearchContextManagement,
|
||||
platformContext,
|
||||
telemetryService,
|
||||
}) => {
|
||||
const inputElement = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
@ -182,6 +197,7 @@ export const SearchContextMenu: React.FunctionComponent<SearchContextMenuProps>
|
||||
query,
|
||||
after: cursor,
|
||||
namespaces: getUserSearchContextNamespaces(authenticatedUser),
|
||||
platformContext,
|
||||
}),
|
||||
])
|
||||
),
|
||||
@ -211,11 +227,13 @@ export const SearchContextMenu: React.FunctionComponent<SearchContextMenuProps>
|
||||
setLastPageInfo,
|
||||
getUserSearchContextNamespaces,
|
||||
fetchSearchContexts,
|
||||
platformContext,
|
||||
])
|
||||
|
||||
const autoDefinedSearchContexts = useObservable(
|
||||
useMemo(() => fetchAutoDefinedSearchContexts().pipe(catchError(error => [asError(error)])), [
|
||||
useMemo(() => fetchAutoDefinedSearchContexts(platformContext).pipe(catchError(error => [asError(error)])), [
|
||||
fetchAutoDefinedSearchContexts,
|
||||
platformContext,
|
||||
])
|
||||
)
|
||||
const filteredAutoDefinedSearchContexts = useMemo(
|
||||
@ -298,6 +316,7 @@ export const SearchContextMenu: React.FunctionComponent<SearchContextMenuProps>
|
||||
selectSearchContextSpec={selectSearchContextSpec}
|
||||
searchFilter={searchFilter}
|
||||
onKeyDown={key => index === 0 && key === 'ArrowUp' && focusInputElement()}
|
||||
telemetryService={telemetryService}
|
||||
/>
|
||||
))}
|
||||
{(loadingState === 'LOADING' || loadingState === 'LOADING_NEXT_PAGE') && (
|
||||
@ -2,11 +2,12 @@ import { storiesOf } from '@storybook/react'
|
||||
import { noop } from 'lodash'
|
||||
import React from 'react'
|
||||
|
||||
import { WebStory } from '../../components/WebStory'
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
import { SearchContextMenuItem } from './SearchContextMenu'
|
||||
|
||||
const { add } = storiesOf('web/searchContexts/SearchContextMenuItem', module)
|
||||
const { add } = storiesOf('search-ui/searchContexts/SearchContextMenuItem', module)
|
||||
.addParameters({
|
||||
chromatic: { viewports: [1200] },
|
||||
})
|
||||
@ -19,7 +20,7 @@ const { add } = storiesOf('web/searchContexts/SearchContextMenuItem', module)
|
||||
add(
|
||||
'selected default item',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextMenuItem
|
||||
spec="@user/test"
|
||||
@ -30,9 +31,10 @@ add(
|
||||
isDefault={true}
|
||||
selectSearchContextSpec={noop}
|
||||
onKeyDown={noop}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -40,7 +42,7 @@ add(
|
||||
add(
|
||||
'highlighted item',
|
||||
() => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchContextMenuItem
|
||||
spec="@user/test"
|
||||
@ -51,9 +53,10 @@ add(
|
||||
isDefault={false}
|
||||
selectSearchContextSpec={noop}
|
||||
onKeyDown={noop}
|
||||
telemetryService={NOOP_TELEMETRY_SERVICE}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
),
|
||||
{}
|
||||
)
|
||||
@ -3,20 +3,27 @@ import HelpCircleOutlineIcon from 'mdi-react/HelpCircleOutlineIcon'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { DropdownItem, DropdownMenu, DropdownToggle, ButtonDropdown } from 'reactstrap'
|
||||
|
||||
import { eventLogger } from '../../tracking/eventLogger'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
interface SearchHelpDropdownButtonProps extends TelemetryProps {
|
||||
isSourcegraphDotCom?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A dropdown button that shows a menu with reference documentation for Sourcegraph search query
|
||||
* syntax.
|
||||
*/
|
||||
export const SearchHelpDropdownButton: React.FunctionComponent = () => {
|
||||
export const SearchHelpDropdownButton: React.FunctionComponent<SearchHelpDropdownButtonProps> = ({
|
||||
isSourcegraphDotCom,
|
||||
telemetryService,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const toggleIsOpen = useCallback(() => setIsOpen(!isOpen), [isOpen])
|
||||
const onQueryDocumentationLinkClicked = useCallback(() => {
|
||||
eventLogger.log('SearchHelpDropdownQueryDocsLinkClicked')
|
||||
telemetryService.log('SearchHelpDropdownQueryDocsLinkClicked')
|
||||
toggleIsOpen()
|
||||
}, [toggleIsOpen])
|
||||
const documentationUrlPrefix = window.context?.sourcegraphDotComMode ? 'https://docs.sourcegraph.com' : '/help'
|
||||
}, [toggleIsOpen, telemetryService])
|
||||
const documentationUrlPrefix = isSourcegraphDotCom ? 'https://docs.sourcegraph.com' : '/help'
|
||||
|
||||
return (
|
||||
<ButtonDropdown isOpen={isOpen} toggle={toggleIsOpen} className="search-help-dropdown-button d-flex">
|
||||
@ -65,7 +72,7 @@ export const SearchHelpDropdownButton: React.FunctionComponent = () => {
|
||||
repo:<strong>my/repo</strong>
|
||||
</code>
|
||||
</li>
|
||||
{window.context?.sourcegraphDotComMode && (
|
||||
{isSourcegraphDotCom && (
|
||||
<li>
|
||||
<code>
|
||||
repo:<strong>github.com/myorg/</strong>
|
||||
@ -116,7 +123,7 @@ export const SearchHelpDropdownButton: React.FunctionComponent = () => {
|
||||
>
|
||||
<ExternalLinkIcon className="icon-inline small" /> All search keywords
|
||||
</a>
|
||||
{window.context?.sourcegraphDotComMode && (
|
||||
{isSourcegraphDotCom && (
|
||||
<div className="alert alert-info small rounded-0 mb-0 mt-1">
|
||||
On Sourcegraph.com, use a <code>repo:</code> filter to narrow your search to ≤500
|
||||
repositories.
|
||||
@ -1,7 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
|
||||
import { SearchPatternType } from '../../../graphql-operations'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/schema'
|
||||
|
||||
import { getFullQuery, Toggles } from './Toggles'
|
||||
|
||||
@ -5,15 +5,19 @@ import RegexIcon from 'mdi-react/RegexIcon'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { isErrorLike } from '@sourcegraph/common'
|
||||
import {
|
||||
SearchPatternTypeProps,
|
||||
CaseSensitivityProps,
|
||||
SearchContextProps,
|
||||
SearchPatternTypeMutationProps,
|
||||
SubmitSearchProps,
|
||||
} from '@sourcegraph/search'
|
||||
import { KEYBOARD_SHORTCUT_COPY_FULL_QUERY } from '@sourcegraph/shared/src/keyboardShortcuts/keyboardShortcuts'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/schema'
|
||||
import { findFilter, FilterKind } from '@sourcegraph/shared/src/search/query/query'
|
||||
import { appendContextFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
|
||||
import { SearchPatternTypeProps, CaseSensitivityProps, SearchContextProps, SearchPatternTypeMutationProps } from '../..'
|
||||
import { SearchPatternType } from '../../../graphql-operations'
|
||||
import { KEYBOARD_SHORTCUT_COPY_FULL_QUERY } from '../../../keyboardShortcuts/keyboardShortcuts'
|
||||
import { isMacPlatform } from '../../../util'
|
||||
import { SubmitSearchProps } from '../../helpers'
|
||||
import { isMacPlatform } from '@sourcegraph/shared/src/util/browserDetection'
|
||||
|
||||
import { CopyQueryButton } from './CopyQueryButton'
|
||||
import { QueryInputToggle } from './QueryInputToggle'
|
||||
@ -35,6 +39,8 @@ export interface TogglesProps
|
||||
* being disabled, because the buttons still render normally.
|
||||
*/
|
||||
interactive?: boolean
|
||||
/** Comes from JSContext only set in the web app. */
|
||||
structuralSearchDisabled?: boolean
|
||||
}
|
||||
|
||||
export const getFullQuery = (
|
||||
@ -64,10 +70,9 @@ export const Toggles: React.FunctionComponent<TogglesProps> = (props: TogglesPro
|
||||
selectedSearchContextSpec,
|
||||
submitSearch,
|
||||
showCopyQueryButton = true,
|
||||
structuralSearchDisabled,
|
||||
} = props
|
||||
|
||||
const structuralSearchDisabled = window.context?.experimentalFeatures?.structuralSearch === 'disabled'
|
||||
|
||||
const submitOnToggle = useCallback(
|
||||
(args: { newPatternType: SearchPatternType } | { newCaseSensitivity: boolean }): void => {
|
||||
submitSearch?.({
|
||||
3
client/search-ui/src/input/toggles/index.ts
Normal file
3
client/search-ui/src/input/toggles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './CopyQueryButton'
|
||||
export * from './QueryInputToggle'
|
||||
export * from './Toggles'
|
||||
@ -3,31 +3,23 @@ import CloseIcon from 'mdi-react/CloseIcon'
|
||||
import ExternalLinkIcon from 'mdi-react/ExternalLinkIcon'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
|
||||
import { SearchContextProps } from '@sourcegraph/search'
|
||||
import { SyntaxHighlightedSearchQuery, Toggles } from '@sourcegraph/search-ui'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { NoResultsSectionID as SectionID } from '@sourcegraph/shared/src/settings/temporary/searchSidebar'
|
||||
import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary/useTemporarySetting'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
import { buildSearchURLQuery } from '@sourcegraph/shared/src/util/url'
|
||||
import { Button, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { SearchContextProps } from '..'
|
||||
import { SyntaxHighlightedSearchQuery } from '../../components/SyntaxHighlightedSearchQuery'
|
||||
import { useTemporarySetting } from '../../settings/temporary/useTemporarySetting'
|
||||
import { useExperimentalFeatures } from '../../stores'
|
||||
import { ModalVideo } from '../documentation/ModalVideo'
|
||||
import searchBoxStyle from '../input/SearchBox.module.scss'
|
||||
import searchContextDropDownStyles from '../input/SearchContextDropdown.module.scss'
|
||||
import { Toggles } from '../input/toggles/Toggles'
|
||||
|
||||
import { AnnotatedSearchInput } from './AnnotatedSearchExample'
|
||||
import styles from './NoResultsPage.module.scss'
|
||||
|
||||
export enum SectionID {
|
||||
SEARCH_BAR = 'search-bar',
|
||||
LITERAL_SEARCH = 'literal-search',
|
||||
COMMON_PROBLEMS = 'common-problems',
|
||||
VIDEOS = 'videos',
|
||||
}
|
||||
|
||||
const noop = (): void => {}
|
||||
|
||||
interface SearchInputExampleProps {
|
||||
@ -173,6 +165,9 @@ const videos = [
|
||||
|
||||
interface NoResultsPageProps extends ThemeProps, TelemetryProps, Pick<SearchContextProps, 'searchContextsEnabled'> {
|
||||
isSourcegraphDotCom: boolean
|
||||
showSearchContext: boolean
|
||||
/** Available to web app through JS Context */
|
||||
assetsRoot?: string
|
||||
}
|
||||
|
||||
export const NoResultsPage: React.FunctionComponent<NoResultsPageProps> = ({
|
||||
@ -180,9 +175,10 @@ export const NoResultsPage: React.FunctionComponent<NoResultsPageProps> = ({
|
||||
isLightTheme,
|
||||
telemetryService,
|
||||
isSourcegraphDotCom,
|
||||
showSearchContext,
|
||||
assetsRoot,
|
||||
}) => {
|
||||
const [hiddenSectionIDs, setHiddenSectionIds] = useTemporarySetting('search.hiddenNoResultsSections')
|
||||
const showSearchContext = useExperimentalFeatures(features => features.showSearchContext ?? false)
|
||||
|
||||
const onClose = useCallback(
|
||||
sectionID => {
|
||||
@ -227,6 +223,7 @@ export const NoResultsPage: React.FunctionComponent<NoResultsPageProps> = ({
|
||||
telemetryService.log('NoResultsVideoPlayed', { video: video.title })
|
||||
}
|
||||
}}
|
||||
assetsRoot={assetsRoot}
|
||||
/>
|
||||
))}
|
||||
</Container>
|
||||
@ -1,19 +1,18 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import { ErrorAlert } from '@sourcegraph/branded/src/components/alerts'
|
||||
import { AggregateStreamingSearchResults } from '@sourcegraph/shared/src/search/stream'
|
||||
import { LoadingSpinner } from '@sourcegraph/wildcard'
|
||||
|
||||
import { ErrorAlert } from '../../components/alerts'
|
||||
|
||||
import { StreamingProgressCount } from './progress/StreamingProgressCount'
|
||||
import styles from './StreamingSearchResults.module.scss'
|
||||
import styles from './StreamingSearchResultsList.module.scss'
|
||||
|
||||
export const StreamingSearchResultFooter: React.FunctionComponent<{
|
||||
results?: AggregateStreamingSearchResults
|
||||
children?: React.ReactChild | React.ReactChild[]
|
||||
}> = ({ results, children }) => (
|
||||
<div className={classNames(styles.streamingSearchResultsContentCentered, 'd-flex flex-column align-items-center')}>
|
||||
<div className={classNames(styles.contentCentered, 'd-flex flex-column align-items-center')}>
|
||||
{(!results || results?.state === 'loading') && (
|
||||
<div className="text-center my-4" data-testid="loading-container">
|
||||
<LoadingSpinner />
|
||||
@ -0,0 +1,4 @@
|
||||
.content-centered {
|
||||
max-width: 65rem;
|
||||
margin: auto;
|
||||
}
|
||||
@ -8,10 +8,14 @@ import SourceRepositoryIcon from 'mdi-react/SourceRepositoryIcon'
|
||||
import React, { useCallback } from 'react'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
import { SearchContextProps } from '@sourcegraph/search'
|
||||
import { SearchResult } from '@sourcegraph/search-ui'
|
||||
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
|
||||
import { FetchFileParameters } from '@sourcegraph/shared/src/components/CodeExcerpt'
|
||||
import { FileMatch } from '@sourcegraph/shared/src/components/FileMatch'
|
||||
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoFileLink'
|
||||
import { VirtualList } from '@sourcegraph/shared/src/components/VirtualList'
|
||||
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
|
||||
import {
|
||||
AggregateStreamingSearchResults,
|
||||
ContentMatch,
|
||||
@ -24,27 +28,28 @@ import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { ThemeProps } from '@sourcegraph/shared/src/theme'
|
||||
|
||||
import { SearchContextProps } from '..'
|
||||
import { AuthenticatedUser } from '../../auth'
|
||||
import { SearchResult } from '../../components/SearchResult'
|
||||
import { SearchUserNeedsCodeHost } from '../../user/settings/codeHosts/OrgUserNeedsCodeHost'
|
||||
|
||||
import { NoResultsPage } from './NoResultsPage'
|
||||
import styles from './StreamingSearchResults.module.scss'
|
||||
import { StreamingSearchResultFooter } from './StreamingSearchResultsFooter'
|
||||
import styles from './StreamingSearchResultsList.module.scss'
|
||||
import { useItemsToShow } from './use-items-to-show'
|
||||
|
||||
export interface StreamingSearchResultsListProps
|
||||
extends ThemeProps,
|
||||
SettingsCascadeProps,
|
||||
TelemetryProps,
|
||||
Pick<SearchContextProps, 'searchContextsEnabled' | 'selectedSearchContextSpec'> {
|
||||
Pick<SearchContextProps, 'searchContextsEnabled'>,
|
||||
PlatformContextProps<'requestGraphQL'> {
|
||||
isSourcegraphDotCom: boolean
|
||||
results?: AggregateStreamingSearchResults
|
||||
location: H.Location
|
||||
allExpanded: boolean
|
||||
fetchHighlightedFileLineRanges: (parameters: FetchFileParameters, force?: boolean) => Observable<string[][]>
|
||||
authenticatedUser: AuthenticatedUser | null
|
||||
showSearchContext: boolean
|
||||
/** Available to web app through JS Context */
|
||||
assetsRoot?: string
|
||||
/** Render prop for `<SearchUserNeedsCodeHost>` */
|
||||
renderSearchUserNeedsCodeHost?: (user: AuthenticatedUser) => JSX.Element
|
||||
}
|
||||
|
||||
export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearchResultsListProps> = ({
|
||||
@ -57,8 +62,11 @@ export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearch
|
||||
isLightTheme,
|
||||
isSourcegraphDotCom,
|
||||
searchContextsEnabled,
|
||||
selectedSearchContextSpec,
|
||||
authenticatedUser,
|
||||
showSearchContext,
|
||||
assetsRoot,
|
||||
renderSearchUserNeedsCodeHost,
|
||||
platformContext,
|
||||
}) => {
|
||||
const resultsNumber = results?.results.length || 0
|
||||
const { itemsToShow, handleBottomHit } = useItemsToShow(location.search, resultsNumber)
|
||||
@ -92,7 +100,7 @@ export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearch
|
||||
icon={SourceCommitIcon}
|
||||
result={result}
|
||||
repoName={result.repository}
|
||||
telemetryService={telemetryService}
|
||||
platformContext={platformContext}
|
||||
/>
|
||||
)
|
||||
case 'repo':
|
||||
@ -101,7 +109,7 @@ export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearch
|
||||
icon={SourceRepositoryIcon}
|
||||
result={result}
|
||||
repoName={result.repository}
|
||||
telemetryService={telemetryService}
|
||||
platformContext={platformContext}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -113,28 +121,21 @@ export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearch
|
||||
allExpanded,
|
||||
fetchHighlightedFileLineRanges,
|
||||
settingsCascade,
|
||||
platformContext,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.streamingSearchResultsContentCentered,
|
||||
'd-flex flex-column align-items-center'
|
||||
)}
|
||||
>
|
||||
<div className={classNames(styles.contentCentered, 'd-flex flex-column align-items-center')}>
|
||||
<div className="align-self-stretch">
|
||||
{isSourcegraphDotCom &&
|
||||
{renderSearchUserNeedsCodeHost &&
|
||||
isSourcegraphDotCom &&
|
||||
searchContextsEnabled &&
|
||||
authenticatedUser &&
|
||||
results?.state === 'complete' &&
|
||||
results?.results.length === 0 && (
|
||||
<SearchUserNeedsCodeHost
|
||||
user={authenticatedUser}
|
||||
orgSearchContext={selectedSearchContextSpec}
|
||||
/>
|
||||
)}
|
||||
results?.results.length === 0 &&
|
||||
renderSearchUserNeedsCodeHost(authenticatedUser)}
|
||||
</div>
|
||||
</div>
|
||||
<VirtualList<SearchMatch>
|
||||
@ -156,6 +157,8 @@ export const StreamingSearchResultsList: React.FunctionComponent<StreamingSearch
|
||||
isSourcegraphDotCom={isSourcegraphDotCom}
|
||||
isLightTheme={isLightTheme}
|
||||
telemetryService={telemetryService}
|
||||
showSearchContext={showSearchContext}
|
||||
assetsRoot={assetsRoot}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@ -2,13 +2,12 @@ import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { Progress } from '@sourcegraph/shared/src/search/stream'
|
||||
|
||||
import { WebStory } from '../../../components/WebStory'
|
||||
|
||||
import { StreamingProgress } from './StreamingProgress'
|
||||
|
||||
const { add } = storiesOf('web/search/results/progress/StreamingProgress', module)
|
||||
const { add } = storiesOf('search-ui/search/results/progress/StreamingProgress', module)
|
||||
.addParameters({
|
||||
design: {
|
||||
type: 'figma',
|
||||
@ -28,9 +27,9 @@ add('0 results, in progress', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="loading" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -43,11 +42,11 @@ add('0 results, in progress, traced', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<StreamingProgress progress={progress} state="loading" onSearchAgain={onSearchAgain} showTrace={true} />
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -60,9 +59,9 @@ add('1 result from 1 repository, in progress', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="loading" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -75,9 +74,9 @@ add('big numbers, done', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="complete" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -91,7 +90,7 @@ add('big numbers, done, traced', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<StreamingProgress
|
||||
progress={progress}
|
||||
@ -100,7 +99,7 @@ add('big numbers, done, traced', () => {
|
||||
showTrace={true}
|
||||
/>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -134,9 +133,9 @@ add('2 results from 2 repositories, complete, skipped with info', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="complete" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -170,9 +169,9 @@ add('2 results from 2 repositories, loading, skipped with info', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="loading" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -216,9 +215,9 @@ add('2 results from 2 repositories, complete, skipped with warning', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="complete" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
@ -262,8 +261,8 @@ add('2 results from 2 repositories, loading, skipped with warning', () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgress progress={progress} state="loading" onSearchAgain={onSearchAgain} />}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
@ -1,13 +1,12 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import { Progress } from '@sourcegraph/shared/src/search/stream'
|
||||
|
||||
import { WebStory } from '../../../components/WebStory'
|
||||
|
||||
import { StreamingProgressSkippedPopover } from './StreamingProgressSkippedPopover'
|
||||
|
||||
const { add } = storiesOf('web/search/results/progress/StreamingProgressSkippedPopover', module).addParameters({
|
||||
const { add } = storiesOf('search-ui/search/results/progress/StreamingProgressSkippedPopover', module).addParameters({
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IyiXZIbPHK447NCXov0AvK/13928-Streaming-search?node-id=280%3A17768',
|
||||
@ -62,7 +61,11 @@ add('popover', () => {
|
||||
],
|
||||
}
|
||||
|
||||
return <WebStory>{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}</WebStory>
|
||||
return (
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
add('only info, all should be closed', () => {
|
||||
@ -94,7 +97,11 @@ add('only info, all should be closed', () => {
|
||||
],
|
||||
}
|
||||
|
||||
return <WebStory>{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}</WebStory>
|
||||
return (
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
|
||||
add('only one info, should be open', () => {
|
||||
@ -116,5 +123,9 @@ add('only one info, should be open', () => {
|
||||
],
|
||||
}
|
||||
|
||||
return <WebStory>{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}</WebStory>
|
||||
return (
|
||||
<BrandedStory>
|
||||
{() => <StreamingProgressSkippedPopover progress={progress} onSearchAgain={() => {}} />}
|
||||
</BrandedStory>
|
||||
)
|
||||
})
|
||||
@ -7,13 +7,12 @@ import SearchIcon from 'mdi-react/SearchIcon'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Collapse, Form, FormGroup, Input, Label } from 'reactstrap'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from '@sourcegraph/search-ui'
|
||||
import { Markdown } from '@sourcegraph/shared/src/components/Markdown'
|
||||
import { Skipped } from '@sourcegraph/shared/src/search/stream'
|
||||
import { renderMarkdown } from '@sourcegraph/shared/src/util/markdown'
|
||||
import { Button } from '@sourcegraph/wildcard'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from '../../../components/SyntaxHighlightedSearchQuery'
|
||||
|
||||
import { StreamingProgressProps } from './StreamingProgress'
|
||||
import styles from './StreamingProgressSkippedPopover.module.scss'
|
||||
|
||||
@ -3,10 +3,9 @@ import userEvent from '@testing-library/user-event'
|
||||
import React from 'react'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import { SearchScope } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
import { Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
|
||||
import { SearchScope } from '../../../schema/settings.schema'
|
||||
|
||||
import { getDynamicFilterLinks, getRepoFilterLinks, getSearchSnippetLinks } from './FilterLink'
|
||||
|
||||
describe('FilterLink', () => {
|
||||
@ -1,17 +1,16 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from '@sourcegraph/search-ui'
|
||||
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoFileLink'
|
||||
import { RepoIcon } from '@sourcegraph/shared/src/components/RepoIcon'
|
||||
import { Settings } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
import { isSettingsValid, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { pluralize } from '@sourcegraph/shared/src/util/strings'
|
||||
import { Button } from '@sourcegraph/wildcard'
|
||||
|
||||
import { SyntaxHighlightedSearchQuery } from '../../../components/SyntaxHighlightedSearchQuery'
|
||||
import { Settings } from '../../../schema/settings.schema'
|
||||
|
||||
import { getFiltersOfKind } from './helpers'
|
||||
import styles from './SearchSidebarSection.module.scss'
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import { QuickLink } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
import { renderWithRouter } from '@sourcegraph/shared/src/testing/render-with-router'
|
||||
|
||||
import { QuickLink } from '../../../schema/settings.schema'
|
||||
|
||||
import { getQuickLinks } from './QuickLink'
|
||||
|
||||
describe('QuickLink', () => {
|
||||
@ -1,11 +1,10 @@
|
||||
import LinkIcon from 'mdi-react/LinkIcon'
|
||||
import React from 'react'
|
||||
|
||||
import { Settings } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
import { isSettingsValid, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { Settings } from '../../../schema/settings.schema'
|
||||
|
||||
import styles from './SearchSidebarSection.module.scss'
|
||||
|
||||
export const getQuickLinks = (settingsCascade: SettingsCascadeProps['settingsCascade']): React.ReactElement[] => {
|
||||
@ -7,6 +7,13 @@ import ExternalLinkIcon from 'mdi-react/ExternalLinkIcon'
|
||||
import React, { ReactElement, useCallback, useMemo, useState } from 'react'
|
||||
import { Collapse } from 'reactstrap'
|
||||
|
||||
import {
|
||||
QueryChangeSource,
|
||||
SearchQueryState,
|
||||
createQueryExampleFromString,
|
||||
updateQueryWithFilterAndExample,
|
||||
QueryExample,
|
||||
} from '@sourcegraph/search'
|
||||
import { Markdown } from '@sourcegraph/shared/src/components/Markdown'
|
||||
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
|
||||
import { FILTERS, FilterType, isNegatableFilter } from '@sourcegraph/shared/src/search/query/filters'
|
||||
@ -15,10 +22,6 @@ import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryServi
|
||||
import { renderMarkdown } from '@sourcegraph/shared/src/util/markdown'
|
||||
import { Button, useLocalStorage, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { NavbarQueryState } from '../../../stores/navbarSearchQueryState'
|
||||
import { QueryChangeSource } from '../../helpers'
|
||||
import { createQueryExampleFromString, updateQueryWithFilterAndExample, QueryExample } from '../../helpers/queryExample'
|
||||
|
||||
import styles from './SearchReference.module.scss'
|
||||
import sidebarStyles from './SearchSidebarSection.module.scss'
|
||||
|
||||
@ -462,7 +465,7 @@ const FilterInfoList = ({ filters, onClick, onExampleClick }: FilterInfoListProp
|
||||
</ul>
|
||||
)
|
||||
|
||||
export interface SearchReferenceProps extends TelemetryProps, Pick<NavbarQueryState, 'setQueryState'> {
|
||||
export interface SearchReferenceProps extends TelemetryProps, Pick<SearchQueryState, 'setQueryState'> {
|
||||
filter: string
|
||||
}
|
||||
|
||||
@ -1,17 +1,25 @@
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import React from 'react'
|
||||
// We need to import `create` to make a mock store just for this story.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import create from 'zustand'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import {
|
||||
BuildSearchQueryURLParameters,
|
||||
SearchPatternType,
|
||||
SearchQueryState,
|
||||
SearchQueryStateStoreProvider,
|
||||
} from '@sourcegraph/search'
|
||||
import { QuickLink, SearchScope } from '@sourcegraph/shared/src/schema/settings.schema'
|
||||
import { Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
import { EMPTY_SETTINGS_CASCADE } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
import { WebStory } from '../../../components/WebStory'
|
||||
import { SearchPatternType } from '../../../graphql-operations'
|
||||
import { QuickLink, SearchScope } from '../../../schema/settings.schema'
|
||||
import { buildSearchURLQuery } from '@sourcegraph/shared/src/util/url'
|
||||
|
||||
import { SearchSidebar, SearchSidebarProps } from './SearchSidebar'
|
||||
|
||||
const { add } = storiesOf('web/search/results/sidebar/SearchSidebar', module).addParameters({
|
||||
const { add } = storiesOf('search-ui/search/results/sidebar/SearchSidebar', module).addParameters({
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/NIsN34NH7lPu04olBzddTw/?node-id=1018%3A13883',
|
||||
@ -19,12 +27,38 @@ const { add } = storiesOf('web/search/results/sidebar/SearchSidebar', module).ad
|
||||
chromatic: { viewports: [544, 577, 993] },
|
||||
})
|
||||
|
||||
const mockUseQueryState = create<SearchQueryState>((set, get) => ({
|
||||
queryState: { query: '' },
|
||||
searchCaseSensitivity: false,
|
||||
searchPatternType: SearchPatternType.literal,
|
||||
searchQueryFromURL: '',
|
||||
setQueryState: queryStateUpdate => {
|
||||
if (typeof queryStateUpdate === 'function') {
|
||||
set({ queryState: queryStateUpdate(get().queryState) })
|
||||
} else {
|
||||
set({ queryState: queryStateUpdate })
|
||||
}
|
||||
},
|
||||
submitSearch: () => {},
|
||||
}))
|
||||
|
||||
const defaultProps: SearchSidebarProps = {
|
||||
caseSensitive: false,
|
||||
patternType: SearchPatternType.literal,
|
||||
selectedSearchContextSpec: 'global',
|
||||
settingsCascade: EMPTY_SETTINGS_CASCADE,
|
||||
telemetryService: NOOP_TELEMETRY_SERVICE,
|
||||
buildSearchURLQueryFromQueryState: (parameters: BuildSearchQueryURLParameters) => {
|
||||
const currentState = mockUseQueryState.getState()
|
||||
|
||||
return buildSearchURLQuery(
|
||||
parameters.query,
|
||||
parameters.patternType ?? currentState.searchPatternType,
|
||||
parameters.caseSensitive ?? currentState.searchCaseSensitivity,
|
||||
parameters.searchContextSpec,
|
||||
parameters.searchParametersList
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
const quicklinks: QuickLink[] = [
|
||||
@ -111,16 +145,26 @@ const filters: Filter[] = [
|
||||
})),
|
||||
]
|
||||
|
||||
add('empty sidebar', () => <WebStory>{() => <SearchSidebar {...defaultProps} />}</WebStory>)
|
||||
add('empty sidebar', () => (
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchQueryStateStoreProvider useSearchQueryState={mockUseQueryState}>
|
||||
<SearchSidebar {...defaultProps} />
|
||||
</SearchQueryStateStoreProvider>
|
||||
)}
|
||||
</BrandedStory>
|
||||
))
|
||||
|
||||
add('with everything', () => (
|
||||
<WebStory>
|
||||
<BrandedStory>
|
||||
{() => (
|
||||
<SearchSidebar
|
||||
{...defaultProps}
|
||||
settingsCascade={{ subjects: [], final: { quicklinks, 'search.scopes': scopes } }}
|
||||
filters={filters}
|
||||
/>
|
||||
<SearchQueryStateStoreProvider useSearchQueryState={mockUseQueryState}>
|
||||
<SearchSidebar
|
||||
{...defaultProps}
|
||||
settingsCascade={{ subjects: [], final: { quicklinks, 'search.scopes': scopes } }}
|
||||
filters={filters}
|
||||
/>
|
||||
</SearchQueryStateStoreProvider>
|
||||
)}
|
||||
</WebStory>
|
||||
</BrandedStory>
|
||||
))
|
||||
@ -4,22 +4,25 @@ import { useHistory } from 'react-router'
|
||||
import StickyBox from 'react-sticky-box'
|
||||
import shallow from 'zustand/shallow'
|
||||
|
||||
import {
|
||||
BuildSearchQueryURLParameters,
|
||||
QueryUpdate,
|
||||
SearchQueryState,
|
||||
SubmitSearchParameters,
|
||||
useSearchQueryStateStoreContext,
|
||||
} from '@sourcegraph/search'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
|
||||
import { SectionID } from '@sourcegraph/shared/src/settings/temporary/searchSidebar'
|
||||
import { TemporarySettings } from '@sourcegraph/shared/src/settings/temporary/TemporarySettings'
|
||||
import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary/useTemporarySetting'
|
||||
import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
|
||||
import { OnboardingTour } from '../../../onboarding-tour/OnboardingTour'
|
||||
import { TemporarySettings } from '../../../settings/temporary/TemporarySettings'
|
||||
import { useTemporarySetting } from '../../../settings/temporary/useTemporarySetting'
|
||||
import { useNavbarQueryState } from '../../../stores'
|
||||
import { NavbarQueryState, QueryUpdate } from '../../../stores/navbarSearchQueryState'
|
||||
import { SubmitSearchParameters } from '../../helpers'
|
||||
|
||||
import { getDynamicFilterLinks, getRepoFilterLinks, getSearchSnippetLinks } from './FilterLink'
|
||||
import { getFiltersOfKind, useLastRepoName } from './helpers'
|
||||
import { getQuickLinks } from './QuickLink'
|
||||
import { getRevisions } from './Revisions'
|
||||
import { RevisionsProps } from './revisions'
|
||||
import { getSearchReferenceFactory } from './SearchReference'
|
||||
import styles from './SearchSidebar.module.scss'
|
||||
import { SearchSidebarSection } from './SearchSidebarSection'
|
||||
@ -32,26 +35,28 @@ export interface SearchSidebarProps
|
||||
filters?: Filter[]
|
||||
className?: string
|
||||
showOnboardingTour?: boolean
|
||||
}
|
||||
|
||||
export enum SectionID {
|
||||
SEARCH_REFERENCE = 'reference',
|
||||
SEARCH_TYPES = 'types',
|
||||
DYNAMIC_FILTERS = 'filters',
|
||||
REPOSITORIES = 'repositories',
|
||||
SEARCH_SNIPPETS = 'snippets',
|
||||
QUICK_LINKS = 'quicklinks',
|
||||
REVISIONS = 'revisions',
|
||||
/**
|
||||
* Not yet implemented in the VS Code extension (blocked on Apollo Client integration).
|
||||
* */
|
||||
getRevisions?: (revisionsProps: Omit<RevisionsProps, 'query'>) => (query: string) => JSX.Element
|
||||
|
||||
/**
|
||||
* Content to render inside sidebar, but before other sections.
|
||||
*/
|
||||
prefixContent?: JSX.Element
|
||||
|
||||
buildSearchURLQueryFromQueryState: (queryParameters: BuildSearchQueryURLParameters) => string
|
||||
}
|
||||
|
||||
const selectFromQueryState = ({
|
||||
queryState: { query },
|
||||
setQueryState,
|
||||
submitSearch,
|
||||
}: NavbarQueryState): {
|
||||
}: SearchQueryState): {
|
||||
query: string
|
||||
setQueryState: NavbarQueryState['setQueryState']
|
||||
submitSearch: NavbarQueryState['submitSearch']
|
||||
setQueryState: SearchQueryState['setQueryState']
|
||||
submitSearch: SearchQueryState['submitSearch']
|
||||
} => ({
|
||||
query,
|
||||
setQueryState,
|
||||
@ -61,7 +66,11 @@ const selectFromQueryState = ({
|
||||
export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props => {
|
||||
const history = useHistory()
|
||||
const [collapsedSections, setCollapsedSections] = useTemporarySetting('search.collapsedSidebarSections', {})
|
||||
const { query, setQueryState, submitSearch } = useNavbarQueryState(selectFromQueryState, shallow)
|
||||
|
||||
// The zustand store for search query state is referenced through context
|
||||
// because there may be different global stores across clients
|
||||
// (e.g. VS Code extension, web app)
|
||||
const { query, setQueryState, submitSearch } = useSearchQueryStateStoreContext()(selectFromQueryState, shallow)
|
||||
|
||||
// Unlike onFilterClicked, this function will always append or update a filter
|
||||
const submitQueryWithProps = useCallback(
|
||||
@ -139,6 +148,7 @@ export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props
|
||||
onNavbarQueryChange: setQueryState,
|
||||
query,
|
||||
selectedSearchContextSpec: props.selectedSearchContextSpec,
|
||||
buildSearchURLQueryFromQueryState: props.buildSearchURLQueryFromQueryState,
|
||||
})}
|
||||
</SearchSidebarSection>
|
||||
<SearchSidebarSection
|
||||
@ -168,7 +178,7 @@ export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props
|
||||
{repoFilterLinks}
|
||||
</SearchSidebarSection>
|
||||
) : null}
|
||||
{repoName ? (
|
||||
{props.getRevisions && repoName ? (
|
||||
<SearchSidebarSection
|
||||
sectionId={SectionID.REVISIONS}
|
||||
className={styles.searchSidebarItem}
|
||||
@ -178,7 +188,7 @@ export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props
|
||||
showSearch={true}
|
||||
clearSearchOnChange={repoName}
|
||||
>
|
||||
{getRevisions({ repoName, onFilterClick: submitQueryWithProps })}
|
||||
{props.getRevisions({ repoName, onFilterClick: submitQueryWithProps })}
|
||||
</SearchSidebarSection>
|
||||
) : null}
|
||||
<SearchSidebarSection
|
||||
@ -221,7 +231,7 @@ export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.searchSidebar, props.className)}>
|
||||
{props.showOnboardingTour && <OnboardingTour className="mb-1" telemetryService={props.telemetryService} />}
|
||||
{props.prefixContent}
|
||||
{body}
|
||||
</div>
|
||||
)
|
||||
@ -1,22 +1,26 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { ReactElement, useCallback } from 'react'
|
||||
|
||||
import {
|
||||
BuildSearchQueryURLParameters,
|
||||
QueryChangeSource,
|
||||
QueryState,
|
||||
SearchContextProps,
|
||||
createQueryExampleFromString,
|
||||
updateQueryWithFilterAndExample,
|
||||
} from '@sourcegraph/search'
|
||||
import { FilterType } from '@sourcegraph/shared/src/search/query/filters'
|
||||
import { updateFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { containsLiteralOrPattern } from '@sourcegraph/shared/src/search/query/validate'
|
||||
import { SearchType } from '@sourcegraph/shared/src/search/stream'
|
||||
import { Button, Link } from '@sourcegraph/wildcard'
|
||||
|
||||
import { SearchContextProps } from '../..'
|
||||
import { buildSearchURLQueryFromQueryState } from '../../../stores'
|
||||
import { QueryChangeSource, QueryState } from '../../helpers'
|
||||
import { createQueryExampleFromString, updateQueryWithFilterAndExample } from '../../helpers/queryExample'
|
||||
import { SearchType } from '../StreamingSearchResults'
|
||||
|
||||
import styles from './SearchSidebarSection.module.scss'
|
||||
|
||||
export interface SearchTypeLinksProps extends Pick<SearchContextProps, 'selectedSearchContextSpec'> {
|
||||
query: string
|
||||
onNavbarQueryChange: (queryState: QueryState) => void
|
||||
buildSearchURLQueryFromQueryState: (queryParameters: BuildSearchQueryURLParameters) => string
|
||||
}
|
||||
|
||||
interface SearchTypeLinkProps extends SearchTypeLinksProps {
|
||||
@ -33,6 +37,7 @@ const SearchTypeLink: React.FunctionComponent<SearchTypeLinkProps> = ({
|
||||
query,
|
||||
selectedSearchContextSpec,
|
||||
children,
|
||||
buildSearchURLQueryFromQueryState,
|
||||
}) => {
|
||||
const builtURLQuery = buildSearchURLQueryFromQueryState({
|
||||
query: updateFilter(query, FilterType.type, type as string),
|
||||
16
client/search-ui/src/results/sidebar/revisions.ts
Normal file
16
client/search-ui/src/results/sidebar/revisions.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { QueryUpdate } from '@sourcegraph/search'
|
||||
|
||||
export enum TabIndex {
|
||||
BRANCHES,
|
||||
TAGS,
|
||||
}
|
||||
|
||||
export interface RevisionsProps {
|
||||
repoName: string
|
||||
onFilterClick: (updates: QueryUpdate[]) => void
|
||||
query: string
|
||||
/**
|
||||
* This property is only exposed for storybook tests.
|
||||
*/
|
||||
_initialTab?: TabIndex
|
||||
}
|
||||
20
client/search-ui/tsconfig.json
Normal file
20
client/search-ui/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"sourceRoot": "src",
|
||||
"rootDir": ".",
|
||||
"outDir": "./out",
|
||||
"baseUrl": "./src",
|
||||
"jsx": "react",
|
||||
},
|
||||
"include": ["./src/**/*", "./*.ts"],
|
||||
"exclude": ["../../node_modules", "./node_modules", "./out"],
|
||||
"references": [
|
||||
{ "path": "../search" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../branded" },
|
||||
{ "path": "../http-client" },
|
||||
{ "path": "../common" },
|
||||
],
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user