Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
svelte: Refactor search results page (#59029)
Browse files Browse the repository at this point in the history
My main goal was to add an empty state for the search results page, but I took the opportunity to adopt/test some of Taiyab's ideas for the search results page (left sidebar with type filters, more condense results display). It's only a crude approximation since his work is still in flux. It wasn't the goal to strictly follow the design.
  • Loading branch information
fkling authored Dec 15, 2023
1 parent 9d77d36 commit 474813e
Show file tree
Hide file tree
Showing 25 changed files with 317 additions and 263 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,13 @@ import { VisuallyHidden } from '@reach/visually-hidden'
import classNames from 'classnames'

import { pluralize } from '@sourcegraph/common'
import type { Progress } from '@sourcegraph/shared/src/search/stream'
import { Icon, Tooltip } from '@sourcegraph/wildcard'

import type { StreamingProgressProps } from './StreamingProgress'
import { limitHit } from './utils'
import { abbreviateNumber, getProgressText } from './utils'

import styles from './StreamingProgressCount.module.scss'

const abbreviateNumber = (number: number): string => {
if (number < 1e3) {
return number.toString()
}
if (number >= 1e3 && number < 1e6) {
return (number / 1e3).toFixed(1) + 'k'
}
if (number >= 1e6 && number < 1e9) {
return (number / 1e6).toFixed(1) + 'm'
}
return (number / 1e9).toFixed(1) + 'b'
}

export const getProgressText = (progress: Progress): { visibleText: string; readText: string } => {
const contentWithoutTimeUnit =
`${abbreviateNumber(progress.matchCount)}` +
`${limitHit(progress) ? '+' : ''} ${pluralize('result', progress.matchCount)} in ` +
`${(progress.durationMs / 1000).toFixed(2)}`
const visibleText = `${contentWithoutTimeUnit}s`
const readText = `${contentWithoutTimeUnit} seconds`
return { visibleText, readText }
}

export const StreamingProgressCount: React.FunctionComponent<
React.PropsWithChildren<
Pick<StreamingProgressProps, 'progress' | 'state'> & { className?: string; hideIcon?: boolean }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import type { Progress } from '@sourcegraph/shared/src/search/stream'
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { Button, Popover, PopoverContent, PopoverTrigger, Position, Icon } from '@sourcegraph/wildcard'

import { CountContent, getProgressText } from './StreamingProgressCount'
import { CountContent } from './StreamingProgressCount'
import { StreamingProgressSkippedPopover } from './StreamingProgressSkippedPopover'
import { getProgressText } from './utils'

import styles from './StreamingProgressSkippedButton.module.scss'

Expand Down
24 changes: 24 additions & 0 deletions client/branded/src/search-ui/results/progress/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
import { pluralize } from '@sourcegraph/common'
import type { Progress, Skipped } from '@sourcegraph/shared/src/search/stream'

export const abbreviateNumber = (number: number): string => {
if (number < 1e3) {
return number.toString()
}
if (number >= 1e3 && number < 1e6) {
return (number / 1e3).toFixed(1) + 'k'
}
if (number >= 1e6 && number < 1e9) {
return (number / 1e6).toFixed(1) + 'm'
}
return (number / 1e9).toFixed(1) + 'b'
}

export const getProgressText = (progress: Progress): { visibleText: string; readText: string } => {
const contentWithoutTimeUnit =
`${abbreviateNumber(progress.matchCount)}` +
`${limitHit(progress) ? '+' : ''} ${pluralize('result', progress.matchCount)} in ` +
`${(progress.durationMs / 1000).toFixed(2)}`
const visibleText = `${contentWithoutTimeUnit}s`
const readText = `${contentWithoutTimeUnit} seconds`
return { visibleText, readText }
}

export const limitHit = (progress: Progress): boolean =>
progress.skipped.some(skipped => skipped.reason.indexOf('-limit') > 0)

Expand Down
1 change: 1 addition & 0 deletions client/web-sveltekit/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ yarn.lock
*.gql.d.ts
src/lib/graphql-operations.ts
src/lib/graphql-types.ts
static/mockServiceWorker.js
2 changes: 2 additions & 0 deletions client/web-sveltekit/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module.exports = {
extends: ['../../.eslintrc.js', 'plugin:storybook/recommended'],
parserOptions: {
...baseConfig.parserOptions,
// This is set in the root config but doesn't work with Svelte components
EXPERIMENTAL_useProjectService: false,
project: [__dirname + '/tsconfig.json', __dirname + '/src/**/tsconfig.json'],
},
plugins: [...baseConfig.plugins, 'svelte3'],
Expand Down
2 changes: 1 addition & 1 deletion client/web-sveltekit/src/lib/branded.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { formatRepositoryStarCount } from '@sourcegraph/branded/src/search-ui/util/stars'
export { limitHit, sortBySeverity } from '@sourcegraph/branded/src/search-ui/results/progress/utils'
export { limitHit, sortBySeverity, getProgressText } from '@sourcegraph/branded/src/search-ui/results/progress/utils'
export { createDefaultSuggestions } from '@sourcegraph/branded/src/search-ui/input/codemirror'
export { parseInputAsQuery } from '@sourcegraph/branded/src/search-ui/input/codemirror/parsedQuery'
export { querySyntaxHighlighting } from '@sourcegraph/branded/src/search-ui/input/codemirror/syntax-highlighting'
Expand Down
1 change: 0 additions & 1 deletion client/web-sveltekit/src/lib/repo/api/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export interface ResolvedRevision extends ResolvedRevisionSpec {

/**
* When `revision` is undefined, the default branch is resolved.
*
* @returns Promise that emits the commit ID. Errors with a `CloneInProgressError` if the repo is still being cloned.
*/
export async function resolveRepoRevision({
Expand Down
2 changes: 1 addition & 1 deletion client/web-sveltekit/src/lib/repo/blob.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { EditorView } from '@codemirror/view'
import { from, Subscription } from 'rxjs'
import { from, type Subscription } from 'rxjs'
import { switchMap, map, startWith } from 'rxjs/operators'
import { get, writable, type Readable, readonly } from 'svelte/store'

Expand Down
Empty file.
69 changes: 0 additions & 69 deletions client/web-sveltekit/src/lib/search/QueryExampleChip.svelte

This file was deleted.

77 changes: 61 additions & 16 deletions client/web-sveltekit/src/lib/search/sidebar.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,71 @@
import type { SidebarFilter } from './utils'
import {
mdiCodeTags,
mdiFileCodeOutline,
mdiPlusMinus,
mdiShapeOutline,
mdiSourceCommit,
mdiSourceRepository,
} from '@mdi/js'

export const searchTypes: SidebarFilter[] = [
import { FilterKind, FilterType, appendFilter, findFilter, updateFilter } from '$lib/shared'

function addOrUpdateTypeFilter(value: string): (query: string) => string {
return query => {
try {
return updateFilter(query, FilterType.type, value)
} catch {
// Ignore error
}
return appendFilter(query, FilterType.type, value)
}
}

function isSelectedTypeFilter(value: string): (query: string) => boolean {
return query => findFilter(query, FilterType.type, FilterKind.Global)?.value?.value === value
}

interface ResultTypeFilter {
label: string
icon: string
getQuery: (query: string) => string
isSelected: (query: string) => boolean
}

export const resultTypeFilter: ResultTypeFilter[] = [
{
label: 'Code',
icon: mdiCodeTags,
getQuery: addOrUpdateTypeFilter('file'),
isSelected: isSelectedTypeFilter('file'),
},
{
label: 'Repositories',
icon: mdiSourceRepository,
getQuery: addOrUpdateTypeFilter('repo'),
isSelected: isSelectedTypeFilter('repo'),
},
{
label: 'Search repos by org or name',
value: 'repo:',
kind: 'utility',
label: 'Paths',
icon: mdiFileCodeOutline,
getQuery: addOrUpdateTypeFilter('path'),
isSelected: isSelectedTypeFilter('path'),
},
{
label: 'Find a symbol',
value: 'type:symbol',
kind: 'utility',
runImmediately: true,
label: 'Symbols',
icon: mdiShapeOutline,
getQuery: addOrUpdateTypeFilter('symbol'),
isSelected: isSelectedTypeFilter('symbol'),
},
{
label: 'Search diffs',
value: 'type:diff',
kind: 'utility',
runImmediately: true,
label: 'Commits',
icon: mdiSourceCommit,
getQuery: addOrUpdateTypeFilter('commit'),
isSelected: isSelectedTypeFilter('commit'),
},
{
label: 'Search commit message',
value: 'type:commit',
kind: 'utility',
label: 'Diffs',
icon: mdiPlusMinus,
getQuery: addOrUpdateTypeFilter('diff'),
isSelected: isSelectedTypeFilter('diff'),
},
]
14 changes: 9 additions & 5 deletions client/web-sveltekit/src/lib/search/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ export function queryStateStore(initial: Partial<Options> = {}, settings: QueryS
}
}

export function submitSearch(
export function getQueryURL(
queryState: Pick<QueryState, 'searchMode' | 'query' | 'caseSensitive' | 'patternType' | 'searchContext'>
): void {
): string {
const searchQueryParameter = buildSearchURLQuery(
queryState.query,
queryState.patternType,
Expand All @@ -150,7 +150,11 @@ export function submitSearch(
queryState.searchMode
)

// no-void conflicts with no-floating-promises
// eslint-disable-next-line no-void
void goto('/search?' + searchQueryParameter)
return 'search?' + searchQueryParameter
}

export function submitSearch(
queryState: Pick<QueryState, 'searchMode' | 'query' | 'caseSensitive' | 'patternType' | 'searchContext'>
): void {
void goto(getQueryURL(queryState))
}
2 changes: 1 addition & 1 deletion client/web-sveltekit/src/lib/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export { type AuthenticatedUser, currentAuthStateQuery } from '@sourcegraph/shar
export { filterExists } from '@sourcegraph/shared/src/search/query/validate'
export { FilterType } from '@sourcegraph/shared/src/search/query/filters'
export { getGlobalSearchContextFilter, findFilter, FilterKind } from '@sourcegraph/shared/src/search/query/query'
export { omitFilter, appendFilter } from '@sourcegraph/shared/src/search/query/transformer'
export { omitFilter, appendFilter, updateFilter } from '@sourcegraph/shared/src/search/query/transformer'
export {
type SettingsCascade,
type SettingsSubject,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
import { getFileMatchUrl, type ContentMatch, ZoektRanking, LineRanking } from '$lib/shared'
import SearchResult from './SearchResult.svelte'
import { getSearchResultsContext } from './SearchResults.svelte'
import { getSearchResultsContext } from './searchResultsContext'
import CodeHostIcon from './CodeHostIcon.svelte'
import RepoStars from './RepoStars.svelte'
import { settings } from '$lib/stores'
import { rankContentMatch } from '$lib/search/results'
import FileSearchResultHeader from './FileSearchResultHeader.svelte'
import { fetchFileRangeMatches } from '$lib/search/api/highlighting'
import CodeExcerpt from './CodeExcerpt.svelte'
import CodeExcerpt from '$lib/search/CodeExcerpt.svelte'
export let result: ContentMatch
Expand Down Expand Up @@ -107,20 +107,20 @@
</a>
</div>
{/each}
{#if collapsible}
<button
type="button"
on:click={() => {
expanded = !expanded
userInteracted = true
}}
class:expanded
>
<Icon svgPath={expanded ? mdiChevronUp : mdiChevronDown} inline aria-hidden="true" />
<span>{expandButtonText}</span>
</button>
{/if}
</div>
{#if collapsible}
<button
type="button"
on:click={() => {
expanded = !expanded
userInteracted = true
}}
class:expanded
>
<Icon svgPath={expanded ? mdiChevronUp : mdiChevronDown} inline aria-hidden="true" />
<span>{expandButtonText}</span>
</button>
{/if}
</SearchResult>

<style lang="scss">
Expand All @@ -129,8 +129,7 @@
text-align: left;
border: none;
padding: 0.25rem 0.5rem;
background-color: var(--border-color);
border-radius: 0 0 var(--border-radius) var(--border-radius);
background-color: var(--code-bg);
color: var(--collapse-results-color);
cursor: pointer;
Expand All @@ -140,15 +139,11 @@
}
}
.matches {
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
background-color: var(--code-bg);
}
.code {
&:not(:first-child) {
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
&:last-child {
border-bottom: none;
}
a {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { mdiAccount } from '@mdi/js'
import SearchResult from './SearchResult.svelte'
import { getSearchResultsContext } from './SearchResults.svelte'
import { getSearchResultsContext } from './searchResultsContext'
import { getOwnerDisplayName, getOwnerMatchURL, buildSearchURLQueryForOwner } from '$lib/search/results'
import UserAvatar from '$lib/UserAvatar.svelte'
import type { PersonMatch } from '$lib/shared'
Expand Down
Loading

0 comments on commit 474813e

Please sign in to comment.