Skip to content

Commit

Permalink
fixed search bar layout and ux: can now click on search results on de…
Browse files Browse the repository at this point in the history
…sktop, new layout results in less janky keyboard navigation
  • Loading branch information
adamleithp committed Dec 18, 2024
1 parent 6dca54c commit 55a6b91
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 58 deletions.
4 changes: 2 additions & 2 deletions frontend/src/lib/components/CommandBar/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export const SearchBar = (): JSX.Element => {
const inputRef = useRef<HTMLInputElement>(null)

return (
<div className="flex w-full h-full">
<div className="grid grid-cols-[8.5rem_1fr] lg:grid-cols-[12.5rem_1fr] w-full h-full">
<SearchTabs inputRef={inputRef} />
<div className="grow flex flex-col overscroll-contain overflow-hidden">
<div className="grid grid-rows-[auto_100%] overscroll-contain overflow-hidden">
<SearchInput ref={inputRef} />
<SearchResults />
</div>
Expand Down
52 changes: 31 additions & 21 deletions frontend/src/lib/components/CommandBar/SearchResult.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { LemonSkeleton } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { TAILWIND_BREAKPOINTS } from 'lib/constants'
import { useWindowSize } from 'lib/hooks/useWindowSize'
import { capitalizeFirstLetter } from 'lib/utils'
import { useLayoutEffect, useRef } from 'react'
import { useSummarizeInsight } from 'scenes/insights/summarizeInsight'
Expand All @@ -22,10 +24,12 @@ type SearchResultProps = {

export const SearchResult = ({ result, resultIndex, focused }: SearchResultProps): JSX.Element => {
const { aggregationLabel } = useValues(searchBarLogic)
const { openResult } = useActions(searchBarLogic)
const { setActiveResultIndex, openResult } = useActions(searchBarLogic)

const ref = useRef<HTMLDivElement | null>(null)

const { width } = useWindowSize()

useLayoutEffect(() => {
if (focused) {
// :HACKY: This uses the non-standard scrollIntoViewIfNeeded api
Expand All @@ -40,27 +44,33 @@ export const SearchResult = ({ result, resultIndex, focused }: SearchResultProps
}, [focused])

return (
<div
className={clsx(
'w-full px-2 hover:bg-bg-3000 border-l-4 border-r border-b cursor-pointer',
focused ? 'bg-bg-3000 border-l-primary-3000' : 'bg-bg-light'
)}
onClick={() => {
openResult(resultIndex)
}}
ref={ref}
>
<div className="px-2 py-3 w-full space-y-0.5 flex flex-col items-start">
<span className="text-muted-3000 text-xs">
{result.type !== 'group'
? tabToName[result.type]
: `${capitalizeFirstLetter(aggregationLabel(result.extra_fields.group_type_index).plural)}`}
</span>
<span className="text-text-3000 font-bold">
<ResultName result={result} />
</span>
<>
<div
className={clsx(
'w-full px-2 hover:bg-bg-3000 border-l-4 border-b cursor-pointer',
focused ? 'bg-bg-3000 border-l-primary-3000' : 'bg-bg-light'
)}
onClick={() => {
if (width && width <= TAILWIND_BREAKPOINTS.md) {
openResult(resultIndex)
} else {
setActiveResultIndex(resultIndex)
}
}}
ref={ref}
>
<div className="px-2 py-3 w-full space-y-0.5 flex flex-col items-start">
<span className="text-muted-3000 text-xs">
{result.type !== 'group'
? tabToName[result.type]
: `${capitalizeFirstLetter(aggregationLabel(result.extra_fields.group_type_index).plural)}`}
</span>
<span className="text-text-3000 font-bold">
<ResultName result={result} />
</span>
</div>
</div>
</div>
</>
)
}

Expand Down
52 changes: 40 additions & 12 deletions frontend/src/lib/components/CommandBar/SearchResultPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { useValues } from 'kea'
import { useActions, useValues } from 'kea'
import { ResultDescription, ResultName } from 'lib/components/CommandBar/SearchResult'
import { LemonButton } from 'lib/lemon-ui/LemonButton'

import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut'

import { tabToName } from './constants'
import { searchBarLogic, urlForResult } from './searchBarLogic'

export const SearchResultPreview = (): JSX.Element | null => {
const { activeResultIndex, combinedSearchResults } = useValues(searchBarLogic)
const { openResult } = useActions(searchBarLogic)

if (!combinedSearchResults || combinedSearchResults.length === 0) {
return null
Expand All @@ -14,17 +18,41 @@ export const SearchResultPreview = (): JSX.Element | null => {
const result = combinedSearchResults[activeResultIndex]

return (
<div className="border bg-bg-light rounded p-6">
<div>{tabToName[result.type]}</div>
<div className="text-text-3000 font-bold text-lg">
<ResultName result={result} />
</div>
<span className="text-[var(--trace-3000)] text-xs">
{location.host}
<span className="text-muted-3000">{urlForResult(result)}</span>
</span>
<div className="mt-2 text-muted">
<ResultDescription result={result} />
<div className="border bg-bg-light rounded p-4 md:p-6">
<div className="space-y-4">
<div>
<div>{tabToName[result.type as keyof typeof tabToName]}</div>
<div className="text-text-3000 font-bold text-lg">
<ResultName result={result} />
</div>
<span className="text-[var(--trace-3000)] text-xs break-all">
{location.host}
<span className="text-muted-3000">{urlForResult(result)}</span>
</span>
<div className="mt-2 text-muted">
<ResultDescription result={result} />
</div>
</div>
<div className="grid grid-cols-[auto_1fr] items-center gap-2">
<LemonButton
type="secondary"
size="small"
onClick={() => {
openResult(activeResultIndex)
}}
tooltip={
<>
Open <KeyboardShortcut enter />
</>
}
aria-label="Open search result"
>
Open
</LemonButton>
<div>
<KeyboardShortcut enter /> Open
</div>
</div>
</div>
</div>
)
Expand Down
28 changes: 7 additions & 21 deletions frontend/src/lib/components/CommandBar/SearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import clsx from 'clsx'
import { useValues } from 'kea'
import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver'

import { DetectiveHog } from '../hedgehogs'
import { searchBarLogic } from './searchBarLogic'
Expand All @@ -10,27 +8,17 @@ import { SearchResultPreview } from './SearchResultPreview'
export const SearchResults = (): JSX.Element => {
const { combinedSearchResults, combinedSearchLoading, activeResultIndex } = useValues(searchBarLogic)

const { ref, size } = useResizeBreakpoints({
0: 'small',
550: 'normal',
})

return (
<div className="SearchResults grow" ref={ref}>
<div className="SearchResults grow">
{!combinedSearchLoading && combinedSearchResults?.length === 0 ? (
<div className="w-full h-full flex flex-col items-center justify-center p-3">
<div className="w-full h-full flex flex-col items-center justify-center p-3 text-center">
<h3 className="mb-0 text-xl">No results</h3>
<p className="opacity-75 mb-0">This doesn't happen often, but we're stumped!</p>
<DetectiveHog height={150} width={150} />
</div>
) : (
<div className="overflow-hidden overscroll-contain flex h-full">
<div
className={clsx(
'border-r bg-bg-3000 overscroll-contain overflow-y-scroll grow-0 shrink-0 w-full',
size !== 'small' && 'max-w-80'
)}
>
<div className="md:grid md:grid-rows-none md:grid-cols-[320px_1fr] overflow-auto md:overflow-hidden overscroll-contain h-full">
<div className="border-r border-b md:border-b-0 bg-bg-3000 overscroll-contain overflow-y-auto w-full">
{combinedSearchLoading && (
<>
<SearchResultSkeleton />
Expand All @@ -48,11 +36,9 @@ export const SearchResults = (): JSX.Element => {
/>
))}
</div>
{size !== 'small' ? (
<div className="p-2 grow">
<SearchResultPreview />
</div>
) : null}
<div className="p-2 grow hidden md:block overflow-auto">
<SearchResultPreview />
</div>
</div>
)}
</div>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/lib/components/CommandBar/SearchTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ type SearchTabsProps = {
export const SearchTabs = ({ inputRef }: SearchTabsProps): JSX.Element | null => {
const { tabsGrouped } = useValues(searchBarLogic)
return (
<div className="flex flex-col border-r bg-bg-light min-w-30 w-1/3 max-w-50 flex-0 overflow-y-auto">
<div className="flex flex-col border-r bg-bg-light overflow-y-auto">
{Object.entries(tabsGrouped).map(([group, tabs]) => (
<div key={group} className={group !== 'all' ? 'pt-1.5' : ''}>
{group !== 'all' && (
<span className="ml-4 text-xxs text-muted uppercase">{groupToName[group]}</span>
<span className="ml-4 text-xxs text-muted uppercase">
{groupToName[group as keyof typeof groupToName]}
</span>
)}
{tabs.map((tab) => (
<SearchBarTab key={tab} tab={tab} inputRef={inputRef} />
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/CommandBar/searchBarLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const searchBarLogic = kea<searchBarLogicType>([
onArrowUp: (activeIndex: number, maxIndex: number) => ({ activeIndex, maxIndex }),
onArrowDown: (activeIndex: number, maxIndex: number) => ({ activeIndex, maxIndex }),
openResult: (index: number) => ({ index }),
setActiveResultIndex: (index: number) => ({ index }),
}),
loaders(({ values, actions }) => ({
rawSearchResponse: [
Expand Down Expand Up @@ -208,6 +209,7 @@ export const searchBarLogic = kea<searchBarLogicType>([
openResult: () => 0,
onArrowUp: (_, { activeIndex, maxIndex }) => (activeIndex > 0 ? activeIndex - 1 : maxIndex),
onArrowDown: (_, { activeIndex, maxIndex }) => (activeIndex < maxIndex ? activeIndex + 1 : 0),
setActiveResultIndex: (_, { index }) => index,
},
],
activeTab: [
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,11 @@ export const SESSION_REPLAY_MINIMUM_DURATION_OPTIONS: LemonSelectOptions<number
]

export const UNSUBSCRIBE_SURVEY_ID = '018b6e13-590c-0000-decb-c727a2b3f462'

export const TAILWIND_BREAKPOINTS = {
sm: 526,
md: 768,
lg: 992,
xl: 1200,
'2xl': 1600,
}
6 changes: 6 additions & 0 deletions frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@
&.LemonInput--type-search {
// NOTE Design: Search inputs are given a specific small width
max-width: 240px;
border-radius: 0;

// Add a focus ring to the element (not the input) when focused
&:has(:focus-visible) {
outline: -webkit-focus-ring-color auto 1px;
}
}

&.LemonInput--type-number {
Expand Down

0 comments on commit 55a6b91

Please sign in to comment.