Skip to content

Commit

Permalink
Merge branch 'master' into dw-virtual-data-warehouse-table-breakdowns
Browse files Browse the repository at this point in the history
  • Loading branch information
EDsCODE authored Mar 18, 2024
2 parents 06c8c83 + c8545b9 commit 4f6690a
Show file tree
Hide file tree
Showing 30 changed files with 958 additions and 153 deletions.
4 changes: 0 additions & 4 deletions docker-compose.base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ services:
KAFKA_HOSTS: 'kafka:9092'
REDIS_URL: 'redis://redis:6379/'


plugins:
command: ./bin/plugin-server --no-restart-loop
restart: on-failure
Expand Down Expand Up @@ -152,8 +151,6 @@ services:
volumes:
- /var/lib/elasticsearch/data
temporal:
environment:
- DB=postgresql
- DB_PORT=5432
Expand Down Expand Up @@ -190,4 +187,3 @@ services:
restart: on-failure
environment:
TEMPORAL_HOST: temporal
2 changes: 1 addition & 1 deletion docker-compose.dev-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,4 @@ services:
- clickhouse
- kafka
- object_storage
- temporal
- temporal
Binary file modified frontend/__snapshots__/lemon-ui-colors--color-palette--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-colors--color-palette--light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-lemon-slider--basic--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-lemon-slider--basic--light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/lemon-ui-lemon-tag--breakdown-tag--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
268 changes: 268 additions & 0 deletions frontend/src/layout/navigation-3000/components/AlgoliaSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import { IconCheckCircle } from '@posthog/icons'
import { LemonButton, LemonInput, LemonTag } from '@posthog/lemon-ui'
import algoliasearch from 'algoliasearch/lite'
import { useActions } from 'kea'
import { useEffect, useRef, useState } from 'react'
import { InstantSearch, useHits, useRefinementList, useSearchBox } from 'react-instantsearch'
import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer'
import { List } from 'react-virtualized/dist/es/List'

import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic'
import { SidePanelTab } from '~/types'

const searchClient = algoliasearch('7VNQB5W0TX', '37f41fd37095bc85af76ed4edc85eb5a')

const rowRenderer = ({ key, index, style, hits, activeOption }: any): JSX.Element => {
const { slug, title, type, resolved } = hits[index]
return (
// eslint-disable-next-line react/forbid-dom-props
<li key={key} style={style} role="listitem" tabIndex={-1} className="p-1 border-b last:border-b-0">
<LemonButton
active={activeOption === index}
to={`https://posthog.com/${slug}`}
className="[&_>span>span]:flex-col [&_>span>span]:items-start [&_>span>span]:space-y-1"
>
<span>
<span className="flex space-x-2 items-center">
<p className="m-0 font-bold font-sans line-clamp-1">{title}</p>
{type === 'question' && resolved && (
<IconCheckCircle className="text-success size-4 flex-shrink-0" />
)}
</span>
<p className="text-xs m-0 opacity-80 font-normal font-sans line-clamp-1">/{slug}</p>
</span>
</LemonButton>
</li>
)
}

const Hits = ({ activeOption }: { activeOption?: number }): JSX.Element => {
const { hits } = useHits()
return (
<ol role="listbox" className="list-none m-0 p-0 h-[80vh]">
<AutoSizer>
{({ height, width }: { height: number; width: number }) => (
<List
scrollToIndex={activeOption}
width={width}
height={height}
rowCount={hits.length}
rowHeight={50}
rowRenderer={(options: any) => rowRenderer({ ...options, hits, activeOption })}
/>
)}
</AutoSizer>
</ol>
)
}

const SearchInput = ({
value,
setValue,
}: {
value: string
setValue: React.Dispatch<React.SetStateAction<string>>
}): JSX.Element => {
const { refine } = useSearchBox()

const handleChange = (value: string): void => {
setValue(value)
refine(value)
}

return <LemonInput onChange={handleChange} value={value} type="search" fullWidth placeholder="Search..." />
}

type Tag = {
type: string
label: string
}

const tags: Tag[] = [
{
type: 'all',
label: 'All',
},
{
type: 'docs',
label: 'Docs',
},
{
type: 'question',
label: 'Questions',
},
{
type: 'tutorial',
label: 'Tutorials',
},
]

type SearchTagProps = Tag & {
active?: boolean
onClick: (type: string) => void
}

const SearchTag = ({ type, label, active, onClick }: SearchTagProps): JSX.Element => {
const { refine, items } = useRefinementList({ attribute: 'type' })
const itemCount = type !== 'all' && items.find(({ value }) => value === type)?.count

const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
e.stopPropagation()
onClick(type)
}

useEffect(() => {
refine(type)
}, [])

return (
<button className="p-0 cursor-pointer bg-bg-light" onClick={handleClick}>
<LemonTag size="medium" type={active ? 'primary' : 'option'}>
<span>{label}</span>
{type !== 'all' && <span>({itemCount ?? 0})</span>}
</LemonTag>
</button>
)
}

