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

Commit

Permalink
Search Filters: Add apply filters button to the new search filters (#…
Browse files Browse the repository at this point in the history
…59764)

* Add apply filters button to the new search filters

* Fix spacing, wording and button variation

* Add tooltip and icons to the filters action buttons

* Use smart concatenation for merging query and filters

* Fix eslint
  • Loading branch information
vovakulikov authored Jan 24, 2024
1 parent effc0aa commit 39ea8a0
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.scroll-wrapper {
position: relative;
height: 100%;
overflow: auto;
display: flex;
Expand Down Expand Up @@ -40,6 +41,32 @@
}
}

.footer {
.footer-content {
margin-top: auto;
display: flex;
flex-direction: column;
gap: 0.5rem;
position: sticky;
bottom: 0;
background-color: var(--code-bg);
border-top: 1px solid var(--border-color);
}

.actions {
margin: 0.75rem 0.5rem 0 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;

&:empty {
display: none;
}
}

.move-icon {
--icon-color: var(--body-color);

margin-left: 0.25rem;
fill: none !important;
transform: rotateX(-180deg);
}
30 changes: 26 additions & 4 deletions client/branded/src/search-ui/results/filters/NewSearchFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { scanSearchQuery, succeedScan } from '@sourcegraph/shared/src/search/que
import type { Filter as QueryFilter } from '@sourcegraph/shared/src/search/query/token'
import { omitFilter, updateFilter } from '@sourcegraph/shared/src/search/query/transformer'
import type { Filter } from '@sourcegraph/shared/src/search/stream'
import { Button, Icon, Tooltip } from '@sourcegraph/wildcard'

import {
authorFilter,
Expand All @@ -22,7 +23,8 @@ import {
toSearchSyntaxTypeFilter,
} from './components/filter-type-list/FilterTypeList'
import { FiltersDocFooter } from './components/filters-doc-footer/FiltersDocFooter'
import { useUrlFilters } from './hooks'
import { ArrowBendIcon } from './components/Icons'
import { mergeQueryAndFilters, useUrlFilters } from './hooks'
import { SearchFilterType } from './types'

import styles from './NewSearchFilters.module.scss'
Expand Down Expand Up @@ -72,6 +74,10 @@ export const NewSearchFilters: FC<NewSearchFiltersProps> = ({ query, filters, on
}
}

const handleApplyButtonFilters = (): void => {
onQueryChange(mergeQueryAndFilters(query, selectedFilters))
}

return (
<div className={styles.scrollWrapper}>
<FilterTypeList value={type} onSelect={handleFilterTypeChange} />
Expand Down Expand Up @@ -144,9 +150,25 @@ export const NewSearchFilters: FC<NewSearchFiltersProps> = ({ query, filters, on
onSelectedFilterChange={setSelectedFilters}
/>

<FiltersDocFooter className={styles.footer} />

{children}
<div className={styles.footerContent}>
<footer className={styles.actions}>
{selectedFilters.length > 0 && (
<Tooltip
placement="right"
content="Moves all your applied filters from this panel into the query bar at the top and resets selected options from this panel."
>
<Button variant="secondary" outline={true} onClick={handleApplyButtonFilters}>
Move filters to the query
<Icon as={ArrowBendIcon} aria-hidden={true} className={styles.moveIcon} />
</Button>
</Tooltip>
)}

{children}
</footer>

<FiltersDocFooter />
</div>
</div>
)
}
40 changes: 40 additions & 0 deletions client/branded/src/search-ui/results/filters/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,43 @@ export const LinkShareIcon: FC<CustomIconProps> = props => (
/>
</svg>
)

export const ArrowBendIcon: FC<CustomIconProps> = props => (
<svg viewBox="0 0 14 14" height="14" width="14" fill="none" {...props}>
<path
id="Vector"
stroke="var(--icon-color)"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
d="m11.5 10.5 -3 3 -3 -3"
/>
<path
id="Vector_2"
stroke="var(--icon-color)"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
d="M2.5 0.5h2c1.06087 0 2.07828 0.421427 2.82843 1.17157C8.07857 2.42172 8.5 3.43913 8.5 4.5v9"
/>
</svg>
)

export const DeleteIcon: FC<CustomIconProps> = props => (
<svg fill="none" viewBox="0 0 14 14" height="14" width="14" {...props}>
<path
stroke="var(--icon-color)"
strokeLinecap="round"
strokeLinejoin="round"
d="m13.5 0.5 -13 13"
strokeWidth="1"
/>
<path
stroke="var(--icon-color)"
strokeLinecap="round"
strokeLinejoin="round"
d="m0.5 0.5 13 13"
strokeWidth="1"
/>
</svg>
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.footer {
padding: 1rem 0.75rem;
border-top: 1px solid var(--border-color);
}

.link {
Expand Down
24 changes: 24 additions & 0 deletions client/branded/src/search-ui/results/filters/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { scanSearchQuery } from '@sourcegraph/shared/src/search/query/scanner'
import { Keyword } from '@sourcegraph/shared/src/search/query/token'
import { Filter } from '@sourcegraph/shared/src/search/stream'
import { useSyncedWithURLState } from '@sourcegraph/wildcard'

Expand Down Expand Up @@ -28,3 +30,25 @@ export function useUrlFilters(): [URLQueryFilter[], (newFilters: URLQueryFilter[

return [filterQuery, setFilterQuery]
}

export function mergeQueryAndFilters(query: string, filters: URLQueryFilter[]): string {
const tokens = scanSearchQuery(query)

// Return original query if it's non-valid query
if (tokens.type === 'error') {
return query
}

const filterQuery = filters.map(f => f.value).join(' ')
const keywords = tokens.term.filter(token => token.type === 'keyword') as Keyword[]
const hasAnd = keywords.some(filter => filter.kind === 'and')
const hasOr = keywords.some(filter => filter.kind === 'or')

// Wrap original query with parenthesize if the query has 'or' or 'and'
// operators, otherwise simple concatenation may not work for all cases.
if (hasOr || hasAnd) {
return `(${query}) ${filterQuery}`.trim()
}

return `${query} ${filterQuery}`.trim()
}
6 changes: 3 additions & 3 deletions client/web/src/search/results/SearchResultsCacheProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useNavigationType, useLocation } from 'react-router-dom'
import { merge, of } from 'rxjs'
import { last, share, tap, throttleTime } from 'rxjs/operators'

import { URLQueryFilter, serializeURLQueryFilters } from '@sourcegraph/branded'
import { URLQueryFilter, serializeURLQueryFilters, mergeQueryAndFilters } from '@sourcegraph/branded'
import type { AggregateStreamingSearchResults, StreamSearchOptions } from '@sourcegraph/shared/src/search/stream'
import type { TelemetryService } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { useObservable } from '@sourcegraph/wildcard'
Expand Down Expand Up @@ -87,8 +87,8 @@ export function useCachedSearchResults(props: CachedSearchResultsInput): Aggrega
cachedResults.current = { query, options, cache: { ...previousCache, [filterCacheKey]: results } }
}

const filterQuery = selectedFilters.map(f => f.value).join(' ')
const stream = streamSearch(of(`${query} ${filterQuery}`.trim()), options).pipe(share())
const extendQueryWithFilters = mergeQueryAndFilters(query, selectedFilters)
const stream = streamSearch(of(extendQueryWithFilters), options).pipe(share())

// If the throttleTime option `trailing` is set, we will return the
// final value, but it also removes the guarantee that the output events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
border-bottom-left-radius: 0;
}

.close-filters {
position: sticky;
bottom: 0.5rem;
margin: 0 0.5rem 0.5rem 0.5rem;
.close-icon {
--icon-color: var(--body-color);

fill: none !important;
width: 0.75rem !important;
margin-right: 0.25rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { FC } from 'react'
import create from 'zustand'

import { NewSearchFilters, useUrlFilters } from '@sourcegraph/branded'
import { DeleteIcon } from '@sourcegraph/branded/src/search-ui/results/filters/components/Icons'
import { Filter } from '@sourcegraph/shared/src/search/stream'
import { Badge, Button, Modal, Panel, useWindowSize } from '@sourcegraph/wildcard'
import { Badge, Button, Icon, Modal, Panel, useWindowSize } from '@sourcegraph/wildcard'

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

Expand Down Expand Up @@ -62,8 +63,9 @@ export const SearchFiltersPanel: FC<SearchFiltersPanelProps> = props => {
onDismiss={() => setFiltersPanel(false)}
>
<NewSearchFilters query={query} filters={filters} onQueryChange={onQueryChange}>
<Button variant="primary" className={styles.closeFilters} onClick={() => setFiltersPanel(false)}>
Close filters panel
<Button variant="secondary" outline={true} onClick={() => setFiltersPanel(false)}>
<Icon as={DeleteIcon} width={14} height={14} aria-hidden={true} className={styles.closeIcon} />{' '}
Close filters
</Button>
</NewSearchFilters>
</Modal>
Expand Down

0 comments on commit 39ea8a0

Please sign in to comment.