Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(3000): help & support panel #20710

Merged
merged 52 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
c82fe79
html for new support pane
corywatilo Feb 23, 2024
9b21270
add algolia search component
smallbrownbike Feb 23, 2024
e5210f8
polish
corywatilo Feb 23, 2024
24ef192
docs are not old anymore
corywatilo Feb 23, 2024
3cfe040
ux improvements
smallbrownbike Feb 24, 2024
26af264
merge conflicts
smallbrownbike Feb 24, 2024
3986b0d
add filters
smallbrownbike Feb 24, 2024
5326050
reset active option
smallbrownbike Feb 24, 2024
48fa591
styles
smallbrownbike Feb 24, 2024
ebaf121
email an engineer button
smallbrownbike Feb 25, 2024
6a1e70d
fix panel title
smallbrownbike Feb 25, 2024
cdbd1bf
add checkmark to resolved questions
smallbrownbike Feb 25, 2024
4a99478
add result count to tags / allow tabbing through tags
smallbrownbike Feb 25, 2024
86b2cbf
Merge branch 'feat/help-support-panel' of https://github.com/PostHog/…
corywatilo Feb 26, 2024
37015ad
polish
corywatilo Feb 26, 2024
34564bd
docs links
corywatilo Feb 26, 2024
5603a65
tooltips
smallbrownbike Feb 26, 2024
5f82b32
inline support form
smallbrownbike Feb 26, 2024
13ebec5
remove unnecessary effect
smallbrownbike Feb 26, 2024
66ffdae
use correct color in tooltip
corywatilo Feb 26, 2024
42cb602
Revert "tooltips"
smallbrownbike Feb 26, 2024
ccd5dc9
conflicts
smallbrownbike Feb 26, 2024
de0a3c3
Merge branch 'master' into feat/help-support-panel
raquelmsmith Mar 8, 2024
ac31c75
hook it up
raquelmsmith Mar 8, 2024
daeff08
fix lockfile
raquelmsmith Mar 8, 2024
06342d2
use iconinfo
raquelmsmith Mar 8, 2024
0f3111c
Update UI snapshots for `chromium` (2)
github-actions[bot] Mar 8, 2024
64e2036
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 8, 2024
57ea241
Update UI snapshots for `chromium` (2)
github-actions[bot] Mar 8, 2024
a8b6357
Update UI snapshots for `chromium` (2)
github-actions[bot] Mar 8, 2024
28370c6
Merge branch 'master' into feat/help-support-panel
raquelmsmith Mar 15, 2024
002cd73
reup lockfile
raquelmsmith Mar 15, 2024
bf15e2f
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 15, 2024
8d81ad2
fix
raquelmsmith Mar 15, 2024
e251e60
Merge branch 'feat/help-support-panel' of https://github.com/PostHog/…
raquelmsmith Mar 15, 2024
8ff4c50
use std colors (except for purple)
raquelmsmith Mar 15, 2024
3e97bfb
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 15, 2024
b3123fb
Update UI snapshots for `chromium` (2)
github-actions[bot] Mar 15, 2024
13bad6c
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 15, 2024
3c6c8dc
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 15, 2024
66b1394
Merge branch 'master' into feat/help-support-panel
raquelmsmith Mar 15, 2024
4ca32b0
Update unit.json
raquelmsmith Mar 15, 2024
d212716
Update docker-compose.dev-full.yml
raquelmsmith Mar 15, 2024
4ebbd2c
Update docker-compose.dev-full.yml
raquelmsmith Mar 15, 2024
f6a0ecd
upgrade @babel/runtime
raquelmsmith Mar 15, 2024
fe96f65
Merge branch 'feat/help-support-panel' of https://github.com/PostHog/…
raquelmsmith Mar 15, 2024
ae0452c
move to a regular dep?
raquelmsmith Mar 15, 2024
05bc212
address pr feedback
raquelmsmith Mar 18, 2024
5167566
add stories
raquelmsmith Mar 18, 2024
8e480fa
Merge branch 'master' into feat/help-support-panel
raquelmsmith Mar 18, 2024
7e0da87
handle arrowLeft and ArrowRight keydown
raquelmsmith Mar 18, 2024
1490d03
Update UI snapshots for `chromium` (1)
github-actions[bot] Mar 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

3 changes: 2 additions & 1 deletion docker-compose.dev-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,5 @@ services:
- clickhouse
- kafka
- object_storage
- temporal
- temporal
raquelmsmith marked this conversation as resolved.
Show resolved Hide resolved

Binary file modified frontend/__snapshots__/lemon-ui-colors--color-palette--dark.png
Copy link
Member

@raquelmsmith raquelmsmith Mar 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@corywatilo did you intend to make this color change? It's causing a lot of changes in the snapshots.

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.
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.
261 changes: 261 additions & 0 deletions frontend/src/layout/navigation-3000/components/AlgoliaSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
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 => {
raquelmsmith marked this conversation as resolved.
Show resolved Hide resolved
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': {
e.preventDefault()
const currTagIndex = tags.findIndex(({ type }) => type === activeTag)
setActiveTag(tags[currTagIndex >= tags.length - 1 ? 0 : 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
Expand Up @@ -37,7 +37,7 @@ export const SIDE_PANEL_TABS: Record<
noModalSupport: true,
},
[SidePanelTab.Support]: {
label: 'Support',
label: 'Help & support',
raquelmsmith marked this conversation as resolved.
Show resolved Hide resolved
Icon: IconSupport,
Content: SidePanelSupport,
},
Expand Down
Loading
Loading