const Tags = ({
activeTag,
setActiveTag,
}: {
activeTag: string
setActiveTag: React.Dispatch<React.SetStateAction<string>>
}): JSX.Element => {
const handleClick = (type: string): void => {
setActiveTag(type)
}

return (
<ul className="list-none m-0 p-0 flex space-x-1 mt-1 mb-0.5 pb-1.5 border-b px-2">
{tags.map((tag) => {
const { type } = tag
return (
<li key={type}>
<SearchTag {...tag} active={activeTag === type} onClick={handleClick} />
</li>
)
})}
</ul>
)
}

const Search = (): JSX.Element => {
const { openSidePanel } = useActions(sidePanelStateLogic)
const { hits } = useHits()
const { items, refine } = useRefinementList({ attribute: 'type' })

const ref = useRef<HTMLDivElement>(null)
const [searchValue, setSearchValue] = useState<string>('')
const [activeOption, setActiveOption] = useState<undefined | number>()
const [activeTag, setActiveTag] = useState('all')
const [searchOpen, setSearchOpen] = useState(false)

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
switch (e.key) {
case 'Enter': {
if (activeOption !== undefined) {
openSidePanel(SidePanelTab.Docs, `https://posthog.com/${hits[activeOption].slug}`)
}
break
}

case 'Escape': {
setSearchOpen(false)
break
}
case 'ArrowDown': {
e.preventDefault()
setActiveOption((currOption) => {
if (currOption === undefined || currOption >= hits.length - 1) {
return 0
}
return currOption + 1
})
break
}
case 'ArrowUp': {
e.preventDefault()
setActiveOption((currOption) => {
if (currOption !== undefined) {
return currOption <= 0 ? hits.length - 1 : currOption - 1
}
})
break
}
case 'Tab':
case 'ArrowRight': {
e.preventDefault()
const currTagIndex = tags.findIndex(({ type }) => type === activeTag)
setActiveTag(tags[currTagIndex >= tags.length - 1 ? 0 : currTagIndex + 1].type)
break
}
case 'ArrowLeft': {
e.preventDefault()
const currTagIndex = tags.findIndex(({ type }) => type === activeTag)
setActiveTag(tags[currTagIndex <= 0 ? tags.length - 1 : currTagIndex - 1].type)
}
}
}

useEffect(() => {
setSearchOpen(!!searchValue)
setActiveOption(0)
}, [searchValue])

useEffect(() => {
setActiveOption(0)
if (activeTag === 'all') {
const filteredItems = items.filter(({ value }) => tags.some(({ type }) => type === value))
filteredItems.forEach(({ value, isRefined }) => {
if (!isRefined) {
refine(value)
}
})
} else {
items.forEach(({ value, isRefined }) => {
if (isRefined) {
refine(value)
}
})
refine(activeTag)
}
}, [activeTag])

useEffect(() => {
const handleClick = (e: any): void => {
if (!ref?.current?.contains(e.target)) {
setSearchOpen(false)
}
}

window.addEventListener('click', handleClick)

return () => {
window.removeEventListener('click', handleClick)
}
}, [])

return (
<div className="relative" ref={ref} onKeyDown={handleKeyDown}>
<SearchInput value={searchValue} setValue={setSearchValue} />
{searchOpen && (
<div className="absolute w-full bg-bg-light z-50 border rounded-lg shadow-xl mt-0.5">
<Tags activeTag={activeTag} setActiveTag={setActiveTag} />
<Hits activeOption={activeOption} />
</div>
)}
</div>
)
}

export default function AlgoliaSearch(): JSX.Element {
return (
<InstantSearch searchClient={searchClient} indexName="prod_posthog_com">
<Search />
</InstantSearch>
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Meta, StoryFn } from '@storybook/react'
import { useActions } from 'kea'
import { router } from 'kea-router'
import { supportLogic } from 'lib/components/Support/supportLogic'
import { useEffect } from 'react'
import { App } from 'scenes/App'
import { urls } from 'scenes/urls'

import { mswDecorator } from '~/mocks/browser'
import { mswDecorator, useStorybookMocks } from '~/mocks/browser'
import organizationCurrent from '~/mocks/fixtures/api/organizations/@current/@current.json'
import { SidePanelTab } from '~/types'

import { sidePanelStateLogic } from './sidePanelStateLogic'
Expand Down Expand Up @@ -59,3 +61,36 @@ export const SidePanelActivation: StoryFn = () => {
export const SidePanelNotebooks: StoryFn = () => {
return <BaseTemplate panel={SidePanelTab.Notebooks} />
}

export const SidePanelSupportNoEmail: StoryFn = () => {
return <BaseTemplate panel={SidePanelTab.Support} />
}

export const SidePanelSupportWithEmail: StoryFn = () => {
const { openEmailForm } = useActions(supportLogic)
useStorybookMocks({
get: {
// TODO: setting available featues should be a decorator to make this easy
'/api/users/@me': () => [
200,
{
email: '[email protected]',
first_name: 'Test Hedgehog',
organization: {
...organizationCurrent,
available_product_features: [
{
key: 'email_support',
name: 'Email support',
},
],
},
},
],
},
})
useEffect(() => {
openEmailForm()
}, [])
return <BaseTemplate panel={SidePanelTab.Support} />
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const SIDE_PANEL_TABS: Record<
noModalSupport: true,
},
[SidePanelTab.Support]: {
label: 'Support',
label: 'Help',
Icon: IconSupport,
Content: SidePanelSupport,
},
Expand Down
Loading

0 comments on commit 4f6690a

Please sign in to comment.